Skip to content

Send Mastodon plain text instead of HTML it shows literally - #447

Merged
paulocastellano merged 2 commits into
mainfrom
fix/mastodon-plain-text
Oct 25, 2026
Merged

paulocastellano merged 2 commits into
mainfrom
fix/mastodon-plain-text

Conversation

@paulocastellano

Copy link
Copy Markdown
Contributor

Problem

A Mastodon post whose content still held editor markup went out with the tags showing:

<p>Teste TryPost <strong>negrito</strong> e <em>itálico</em></p><p>Segunda linha #teste example.com</p>

ContentSanitizer gave Mastodon its own arm (stripUnsafeHtml), which kept <p>, <strong>, <em>, <b>, <i>, <a>, <br> and <span>. It was added on 2026-04-01 (57ad4e2c7) on the assumption that Mastodon accepts an HTML subset. It does not:

  • API: POST /api/v1/statuses takes status as "the text content of the status", and no parameter selects HTML. Forks such as glitch-soc accept content_type, but we never send it.
  • Source: app/lib/text_formatter.rb runs every piece of text that is not a URL, hashtag or mention through h() (an HTML escape), then builds its own <p> and <br> from line breaks with simple_format.

Text typed in today's composer is plain and was not affected. Only content that still holds HTML from the old rich editor was.

Fix

  • Server: Mastodon now goes through the default plain-text arm, like X, Threads or Instagram. Tags are removed, block tags become line breaks and entities are decoded. Mastodon then links hashtags, mentions and URLs on its own. stripUnsafeHtml() and the now unused TAG_PATTERN are gone, and the Mastodon arm of displayText() too.
  • Composer: the character counter in resources/js/lib/characters.ts loses its Mastodon exception, so it still counts what the server sends. With no per-network rule left, displayText() and displayLength() no longer take a platform.
  • Line breaks: three or more line breaks now collapse to two on Mastodon, as on every other network. Readers see no difference, because simple_format already renders \n\n\n like \n\n.

Tests

  • Sanitizer: the tests that required HTML on Mastodon are replaced by six cases. They cover editor paragraphs, a link that keeps its text, scripts and blocks, entities decoded once, plain text left as typed, and collapsed line breaks. Each asserts both sanitize() and displayText().
  • Publisher: MastodonPublisherTest covers a post and a thread reply that still hold editor HTML, and checks the plain text sent in each status.
  • Counter: DisplayLengthParityTest keeps the composer counter and the server in step.
  • Results: without the fix, the 6 new tests fail. The full suite passes, and so do types and lint.
  • Manual check: a draft holding the HTML above was published from local to a real Mastodon account. Before the fix, it showed the tags. After it, it showed two lines with #teste as a hashtag, the link clickable and the link card.

Not in this PR

  • Telegram: the stray > turned out not to be a problem. tdlib's parse_html (td/telegram/MessageEntity.cpp) copies every byte except < and a decodable & as text.
  • The preview of an old post with HTML: in the post list and the details dialog it still shows the stored markup on every network. That is left as it is for now.

Paulo Castellano added 2 commits October 23, 2026 12:09
Mastodon takes status as plain text and HTML-escapes all of it (TextFormatter#rewrite), so a post that still held editor markup published as <p>Hello <strong>x</strong></p>. Mastodon now goes through the same plain-text arm as the other networks, and the composer counts its text the same way.
@paulocastellano
paulocastellano merged commit a1fef5a into main Oct 25, 2026
17 of 19 checks passed
@paulocastellano
paulocastellano deleted the fix/mastodon-plain-text branch October 25, 2026 18:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant