fix(composer): no LaTeX conversion inside a typed markdown fence or backtick span (#184 O3)

In markdown mode each paragraph line is serialised before parseBlockMD
joins them, so $x$ inside a ``` fence became data-mx-maths markup
inside the resulting <pre><code> (rendered as math in a code block).
Track fence state across lines and skip math for fenced lines and for
backtick code spans. Unit tests added; verified in the browser.

Also: scripts/dev-homeserver.sh enables MSC4140 delayed events so
scheduled messages work locally.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
2026-09-18 18:26:15 -04:00
co-authored by Claude Opus 5
parent 58a716c734
commit 22371f8156
3 changed files with 48 additions and 0 deletions
+20
View File
@@ -36,6 +36,18 @@ const textToCustomHtml = (node: Text, opts: OutputOptions): string => {
// applied inside inline code. Non-math text recurses with allowMath off so it
// still gets the normal marks + inline-markdown treatment.
if (opts.allowMath && !node.code) {
// Markdown inline code spans (`…`) are literal too: apply math only to the
// text between them (Gitea #184 O3 — `$x$` inside backticks stayed math).
if (opts.allowInlineMarkdown && /`[^`]*`/.test(node.text)) {
return node.text
.split(/(`+[^`]*`+)/)
.map((part) =>
part.startsWith('`')
? textToCustomHtml({ ...node, text: part }, { ...opts, allowMath: false })
: textToCustomHtml({ ...node, text: part }, opts),
)
.join('');
}
const segments = splitMathSegments(node.text);
if (segments.some((seg) => seg.type !== 'text')) {
return segments
@@ -128,12 +140,20 @@ export const toMatrixCustomHTML = (
opts: OutputOptions,
): string => {
let markdownLines = '';
// Inside a markdown ``` fence every line is literal: no `$…$` math conversion
// (the fence is only recognised by parseBlockMD after the lines are joined,
// which is too late — Gitea #184 O3).
let inFence = false;
const parseNode = (n: Descendant, index: number, targetNodes: Descendant[]) => {
if (opts.allowBlockMarkdown && 'type' in n && n.type === BlockType.Paragraph) {
const isFenceLine = /^\s*```/.test(toPlainText(n, false));
const literal = inFence || isFenceLine;
if (isFenceLine) inFence = !inFence;
const line = toMatrixCustomHTML(n, {
...opts,
allowInlineMarkdown: false,
allowBlockMarkdown: false,
allowMath: opts.allowMath && !literal,
})
.replace(/<br\/>$/, '\n')
.replace(/^(\\*)&gt;/, '$1>');