Skip to content

Mastodon Format ​

Mastodon uses a constrained subset of HTML for rich text in posts and profiles. RelationalText imports and exports this format using the org.joinmastodon.facet namespace with feature names matching the HTML element names.

Functions ​

ts
import { from, to } from 'relational-text/registry'

from('mastodon', html: string): Document ​

Parse a Mastodon / ActivityPub HTML string into a Document.

ts
const doc = from('mastodon', 
  '<p>Hello <span class="h-card"><a href="/@alice" class="u-url mention">@alice</a></span>!</p>'
)

Handles Mastodon's constrained HTML:

  • <p> paragraphs
  • <br /> line breaks
  • <a href> hyperlinks
  • <strong> / <em> inline marks
  • <code> / <pre> code elements
  • <span class="h-card"> mention microformat
  • <a class="hashtag"> hashtag links

to('mastodon', doc: Document): string ​

Render a Document to a Mastodon-compatible HTML string.

ts
const html = to('mastodon', doc)
// '<p>Hello <span class="h-card"><a href="/@alice" class="u-url mention">@alice</a></span>!</p>\n'

Automatically applies any registered lenses targeting org.joinmastodon.facet via lensGraph.autoTransform(). This means a Document parsed from Markdown will automatically convert CommonMark features to Mastodon features before rendering.

ensureMastodonLexicon(): void ​

Register the Mastodon lexicon and the Mastodon→CommonMark lens. Called automatically by from('mastodon', ...) and to('mastodon', ...) on first use. Safe to call multiple times.

Feature Mapping ​

Inline Marks ​

HTMLFeature nameNotes
<strong>, <b>strong
<em>, <i>em
<code>code
<a href>aattrs: href
<br />brentity, 1 newline character in text
Mention microformatmentionattrs: href, handle
Hashtag linkhashtagattrs: href, tag

Block Elements ​

HTMLFeature name
<p>p
<pre>pre

Lens Integration ​

When ensureMastodonLexicon() is called, it registers a Mastodon→CommonMark lens with autoApply: true. This means:

  • to('markdown', mastodonDoc) works without any extra conversion steps
  • to('mastodon', markdownDoc) works via the inverse CommonMark→Mastodon lens
ts
import { from, to } from 'relational-text/registry'

// Mastodon → Markdown
const mastodonHtml = '<p>Hello <strong>world</strong>!</p>'
const doc = from('mastodon', mastodonHtml)
const md = to('markdown', doc)
// 'Hello **world**!\n\n'

// Markdown → Mastodon
const mdDoc = from('markdown', 'Hello **world**!')
const mastodon = to('mastodon', mdDoc)
// '<p>Hello <strong>world</strong>!</p>\n'

Mastodon-to-CommonMark Lens ​

The lens JSON is included in the package and registered automatically:

json
{
  "$type": "org.relationaltext.lens",
  "source": "org.joinmastodon.facet",
  "target": "org.commonmark.facet",
  "rules": [
    { "match": { "name": "strong" }, "replace": { "name": "strong" } },
    { "match": { "name": "em" },     "replace": { "name": "emphasis" } },
    { "match": { "name": "code" },   "replace": { "name": "code-span" } },
    { "match": { "name": "a" },
      "replace": { "name": "link", "renameAttrs": { "href": "uri" } } },
    { "match": { "name": "mention" },
      "replace": { "name": "link", "renameAttrs": { "href": "uri" }, "dropAttrs": ["handle"] } },
    { "match": { "name": "hashtag" },
      "replace": { "name": "link", "renameAttrs": { "href": "uri" }, "dropAttrs": ["tag"] } },
    { "match": { "name": "br" },    "replace": { "name": "line-break" } },
    { "match": { "name": "p" },     "replace": { "name": "paragraph" } },
    { "match": { "name": "pre" },   "replace": { "name": "code-block" } }
  ]
}

Examples ​

Parse a Mastodon mention ​

ts
import { from } from 'relational-text/registry'

const html = `
  <p>Hello
    <span class="h-card">
      <a href="https://mastodon.social/@alice" class="u-url mention">@alice</a>
    </span>,
    check out <a href="https://example.com">this link</a>
    and <a href="/tags/rust" class="mention hashtag" rel="tag">#<span>rust</span></a>
  </p>
`
const doc = from('mastodon', html)
const json = doc.toJSON()
// text: "\uFFFCHello @alice, check out this link and #rust"
// facets: mention, link, hashtag features

Cross-format conversion ​

ts
import { from, to } from 'relational-text/registry'

const doc = from('mastodon', '<p><strong>Bold</strong> and <em>italic</em></p>')
const html = to('html', doc)
// '<p><strong>Bold</strong> and <em>italic</em></p>\n'