or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

delta-operations.mdeditor-core.mdformatting-system.mdindex.mdmodule-system.mdregistry-system.mdtheme-system.md

registry-system.mddocs/

0

# Registry System

1

2

Dynamic registration system for formats, modules, themes, and blots enabling extensibility and customization. The registry system allows developers to register custom components, override existing ones, and import registered components at runtime.

3

4

## Capabilities

5

6

### Static Registration Methods

7

8

Methods on the Quill class for registering and importing components.

9

10

```typescript { .api }

11

class Quill {

12

/** Registry of imported components */

13

static imports: Record<string, unknown>;

14

15

/**

16

* Register component with path and target

17

* @param path - Registration path (e.g., 'formats/bold')

18

* @param target - Component to register

19

* @param overwrite - Whether to overwrite existing registration

20

*/

21

static register(path: string, target: any, overwrite?: boolean): void;

22

23

/**

24

* Register multiple components from object

25

* @param targets - Object with path->component mappings

26

* @param overwrite - Whether to overwrite existing registrations

27

*/

28

static register(targets: Record<string, any>, overwrite?: boolean): void;

29

30

/**

31

* Register single component (auto-detect path from blotName/attrName)

32

* @param target - Component with blotName or attrName property

33

* @param overwrite - Whether to overwrite existing registration

34

*/

35

static register(target: RegistryDefinition, overwrite?: boolean): void;

36

37

/**

38

* Import registered component

39

* @param name - Component path or name

40

* @returns Imported component or undefined

41

*/

42

static import(name: string): unknown;

43

44

/**

45

* Import core module

46

* @param name - 'core/module'

47

* @returns Module base class

48

*/

49

static import(name: 'core/module'): typeof Module;

50

51

/**

52

* Import theme

53

* @param name - Theme path (e.g., 'themes/snow')

54

* @returns Theme class

55

*/

56

static import(name: `themes/${string}`): typeof Theme;

57

58

/**

59

* Import Parchment

60

* @param name - 'parchment'

61

* @returns Parchment namespace

62

*/

63

static import(name: 'parchment'): typeof Parchment;

64

65

/**

66

* Import Delta

67

* @param name - 'delta'

68

* @returns Delta class

69

*/

70

static import(name: 'delta'): typeof Delta;

71

72

/**

73

* Find blot instance for DOM node

74

* @param node - DOM node to find blot for

75

* @param bubble - Whether to search up the DOM tree

76

* @returns Blot instance or null

77

*/

78

static find(node: Node, bubble?: boolean): Blot | null;

79

80

/**

81

* Set debug level for logging

82

* @param level - Debug level or boolean

83

*/

84

static debug(level: DebugLevel | boolean): void;

85

}

86

87

interface RegistryDefinition {

88

blotName?: string;

89

attrName?: string;

90

[key: string]: any;

91

}

92

93

type DebugLevel = 'error' | 'warn' | 'log' | 'info';

94

```

95

96

**Usage Examples:**

97

98

```typescript

99

import Quill from 'quill';

100

101

// Register single component with path

102

Quill.register('formats/highlight', HighlightBlot);

103

104

// Register multiple components

105

Quill.register({

106

'formats/mention': MentionBlot,

107

'modules/autoformat': AutoFormatModule,

108

'themes/minimal': MinimalTheme

109

});

110

111

// Register with auto-detection (uses blotName)

112

class CustomBlot extends Inline {

113

static blotName = 'custom';

114

}

115

Quill.register(CustomBlot); // Registers as 'formats/custom'

116

117

// Import registered components

118

const BoldFormat = Quill.import('formats/bold');

119

const ToolbarModule = Quill.import('modules/toolbar');

120

const SnowTheme = Quill.import('themes/snow');

121

122

// Import core components

123

const Module = Quill.import('core/module');

124

const Parchment = Quill.import('parchment');

125

const Delta = Quill.import('delta');

126

```

127

128

### Registration Paths

129

130

Standard paths for registering different types of components.

131

132

```typescript { .api }

133

// Format registration paths

134

'formats/bold' // Bold inline format

135

'formats/italic' // Italic inline format

136

'formats/header' // Header block format

137

'formats/list' // List block format

138

'formats/image' // Image embed format

139

'formats/link' // Link inline format

140

141

// Module registration paths

142

'modules/toolbar' // Toolbar module

143

'modules/keyboard' // Keyboard module

144

'modules/history' // History module

145

'modules/clipboard' // Clipboard module

146

'modules/uploader' // Uploader module

147

148

// Theme registration paths

149

'themes/snow' // Snow theme

150

'themes/bubble' // Bubble theme

151

152

// Blot registration paths

153

'blots/inline' // Inline blot

154

'blots/block' // Block blot

155

'blots/embed' // Embed blot

156

'blots/scroll' // Scroll blot

157

'blots/container' // Container blot

158

159

// Attributor registration paths

160

'attributors/attribute/direction' // Attribute-based direction

161

'attributors/class/color' // Class-based color

162

'attributors/style/font' // Style-based font

163

164

// Core component paths

165

'core/module' // Module base class

166

'core/theme' // Theme base class

167

'parchment' // Parchment system

168

'delta' // Delta class

169

170

// UI component paths

171

'ui/picker' // Picker component

172

'ui/tooltip' // Tooltip component

173

'ui/icons' // Icon definitions

174

```

175

176

**Usage Examples:**

177

178

```typescript

179

// Register formats

180

Quill.register('formats/highlight', HighlightFormat);

181

Quill.register('formats/spoiler', SpoilerFormat);

182

183

// Register modules

184

Quill.register('modules/counter', WordCountModule);

185

Quill.register('modules/autosave', AutoSaveModule);

186

187

// Register themes

188

Quill.register('themes/dark', DarkTheme);

189

Quill.register('themes/minimal', MinimalTheme);

190

191

// Register attributors

192

Quill.register('attributors/style/line-height', LineHeightAttributor);

193

194

// Use registered components

195

const quill = new Quill('#editor', {

196

theme: 'dark',

197

formats: ['bold', 'italic', 'highlight', 'spoiler'],

198

modules: {

199

counter: { container: '#word-count' },

200

autosave: { interval: 5000 }

201

}

202

});

203

```

204

205

### Custom Format Registration

206

207

Register custom text formats and blots.

208

209

```typescript { .api }

210

// Custom inline format

211

class Highlight extends Inline {

212

static blotName = 'highlight';

213

static tagName = 'MARK';

214

static className = 'ql-highlight';

215

216

static create(value) {

217

const node = super.create();

218

if (value) {

219

node.setAttribute('data-color', value);

220

node.style.backgroundColor = value;

221

}

222

return node;

223

}

224

225

static formats(domNode) {

226

return domNode.getAttribute('data-color') || true;

227

}

228

229

format(name, value) {

230

if (name !== this.statics.blotName || !value) {

231

super.format(name, value);

232

} else {

233

this.domNode.setAttribute('data-color', value);

234

this.domNode.style.backgroundColor = value;

235

}

236

}

237

}

238

239

// Custom block format

240

class Alert extends Block {

241

static blotName = 'alert';

242

static tagName = 'DIV';

243

static className = 'alert';

244

245

static create(value) {

246

const node = super.create();

247

node.classList.add(`alert-${value || 'info'}`);

248

return node;

249

}

250

251

static formats(domNode) {

252

const classList = domNode.classList;

253

for (const className of classList) {

254

if (className.startsWith('alert-')) {

255

return className.replace('alert-', '');

256

}

257

}

258

return undefined;

259

}

260

}

261

262

// Custom embed format

263

class Tweet extends BlockEmbed {

264

static blotName = 'tweet';

265

static tagName = 'DIV';

266

static className = 'tweet-embed';

267

268

static create(value) {

269

const node = super.create();

270

node.setAttribute('data-tweet-id', value);

271

node.innerHTML = `<p>Loading tweet ${value}...</p>`;

272

// Load tweet content asynchronously

273

this.loadTweet(node, value);

274

return node;

275

}

276

277

static value(domNode) {

278

return domNode.getAttribute('data-tweet-id');

279

}

280

281

static loadTweet(node, tweetId) {

282

// Implementation for loading tweet content

283

}

284

}

285

```

286

287

**Usage Examples:**

288

289

```typescript

290

// Register custom formats

291

Quill.register('formats/highlight', Highlight);

292

Quill.register('formats/alert', Alert);

293

Quill.register('formats/tweet', Tweet);

294

295

// Use custom formats

296

const quill = new Quill('#editor', {

297

formats: ['bold', 'italic', 'highlight', 'alert', 'tweet']

298

});

299

300

// Apply custom formatting

301

quill.formatText(0, 10, 'highlight', 'yellow');

302

quill.formatLine(20, 1, 'alert', 'warning');

303

quill.insertEmbed(30, 'tweet', '1234567890');

304

```

305

306

### Custom Module Registration

307

308

Register custom modules to extend editor functionality.

309

310

```typescript { .api }

311

// Custom module

312

class AutoSave extends Module {

313

static DEFAULTS = {

314

interval: 10000, // 10 seconds

315

url: '/autosave',

316

method: 'POST'

317

};

318

319

constructor(quill, options) {

320

super(quill, options);

321

this.timer = null;

322

this.lastSaved = '';

323

this.setupAutoSave();

324

}

325

326

setupAutoSave() {

327

this.quill.on('text-change', () => {

328

this.scheduleAutoSave();

329

});

330

}

331

332

scheduleAutoSave() {

333

if (this.timer) {

334

clearTimeout(this.timer);

335

}

336

337

this.timer = setTimeout(() => {

338

this.save();

339

}, this.options.interval);

340

}

341

342

save() {

343

const content = JSON.stringify(this.quill.getContents());

344

if (content !== this.lastSaved) {

345

fetch(this.options.url, {

346

method: this.options.method,

347

headers: { 'Content-Type': 'application/json' },

348

body: content

349

}).then(() => {

350

this.lastSaved = content;

351

console.log('Auto-saved');

352

});

353

}

354

}

355

}

356

357

// Mention module

358

class Mentions extends Module {

359

static DEFAULTS = {

360

source: null,

361

mentionDenotationChars: ['@'],

362

showDenotationChar: true,

363

allowedChars: /^[a-zA-Z0-9_]*$/,

364

minChars: 0,

365

maxChars: 31,

366

offsetTop: 2,

367

offsetLeft: 0

368

};

369

370

constructor(quill, options) {

371

super(quill, options);

372

this.setupMentions();

373

}

374

375

setupMentions() {

376

this.quill.keyboard.addBinding({

377

key: '@'

378

}, this.handleMentionChar.bind(this));

379

}

380

381

handleMentionChar(range, context) {

382

// Implementation for mention handling

383

this.showMentionList(range);

384

}

385

386

showMentionList(range) {

387

// Show mention dropdown

388

}

389

}

390

```

391

392

**Usage Examples:**

393

394

```typescript

395

// Register custom modules

396

Quill.register('modules/autosave', AutoSave);

397

Quill.register('modules/mentions', Mentions);

398

399

// Use custom modules

400

const quill = new Quill('#editor', {

401

modules: {

402

autosave: {

403

interval: 5000,

404

url: '/api/documents/123/autosave'

405

},

406

mentions: {

407

source: (searchTerm, renderList) => {

408

// Fetch mention suggestions

409

fetch(`/api/users/search?q=${searchTerm}`)

410

.then(response => response.json())

411

.then(users => {

412

renderList(users.map(user => ({

413

id: user.id,

414

value: user.username,

415

label: user.displayName

416

})));

417

});

418

}

419

}

420

}

421

});

422

```

423

424

### Custom Theme Registration

425

426

Register custom themes with unique UI and behavior.

427

428

```typescript { .api }

429

// Custom theme

430

class DarkTheme extends SnowTheme {

431

static DEFAULTS = {

432

...SnowTheme.DEFAULTS,

433

modules: {

434

...SnowTheme.DEFAULTS.modules,

435

toolbar: [

436

[{ 'header': [1, 2, 3, false] }],

437

['bold', 'italic', 'underline'],

438

[{ 'color': [] }, { 'background': [] }],

439

['link', 'image'],

440

['clean']

441

]

442

}

443

};

444

445

constructor(quill, options) {

446

super(quill, options);

447

this.applyDarkTheme();

448

}

449

450

applyDarkTheme() {

451

this.quill.container.classList.add('ql-dark');

452

this.addDarkStyles();

453

}

454

455

addDarkStyles() {

456

// Add dark theme CSS

457

const style = document.createElement('style');

458

style.textContent = `

459

.ql-dark .ql-editor {

460

background: #2d2d2d;

461

color: #ffffff;

462

}

463

.ql-dark .ql-toolbar {

464

background: #1e1e1e;

465

border-color: #444;

466

}

467

`;

468

document.head.appendChild(style);

469

}

470

}

471

472

// Minimal theme

473

class MinimalTheme extends Theme {

474

static DEFAULTS = {

475

modules: {}

476

};

477

478

constructor(quill, options) {

479

super(quill, options);

480

this.quill.container.classList.add('ql-minimal');

481

}

482

483

init() {

484

// Minimal initialization

485

this.quill.root.classList.add('ql-minimal-editor');

486

}

487

}

488

```

489

490

**Usage Examples:**

491

492

```typescript

493

// Register custom themes

494

Quill.register('themes/dark', DarkTheme);

495

Quill.register('themes/minimal', MinimalTheme);

496

497

// Use custom themes

498

const darkQuill = new Quill('#dark-editor', {

499

theme: 'dark'

500

});

501

502

const minimalQuill = new Quill('#minimal-editor', {

503

theme: 'minimal'

504

});

505

```

506

507

### Registry Querying and Inspection

508

509

Methods for inspecting and querying the registry.

510

511

```typescript { .api }

512

// Check if component is registered

513

const isRegistered = Quill.import('formats/custom') !== undefined;

514

515

// Get all registered formats

516

const formats = Object.keys(Quill.imports)

517

.filter(key => key.startsWith('formats/'))

518

.map(key => key.replace('formats/', ''));

519

520

// Get all registered modules

521

const modules = Object.keys(Quill.imports)

522

.filter(key => key.startsWith('modules/'))

523

.map(key => key.replace('modules/', ''));

524

525

// Get all registered themes

526

const themes = Object.keys(Quill.imports)

527

.filter(key => key.startsWith('themes/'))

528

.map(key => key.replace('themes/', ''));

529

```

530

531

**Usage Examples:**

532

533

```typescript

534

// List available components

535

console.log('Available formats:',

536

Object.keys(Quill.imports)

537

.filter(key => key.startsWith('formats/'))

538

.map(key => key.replace('formats/', ''))

539

);

540

541

console.log('Available modules:',

542

Object.keys(Quill.imports)

543

.filter(key => key.startsWith('modules/'))

544

.map(key => key.replace('modules/', ''))

545

);

546

547

// Check if specific component exists

548

function hasFormat(formatName) {

549

return Quill.import(`formats/${formatName}`) !== undefined;

550

}

551

552

function hasModule(moduleName) {

553

return Quill.import(`modules/${moduleName}`) !== undefined;

554

}

555

556

// Dynamic component loading

557

function loadComponent(type, name) {

558

const path = `${type}/${name}`;

559

const component = Quill.import(path);

560

561

if (!component) {

562

throw new Error(`Component ${path} not found`);

563

}

564

565

return component;

566

}

567

568

// Usage

569

const BoldFormat = loadComponent('formats', 'bold');

570

const ToolbarModule = loadComponent('modules', 'toolbar');

571

```

572

573

### Overriding Existing Components

574

575

Replace built-in components with custom implementations.

576

577

```typescript { .api }

578

// Override built-in bold format

579

class CustomBold extends Inline {

580

static blotName = 'bold';

581

static tagName = 'STRONG';

582

583

static create() {

584

const node = super.create();

585

node.setAttribute('data-custom', 'true');

586

return node;

587

}

588

}

589

590

// Override with overwrite flag

591

Quill.register('formats/bold', CustomBold, true);

592

593

// Override toolbar module

594

class CustomToolbar extends Toolbar {

595

constructor(quill, options) {

596

super(quill, options);

597

this.addCustomFeatures();

598

}

599

600

addCustomFeatures() {

601

// Add custom toolbar features

602

}

603

}

604

605

Quill.register('modules/toolbar', CustomToolbar, true);

606

```

607

608

**Usage Examples:**

609

610

```typescript

611

// Custom implementation of existing components

612

class EnhancedHistory extends History {

613

constructor(quill, options) {

614

super(quill, options);

615

this.addHistoryPersistence();

616

}

617

618

addHistoryPersistence() {

619

// Save history to localStorage

620

this.quill.on('text-change', () => {

621

localStorage.setItem('quill-history', JSON.stringify(this.stack));

622

});

623

624

// Restore history on load

625

const saved = localStorage.getItem('quill-history');

626

if (saved) {

627

this.stack = JSON.parse(saved);

628

}

629

}

630

}

631

632

// Override existing history module

633

Quill.register('modules/history', EnhancedHistory, true);

634

635

// Now all editors use enhanced history

636

const quill = new Quill('#editor', {

637

modules: {

638

history: true // Uses EnhancedHistory

639

}

640

});

641

```