Skip to content
QuireKit
Core docs
Alpha · evolving API
Extension developmentView Markdown ↗

Input rules and state plugins

Turn ==text== into a mark and observe selection through a PM plugin without duplicating content.

Input rules transform a pattern the user just typed. PM plugins retain state derived from transactions. Both belong to the same Editor; neither requires a second text model synchronized through page listeners.

Input rules: convert after the closing delimiter

The readingHighlight example converts ==important== when the final = is typed. It removes the delimiters and marks the captured text. The last regular-expression capture is the text to retain; $ anchors the match at the current input position.

core/rules/highlight.tsts

Add createReadingHighlight() to the Editor's extensions. The persisted tone defaults to note and maps to HTML data-tone. parseHTML() recognizes only this application's marked elements instead of assigning the same business meaning to every external <mark>.

renderHTML() covers DOM and HTML conversion. Canvas/SVG require a separate paint-style declaration. An HTML tag does not automatically paint a canvas mark. The example also declares no Markdown ==...== conversion: a typing shortcut and a file format are separate contracts.

Paste rules: process multiple matches

Input rules usually anchor at the end. Paste rules use g to process every match in the pasted text. Both example rules produce the same canonical mark and JSON meaning.

setContent(), JSON restoration and server imports should not depend on interactive input rules. Use the relevant format parser. Creation options enableInputRules / enablePasteRules can disable rules; see Editor options.

PM plugins: retain only the required projection

To show selection positions, createSelectionRange(notify) stores from / to under a PluginKey. Query it through readSelectionRange(editor.state.pm). Its mounted plugin view notifies UI only when the range changes.

core/rules/selection.tsts

Install createSelectionRange(range => updateSelectionLabel(range)), supplying your application's UI function. These are document positions, not character counts: structural boundaries, UTF-16 and multiple blocks affect their difference.

apply() compares two positions without scanning content or issuing commands. Reusing the old value tells the observer that no UI update is required. Headless Core retains plugin state; view() and its UI notifications exist only while the corresponding view is mounted.

Resources and verification

Release DOM listeners, timers and subscriptions in the plugin view's destroy(). Extension onDestroy can release resources owned by the whole Editor. Avoid asynchronous work in apply() or unconditional observer writes that form transaction loops.

Test typing, two matches in one paste, undo, JSON restoration and selection movement separately. Restoration must not rerun regular-expression conversion, and selection changes must not modify content. Use filterTransaction / appendTransaction only when your feature needs to affect the global transaction flow; see the extension API.

QuireKitQuireKit · Core, plugins and Classic docs