Marks & links

A mark is inline formatting applied to a run — bold, italic, underline, small-caps, a link, ruby annotation. Marks are declared in config.marks as an array of MarkDef, and applied in the model’s runs — to text runs and to object runs alike (a linked inline image is an object run under an href mark). Like block types, marks nest freely, and their definition order is their inference precedence.

MarkDef

Field Type Required Description
id string Unique mark id. One class-safe token.
label string Display label; default: the id.
editor object { "shortcut": string } — e.g. "Mod-b".
style StyleDecls Inline-safe CSS-subset declarations (only the mark-legal properties — see Styling).
syntax object Per-syntax spelling, keyed by syntax id.

A mark’s per-syntax spelling mirrors a block’s but is simpler:

MarkSyntax  = { match?: MarkMatch[], write?: MarkSpelling }
MarkMatch   = { construct?: string, class?: string }   // inline tag + class (HTML), or a marker name
MarkSpelling = { tag?: string, class?: string, delim?: string, construct?: string }
  • tag / class — the HTML inline element and class.
  • delim — the Markdown / Fountain delimiter ("*", "**", "_"; a Markdown link is "[]").
  • construct — the FDX text Style name (Bold / Italic / Underline / Strikeout).

HTML mark inference is class-then-tag, exactly like blocks: a <span class="x"> matches the mark claiming that class before any mark claiming bare <span>.

Example

{
  "id": "em",
  "label": "Italic",
  "editor": { "shortcut": "Mod-i" },
  "syntax": {
    "html":     { "write": { "tag": "em" } },
    "md":       { "write": { "delim": "*" } },
    "fountain": { "write": { "delim": "*" } },
    "fdx":      { "write": { "construct": "Italic" } }
  }
}

A mark with no match in a syntax is not inferred there, but can still be written — so a mark can round-trip through a syntax that can only express it on serialize.

A link is a mark carrying an href attribute (so its MarkUse takes the object form, { id, attrs: { href } }). It may also carry a title — the CommonMark [text](href "title") title, HTML’s <a title>; support is tiered like any attribute. There are two target species:

An external href is a URL of any scheme. It is available wherever the syntax can spell a link — HTML <a>, Markdown [text](url). Support is tiered: Fountain, for example, has no link syntax, so an external link does not survive a round-trip through it.

An internal href is a reference into the document, #-prefixed. Resolution precedence:

Form Resolves to
#^<id> A block ref — the block carrying that id.
#<section-id> A section ref — a config.sections[].id.
#<name> / running number A structural ref to a division — by heading text or running number (“Scene 12”), resolved against the derived outline.

Structural refs work in every syntax and need no stored id — but renaming a heading breaks links to it (as in Markdown or a wiki); an editor may offer to fix references.

Ids are written on demand

A block carries a persisted id in the file only if something targets it — a link, a translation origin anchor — or the user pinned one. The parser never mints ids into the file; ephemeral editor-internal ids are separate and non-persisted. Each syntax has its own id spelling: HTML’s id attribute, Markdown’s Obsidian-style trailing ^id, FDX element ids, JSON trivially. This “on-demand” rule is what keeps content-derived syntaxes lossless — nothing is written that the text doesn’t need.