Context
Glossary of canonical terms for morg. Terms are singular, capitalized as shown.
Round Trip
One full conversion cycle returning to the starting format: md → org → md or org → md → org.
Canonical Form
The stable output shape morg produces for a format (default stringifier settings). Every Round Trip lands on Canonical Form.
Convergence
The core correctness guarantee: for any input x, roundtrip(roundtrip(x)) === roundtrip(x) byte-for-byte, in both directions. Corollary: input already in Canonical Form is a Round Trip identity (roundtrip(x) === x). Strict byte-losslessness for arbitrary input is explicitly a non-goal.
Recorded Style
The document-level Markdown style knobs (bullet, emphasis, strong,
fence, rule marker and repetition) detected in a Markdown source and
stored in the org file as a #+MORG_MARKDOWN_STYLE: keyword, so org → md
restores the source's own markers instead of Canonical Form. Opt-in
(recordStyle), and document-wide: a marker the source uses two ways
is not recorded, because there is no honest single answer. It does not
weaken Convergence; it widens the set of inputs for which the Round
Trip is already an identity (ADR 0004).
Frontmatter Block
The org comment block, #+begin_comment morg_frontmatter, that carries
a Markdown file's YAML frontmatter verbatim and inert through org. Its
counterpart: morg_keywords, the frontmatter entry that carries an org
file's leading keywords through Markdown (ADR 0005).
Md-ism
A Markdown construct with no native Org equivalent. Preserved during md → org as org properties so the Round Trip can restore it.
Org-ism
An Org construct with no native Markdown equivalent. Serialized during org → md as key:: value conventions by default.
Web UI
The client-side converter hosted on GitHub Pages. Conversions run entirely in the browser; no input ever leaves it. Opening and saving files is the browser reading and writing local files, not a transfer. The UI therefore says "Open file" and "Download", never "Upload".
Embed Page
The chrome-less converter page of the Web UI, designed to be iframed, both by the Web UI's own converter page and by third-party sites. Its height is the host's to set but morg's to know, so it reports it (see Height Message) rather than leaving every host to guess the same number.
Height Message
What the Embed Page posts to its host whenever its content height
changes: { type: "morg:height", height }. It is the page's own
layout and nothing else: no host ever learns what is being converted.
The converter page is its first host: it sizes its own frame to it,
the same way the README asks a third-party host to.
End-to-End Test
A Playwright spec under tests/e2e/, run against the built Web UI
served by vite preview, not the dev server and not happy-dom. It
exists for what neither of those can answer: whether the conversion
worker really runs, whether a real file, clipboard or download behaves,
and what axe makes of the accessibility tree.
Source Layout
The src/ root is the public library surface: everything index.ts
exports lives there (the pipeline modules markdownToOrg.ts /
orgToMarkdown.ts, their composition normalize.ts, plus config.ts
and options.ts). src/core/ is internal AST machinery, reachable
only through the root pipelines: adapters (src/cli/, web/)
import root modules, never core/. src/presets/ holds the dialect
plugins (see Preset) and their registry.
Anything both adapters need lives in src/ rather than in either of
them: conversionOptions.ts layers explicit overrides over the config,
withinFormat.ts translates or normalizes when one format is on both
sides, and presets/registry.ts maps a preset name to a Preset. Duplicating
one of those in an adapter is how the two drift apart.
web/src/ splits the same way, for a reason the bundler enforces.
web/src/pipeline/ is everything that reaches src/: convert.ts,
the runner.ts that decides where a conversion happens, and the
worker.ts it speaks to. The rule is about convert.ts specifically:
no static import may reach it from the main bundle. runner.ts is
light and statically imported, but gets to convert.ts through a
dynamic import(), which is what keeps the embed bundle at 20 kB
rather than 340. web/src/ui/ is the converter page's controls, and
imports pipeline/ for types only. The entries (main.ts, site.ts)
and direction.ts (the pipeline's vocabulary, deliberately free of
the pipeline itself, because ui/ reads it on every keystroke) sit at
the root. tests/web/ mirrors this.
Preset
A named bundle of dialect-specific transforms applied on top of the dialect-agnostic core (e.g. logseq). The core pipelines never contain dialect knowledge. Options that only have observable effect in a dialect are scoped to their Preset, not the core. A Preset may also take a whole conversion over (Logseq: a page is an outline of blocks, each converted as a fragment by the core). A Preset has a Dialect per format it knows (Logseq: md and org; Obsidian: md only).
Dialect
One editor's flavour of one format (Logseq org, Logseq md, Obsidian md). A Preset supplies at most one Dialect per format; reading and writing a Dialect are separate halves.
Vanilla
The Preset with no Dialect: plain Org, and Markdown as CommonMark, GFM, footnotes, $ math and YAML frontmatter, as the core reads and writes them. CommonMark and GFM are not Presets but Vanilla's base (ADR 0007). A named value, so one side can be set back to it.
Avoid: none, default, generic
Spelling
How a format writes a construct. For an org construct Markdown cannot write losslessly in its own syntax, Markdown has a second spelling, HTML (a definition list as Term / : def or <dl>); [markdown.output] picks which one to write, and the reader reads its Dialect's own spelling always, the HTML one only when [markdown.input.interpretHtml] asks. A construct with no spelling in the output goes through verbatim, as org text org re-parses (ADR 0007).
Avoid: fallback, HTML mode
Input / Output Section
The config sections of one format, named by side as Presets are: [markdown.input] is about reading Markdown, [markdown.output] and [org.output] about writing it, [markdown] sets both of Markdown's sides. A conversion uses the sections of the formats it reads and writes, so the same option means the same in md → org, org → md and md → md (ADR 0007).
Avoid: markdownToOrg, orgToMarkdown
Input Preset / Output Preset
The Preset whose Dialect the input is read in, and the one the output is written in. They may differ (Logseq org → Vanilla md); preset sets both. Asking a Preset for a Dialect it lacks (Obsidian as the Input Preset of org) is an error. Never auto-detected, nor hinted at by a warning: a page often carries no telling syntax, and a vault must convert the same way file by file.
Block
One unit of a Logseq page's outline. On the Vanilla side a Block is what each format natively calls an outline unit: a headline in org (so planning still reaches the agenda), a list item in Markdown. A Block's meta (task marker, planning, properties) travels with it: in Markdown as a task checkbox and key:: value lines inside the item.
Avoid: node, bullet
Translation
A conversion within one format between two Dialects (Logseq org → Vanilla org). It changes only what the Dialects write differently and keeps a Block's content as written; one Dialect on both sides is normalizing instead (ADR 0006). Avoid: same-format conversion
Carried Construct
A Dialect construct with no Vanilla equivalent (a page or block ref, a macro, a query, a highlight). The Input Preset keeps it marked rather than interpreting it; a Dialect's writer renders it in its own syntax, a Vanilla writer as literal text, so the return trip can restore it. Avoid: passthrough, verbatim node