# Quill — Administrator & Architecture Guide

This guide is the in‑depth technical reference for **Quill 2.0.3**: its architecture, internal model, module system, build and release pipeline, security considerations, and code‑level internals. It is written for developers integrating Quill at scale, architects evaluating it, and contributors working on the codebase itself.

Quill is not a deployed service — it is a **browser library** published to npm and a CDN, accompanied by a documentation website. "Operations" here therefore means building, testing, releasing, and embedding the library safely, rather than running a server.

> [!NOTE]
> For day‑to‑day integration usage — installation, configuration options, the editor API, and recipes — see the companion **User Guide**. This guide assumes that material and goes deeper into how Quill works and how to operate it as a dependency.

---

## 1. Executive Summary

Quill is a modular, API‑driven rich text editor built on a structured document model rather than on `contenteditable` HTML soup. Three ideas define its architecture:

1. **The document is a Delta.** Content is a JSON op list (`quill-delta`), not HTML. This makes content lossless, diff‑able, transformable, and collaboration‑friendly.
2. **The DOM is a tree of blots.** Quill renders through **Parchment**, a document model that maps document nodes ("blots") to DOM nodes. Formats are blots or attributors registered in a shared registry.
3. **Everything else is a module.** The toolbar, keyboard handling, history, clipboard, uploader, tables, and syntax highlighting are all swappable modules layered onto a small core.

| Property | Value |
|----------|-------|
| Version | 2.0.3 |
| License | BSD‑3‑Clause |
| Language | TypeScript (compiled to ES modules) |
| Runtime | Browser (DOM) |
| Core dependencies | `parchment` ^3.0.0, `quill-delta` ^5.1.0, `eventemitter3` ^5.0.1, `lodash-es` ^4.17.21 |
| Repository layout | npm‑workspaces monorepo (`packages/quill`, `packages/website`) |
| Build | webpack (library), Next.js (website) |
| Tests | Vitest (unit + fuzz), Playwright (e2e) |
| Distribution | npm (`quill`) + jsDelivr CDN |

> [!IMPORTANT]
> The GitHub issue tracker for this snapshot shows active community concern about maintenance cadence and at least one open security advisory (CVE‑2025‑15056 / GHSA‑v3m3‑f69x‑jf25). Treat Quill as stable and widely deployed, but factor maintenance responsiveness and your own paste‑sanitization strategy into adoption decisions. See [Security](#7-security-analysis).

---

## 2. Architecture

### 2.1 High‑level component map

```
                         ┌──────────────────────────────────────┐
                         │              Quill (core)            │
                         │  public API · option expansion ·     │
                         │  static registry / import / register │
                         └───────────────┬──────────────────────┘
                                         │ orchestrates
   ┌──────────────┬───────────────┬──────┴───────┬──────────────┬───────────────┐
   ▼              ▼               ▼              ▼              ▼               ▼
┌────────┐   ┌─────────┐    ┌──────────┐   ┌───────────┐  ┌────────┐    ┌──────────┐
│ Editor │   │Selection│    │Composition│  │  Emitter  │  │ Theme  │    │ Modules  │
│ Delta  │   │ native  │    │  IME /    │  │ event bus │  │ snow / │    │ toolbar  │
│ sync   │   │ range   │    │ compose   │  │(eventemit-│  │ bubble │    │ keyboard │
│        │   │ bridge  │    │  events   │  │  ter3)    │  │ /core  │    │ history… │
└───┬────┘   └────┬────┘    └─────┬─────┘  └─────┬─────┘  └────────┘    └──────────┘
    │             │               │              │
    ▼             ▼               ▼              ▼
┌──────────────────────────────────────────────────────────┐
│                    Scroll (Parchment ScrollBlot)          │
│   the root blot — a tree of block/inline/embed/text blots │
│   mapped onto the contenteditable DOM (.ql-editor)        │
└──────────────────────────────────────────────────────────┘
                              │ renders
                              ▼
                ┌──────────────────────────────┐
                │  contenteditable DOM (.ql-…)  │
                └──────────────────────────────┘
```

### 2.2 The core objects

The `Quill` constructor wires up a fixed set of collaborators (`packages/quill/src/core/quill.ts`):

| Object | Source | Responsibility |
|--------|--------|----------------|
| **Scroll** | Parchment `ScrollBlot` (`blots/scroll.ts`) | The root blot. Owns the blot tree and the editor's root DOM node (`.ql-editor`). All structural mutations flow through it. |
| **Editor** | `core/editor.ts` | The bridge between Deltas and the blot tree. Translates `applyDelta`, `insertText`, `formatLine`, etc. into Scroll operations, and keeps a canonical `delta` in sync via `update()`. |
| **Selection** | `core/selection.ts` | Bridges the browser's native `Selection`/`Range` to Quill `Range` (`{index, length}`) objects, handling focus, bounds, and cursor restoration across mutations. |
| **Composition** | `core/composition.ts` | Manages IME composition (`compositionstart`/`compositionend`), batching DOM changes so multi‑keystroke input (CJK, accents) commits atomically. |
| **Emitter** | `core/emitter.ts` | The event bus (extends `eventemitter3`). Owns the event/source name constants and dispatches DOM events to the right instance. |
| **Theme** | `core/theme.ts` + `themes/*` | Builds the toolbar/tooltip UI and decides which modules are added. |
| **Modules** | `modules/*` | Feature units (toolbar, keyboard, history, clipboard, uploader, syntax, table…). |

### 2.3 Construction sequence

When you call `new Quill(container, options)`:

1. **`expandConfig()`** resolves the container selector, picks the theme constructor, merges Quill defaults + theme defaults + your options + per‑module defaults, and resolves the registry (custom `registry` > `formats` filter > shared global).
2. The container is cleared, marked `.ql-container`, and registered in a global `WeakMap` (`core/instances.ts`) keyed by DOM node.
3. A `.ql-editor` root is created and the **Scroll** blot is instantiated from the registry.
4. **Editor**, **Selection**, and **Composition** are constructed around the Scroll.
5. The theme is instantiated; it adds the default modules — `keyboard`, `clipboard`, `history`, `uploader`, plus `input` and `uiNode` — and any theme‑specific modules, then runs `theme.init()`.
6. Event wiring connects Scroll updates back into the Editor's Delta sync and toggles the `.ql-blank` class.
7. If the container had initial HTML, it is converted through the clipboard and set as the initial contents; history is cleared; `placeholder`/`readOnly` are applied.

> [!NOTE]
> The shared `globalRegistry` (a `Parchment.Registry`) is created once at module load. Because it is shared across all editor instances by default, registering a format with `Quill.register` affects every editor on the page unless you pass a per‑instance `registry`.

### 2.4 The Delta synchronization loop

The heart of Quill is keeping a canonical Delta in lockstep with a `contenteditable` DOM that the user (and the browser) mutate unpredictably.

```
user types / pastes / IME      API call (insertText, format, …)
        │                                │
        ▼                                ▼
  DOM mutation                    Editor.<method>()
        │                                │
   MutationObserver                 Scroll mutation
        │                                │
        ▼                                ▼
  Scroll.update() ──────► Emitter SCROLL_UPDATE ──► Editor.update()
        │                                                  │
        ▼                                                  ▼
  diff old vs. new Delta  ◄──────────────────────  new canonical delta
        │
        ▼
  TEXT_CHANGE (change, oldContents, source)  ──► your handlers
```

`Editor.update()` is the reconciliation point. It recomputes the document Delta from the blot tree, diffs it against the previous Delta, and emits the change. A fast path optimizes single‑character ASCII edits to avoid a full re‑diff.

---

## 3. The Document Model (Parchment & Delta)

### 3.1 Blots

Quill's content tree is made of **blots** (from Parchment). Each blot wraps a DOM node and declares a `blotName`, a `tagName`, and an optional `className`. Quill defines these core blots in `packages/quill/src/blots/`:

| Blot | `blotName` | DOM | Role |
|------|-----------|-----|------|
| `Scroll` | `scroll` | `DIV.ql-editor` | Root container; the editing surface. |
| `Block` | `block` | `P` | A block‑level line. |
| `BlockEmbed` | (block embed) | varies | A block‑level embed (e.g. video). |
| `Break` | `break` | `BR` | Empty‑line placeholder. |
| `Inline` | `inline` | `SPAN` | Base for inline formats. |
| `Cursor` | `cursor` | `SPAN.ql-cursor` | Zero‑width cursor anchor. |
| `Embed` | — | varies | Base for inline embeds. |
| `Text` | `text` | text node | Raw text leaf. |
| `Container` | `container` | — | Structural grouping (e.g. list/table containers). |

### 3.2 Formats

Formats are blots or **attributors** registered under `formats/{name}`. Quill ships these in `packages/quill/src/formats/`:

| Category | Formats (blotName → tag) |
|----------|--------------------------|
| Inline marks | `bold`→`STRONG/B`, `italic`→`EM/I`, `underline`→`U`, `strike`→`S/STRIKE`, `code`→`CODE`, `script`→`SUB/SUP` |
| Inline attributes | `color`, `background`, `font`, `size`, `link`→`A` |
| Block / line | `header`→`H1…H6`, `blockquote`→`BLOCKQUOTE`, `code-block`→`DIV.ql-code-block`, `list`→`LI` (+`list-container`→`OL`), `align`, `indent`, `direction` |
| Embeds | `image`→`IMG`, `video`→`IFRAME.ql-video`, `formula`→`SPAN.ql-formula`, `table`→`TD` (+`table-row`/`table-body`/`table-container`) |

Formats come in three implementation shapes:

- **Blot formats** (e.g. `bold`, `link`, `header`) wrap content in a DOM element.
- **Class attributors** apply `ql-{format}-{value}` classes (used for things like alignment when class‑based).
- **Style attributors** apply inline CSS (e.g. `color`, `background`, `size`, `font` have style variants used during paste normalization).

> [!TIP]
> Registering a format via `Quill.register(BlotOrAttributor)` infers the name from `blotName`/`attrName` and adds it under `formats/{name}`. Anything registered under `blots/` or `formats/` is also pushed into the global Parchment registry so the Scroll can build it.

### 3.3 Delta

`quill-delta` (re‑exported as `Quill.import('delta')`) provides the `Delta` class with `insert`/`retain`/`delete` ops, plus `compose`, `diff`, `transform`, and `invert`. These operations are what make:

- **History** work — undo entries are inverse Deltas (`record()` stores `changeDelta.invert(oldDelta)`).
- **Collaboration** possible — `transform` is operational transformation, letting concurrent edits be rebased.
- **Tables/embeds** composable — embed handlers (`tableEmbed.ts`) register custom `compose`/`transform`/`invert` logic with Delta.

---

## 4. The Module System

### 4.1 The Module base class

All modules extend the tiny base in `core/module.ts`:

```ts
abstract class Module<T extends {} = {}> {
  static DEFAULTS = {};
  constructor(public quill: Quill, protected options: Partial<T> = {}) {}
}
```

A module receives its host `Quill` instance and merged options. It wires up its behavior in the constructor (event listeners, key bindings, DOM hooks). Modules are added by the theme via `theme.addModule(name)`, which looks the module class up from the registry (`modules/{name}`), merges `Module.DEFAULTS` with user options, and constructs it.

### 4.2 Built‑in modules and their defaults

| Module | Source | `DEFAULTS` (key facts) |
|--------|--------|------------------------|
| `clipboard` | `modules/clipboard.ts` | `matchers: []` — user matchers append to a built‑in matcher list covering text, breaks, lists, code, tables, Word/Docs normalization. |
| `keyboard` | `modules/keyboard.ts` | Binds Enter, Backspace/Delete (platform‑specific), and arrow/UI‑node navigation. `shortKey` resolves to `metaKey` on macOS, `ctrlKey` elsewhere. |
| `history` | `modules/history.ts` | `delay: 1000`, `maxStack: 100`, `userOnly: false`. Binds undo/redo shortcuts and `beforeinput` history events. |
| `uploader` | `modules/uploader.ts` | `mimetypes: ['image/png','image/jpeg']`; default handler reads files as data URLs and inserts image embeds. |
| `toolbar` | `modules/toolbar.ts` | `container: null` plus handlers for `clean`, `direction`, `indent`, `link`, `list`. Builds toolbar DOM from array config or binds to existing controls by `ql-{format}` class. |
| `syntax` | `modules/syntax.ts` | `hljs: window.hljs`, `interval: 1000`, and a 14‑language picker list. Requires highlight.js. |
| `table` | `modules/table.ts` | Registers table blots; exposes insert/delete row/column/table operations and auto‑balances cells. |
| `tableEmbed` | `modules/tableEmbed.ts` | Registers a `table-embed` Delta embed handler (compose/transform/invert) for OT‑safe tables. |
| `input` | `modules/input.ts` | Normalizes `beforeinput` insert/replace events and IME start, replacing selected ranges consistently. |
| `uiNode` | `modules/uiNode.ts` | Handles caret movement around UI nodes (e.g. list/code‑block selectors), including RTL and `Ctrl+A` on macOS. |

### 4.3 Themes as module orchestrators

Themes (`themes/snow.ts`, `themes/bubble.ts`, base `themes/base.ts`) extend `BaseTheme`. Each:

- Supplies a **default toolbar config** if the toolbar module is enabled without a container.
- Builds toolbar **buttons and pickers** with the SVG icon set (`ui/icons.ts`, `ui/picker.ts`, `ui/color-picker.ts`, `ui/icon-picker.ts`).
- Wires a **tooltip** (`ui/tooltip.ts`) for link editing (`snow`) or selection‑anchored formatting (`bubble`).
- Adds a theme‑specific `link` handler (snow shows an inline edit tooltip; bubble opens the tooltip editor).

```
snow default toolbar:   [header 1/2/3] · [bold italic underline link] · [list ordered/bullet] · [clean]
bubble default toolbar: [bold italic link] · [header 1, header 2, blockquote]
```

---

## 5. Repository Structure

```
quill/                              (monorepo root — npm workspaces)
├── package.json                    workspace config; build/start/lint scripts
├── tsconfig.json
├── packages/
│   ├── quill/                      THE LIBRARY (published as `quill`)
│   │   ├── package.json            deps, webpack/vitest/playwright scripts
│   │   ├── webpack.config.cjs      build configuration
│   │   ├── webpack.common.cjs
│   │   ├── babel.config.cjs        preset-env + preset-typescript
│   │   ├── playwright.config.ts    e2e config
│   │   ├── scripts/build           build entry (`./scripts/build production`)
│   │   └── src/
│   │       ├── core.ts             full build entry (registers all formats/modules)
│   │       ├── quill.ts            re-export of core/quill
│   │       ├── core/               quill, editor, selection, emitter, composition,
│   │       │                       theme, module, logger, instances, utils/
│   │       ├── blots/              block, inline, text, break, cursor, embed,
│   │       │                       container, scroll
│   │       ├── formats/            21 format definitions
│   │       ├── modules/            clipboard, keyboard, history, toolbar, syntax,
│   │       │                       table, tableEmbed, uploader, input, uiNode,
│   │       │                       normalizeExternalHTML/
│   │       ├── themes/             base, snow, bubble
│   │       ├── ui/                 picker, color-picker, icon-picker, icons, tooltip
│   │       └── assets/             *.styl stylesheets + SVG icons
│   │       └── test/               unit/ (vitest), fuzz/ (vitest), e2e/ (playwright)
│   └── website/                    DOCS SITE (private, Next.js + MDX)
│       ├── next.config.mjs
│       ├── env.js
│       ├── content/                docs/*.mdx, blog/*.mdx, guides/*.mdx
│       └── src/                    pages/, components/, playground/, data/, svg/
└── scripts/                        changelog.mjs, release.js, utils/configGit.mjs
```

### 5.1 Build entries

- **Full build** (`src/core.ts`) imports and registers every standard format and module, plus both themes. This is what `import 'quill'` resolves to.
- **Core build** (`quill/core`) provides only the editor core; consumers register what they need.

Versioning is injected at build time: `Quill.version` reads a `QUILL_VERSION` global defined by `babel-plugin-transform-define`, falling back to `'dev'`.

---

## 6. Building, Testing & Releasing

### 6.1 Prerequisites

| Requirement | Value |
|-------------|-------|
| Node.js | A version compatible with the toolchain (webpack 5, Next.js 14, TypeScript 5.4). |
| npm | `>= 8.2.3` (enforced via `engineStrict`). |
| Workspaces | The repo uses npm workspaces (`packages/*`); run install at the root. |

```bash
npm install            # install all workspace deps from the repo root
```

### 6.2 Development servers

| Command | Effect |
|---------|--------|
| `npm start` | Runs both the library dev server (webpack‑dev‑server, port 9080) and the website (Next.js, port 9000) in parallel. |
| `npm run start:quill` | Library dev server only. |
| `npm run start:website` | Website only, with `NEXT_PUBLIC_LOCAL_QUILL=true` so the site uses the local Quill build. |

### 6.3 Building

| Command | Effect |
|---------|--------|
| `npm run build` | Builds both packages in parallel. |
| `npm run build:quill` | Builds the library (`./scripts/build production` → webpack → `dist/`). |
| `npm run build:website` | Builds the docs site (`next build`). |

The library build emits `dist/quill.js`, `dist/quill.core.js`, and the stylesheets `quill.core.css`, `quill.snow.css`, `quill.bubble.css` (compiled from the Stylus `.styl` assets), plus source maps.

### 6.4 Testing

| Command | Framework | Scope |
|---------|-----------|-------|
| `npm test` (in `packages/quill`) | runs all suites in sequence | unit → fuzz → e2e |
| `test:unit` | Vitest (`@vitest/browser`) | Core, blots, formats, modules unit tests. |
| `test:fuzz` | Vitest | Randomized `editor` and `tableEmbed` operation fuzzing — validates Delta compose/transform invariants. |
| `test:e2e` | Playwright | Full browser interaction tests (typing, history, lists, selection replacement). |

> [!TIP]
> The **fuzz** suite is the strongest correctness signal for the Delta/OT logic — it generates random operation sequences and checks that `compose`/`transform`/`invert` hold. Run it after any change to `editor.ts`, `history.ts`, or `tableEmbed.ts`.

### 6.5 Linting

`npm run lint` runs across all workspaces. The library lints with ESLint (TypeScript, import resolution, prettier, jsx‑a11y, require‑extensions) and `tsc --noEmit` for type checking. Prettier is configured for single quotes.

### 6.6 Release

Release tooling lives in `scripts/`:

| Script | Purpose |
|--------|---------|
| `scripts/release.js` | Drives the package release process. |
| `scripts/changelog.mjs` | Generates the changelog. |
| `scripts/utils/configGit.mjs` | Git configuration helper used during release. |

The published artifact is the `quill` package (`packages/quill`), with `main: quill.js`, type `module`. The website is private (`"private": true`) and not published to npm — it is deployed separately as the documentation site at quilljs.com.

> [!IMPORTANT]
> The CDN (jsDelivr) serves the published npm package directly. Releasing a new version to npm makes it available at `https://cdn.jsdelivr.net/npm/quill@<version>/dist/...`. There is no separate CDN upload step — CDN currency is a function of the npm release.

---

## 7. Security Analysis

Quill edits a `contenteditable` surface and imports external HTML on paste, which puts it on the **XSS attack surface** of any application that embeds it. Understand the boundaries before deploying.

### 7.1 Trust boundaries

| Boundary | Concern |
|----------|---------|
| **Paste / drop input** | Arbitrary HTML from the clipboard (including from other apps) is parsed and normalized. Quill converts it to a Delta through matchers, which discards most active content, but integrators must not assume the result is a sanitizer for arbitrary attacker HTML. |
| **`dangerouslyPasteHTML`** | Explicitly named to flag risk: it inserts HTML through clipboard conversion. Never feed it unsanitized, untrusted strings. |
| **Stored Delta → rendered HTML** | If you persist Deltas and later render `getSemanticHTML()` (or your own renderer) into a page, the rendering site is responsible for output encoding/sanitization, especially for `link`, `image`, and `video` (URL/`href`/`src`) values. |
| **Embeds (`link`, `image`, `video`)** | URL‑bearing formats. Validate/allow‑list schemes (e.g. reject `javascript:`) at your application layer; Quill does not guarantee scheme filtering for every path. |

### 7.2 What Quill does and does not do

- **Does:** parse paste HTML in a detached `DOMParser` document, strip to a structured Delta via the matcher pipeline, normalize Word/Google‑Docs markup, and constrain content to registered formats (when you set `formats`/`registry`).
- **Does not:** act as a general‑purpose HTML sanitizer, validate URL schemes comprehensively, or protect a downstream renderer. Those are your responsibility.

> [!CAUTION]
> Restrict allowed formats with the `formats` option and sanitize on render. The strongest defense against stored‑XSS is to (a) store Deltas, (b) constrain formats, and (c) sanitize/encode at the rendering boundary — not to rely on the editor alone.

### 7.3 Known issues from the community tracker

The issue tracker for this snapshot includes:

- **CVE‑2025‑15056 / GHSA‑v3m3‑f69x‑jf25** (open) — a request for a patched version and maintainer guidance. Track upstream for remediation status before deploying in high‑risk contexts.
- **`getSemanticHTML` correctness** (#4509, open, heavily discussed) — semantic HTML export has edge cases; do not assume byte‑perfect output for complex documents (notably nested lists, #979/#4805).
- **Accessibility gaps** (#4439, #4806) — toolbar keyboard navigation and certain tooltip controls have reported a11y limitations relevant to compliance reviews.

> [!WARNING]
> Several open issues question maintenance responsiveness (#4622, #4800, #4803). For security‑sensitive deployments, plan for the possibility that you may need to carry local patches or contribute fixes upstream.

### 7.4 Secrets and network

Quill has **no secrets, no network calls, and no telemetry** of its own. It does not phone home. The only network considerations are (a) the CDN you load it from and (b) the default uploader, which converts dropped images to inline data URLs client‑side (no upload occurs unless you supply a handler).

---

## 8. Code Quality & Conventions

| Aspect | Observation |
|--------|-------------|
| **Language** | TypeScript throughout the library, compiled with Babel (`preset-env` + `preset-typescript`) and type‑checked with `tsc --noEmit`. |
| **Module style** | ES modules with explicit `.js` extensions in imports (enforced by `eslint-plugin-require-extensions`), enabling clean ESM output. |
| **Separation of concerns** | A deliberately small `core/` (quill, editor, selection, emitter, composition) with all features pushed into `modules/`. New behavior should be a module, not a core change. |
| **Document model isolation** | Rendering and structure live entirely in Parchment blots (`blots/`, `formats/`); the editor never manipulates the DOM ad hoc — it goes through Scroll. |
| **Testing rigor** | Three‑tier testing (unit + fuzz + e2e). The fuzz layer specifically guards OT invariants — a notable quality investment for an editor. |
| **Type pragmatism** | The codebase contains a number of `@ts-expect-error` annotations around DOM/Parchment seams, reflecting the friction of typing live‑DOM editing. These are documented escape hatches, not silent `any`s. |
| **Formatting** | Prettier (single quotes) + ESLint with import resolution, a11y, and prettier integration. |

> [!NOTE]
> The recurring `@ts-expect-error // Fix me later` comments are the clearest "tech debt" signal in the codebase — they mark spots where the type system and the DOM editing reality diverge. They are safe but worth tightening over time.

---

## 9. Integration & Operations Playbook

### 9.1 Embedding checklist

- [ ] Choose full vs. core build deliberately (bundle size vs. convenience).
- [ ] Include the **theme stylesheet** matching your `theme` option.
- [ ] Pin the version (npm `quill@2.0.3` or a versioned CDN URL) — avoid floating `@latest` in production.
- [ ] Decide your **storage format** (store Deltas, not HTML).
- [ ] Constrain **allowed formats** to what your product needs.
- [ ] Define a **sanitization strategy** for any HTML you render from stored content.
- [ ] If using `syntax`, ensure highlight.js loads before Quill.
- [ ] Provide a real `uploader.handler` if you need server‑side image storage (the default inlines data URLs).

### 9.2 Performance tuning

| Lever | Effect |
|-------|--------|
| Core build + selective format/module registration | Smaller bundle, less per‑keystroke work. |
| `modules.syntax.interval` | Raise it to reduce re‑highlight frequency on large code blocks. |
| `modules.history.maxStack` | Lower it to cap memory in long sessions. |
| Document size | Split very large documents; the Delta diff cost grows with content. |
| Disable unused modules | Set `clipboard`/`keyboard`/`history`/`uploader` to `false` only if you truly don't need them (most apps need clipboard + keyboard). |

### 9.3 Upgrading

Quill 2.x changed the document/registry model versus 1.x (the website's `upgrading-to-2-0` guide covers specifics). When upgrading:

1. Review custom blots/formats against the Parchment 3 API.
2. Re‑validate any custom clipboard matchers.
3. Run your integration's e2e tests against the new build before shipping.

### 9.4 Diagnostics

```js
Quill.debug('info'); // 'error' | 'warn' | 'log' | 'info' | true/false
```

Logging is namespaced (`quill`, `quill:events`, `quill:selection`, `quill:clipboard`, `quill:keyboard`, `quill:toolbar`). The default level is `warn`. Because `Quill.debug` is static, it affects every editor on the page.

> [!CAUTION]
> Leaving debug at `info`/`log` in production is noisy and can leak document structure into the console. Keep it at `warn` (the default) in production builds.

---

## 10. Troubleshooting (Operational)

### Build fails: TypeScript errors only with `--skipLibCheck` off

**Cause.** The library lints types with `tsc --noEmit --skipLibCheck`. Third‑party type mismatches can surface without `skipLibCheck`.

**Solution.** Match the project's lint command (`lint:tsc`) rather than a bespoke `tsc` invocation.

### Website dev server shows the published Quill, not my local changes

**Cause.** The website only uses the local build when `NEXT_PUBLIC_LOCAL_QUILL=true`.

**Solution.** Use `npm run start:website` (which sets the flag) or `npm start` at the root, not a bare `next dev`.

### Fuzz tests fail after editing Delta logic

**Cause.** A change to `editor.ts`, `history.ts`, or `tableEmbed.ts` broke an OT invariant (compose/transform/invert).

**Solution.** Treat fuzz failures as correctness regressions, not flakiness — the seed that failed encodes a concrete violating sequence. Reproduce and fix the invariant.

### `Cannot import {name}. Are you sure it was registered?`

**Cause.** `Quill.import(name)` was called for a path never registered (common when mixing core build with full‑build assumptions).

**Solution.** Register the component first, or use the full build which registers all standard formats/modules.

### `Cannot load {name} module. Are you sure you registered it?`

**Cause.** A module named in `modules` options isn't registered.

**Solution.** Register the module class under `modules/{name}` before construction, or remove the option.

### Port conflict on dev start

**Cause.** The library dev server defaults to webpack port 9080 and the website to 9000 (from the root `package.json` `config.ports`).

**Solution.** Free the ports or override via the npm config port variables.

---

## 11. Quick Reference

### Workspace scripts (root)

| Command | Action |
|---------|--------|
| `npm start` | Library + website dev servers (parallel). |
| `npm run build` | Build both packages. |
| `npm run build:quill` / `build:website` | Build one package. |
| `npm run lint` | Lint all workspaces. |

### Library scripts (`packages/quill`)

| Command | Action |
|---------|--------|
| `npm run build` | Production webpack build → `dist/`. |
| `npm start` | webpack‑dev‑server (port 9080). |
| `npm test` | unit → fuzz → e2e. |
| `npm run test:unit` / `test:fuzz` / `test:e2e` | Individual suites. |
| `npm run lint` | ESLint + `tsc --noEmit`. |

### Distribution artifacts (`dist/`)

| File | Description |
|------|-------------|
| `quill.js` | Full build (all formats/modules/themes). |
| `quill.core.js` | Core build. |
| `quill.core.css` | Core styling. |
| `quill.snow.css` | Snow theme (includes core). |
| `quill.bubble.css` | Bubble theme (includes core). |

### Key environment / config values

| Name | Where | Meaning |
|------|-------|---------|
| `QUILL_VERSION` | build‑time define | Becomes `Quill.version` (falls back to `'dev'`). |
| `NEXT_PUBLIC_LOCAL_QUILL` | website env | `true` → site uses the local Quill build. |
| `config.ports.webpack` (9080) | root `package.json` | Library dev server port. |
| `config.ports.website` (9000) | root `package.json` | Website dev server port. |
| `npm >= 8.2.3` | `engines` | Enforced minimum npm. |

### Error message → meaning

| Message | Meaning |
|---------|---------|
| `Invalid Quill container` | Container selector matched nothing / null passed. |
| `Invalid theme {name}. Did you register it?` | Unknown theme name. |
| `Syntax module requires highlight.js…` | `syntax` enabled without highlight.js. |
| `Cannot import {name}…` | `Quill.import` of an unregistered path. |
| `Cannot load {name} module…` | Unregistered module in options. |

---

_Generated June 2026 from slab/quill at 539cbff. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
