mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 09:41:11 +00:00
fix(matrix): compact loose list HTML for consistent Element rendering
Loose lists (blank lines between items) produce <li><p>...</p></li> via markdown-it, causing Element to render list numbers on separate lines from their content. Fix by setting hidden=true on paragraph tokens inside list items before rendering, mirroring what markdown-it already does for tight lists. Closes #60997. Thanks @gucasbrg. Co-Authored-By: Claude claude-opus-4-6 <noreply@anthropic.com> Signed-off-by: Jakub Rusz <jrusz@proton.me>
This commit is contained in:
committed by
Peter Steinberger
parent
26a5ab1c6f
commit
be5eebd3d4
@@ -50,6 +50,55 @@ describe("markdownToMatrixHtml", () => {
|
||||
expect(html).toContain("<br");
|
||||
});
|
||||
|
||||
it("compacts loose ordered lists without paragraph tags", () => {
|
||||
const html = markdownToMatrixHtml("1. first\n\n2. second\n\n3. third");
|
||||
expect(html).toContain("<ol>");
|
||||
expect(html).toContain("<li>");
|
||||
expect(html).not.toContain("<p>");
|
||||
});
|
||||
|
||||
it("compacts loose unordered lists without paragraph tags", () => {
|
||||
const html = markdownToMatrixHtml("- one\n\n- two\n\n- three");
|
||||
expect(html).toContain("<ul>");
|
||||
expect(html).not.toContain("<p>");
|
||||
});
|
||||
|
||||
it("keeps tight lists unchanged", () => {
|
||||
const html = markdownToMatrixHtml("- one\n- two");
|
||||
expect(html).toContain("<ul>");
|
||||
expect(html).not.toContain("<p>");
|
||||
});
|
||||
|
||||
it("preserves inline formatting in loose lists", () => {
|
||||
const html = markdownToMatrixHtml("1. **bold**\n\n2. _italic_");
|
||||
expect(html).toContain("<strong>bold</strong>");
|
||||
expect(html).toContain("<em>italic</em>");
|
||||
expect(html).not.toContain("<p>");
|
||||
});
|
||||
|
||||
it("does not strip paragraph tags outside lists", () => {
|
||||
const html = markdownToMatrixHtml("Hello\n\nWorld");
|
||||
expect(html).toContain("<p>Hello</p>");
|
||||
expect(html).toContain("<p>World</p>");
|
||||
});
|
||||
|
||||
it("compacts nested sublists without paragraph tags", () => {
|
||||
const html = markdownToMatrixHtml("1. parent\n\n - child\n\n2. other");
|
||||
expect(html).toContain("<ol>");
|
||||
expect(html).toContain("<ul>");
|
||||
expect(html).not.toContain("<p>");
|
||||
});
|
||||
|
||||
it("compacts loose lists with mentions via renderMarkdownToMatrixHtmlWithMentions", async () => {
|
||||
const result = await renderMarkdownToMatrixHtmlWithMentions({
|
||||
markdown: "1. hello @alice:example.org\n\n2. bye",
|
||||
client: createMentionClient(),
|
||||
});
|
||||
expect(result.html).not.toContain("<p>");
|
||||
expect(result.html).toContain('href="https://matrix.to/#/%40alice%3Aexample.org"');
|
||||
expect(result.mentions).toEqual({ user_ids: ["@alice:example.org"] });
|
||||
});
|
||||
|
||||
it("renders qualified Matrix user mentions as matrix.to links and m.mentions metadata", async () => {
|
||||
const result = await renderMarkdownToMatrixHtmlWithMentions({
|
||||
markdown: "hello @alice:example.org",
|
||||
|
||||
@@ -309,9 +309,28 @@ function mutateInlineTokensWithMentions(params: {
|
||||
return { children: nextChildren, roomMentioned };
|
||||
}
|
||||
|
||||
// Compact loose lists by hiding paragraph tokens inside list items,
|
||||
// mirroring what markdown-it already does for tight lists. Without this
|
||||
// Element renders <p> margins inside <li>, splitting numbers from content.
|
||||
function compactLooseListTokens(tokens: MarkdownToken[]): void {
|
||||
let insideListItem = 0;
|
||||
for (const token of tokens) {
|
||||
if (token.type === "list_item_open") {
|
||||
insideListItem++;
|
||||
} else if (token.type === "list_item_close") {
|
||||
insideListItem--;
|
||||
} else if (insideListItem > 0) {
|
||||
if (token.type === "paragraph_open" || token.type === "paragraph_close") {
|
||||
token.hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function markdownToMatrixHtml(markdown: string): string {
|
||||
const rendered = md.render(markdown ?? "");
|
||||
return rendered.trimEnd();
|
||||
const tokens = md.parse(markdown ?? "", {});
|
||||
compactLooseListTokens(tokens);
|
||||
return md.renderer.render(tokens, md.options, {}).trimEnd();
|
||||
}
|
||||
|
||||
async function resolveMarkdownMentionState(params: {
|
||||
@@ -366,6 +385,7 @@ export async function renderMarkdownToMatrixHtmlWithMentions(params: {
|
||||
client: MatrixClient;
|
||||
}): Promise<{ html?: string; mentions: MatrixMentions }> {
|
||||
const state = await resolveMarkdownMentionState(params);
|
||||
compactLooseListTokens(state.tokens);
|
||||
const html = md.renderer.render(state.tokens, md.options, {}).trimEnd();
|
||||
return {
|
||||
html: html || undefined,
|
||||
|
||||
Reference in New Issue
Block a user