Block type contract
A Block type is plain data. The types come from packages/editor/src/features/composites/block-types.ts, block-types-json.ts and core/properties.ts; the Block type types are exported from @skaile/forge-editor/composites and @skaile/forge-editor/composites/schema; PropertyDefinition and PROPERTY_TYPES only from @skaile/forge-editor/core. For how to write and load them, see Block types in code and JSON.
BlockType
BlockType extends BlockTypeData ({ id, capabilities }). A Block type JSON file is one array of BlockType.
| Field | Type | Meaning |
|---|---|---|
id | string | Stored in the blockType attribute of a Typed block, in Markdown fences and in AI requests. 1 to 50 letters, digits, _ or - (checked by parseBlockTypes). |
label | string | Name shown to the user. |
icon | string | Iconify name, e.g. i-lucide-gavel. |
capabilities | { id: string, config?: unknown }[] | The capabilities the Typed blocks of this type have, with their config. A capability that is not listed is absent. No other field switches a behaviour. |
titlePlaceholder | string? | Placeholder of the empty title row. |
descriptionPlaceholder | string? | Placeholder of the empty description row. |
chrome | { tags?, tone?, controls? }? | How much chrome the type shows (below). Absent: all of it. |
chrome:
tags: falsehides the tag row of the type.toneis a CSS colour orvar(...). A Typed block of the type getsdata-block-type-toneand--sk-block-type-toneon its wrapper (tinted surface and a left border), and its tree rows tint the type icon.controls: falsemakes the chrome of the Typed block read-only although the editor is editable: no basket menu, status menu, title-visibility toggle, tag editing or drag handle on the type icon, and a read-only details popover. An existing basket, status and tags still show as chips. Defaulttrue.
Config of the built-in capabilities
| Interface | Capability | Members |
|---|---|---|
ContainerConfig | container | accepts?: readonly string[] | 'all': the Block types accepted as nested Typed blocks; absent: none. Content that is no Typed block is always allowed. Only new moves are checked. |
PropertiesConfig | properties | definitions: readonly PropertyDefinition[] |
SourceConfig | source | media: readonly SourceMedia[] |
ImportConfig | import | media?: readonly SourceMedia[]: the media the type takes. Absent or empty: the type takes every imported file that no other type takes, text and Markdown included. |
BlockTypeAi | ai-brief | see BlockTypeAi |
SourceMedia is 'image' | 'audio' | 'video' | 'file' (SOURCE_MEDIA_TYPES).
BlockTypeAi
| Field | Meaning |
|---|---|
purpose | One or two sentences: what a Typed block of this type is for and what its body holds. Goes into every AI request's block model (chat, generate, tasks). |
instructions? | Default instruction for filling the body of a Typed block of this type (what to write, structure, tone). |
instructionsField? | Key of a textarea property. A Typed block whose value is non-empty uses it instead of instructions. |
contentTemplate? | AI content template: target text as Markdown. {key} placeholders are replaced by the Typed block's meta[key] before it goes to the model; unknown ones stay. |
instructions, instructionsField and contentTemplate are read only by the AI fill (fillComposite).
PropertyDefinition
One Block property of a Block type, stored under key in the meta attribute of the Typed block. Defined in core/properties.ts.
| Member | Type | Meaning |
|---|---|---|
key | string | Key under which the value is stored in meta. Non-empty; must not start with $ (reserved for the pseudo-fields of a Block query). |
label | string | Label in the inspector, the details popover and the table. |
type | 'text' | 'textarea' | 'date' | 'number' | 'select' | 'multiselect' | 'reference' | 'bibliography' | 'derivation' | 'checkbox' | 'url' | 'status' | 'reference-list' | 'source-anchor' | The entries of PROPERTY_TYPES. bibliography: CSL-JSON item under meta.bib. derivation: read-only Derivation of a Derived block. reference: block ID of another Typed block. multiselect: several of options, stored as string[]; the key is removed when none is chosen. checkbox: true; unchecked removes the key. url: a link as text. status: one of options, whose options carry a group. reference-list: BlockAddress[] (documentId '' = this Document). source-anchor: SourceAnchor, a passage of a PDF (page, rects, quote); read-only in the form. |
options | PropertyOption[]? ({ value: string, label: string, group?: 'todo' | 'doing' | 'done' }) | Choices of a select, multiselect or status property; required for multiselect. group belongs to status options. |
targets | readonly string[]? | reference only: the Block type ids the reference may point to. Absent: any Typed block. |
hidden | boolean? | Stored and queried like any property, but not shown in the Block inspector, the details popover or the Block table. |
For the behaviour of each type, see Block properties. PROPERTY_KINDS (@skaile/forge-editor/core) maps every type to a PropertyKind (ops, sortKey, groupKey, aggregates, text, longText, align); the filter, aggregate and environment types it uses (FilterOp, AggregateFn, QueryEnv) live in core/query-types.ts.
Functions
| Export | Returns |
|---|---|
defaultBlockTypes(t) | the built-in Block types in the language of translator t (createTranslator('de')) |
blockTypeOf(blockTypes, id) | the Block type with that id; an unknown id gets the presentation of the section type (else the first type) under its own id and label |
blockTypeConfigOf(type, id), listsCapability(type, id) | the config the type gives capability id (undefined when it gives none) and whether it lists it. Both are in @skaile/forge-editor/core (Block capabilities). |
isRootBlockType(type) | whether the container config of the type says root: true: the type stands for the file of a document tree (the built-in Document type) |
propertiesOf(type) | the Property definitions of the type (none without the properties capability) |
sourceMediaOf(type) | the media of its source capability, or undefined |
aiBriefOf(type) | its ai-brief config, or undefined (the AI fill is not offered then) |
acceptsChild(blockTypes, parentType, childType) | whether parentType accepts childType as nested Typed block; a parent type the list does not know accepts nothing |
acceptsImport(blockTypes, parentType) | whether an imported file can go into a Typed block of parentType |
importTypeOf(blockTypes, media) | the id of the Block type that takes an imported file of media (null: text, Markdown or another file the media pipeline does not know): the first type whose import names media, else the first that lists import without media; undefined when none takes it |
aiBlockTypesOf(blockTypes) | the Block types as the AI chat model learns them: AiContextBlockType[], { id, label, fields, purpose? } (fields: the Property definitions) |
JSON files
| Export | Does |
|---|---|
parseBlockTypes(input, { registry? }) | reads a JSON array of BlockType (text or parsed value) and checks each entry: the fields (strict: unknown members are refused), a unique id, every listed capability exists and is listed once, and its config passes the configSchema. A capability without a configSchema takes no config. registry defaults to the built-in capabilities and those of the composites feature; pass preset.capabilities when a Feature adds more. Throws BlockTypeJsonError. |
mergeBlockTypes(base, extra) | layers extra over base: a type with an id that base has replaces it in place, the others follow |
BlockTypeJsonError | Error with issues: BlockTypeJsonIssue[]; message has one line per issue |
BlockTypeJsonIssue | { index, typeId, capability, path, message }: the entry's position (-1 for the file), its id (null when it has none), the capability whose config is wrong (null for the entry itself) and the path inside the entry or config, e.g. definitions[0].type |
Where Block types go in
| Place | Option |
|---|---|
compositeFeature({ blockTypes }) (Vue and React), compositeFeatureCore(ui, { blockTypes }) | the Block types of the editor |
compositeSchemaExtensions({ blockTypes }) | schema only; Block types are storage, not schema |
composeEditor({ blockTypes }), AdvancedEditor and ComposedSurface prop blockTypes | the same, for the surfaces |
installEditorServer(nitroApp, { ports, blockTypes }), provideEditorServer({ ports, blockTypes }), AiHandlerContext.blockTypes | the server side: the AI chat describes these types to the model |
Without blockTypes the built-in types apply: the editor falls back to defaultBlockTypes in the language of its translator.