API reference
Everything the ModelSyntax extension exposes: install, a minimal editor, every configure() option, the setModelData command, localization, and the re-exported engine.
Install
The model-language engine ships with the package, so there is nothing else to add. React and Tiptap are peer dependencies you already have.
Quick start
Add ModelSyntax to any Tiptap editor and hand it a set of namespaces (for autocomplete) and a field schema (for validation).
import { EditorContent, useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { ModelSyntax } from "tiptap-model-language";
const namespaces = [
{ key: "contact", label: "Contact", fields: [
{ key: "first_name", type: "string", label: "First name" },
{ key: "priority", type: "enum", label: "Priority",
values: ["high", "low"] },
]},
];
const schema = [
{ path: "contact.first_name", type: "string", nullable: true },
{ path: "contact.priority", type: "enum", values: ["high", "low"] },
];
function TemplateEditor() {
const editor = useEditor({
extensions: [
StarterKit,
ModelSyntax.configure({ namespaces, schema }),
],
});
return <EditorContent editor={editor} />;
}Options
Pass any of these to ModelSyntax.configure({…}). Only namespaces and schema are usually needed.
| Prop | Type | Default | Description |
|---|---|---|---|
namespaces | MlNamespace[] | [] | Autocomplete + highlighting groups, keyed by prefix (contact, system, …). The static case; for async or changing data push via setModelData instead. |
schema | FieldSchema | [] | Flattened org field schema (dotted paths, types, nullability, enum values) used by local validation. The static case; push via setModelData for async data. |
skipValidation | boolean | false | Turn off the model-language validate() pass entirely. Structural squiggles (unbalanced blocks, mistyped operators) still render. |
debounceMs | number | 300 | How long to wait after the last keystroke before running validation. |
severities | DiagnosticSeverity[] | ["error", "warning", "info"] | Which severities render inline as squiggles. Drop a level to hide it (e.g. omit info for a quieter editor). |
labels | Partial<ModelLanguageLabels> | {} | Override any user-facing string, merged over the English defaults. See Localization below. |
translateDiagnostic | (d: TemplateDiagnostic) => string | undefined | undefined | Localize an engine diagnostic by its stable code (ML001, ML101, …). Return undefined to keep the engine's English. Branch on d.code, never on d.message. |
onResult | (result: ModelValidationResult) => void | undefined | Fires after every validation pass with the full diagnostics list and token estimate. Use it for a diagnostics panel or a token meter. |
Commands
When the schema loads asynchronously or changes at runtime, push it in with the setModelData command instead of the static options. It re-validates the document right away.
// Push namespaces + schema at runtime. Re-validates immediately.
editor.commands.setModelData({ namespaces, schema });onResult
Every validation pass calls onResult with the full result, so you can render a diagnostics panel or a token meter outside the editor.
| Prop | Type | Default | Description |
|---|---|---|---|
diagnostics | TemplateDiagnostic[] | required | One entry per diagnostic: { code, severity, message, fieldPath? }. |
maxTokenEstimate | number | null | required | Upper-bound token estimate for the rendered template, or null when unavailable. |
ModelSyntax.configure({
namespaces,
schema,
onResult: ({ diagnostics, maxTokenEstimate }) => {
// diagnostics: { code, severity, message, fieldPath? }[]
setErrors(diagnostics);
setTokens(maxTokenEstimate);
},
});Localization
Every string the extension renders is overridable through labels, and engine diagnostics are localized by their stable code through translateDiagnostic. Overrides merge over the English defaults, so you only supply the strings you want to change.
ModelSyntax.configure({
namespaces,
schema,
labels: {
quickFix: "Виправити",
addDefault: "Додати default",
unclosedBlock: (name) => `Не закрито, потрібен {{/${name}}}`,
severity: { error: "помилка", warning: "попередження", info: "інфо" },
},
// Localize engine diagnostics by their stable code.
translateDiagnostic: (d) =>
d.code === "ML101" ? "Невідоме поле." : undefined,
});Every label in labels
| Prop | Type | Default | Description |
|---|---|---|---|
expectedOperator | string | required | Shown when a value path in a condition is not followed by an operator. |
unclosedBlock | (name: string) => string | required | A block opener (if / for / #name) that never closed in scope. |
noOpenBlock | (name: string) => string | required | A close or branch tag with no matching opener. |
addDefault | string | required | Quick-fix button: add a | default: … filter. |
changeTo | (operator: string) => string | required | Quick-fix button: replace a mistyped operator. |
addCloseTag | (tag: string) => string | required | Quick-fix button: insert a close tag, e.g. {{/if}}. |
quickFix | string | required | Fallback quick-fix button text. |
noMatches | string | required | Empty autocomplete menu text. |
severity | Record<DiagnosticSeverity, string> | required | Severity chip text in the hover tooltip (rendered uppercase). |
Engine API
The model-language engine is re-exported from this package, so you can validate and render templates with the exact engine the editor uses, no extra install.
| Prop | Type | Default | Description |
|---|---|---|---|
parse | (source: string) => ParseResult | required | Parse a template string into an AST. Returns { ast, … }. |
validate | (source: string, schema: FieldSchema, opts?) => ValidateResult | required | Validate a template against a schema. Returns { ast, diagnostics, maxTokenEstimate }. |
render | (ast, data: DataSnapshot, schema, opts?) => RenderResult | required | Render an AST against live data. Returns { text, … }. Pass { now } for deterministic dates. |
serialize | (ast) => string | required | Serialize an AST back to its template string. |
import { parse, render, validate } from "tiptap-model-language";
// Validate a template against a schema (same pass the extension runs).
const { diagnostics, maxTokenEstimate } = validate(template, schema);
// Render the final text against live data.
const { ast } = parse(template);
const { text } = render(ast, snapshot, schema, { now: new Date() });