diff --git a/internal/markdown/convert.go b/internal/markdown/convert.go index efe6983..c749886 100644 --- a/internal/markdown/convert.go +++ b/internal/markdown/convert.go @@ -50,6 +50,9 @@ func init() { // Convert converts Markdown source to HTML. // plantumlServer is the PlantUML server URL for code block conversion. func Convert(source []byte, plantumlServer string) ([]byte, error) { + // Normalize CRLF to LF so all preprocessors and the parser see consistent line endings. + source = bytes.ReplaceAll(source, []byte("\r\n"), []byte("\n")) + // Pre-process: expand single-line $$...$$ to multi-line for goldmark-mathjax source = PreprocessMathBlocks(source) diff --git a/internal/markdown/convert_test.go b/internal/markdown/convert_test.go new file mode 100644 index 0000000..06ebfcb --- /dev/null +++ b/internal/markdown/convert_test.go @@ -0,0 +1,22 @@ +package markdown + +import ( + "strings" + "testing" +) + +func TestConvert_CRLFMathBlock(t *testing.T) { + // Regression: CRLF line endings must not break $$ math block rendering. + input := []byte("$$\r\ny = x^2\r\n$$\r\n") + html, err := Convert(input, "") + if err != nil { + t.Fatalf("Convert returned error: %v", err) + } + s := string(html) + if !strings.Contains(s, `\(y = x^2\)`) && !strings.Contains(s, `y = x^2`) { + t.Errorf("math content missing from output:\n%s", s) + } + if strings.Contains(s, `\(\)`) || strings.Contains(s, `\(\r`) { + t.Errorf("CRLF leaked into math output:\n%s", s) + } +}