Rich Text Editor
================

Rich Text Editor is a browser-based WYSIWYG editor with toolbar customization, plugins, localization, image editing, PDF export, and classic HTML APIs.

## What's in v2.2

A full collaborative-document workspace, not just a textarea replacement:

- **AI Toolkit** &mdash; in-editor proofread / rewrite / summarize / translate / chat, with streaming responses, an inline preview drawer, and a persistent accept/reject review ledger. Bring your own provider (OpenAI / Anthropic / Azure OpenAI / Groq / Ollama / custom) via `editor.aiToolkit.setResolver()` or the server-side `IRichTextBoxAiResolver` interface.
- **Per-node Yjs CRDT engine** *(new in 2.2)* &mdash; `editor.collab.attach({ doc, provider, textSync: "crdt" })` activates character-level concurrent typing on the same paragraph with deterministic merge resolution, presence cursors via `Y.RelativePosition`-encoded awareness, and per-author `Y.UndoManager` (Ctrl-Z undoes only your edits). Pairs with any Yjs provider you self-host. Ships in the base license.
- **Slash commands & mentions** &mdash; `/` at the start of a line opens a command palette; `@` and `#` open mention pickers via `editor.slashCommands.register()` and `editor.mentions.register()`.
- **Comments & tracked changes** &mdash; threaded inline comments on text selections and block/media content such as images or tables (`editor.comments.add/reply/resolve`), tracked-changes mode, per-author colors and labels.
- **Comments-only review mode** &mdash; set `commentsOnly: true` or call `editor.setCommentsOnly(true)` to lock document edits while still allowing inline comments and review discussion.
- **Restricted editing templates** &mdash; set `restrictedEditingMode: true` to lock the document except for spans tagged `data-rte-editable="true"`. Mark fillable regions at runtime with `editor.restrictedEditing.markSelection()`; toggle the lock via `editor.restrictedEditing.enable()` / `disable()` / `goToNext()`. Use it for forms, contracts, fillable templates — only the marked regions accept input.
- **Revision history** &mdash; named snapshots, diff view, restore (`editor.revisionHistory.snapshot/diff/restore`).
- **Dictation** &mdash; Web Speech API integration (`editor.dictation.toggle()`) with multi-language support.
- **DOCX import + export** &mdash; import Word documents through `editor.aiToolkit.importDocx({ file?, mode? })` and generate deterministic HTML &rarr; .docx via `editor.aiToolkit.exportDocx({ filename })`; preserves headings, lists, tables, images.
- **Structured content bridge** &mdash; `editor.getJSON()` / `setJSON()`, `fromMarkdown()` / `toMarkdown()`, `renderHTML()`, `validateStructuredContent()` &mdash; for SSR, JSON storage, and Markdown round-trip. The packaged schema now preserves merge fields, ordered-list marker styles (`A` / `a` / `I` / `i`), table-of-contents blocks, footnotes, and editor page breaks instead of collapsing them into opaque HTML.
- **Accessibility audit helper** &mdash; `RichTextEditor.auditAccessibility()` / `auditAccessibility()` catches empty headings, skipped heading levels, missing image alt text, and tables without header cells in browser or Node workflows.
- **In-editor accessibility repair panel** &mdash; the packaged `accessibilitychecker` plugin adds a side panel (`editor.accessibilityChecker.open/close/toggle/run/repair`) with focused fixes for missing image alt text, empty headings, skipped heading levels, and headerless tables. Use `accessibilityCheckerAutoOpen` to keep the workflow visible during review passes.
- **Restricted editing templates** &mdash; the packaged `restrictedediting` plugin lets apps lock the document root while keeping marked islands editable (`editor.restrictedEditing.enable/disable/toggle/list/goToNext/goToPrevious`). Mark reusable fields with `data-rte-editable` in template HTML or call `editor.restrictedEditing.markSelection({ label })` while authoring.
- **Document outline + TOC helpers** &mdash; `RichTextEditor.getDocumentOutline()` / `getDocumentOutline()` extracts a flat heading outline (`id`, `text`, `level`, `path`), while `getTableOfContents()` returns a nested tree and `syncTableOfContents()` repopulates packaged TOC blocks from live headings.
  The same TOC helpers can also persist TinyMCE-style display settings such as `ordered`, `orderedListType`, and `includeTitle` for numbered or titleless navigation blocks.
  TOC items can also expose optional `itemIndex` / `indexLabel` metadata via `indexMode: "linear" | "hierarchical"` so custom sidebars can mirror Tiptap-style numbered navigation without reparsing headings twice.
- **Footnote extraction helper** &mdash; `RichTextEditor.getFootnotes()` / `getFootnotes()` returns sorted `{ id, number, refId, text, path }` metadata from HTML or structured JSON so review sidebars and DOCX import screens can inspect Word-style citations without reparsing the document.
- **Live document outline panel** &mdash; the packaged `documentoutline` plugin adds a toggleable browser-side heading sidebar (`editor.documentOutline.open/close/toggle/list/refresh`) so long documents get CKEditor-style section navigation without custom app code. Use `documentOutlineAutoOpen`, `documentOutlineMinLevel`, and `documentOutlineMaxLevel` to tune the panel.
- **Packaged content minimap** &mdash; the packaged `contentminimap` plugin adds a TinyMCE/CKEditor-style miniature document preview (`editor.contentMinimap.open/close/toggle/refresh/isOpen`) with click-and-drag viewport navigation for long documents. Use `contentMinimapAutoOpen`, `contentMinimapWidth`, and `contentMinimapScale` to tune the panel.
- **Checklist / task-list Markdown** &mdash; `- [x] done` and `- [ ] todo` round-trip through `fromMarkdown()` / `toMarkdown()` and render as disabled checkbox lists in HTML / SSR output.
- **Markdown shortcut helper parity** &mdash; `getMarkdownShortcutAction()` now recognizes headings, quotes, ordered/bullet/task lists, horizontal rules (`---`) and fenced code blocks (````` / `~~~lang`) so custom wrappers can mirror the same typing affordances exposed by current editor competitors.
- **Word + character statistics** &mdash; `editor.getTextStatistics()` / `getSelectionStatistics()` expose document + selection counts, and the built-in bottom bar can show characters, words, or both.
- **Case change commands** &mdash; built-in `ucase`, `lcase`, and `titlecase` commands let toolbars or custom integrations quickly apply Upper Case, Lower Case, or Title Case transforms without leaving the editor.
- **Document metrics helper** &mdash; `RichTextEditor.getDocumentMetrics()` / `getDocumentMetrics()` adds paragraph, sentence, heading, image, table, link, and estimated reading-time counts for analytics and publishing workflows.
- **Document page setup metadata** &mdash; `RichTextEditor.getDocumentPageSetup()` / `setDocumentPageSetup()` lets apps persist page format, margins, orientation, and export header/footer HTML in the structured-content document for print/PDF workflows.
- **Framework wrappers** &mdash; React (`@richscripts/richtexteditor/react`), Vue (`@richscripts/richtexteditor/vue`), Angular (`@richscripts/richtexteditor/angular`), Blazor wrapper, ASP.NET Core tag helper (`<richtextbox>` from the sister NuGet package).
  Framework refs/components also expose the document-workflow helpers needed for outline, TOC, page-setup, metrics, statistics, and accessibility flows without manually round-tripping through `getJSON()`.
- **AI ghost-text autocomplete** *(2.3.0)* &mdash; Copilot / Notion-AI-style inline completion: after a typing pause a greyed suggestion appears after the caret continuing your sentence; **Tab** accepts, **Esc** / typing dismisses. Bring-your-own resolver (`config.ghostTextResolver({ before, after, full })` &rarr; string | Promise); inert until `config.ghostText` and a resolver are set, so no model ships and no network call happens on its own. **Serialization-safe** (the suggestion is a `contenteditable=false` span the caret stays before; `getHTMLCode`/`getJSON` strip it) and **undo-friendly** (accept via `execCommand insertText`). API: `editor.requestGhostText()` / `acceptGhostText()` / `dismissGhostText()` / `hasGhostText()` / `setGhostTextResolver()`; slash `/ai complete`.
- **Local draft autosave & crash recovery** *(2.3.0)* &mdash; persists the document to `localStorage` as you type and offers to restore the unsaved draft on the next load (Gmail/Notion "never lose your work"). Opt-in via `config.localDraft = true` (or `{ key, intervalMs, ttlMs, promptRestore }`). The draft is never auto-applied (server content shows until the user clicks **Restore**); stale drafts are pruned; saves on `beforeunload`. API: `editor.saveLocalDraft()` / `hasLocalDraft()` / `getLocalDraft()` / `restoreLocalDraft()` / `clearLocalDraft()`.
- **Copy / Download as Markdown** *(2.3.0)* &mdash; Notion-style export surfacing the core `toMarkdown()` engine: slash `/copy as markdown` + `/download as markdown`, or `editor.getMarkdown()` / `editor.copyAsMarkdown()` (clipboard, execCommand fallback) / `editor.downloadMarkdown(name?)` (saves a `.md` file, filename derived from the first heading). Read-only; disable with `config.markdownExportEnabled = false`.
- **Typewriter & focus writing modes** *(2.3.0)* &mdash; two distraction-free modes. *Typewriter mode* pins the caret line to a fixed vertical anchor and scrolls the editable as you type (iA Writer / Typora / Ulysses style); *focus mode* dims every block except the one the caret is in (Notion / iA Writer style). Toggle independently via slash `/typewriter` + `/focus mode` or `editor.toggleTypewriterMode()` / `editor.toggleFocusMode()` (plus `set…`/`is…`). Both off by default (`config.typewriterModeEnabled`, `config.focusModeEnabled`, anchor via `config.typewriterAnchor`). **Serialization-safe**: purely presentational &mdash; focus state is runtime-only CSS classes, stripped around `getHTMLCode`/`getJSON`; typewriter mode only scrolls.
- **Foldable headings** *(2.3.0)* &mdash; Obsidian/Notion/Word-style section folding: hover a heading and click the gutter chevron (or `editor.toggleHeadingFold(h)` / `editor.foldAtCaret()` / `editor.unfoldAll()`, slash `/fold section`) to collapse every block beneath it, up to the next heading of the same or higher level. **Serialization-safe**: fold state is runtime-only CSS classes (the chevron is a CSS `::before`), and `getHTMLCode`/`getJSON` are wrapped to strip them &mdash; saved markup stays clean and folded content is never lost. Caret-safe. Disable with `config.foldHeadingsEnabled = false`.
- **Auto-embed media URLs on paste** *(2.3.0)* &mdash; paste a bare YouTube or Vimeo URL on its own line and it becomes a responsive 16:9 embed (Notion/Medium style); also available as `editor.embedUrl(url)`. **Injection-safe by construction**: the iframe `src` is rebuilt from a sanitized video id, never the raw input. Mid-paragraph and text-selected pastes stay links. Add providers via `config.autoEmbedResolver(url)`; disable with `config.autoEmbedEnabled = false`.
- **Interactive to-do / task lists** *(2.3.0)* &mdash; `editor.execCommand("inserttodolist")` (slash `/to-do`, toolbar) inserts a checklist whose items each have a clickable checkbox; clicking toggles open/done (check + strike-through), Notion/GitHub style. The done state lives in `data-checked` and round-trips through `getHTMLCode` / `getJSON`; checkboxes are `contenteditable="false"` spans (no fragile `<input>`s) and re-bind on load via event delegation. Enter-created items are auto-normalized to stay well-formed.
- **Emoji `:shortcode:` autocomplete** *(2.3.0)* &mdash; GitHub/Slack/Discord-style inline emoji entry: type a complete `:shortcode:` and it becomes the emoji (`:fire:` → 🔥, `:+1:` → 👍, `:tada:` → 🎉), or type `:` + a partial query for a keyboard-navigable popup (↑/↓, Enter/Tab, Esc). ~140 curated codes built in; extend or replace via `config.emojiShortcodes`. Word-boundary gated (so `path:fire:` is ignored) and inert inside code. Complements the emoji picker with fast keyboard-only entry. Disable with `config.emojiAutocompleteEnabled = false`.
- **Read-aloud / text-to-speech** *(2.3.0)* &mdash; `editor.execCommand("readaloud")` (slash `/read aloud`) opens a non-modal control bar (play/pause, stop, voice, rate) and reads the selection &mdash; or the whole document &mdash; aloud, **highlighting each sentence as it is spoken** (native selection, content untouched). Browser-native via the Web Speech API: no library bundled, no backend, no key. The speech-out complement to dictation; an accessibility win. API: `editor.readAloud({rate,voice})` / `editor.stopReadAloud()`; tune with `config.readAloudRate` / `config.readAloudHighlight`.
- **Mermaid diagrams** *(2.3.0)* &mdash; `editor.execCommand("insertdiagram")` (slash `/diagram`) authors diagrams-as-code (flowchart, sequence, gantt, pie, class, state…) with a live preview and renders to SVG. No renderer bundled &mdash; uses `config.diagramResolver` (server-side render like Kroki), else a host-page `window.mermaid`, else a source `<pre>` fallback; the Mermaid source round-trips in `data-diagram` and re-renders via `editor.renderDiagrams()`. Double-click to edit; injection-safe `contenteditable` blocks. Pairs with the equation editor's same host-supplies-the-renderer model.
- **Inline Markdown autoformat** *(2.3.0)* &mdash; type the closing delimiter and the span converts in place: `**bold**`/`__bold__` → **bold**, `*italic*`/`_italic_` → *italic*, `` `code` `` → inline code, `~~strike~~` → strikethrough. Complements the existing block shortcuts (`# `, `> `, `- `, `1. `, `---`). Word-boundary gated so math (`2*3`), globs and `snake_case` are never reformatted; inert inside code/links; fully undoable. Disable with `config.markdownInlineEnabled = false`.
- **Email inline-style export** *(2.3.0)* &mdash; `editor.getEmailHTML()` returns email-client-safe HTML: curated **computed** styles written **inline** on every element (since Outlook/Gmail strip `<style>` and classes), `class`/`id`/`contenteditable`/`data-rte-*` removed, `<script>`/`<style>` dropped, content wrapped in a centered max-width table. `{ wrap:false }` for a bare fragment, `{ width:N }` for a custom width; tune the inlined set via `config.emailInlineProperties`. `editor.execCommand("emailexport")` (slash `/email` or toolbar) opens a copy-ready dialog.
- **Smart chips** *(2.3.0)* &mdash; Google-Docs-style inline chips: `editor.execCommand("insertdatechip")` drops an interactive date pill (picker + machine-readable value); `insertchip` opens a Date/Person/Link menu. Person/Link chips use the optional `config.chipResolver(query, type)` for rich data, with a plain-input fallback. Atomic, injection-safe, round-trips through `getHTMLCode`.
- **Block drag-handles** *(2.3.0)* &mdash; Notion-style ⋮⋮ handle on hover lets you drag-reorder top-level blocks. On by default; disable with `config.blockDragHandles = false`. The handle never enters the saved content.
- **Spell check** *(2.3.0)* &mdash; `editor.execCommand("spellcheck")`. Native browser spell-check by default (zero infra); wire `config.spellCheckResolver(text) => Promise<[{word, suggestions}]>` (LanguageTool / server / LLM) for a review panel with click-to-replace suggestions. Insert from the slash menu (`/spellcheck`) or toolbar.
- **Web-bookmark preview cards** *(2.3.0)* &mdash; `editor.execCommand("insertbookmark")` turns a URL into a rich link-preview card (favicon + title + description + domain). Works with zero backend (basic card from URL + favicon) or wire `config.bookmarkResolver(url) => Promise<{title,description,image,favicon}>` for full unfurled previews. Insert from the slash menu (`/bookmark`) or toolbar; `http(s)`-only with escaped metadata and `rel="noopener"`.
- **Equation editor** *(2.3.0)* &mdash; `editor.execCommand("insertmath")` opens a TeX/LaTeX dialog with a live KaTeX/MathJax preview and edit-in-place support; emits stable `.rte-math-inline` markup that renders via whatever math renderer the host page loads. Insert from the slash menu (`/equation`) or toolbar.
- **Modern block types** *(2.3.0)* &mdash; **callouts** (info / success / warning / danger / note admonition boxes), **multi-column layouts** (2–4 responsive columns), and **toggle / collapsible blocks** (native `<details>`). Insert from the slash menu (`/`) or the toolbar; customize callout variants via `config.calloutTypes`. Editor-native (no server), round-trips through `getHTMLCode` / `getJSON`. Closes the Notion / Google Docs / CKEditor block-type gap.
- **Paste-pipeline hardening** *(2.2.4)* &mdash; Word lists pre-converted before the attribute scrubber strips the metadata; TOC / PAGEREF / HYPERLINK / SEQ / STYLEREF / NOTEREF / REF / FORMTEXT / FORMCHECKBOX / DATE / TIME / FILENAME / AUTHOR / TITLE / SUBJECT / INDEX / MERGEFIELD field instructions stripped even when scrubbed-bare; instruction text split across continuation `<span>` runs caught with false-positive-safe guards; `<style>` / `<script>` / `<head>` / `<meta>` defense-in-depth; tool-specific class + `data-*` strip for Notion (`notion-*`, `data-block-id`), CKEditor (`cke_*`, `data-cke-*`), TinyMCE (`mce_*`, `data-mce-*`), Atlassian (`confluence-*`, `data-confluence-*`), and Google Docs (`docs-internal-*`, `data-google-*`); cleanup of empty `<span></span>` / `<p></p>` debris; smart-paste source toast detects Word / Google Docs / LibreOffice / Notion / Excel / Confluence. Opt-out via `smartPasteToast: false`.
- **Smart link paste** *(2.2.4)* &mdash; pasting a URL or email while text is selected wraps the selection in `<a href>` instead of replacing it. Matches Google Docs / Notion / TipTap / ProseMirror behavior. Opt-out via `smartLinkPaste: false`.
- **Mobile toolbar mode**, **classic toolbar customization**, **plugins**, **localization**, **image editing**, **PDF export**, **paste-from-Word fidelity**, **find & replace**, **autosave**, **draft recovery**.
- **Configurable empty-editor placeholders** &mdash; set `placeholder`, `placeholderShowOnlyWhenEditable`, and `placeholderColor` to match the guided empty-state UX offered by current Tiptap and TinyMCE setups.
- **TypeScript declarations** &mdash; 487-line `index.d.ts` with typed signatures for all 31 documented editor instance methods, the `RichTextEditorCommand` string-literal union (50+ built-in commands), event-handler signatures, and the `RichTextEditorConfig` interface.

The structured content bridge now returns a semantic document model while still carrying compatibility HTML and plain text:

```json
{
  "type": "doc",
  "version": 2,
  "format": "richtexteditor-json",
  "html": "<p>Hello world</p>",
  "text": "Hello world",
  "content": [
    {
      "type": "paragraph",
      "content": [
        { "type": "text", "text": "Hello world" }
      ]
    }
  ]
}
```

## Asset setup

Serve the `richtexteditor/` folder from your app. The React and Vue wrappers default to loading assets from `/richtexteditor`.

## Markdown, validation, and static rendering

```js
import {
  auditAccessibility,
  createStructuredContent,
  fromMarkdown,
  getDocumentPageSetup,
  getDocumentMetrics,
  getDocumentOutline,
  getTableOfContents,
  getTextStatistics,
  renderHTML,
  repairAccessibilityIssue,
  setDocumentPageSetup,
  syncTableOfContents,
  toMarkdown,
  validateStructuredContent
} from "@richscripts/richtexteditor/integrations/shared";

const documentModel = fromMarkdown("## Hello\n\nThis now round-trips through **Markdown**.");
const html = renderHTML(documentModel);
const markdown = toMarkdown(documentModel);
const pagedDocument = setDocumentPageSetup(documentModel, {
  format: "Letter",
  margins: { top: "1in", right: "0.75in", bottom: "1in", left: "0.75in" }
});
const pageSetup = getDocumentPageSetup(pagedDocument);
const metrics = getDocumentMetrics(documentModel);
const outline = getDocumentOutline(documentModel);
const toc = getTableOfContents(documentModel, {
  minLevel: 2,
  maxLevel: 4,
  indexMode: "hierarchical"
});
const stats = getTextStatistics(documentModel);
const accessibility = auditAccessibility(documentModel);
const repairedDocument = repairAccessibilityIssue(documentModel, accessibility.issues[0], {
  altText: "Hero illustration"
});
const validation = validateStructuredContent(documentModel);
const syncedTocDocument = syncTableOfContents(documentModel, {
  ordered: true,
  orderedListType: "I",
  includeTitle: false
});
```

Structured content can also preserve document-automation nodes emitted by the editor UI:

```js
const documentModel = createStructuredContent(
  '<p>Hello <span data-rte-merge-field="Customer.Name">Customer Name</span><sup data-rte-footnote-ref="1"><a href="#note-1">[1]</a></sup></p>' +
  '<div data-rte-footnotes="true"><div>Footnotes</div><ol><li id="note-1" data-rte-footnote-item="1">Imported from a template. <a href="#ref-1">↩</a></li></ol></div>'
);

console.log(documentModel.content[0].content[1].type); // "mergeField"
console.log(toMarkdown(documentModel)); // includes {{Customer.Name}} and [^1]
```

These helpers can now also run in Node for server-side rendering and content conversion without requiring a browser DOM.

```js
const {
  auditAccessibility,
  createStructuredContent,
  getDocumentMetrics,
  getDocumentPageSetup,
  getDocumentOutline,
  getTableOfContents,
  getTextStatistics,
  renderHTML,
  repairAccessibilityIssue,
  setDocumentPageSetup,
  syncTableOfContents,
  toMarkdown,
  validateStructuredContent
} = require("@richscripts/richtexteditor/server");

const documentModel = createStructuredContent("<div data-rte-toc=\"true\"><div>Contents</div></div><h2>Hello</h2><p><strong>Server</strong> render</p>");
const html = renderHTML(documentModel);
const markdown = toMarkdown(documentModel);
const pagedDocument = setDocumentPageSetup(documentModel, {
  format: "A4",
  orientation: "portrait",
  margins: { top: "20mm", right: "16mm", bottom: "20mm", left: "16mm" }
});
const pageSetup = getDocumentPageSetup(pagedDocument);
const metrics = getDocumentMetrics(documentModel);
const outline = getDocumentOutline(documentModel);
const toc = getTableOfContents(documentModel, {
  minLevel: 2,
  maxLevel: 4,
  indexMode: "hierarchical"
});
const synced = syncTableOfContents(documentModel, {
  ordered: true,
  orderedListType: "A",
  includeTitle: false
});
const stats = getTextStatistics(documentModel);
const accessibility = auditAccessibility(documentModel);
const repairedDocument = repairAccessibilityIssue(documentModel, accessibility.issues[0], {
  altText: "Server-rendered architecture diagram"
});
const validation = validateStructuredContent(documentModel);
```

Use `@richscripts/richtexteditor/server` when you only need conversion and rendering on the server. Use `@richscripts/richtexteditor/integrations/shared` when you also need browser asset loading and editor bootstrapping helpers.

The same server/shared helpers also expose `getTextStatistics(value)` for consistent word and character counts outside the browser. The React, Vue, and Angular package entry points re-export this same helper family so framework apps can import editor components and document-workflow utilities from one place.

## DOCX import

Current competitors like CKEditor, TinyMCE, and Tiptap all market Word/DOCX import alongside export. Rich Text Editor now ships a matching browser helper for the client side of that flow:

```js
await editor.aiToolkit.importDocx({
  url: "/richtextbox/import/docx",
  mode: "replace"
});
```

Pass `file` to skip the built-in file picker, or use `mode: "insert"` to insert the converted HTML at the current caret instead of replacing the whole document. The helper expects the server endpoint to return either raw HTML or JSON with an `html` field.

## React

```jsx
import { useState } from "react";
import { RichTextEditorReact } from "@richscripts/richtexteditor/react";

export default function EditorPage() {
  const [html, setHtml] = useState("<p>Hello world</p>");

  return (
    <RichTextEditorReact
      value={html}
      onChange={setHtml}
      assetBasePath="/richtexteditor"
      style={{ minHeight: 360 }}
    />
  );
}
```

## Vue

```js
import { createApp, ref } from "vue";
import { RichTextEditorVue } from "@richscripts/richtexteditor/vue";

const app = createApp({
  components: { RichTextEditorVue },
  setup() {
    const documentModel = ref({
      type: "doc",
      version: 2,
      format: "richtexteditor-json",
      content: [
        {
          type: "paragraph",
          content: [{ type: "text", text: "Hello world" }]
        }
      ]
    });

    return { documentModel };
  }
});
```

```vue
<RichTextEditorVue
  v-model="documentModel"
  value-format="json"
  asset-base-path="/richtexteditor"
  style="min-height: 360px"
/>
```

## Angular

Angular apps can now use the packaged standalone wrapper component:

```ts
import { Component } from "@angular/core";
import { RichTextEditorAngularComponent } from "@richscripts/richtexteditor/angular";

@Component({
  selector: "app-root",
  standalone: true,
  imports: [RichTextEditorAngularComponent],
  template: `
    <rich-text-editor
      [assetBasePath]="'/assets/richtexteditor'"
      [value]="documentModel"
      [valueFormat]="'json'"
      (change)="handleChange($event.value)"
    />
  `
})
export class AppComponent {
  documentModel = {
    type: "doc",
    version: 2,
    format: "richtexteditor-json",
    content: [
      {
        type: "paragraph",
        content: [{ type: "text", text: "Hello from Angular" }]
      }
    ]
  };

  handleChange(value: unknown) {
    this.documentModel = value as typeof this.documentModel;
  }
}
```

The Angular wrapper exposes the same JSON bridge and convenience instance methods as the React and Vue wrappers: `focus()`, `getEditor()`, `getHTMLCode()`, `getJSON()`, `setHTMLCode()`, `setJSON()`, and plugin accessors such as `getAiToolkit()` / `getCollab()` / `getComments()`.

The repo also includes a full Angular starter in `Demos/rte-ng-ts/` that uses the same wrapper pattern.

## Classic usage

```html
<link rel="stylesheet" href="/richtexteditor/rte_theme_default.css" />
<script src="/richtexteditor/rte.js"></script>
<script src="/richtexteditor/plugins/all_plugins.js"></script>
<!-- Production: swap the line above for the minified bundle (~56% smaller, identical behaviour):
     <script src="/richtexteditor/plugins/all_plugins.min.js"></script> -->

<div id="editor"></div>
<script>
  var editor = new RichTextEditor("#editor", {
    placeholder: "Write something...",
    placeholderShowOnlyWhenEditable: true,
    commentsOnly: false,
    restrictedEditingMode: false,
    accessibilityCheckerAutoOpen: false,
    documentOutlineAutoOpen: false,
    documentOutlineMinLevel: 1,
    documentOutlineMaxLevel: 3
  });
  editor.setHTMLCode("<p>Hello world</p>");
  console.log(editor.getJSON());
</script>
```

Restricted editing is meant for contract, intake, and mail-merge style templates:

```html
<p>Hello <span data-rte-editable="true" data-rte-editable-label="Customer Name">Customer Name</span>,</p>
<p>Your case number is <span data-rte-editable="true" data-rte-editable-label="Case ID">0000</span>.</p>
```

```js
editor.setHTMLCode(templateHtml);
editor.restrictedEditing.enable();
editor.restrictedEditing.goToNext();
```

Template authors can also mark regions interactively before switching modes:

```js
editor.restrictedEditing.markSelection({ label: "Account manager" });
editor.restrictedEditing.markSelection({ block: true, label: "Custom notes" });
editor.restrictedEditing.enable();
```

In order to start using Rich Text Editor, configure or customize it further, please visit [the documentation](http://richtexteditor.com/docs/).

## License

For licensing information please read [Licensing Info](http://richtexteditor.com/license.aspx).
