NotectlEditor
NotectlEditor is the <notectl-editor> Web Component — the public entry point to the editor.
Creating an Editor
Section titled “Creating an Editor”Factory Function (Recommended)
Section titled “Factory Function (Recommended)”import { createEditor } from '@notectl/core';
const editor = await createEditor({ placeholder: 'Start typing...', autofocus: true,});document.body.appendChild(editor);Manual Construction
Section titled “Manual Construction”const editor = document.createElement('notectl-editor') as NotectlEditor;document.body.appendChild(editor);await editor.init({ placeholder: 'Start typing...' });Configuration
Section titled “Configuration”interface NotectlEditorConfig { /** Controls which inline marks are enabled (auto-configures TextFormattingPlugin). */ features?: Partial<TextFormattingConfig>; /** Plugins to register (headless mode — no toolbar). */ plugins?: readonly Plugin[]; /** Declarative toolbar layout — shorthand array or full ToolbarConfig. */ toolbar?: ReadonlyArray<ReadonlyArray<Plugin>> | ToolbarConfig; /** Placeholder text shown when editor is empty. */ placeholder?: string; /** Read-only mode. */ readonly?: boolean; /** Focus the editor on initialization. */ autofocus?: boolean; /** Maximum undo history depth. */ maxHistoryDepth?: number; /** * Implicit Markdown behavior: live "shorthand" typing transforms (`# ` -> heading, * `**bold**` -> bold, ...) and Markdown auto-detection on paste. `true` (default) * enables both; `false` keeps typed and pasted Markdown literal; the object form * controls each axis independently. See the Markdown guide. */ markdown?: boolean | { shorthand?: boolean; paste?: 'auto' | 'never' }; /** Theme preset or custom Theme object. Defaults to ThemePreset.Light. */ theme?: ThemePreset | Theme; /** Optional nonce for fallback runtime <style> elements. */ styleNonce?: string; /** Paper size for WYSIWYG page layout. When set, content renders at exact paper width. */ paperSize?: PaperSize; /** Document-level text direction. When set, applies `dir` on the content element. */ dir?: 'ltr' | 'rtl'; /** Editor locale. Defaults to Locale.BROWSER (auto-detect from navigator.language). */ locale?: Locale;}ToolbarConfig
Section titled “ToolbarConfig”When you need control over responsive overflow behavior, pass a ToolbarConfig object instead of the shorthand array:
interface ToolbarConfig { /** Plugin groups defining toolbar layout. */ readonly groups: ReadonlyArray<ReadonlyArray<Plugin>>; /** Responsive overflow behavior. Default: ToolbarOverflowBehavior.BurgerMenu */ readonly overflow?: ToolbarOverflowBehavior;}import { createEditor } from '@notectl/core';import { ToolbarOverflowBehavior } from '@notectl/core/plugins/toolbar';
const editor = await createEditor({ toolbar: { groups: [ [new TextFormattingPlugin()], [new HeadingPlugin()], ], overflow: ToolbarOverflowBehavior.Flow, },});See the Toolbar Configuration guide for details on overflow modes.
Content API
Section titled “Content API”getJSON(): Document
Section titled “getJSON(): Document”Returns the document as a JSON-serializable Document object.
setJSON(doc: Document): void
Section titled “setJSON(doc: Document): void”Replaces the editor content with the given document.
getContentHTML(options?): Promise<string | ContentCSSResult>
Section titled “getContentHTML(options?): Promise<string | ContentCSSResult>”Returns sanitized HTML representation of the document. The return type depends on the options:
// Default — returns inline-styled HTML stringconst html = await editor.getContentHTML();
// Pretty-printed — returns indented HTML stringconst pretty = await editor.getContentHTML({ pretty: true });
// Clean export HTML — no editor-internal data-block-id attributesconst clean = await editor.getContentHTML({ includeBlockIds: false });
// Class-based CSS mode — returns { html, css, styleMap } objectconst { html, css } = await editor.getContentHTML({ cssMode: 'classes' });const { html, css } = await editor.getContentHTML({ cssMode: 'classes', pretty: true });Overloads
Section titled “Overloads”getContentHTML(): Promise<string>;getContentHTML(options: ContentHTMLOptions & { cssMode?: 'inline' }): Promise<string>;getContentHTML(options: ContentHTMLOptions & { cssMode: 'classes' }): Promise<ContentCSSResult>;ContentHTMLOptions
Section titled “ContentHTMLOptions”interface ContentHTMLOptions { readonly pretty?: boolean; readonly cssMode?: CSSMode; // 'inline' (default) | 'classes' readonly includeBlockIds?: boolean; // default: true}ContentCSSResult
Section titled “ContentCSSResult”interface ContentCSSResult { readonly html: string; // HTML with class attributes instead of inline styles readonly css: string; // Collected CSS rules for the classes used readonly styleMap: ReadonlyMap<string, string>; // Maps class names to CSS declarations for round-trip}CSS Mode Details
Section titled “CSS Mode Details”When cssMode: 'classes' is set, dynamic marks (text color, highlight, font size, font family) are serialized as CSS class names instead of inline style attributes. This is useful for rendering exported HTML in strict CSP environments where style-src-attr: 'none' blocks inline styles.
const { html, css } = await editor.getContentHTML({ cssMode: 'classes' });// html: '<p class="notectl-align-center"><strong><span class="notectl-s0">Hello</span></strong></p>'// css: '.notectl-s0 { color: #ff0000; }\n.notectl-align-center { text-align: center; }'Identical style combinations are deduplicated — multiple elements with the same styles share a single class name and CSS rule.
See the CSP guide for integration examples.
Clean HTML Output (includeBlockIds)
Section titled “Clean HTML Output (includeBlockIds)”By default every block element carries a data-block-id attribute. This is part of notectl’s wire format: it lets setContentHTML(getContentHTML()) preserve block identity so the caret stays put across content round-trips driven by external sync (see Round-Trip Identity).
If you treat the output as a final artifact — persisting to a database, validating tags/attributes server-side, rendering it, or handing it to another system — the editor-internal data-block-id is noise. Pass includeBlockIds: false to omit it:
const clean = await editor.getContentHTML({ includeBlockIds: false });// '<p>Hello</p><p>World</p>' — no data-block-id
// Works in class mode tooconst { html } = await editor.getContentHTML({ cssMode: 'classes', includeBlockIds: false });The default (true) keeps the current behavior; this is intentional, since flipping it would silently break the caret for existing binding-based integrations. The trade-off when opting out: round-trips of the cleaned HTML generate fresh IDs and no longer preserve the caret.
setContentHTML(html: string, options?: SetContentHTMLOptions): Promise<void>
Section titled “setContentHTML(html: string, options?: SetContentHTMLOptions): Promise<void>”Parses HTML and sets it as the editor content.
By default each serialized block carries a data-block-id attribute (part of the wire format). When setContentHTML parses HTML produced by getContentHTML, those IDs are adopted so block identity round-trips — this is what keeps the caret stable when an external owner (Angular signal form, RxJS pipe, …) writes back the same content on every keystroke. Externally pasted HTML without data-block-id (including output of getContentHTML({ includeBlockIds: false })) works as before; fresh IDs are generated. See Round-Trip Identity.
getContentMarkdown(options?: MarkdownSerializeOptions): Promise<string>
Section titled “getContentMarkdown(options?: MarkdownSerializeOptions): Promise<string>”Serializes the document to Markdown (CommonMark + GFM). Async and lazy: the Markdown engine is dynamically imported only on first use, so editors that never call it pay no bundle cost.
const md = await editor.getContentMarkdown();const gfm = await editor.getContentMarkdown({ flavor: 'gfm', bullet: '-' });setContentMarkdown(markdown: string, options?: MarkdownParseOptions): Promise<void>
Section titled “setContentMarkdown(markdown: string, options?: MarkdownParseOptions): Promise<void>”Parses Markdown and sets it as the editor content. Async and lazy like getContentMarkdown. Existing top-level block IDs are reused in document order, so setContentMarkdown(await getContentMarkdown()) preserves block identity and keeps the caret stable for unchanged blocks (see Round-Trip Identity).
await editor.setContentMarkdown('# Title\n\nA **bold** paragraph.');These explicit methods are always available regardless of the markdown config option, which only governs implicit shorthand typing and paste auto-detection. See the Markdown guide for serialize/parse options and the full feature matrix.
getText(): string
Section titled “getText(): string”Returns plain text content (blocks joined by \n).
setText(value: string): void
Section titled “setText(value: string): void”Replaces editor content from plain text. Each \n becomes a paragraph.
editor.setText('First paragraph\nSecond paragraph');Existing top-level block IDs are reused in document order, so the caret survives setText(getText()) round-trips. When value equals the current text, the call is a no-op — selection and history remain untouched. See Round-Trip Identity.
isEmpty(): boolean
Section titled “isEmpty(): boolean”Returns true if the editor contains only a single empty paragraph.
Command API
Section titled “Command API”commands
Section titled “commands”Object with convenience methods for common operations. These are a fixed set of shortcuts — for plugin-registered commands, use executeCommand():
editor.commands.toggleBold();editor.commands.toggleItalic();editor.commands.toggleUnderline();editor.commands.undo();editor.commands.redo();editor.commands.selectAll();Returns an object that checks if the built-in convenience commands can be executed. For plugin-registered commands, use executeCommand() directly.
const can = editor.can();can.toggleBold(); // booleancan.toggleItalic(); // booleancan.toggleUnderline(); // booleancan.undo(); // booleancan.redo(); // booleancan.selectAll(); // booleanexecuteCommand(name: string): boolean
Section titled “executeCommand(name: string): boolean”Executes a named command registered by any plugin. Returns true if handled.
canExecuteCommand(name: string): boolean
Section titled “canExecuteCommand(name: string): boolean”Returns whether a named command can be executed.
editor.executeCommand('toggleStrikethrough');editor.executeCommand('insertHorizontalRule');configurePlugin(pluginId: string, config: PluginConfig): void
Section titled “configurePlugin(pluginId: string, config: PluginConfig): void”Updates a plugin’s configuration at runtime.
State API
Section titled “State API”getState(): EditorState
Section titled “getState(): EditorState”Returns the current immutable editor state.
get isReadOnly(): boolean
Section titled “get isReadOnly(): boolean”Returns the current read-only state.
if (editor.isReadOnly) { console.log('Editor is in read-only mode');}dispatch(tr: Transaction): void
Section titled “dispatch(tr: Transaction): void”Dispatches a transaction through the middleware chain.
In read-only mode, mutating transactions are silently dropped at the view layer. Selection-only transactions and transactions flagged via TransactionBuilder.readonlyAllowed() (used by opt-in features like checklist toggling) still apply. This guard is centralized in the view, so plugin-side NodeView controls (e.g. table delete/add-row buttons, code-block delete) are inert in read-only mode without per-plugin code.
Note: low-level state replacement APIs (setJSON, setHTML, replaceState) bypass dispatch and the read-only guard — they always succeed.
Event API
Section titled “Event API”on<K>(event: K, callback): void
Section titled “on<K>(event: K, callback): void”Subscribe to an event.
off<K>(event: K, callback): void
Section titled “off<K>(event: K, callback): void”Unsubscribe from an event.
Events
Section titled “Events”| Event | Payload | Description |
|---|---|---|
stateChange | { oldState, newState, transaction } | Every state change |
selectionChange | { selection: EditorSelection } | Cursor/selection moved |
focus | undefined | Editor gained focus |
blur | undefined | Editor lost focus |
ready | undefined | Initialization complete |
Plugin Service API
Section titled “Plugin Service API”getService<T>(key: ServiceKey<T>): T | undefined
Section titled “getService<T>(key: ServiceKey<T>): T | undefined”Retrieves a typed service registered by any plugin. Returns undefined if not found.
import { TableSelectionServiceKey } from '@notectl/core/plugins/table';
const tableService = editor.getService(TableSelectionServiceKey);tableService?.getSelectedCells();onPluginEvent<T>(key: EventKey<T>, callback: PluginEventCallback<T>): () => void
Section titled “onPluginEvent<T>(key: EventKey<T>, callback: PluginEventCallback<T>): () => void”Subscribes to typed plugin events from outside the plugin system. Returns an unsubscribe function.
import { BEFORE_PRINT, AFTER_PRINT } from '@notectl/core/plugins/print';
const unsubscribe = editor.onPluginEvent(BEFORE_PRINT, () => { console.log('Printing...');});
// Later: unsubscribe();Theme API
Section titled “Theme API”setTheme(theme: ThemePreset | Theme): void
Section titled “setTheme(theme: ThemePreset | Theme): void”Changes the theme at runtime. Accepts a preset string ('light', 'dark', 'system') or a custom Theme object.
import { ThemePreset } from '@notectl/core';
editor.setTheme(ThemePreset.Dark);editor.setTheme(myCustomTheme);getTheme(): ThemePreset | Theme
Section titled “getTheme(): ThemePreset | Theme”Returns the current theme setting.
See the Theming guide for full details on presets, custom themes, and CSS custom properties.
Paper Size API
Section titled “Paper Size API”getPaperSize(): PaperSize | undefined
Section titled “getPaperSize(): PaperSize | undefined”Returns the currently configured paper size, or undefined if the editor uses fluid layout.
import { PaperSize } from '@notectl/core';
editor.configure({ paperSize: PaperSize.DINA4 });editor.getPaperSize(); // 'din-a4'See the Paper Size guide for full details on WYSIWYG page layout and print integration.
Locale API
Section titled “Locale API”locale Config Option
Section titled “locale Config Option”Sets the editor language for all plugins. Defaults to Locale.BROWSER which auto-detects from navigator.language.
import { createEditor, Locale } from '@notectl/core';
const editor = await createEditor({ locale: Locale.DE, toolbar: [/* ... */],});See the Internationalization guide for full details on global and per-plugin locale configuration, custom locales, and available languages.
Markdown API
Section titled “Markdown API”markdown Config Option
Section titled “markdown Config Option”Controls notectl’s implicit Markdown behavior: the live “shorthand” typing transforms (# to heading, **bold** to bold, - to list, and so on) and Markdown auto-detection on paste. Defaults to true (both on).
import { createEditor } from '@notectl/core';
// Literal authoring: typed and pasted Markdown stays as plain textawait createEditor({ markdown: false });
// Keep literal typing, but still auto-detect pasted Markdownawait createEditor({ markdown: { shorthand: false } });This option only affects automatic interpretation. The explicit getContentMarkdown() / setContentMarkdown() methods, the toolbar, and keyboard shortcuts such as Mod-B stay available regardless, so markdown: false removes the typed shorthand, not the bold or heading capability itself. For per-feature control, every shorthand-registering plugin also accepts an inputRule flag.
See the Markdown guide for the full resolution table and per-plugin control.
Lifecycle
Section titled “Lifecycle”whenReady(): Promise<void>
Section titled “whenReady(): Promise<void>”Returns a promise that resolves when the editor is fully initialized.
configure(config: Partial<NotectlEditorConfig>): void
Section titled “configure(config: Partial<NotectlEditorConfig>): void”Updates configuration at runtime. Active side-effects for placeholder, readonly, paperSize, and dir. To change the theme at runtime, use setTheme() instead.
styleNonce is accepted in configure() but evaluated during initialization.
registerPlugin(plugin: Plugin): void
Section titled “registerPlugin(plugin: Plugin): void”Registers a plugin. Must be called before init() or before the element is added to the DOM. Throws if called after initialization.
destroy(): Promise<void>
Section titled “destroy(): Promise<void>”Cleans up the editor. The editor can be re-initialized after destruction.
HTML Attributes
Section titled “HTML Attributes”| Attribute | Description |
|---|---|
placeholder | Placeholder text (reflected) |
readonly | Read-only mode (reflected) |
theme | Theme preset: "light", "dark", or "system" |
paper-size | Paper size: "din-a4", "din-a5", "us-letter", or "us-legal" |
dir | Text direction: "ltr" or "rtl" |