From 7bfe1c18fc658a0b8a239c938aed3e0b684b6120 Mon Sep 17 00:00:00 2001 From: Oleg Samarin Date: Thu, 7 May 2026 22:48:27 +0300 Subject: [PATCH] Fixed markdown preview not rendering nested list structure Replaced flat ordered/unordered list rendering with a stack-based nested renderer that respects indentation levels, producing proper nested
    /"); + typeStack.set(typeStack.size() - 1, ordered); + html.append(ordered ? "
      " : "
        "); + } + } + + String content = line.replaceFirst("^\\s*(?:\\d+\\.|[-*])\\s+", ""); + html.append("
      • ").append(inlineToHtml(content)).append("
      • "); + i++; + } + + for (int k = typeStack.size() - 1; k >= 0; k--) { + html.append(typeStack.get(k) ? "
    " : ""); + } + return i; + } + + private static int leadingSpaces(String line) { + int n = 0; + while (n < line.length() && line.charAt(n) == ' ') n++; + return n; + } + private MarkdownRenderer() {} } diff --git a/src/test/java/io/github/nbclaudecodegui/ui/common/MarkdownRendererTest.java b/src/test/java/io/github/nbclaudecodegui/ui/common/MarkdownRendererTest.java index 443ad51c..0ea16901 100644 --- a/src/test/java/io/github/nbclaudecodegui/ui/common/MarkdownRendererTest.java +++ b/src/test/java/io/github/nbclaudecodegui/ui/common/MarkdownRendererTest.java @@ -266,6 +266,49 @@ void testHeadingSlugStripNonAlphanumeric() { assertTrue(html.contains(""), "slug strips punctuation"); } + // ------------------------------------------------------------------------- + // toHtml() — nested lists + // ------------------------------------------------------------------------- + + @Test + void testNestedOrderedList() { + String md = "1. first\n 1. sub-first\n 2. sub-second\n2. second"; + String html = MarkdownRenderer.toHtml(md); + // outer ol must contain an inner ol + int outerOl = html.indexOf("
      "); + int innerOl = html.indexOf("
        ", outerOl + 1); + assertTrue(innerOl > outerOl, "inner
          must exist inside outer
            "); + assertTrue(html.contains("
          1. first
          2. ") || html.contains("
          3. first"), + "outer first item"); + assertTrue(html.contains("sub-first"), "sub-first item"); + assertTrue(html.contains("sub-second"), "sub-second item"); + assertTrue(html.contains("second"), "outer second item"); + // must have at least 2
          closing tags + long closeCount = html.chars().filter(c -> c == '<').count(); + assertTrue(html.indexOf("
        ", html.indexOf("
      ") + 1) > 0, + "at least 2
    tags expected"); + } + + @Test + void testNestedUnorderedList() { + String md = "- alpha\n - sub-alpha\n - sub-beta\n- beta"; + String html = MarkdownRenderer.toHtml(md); + int outerUl = html.indexOf("
      "); + int innerUl = html.indexOf("
        ", outerUl + 1); + assertTrue(innerUl > outerUl, "inner
          must exist inside outer
            "); + assertTrue(html.contains("alpha"), "outer alpha"); + assertTrue(html.contains("sub-alpha"), "sub-alpha"); + } + + @Test + void testMixedNestedList() { + String md = "1. item\n - nested-ul\n2. item2"; + String html = MarkdownRenderer.toHtml(md); + assertTrue(html.contains("
              "), "outer ol"); + assertTrue(html.contains("
                "), "inner ul"); + assertTrue(html.contains("nested-ul"), "nested item text"); + } + // ------------------------------------------------------------------------- // resolveImagePaths() // -------------------------------------------------------------------------