Appearance
Plugins
Generative DOM's core is a plugin host. All markdown syntax -- headings, bold, lists, code blocks -- is implemented as plugins. This guide explains the plugin system architecture and how to work with plugins.
Registering Plugins
Plugins are passed to the GenerativeDom constructor as an array:
ts
import { GenerativeDom } from '@generative-dom/core';
import { markdownBase, markdownInline, markdownHeading } from '@generative-dom/plugins';
const md = new GenerativeDom({
container: document.getElementById('output'),
plugins: [markdownBase(), markdownInline(), markdownHeading()],
});Each plugin is a factory function that returns an GenerativeDomPlugin object. The factory pattern allows plugins to accept configuration options.
Priority System
Plugins declare a priority number. Lower numbers are matched first during tokenization. This determines which plugin gets first chance to handle each position in the buffer.
| Range | Purpose | Examples |
|---|---|---|
| 0--99 | Custom syntax | events (40), interactive (45), custom-elements (50), highlight (95) |
| 100--199 | Block-level markdown | markdown-heading (100), markdown-code (100), markdown-list (100) |
| 200--299 | Inline markdown | markdown-inline (200), markdown-link (200) |
| 300+ | Fallback | markdown-base (300) |
When two plugins have the same priority, registration order breaks the tie (first registered wins).
Dynamic Registration
Plugins can be added after construction:
ts
const md = new GenerativeDom({
container: el,
plugins: [markdownBase()],
});
// Add more plugins later
md.register(markdownHeading());
md.register(markdownInline());Dynamic registration triggers init() on the new plugin and re-sorts the plugin list by priority. Tokens not yet rendered may be re-matched against the new plugin.
Plugins cannot be removed after construction. To change the plugin set, call destroy() and create a new instance.
Plugin Lifecycle
| Method | When Called | Required |
|---|---|---|
init(ctx) | Once, during registration | Optional |
matchBlock(buffer, pos) | Each tokenization cycle, per buffer position | Optional |
matchInline(text, pos) | During inline parsing within blocks | Optional |
render(token, ctx) | Once per token during DOM construction | Required |
cleanup(element) | When a DOM element is recycled or removed | Optional |
destroy() | Once, on GenerativeDom.destroy() | Optional |
A plugin without at least one matchBlock or matchInline method will never produce tokens and effectively does nothing.
Plugin Configuration
Plugins accept options through their factory functions:
ts
import { highlight } from '@generative-dom/plugins';
// Plugin-specific config is the factory function's concern
const highlightPlugin = highlight({
languages: ['javascript', 'typescript', 'html'],
theme: 'dark',
});The core never inspects these options. It only sees the returned GenerativeDomPlugin object.
Error Boundaries
The core wraps every plugin method call in a try/catch:
ts
try { plugin.matchBlock(buffer, pos) } catch (e) { log(e); return null; }
try { plugin.render(token, ctx) } catch (e) { log(e); return createText('[render error]'); }
try { plugin.cleanup(element) } catch (e) { log(e); /* continue */ }
try { plugin.destroy() } catch (e) { log(e); /* continue */ }A broken plugin never crashes the entire Generative DOM instance. Errors are logged with the plugin name via console.error.
Composing Plugin Sets
Since plugins are just objects, you can compose sets for different use cases:
ts
// Minimal set for plain text with headings
const minimal = [markdownBase(), markdownHeading()];
// Full markdown support
const full = [
markdownBase(),
markdownInline(),
markdownHeading(),
markdownCode(),
markdownQuote(),
markdownList(),
markdownLink(),
];
// With syntax highlighting
const withHighlight = [...full, highlight()];See Writing Plugins for a step-by-step guide to creating your own plugin.