diff --git a/frontend/src/postBodyDisplay.test.ts b/frontend/src/postBodyDisplay.test.ts index 743739697..d757a77b0 100644 --- a/frontend/src/postBodyDisplay.test.ts +++ b/frontend/src/postBodyDisplay.test.ts @@ -61,6 +61,28 @@ describe("splitPostBody", () => { ]); }); + it("preserves nested HTML list depth as semantic indentation", () => { + expect( + splitPostBody("
  1. Parent
    1. Child
  2. Sibling
"), + ).toEqual([ + { kind: "text", text: "Parent" }, + { kind: "text", text: "Child", indentLevel: 1 }, + { kind: "text", text: "Sibling" }, + ]); + }); + + it("preserves nested list depth when item text is wrapped in a block child", () => { + expect( + splitPostBody( + "
  1. Parent

    1. Child

  2. Sibling

", + ), + ).toEqual([ + { kind: "text", text: "Parent" }, + { kind: "text", text: "Child", indentLevel: 1 }, + { kind: "text", text: "Sibling" }, + ]); + }); + it("labels HTML, Word, and OOXML footnotes in the fallback renderer", () => { expect( splitPostBody( diff --git a/frontend/src/postBodyDisplay.ts b/frontend/src/postBodyDisplay.ts index 1f69962e9..5c59470f1 100644 --- a/frontend/src/postBodyDisplay.ts +++ b/frontend/src/postBodyDisplay.ts @@ -143,11 +143,20 @@ function indentMarker(width: number): string { function stripHtmlTags(text: string): string { text = markFootnoteTags(text).replace(/]*>(.*?)<\/sup>/gi, "^$1"); + let listDepth = 0; const withBoundaries = text .replace(BREAK_TAG, "\n") .replace(BLOCK_TAG, (tag) => { - if (/^<\//.test(tag)) return "\n\n"; - return `\n\n${indentMarker(declaredIndentWidth(tag))}`; + const name = tag.match(/^<\/?\s*([a-z0-9:]+)/i)?.[1]?.toLowerCase() ?? ""; + const closing = /^<\//.test(tag); + if (name === "ul" || name === "ol") { + if (closing) listDepth = Math.max(0, listDepth - 1); + else listDepth += 1; + return "\n\n"; + } + if (closing) return "\n\n"; + const nestedListIndent = !closing && listDepth > 0 ? Math.max(0, listDepth - 1) * 4 : 0; + return `\n\n${indentMarker(declaredIndentWidth(tag) + nestedListIndent)}`; }) .replace(WORD_INDENT_TAG, (tag) => indentMarker(declaredIndentWidth(tag))); const withoutTags = withBoundaries.replace(HTML_TAG, (tag) => {