Skip to content

Search docs

Search Cortex documentation pages and headings.

Plugin Development

Markdown and Properties

Extend Markdown rendering, callouts, plugin-scoped styles, and note property types.

On this page

Markdown extensions require the markdown:extensions capability. Property type extensions require properties:types.

Inline Replacements

Use inline registrations for simple regex replacements:

this.registerMarkdownInline({	id: "smile-shortcode",	pattern: ":smile:",	replacement: { type: "text", content: "smile" },})

Inline registrations are lightweight and shared by editor and renderer consumers.

Semantic Transforms

Use semantic registrations when output should work across Live Preview, Reading View, and export. Semantic output must be portable nodes, not arbitrary DOM.

this.registerMarkdownSemantic({	id: "ticket-links",	selector: { type: "text" },	priority: 10,	transform: ({ node }) => {		if (node.type !== "text" || !node.value.includes("APP-")) return null		return node	},})

Supported portable nodes include text, container, span, link, image, and code.

Callout Types

this.registerCalloutType({	type: "decision",	aliases: ["decide"],	label: "Decision",	color: "var(--accent)",	backgroundColor: "var(--accent-subtle)",})

Later registrations win. Disposing a registration restores the previous definition.

Preprocessors and Unified Processors

Preprocessors and Unified processors run only on reading-view and export surfaces:

this.registerMarkdownPreprocessor({	id: "append-footer",	surfaces: ["reading-view"],	preprocess: (markdown) => `${markdown}\n\n---\nRendered by plugin.`,})

Live Preview integrations should use inline registrations, semantic registrations, callouts, fold providers, or editor extensions instead.

Plugin styles.css

A plugin can include a root styles.css file only when it declares markdown:extensions.

Plugin CSS is scoped to .markdown-surface. Selectors that do not already target .markdown-surface are prefixed by the host. Selectors that escape the Markdown surface with sibling combinators are rejected.

Allowed at-rules:

  • @container
  • @layer
  • @media
  • @supports

Blocked at-rules include @font-face, @import, @keyframes, @namespace, and @page.

Property Types

this.registerPropertyType({	type: "rating",	baseType: "number",	displayName: "Rating",	icon: "star",	deserialize: (value) => Number(value ?? 0),	serialize: (value) => Number(value ?? 0),	validate: (value) => ({		valid: typeof value === "number" && value >= 0 && value <= 5,		message: "Rating must be between 0 and 5",	}),})

Property type ids are namespaced internally by plugin id.