From 0fbaa2b16ca16c1ba15ba3334f75cf5bd2cfcb23 Mon Sep 17 00:00:00 2001 From: Guido Jouret Date: Fri, 20 Mar 2026 08:29:40 +0100 Subject: [PATCH] Fix mermaid diagram rendering with YAML frontmatter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mermaid diagrams using YAML frontmatter (--- config blocks) failed to render in preview, showing as plain text code blocks instead of graphical diagrams. Three issues were identified and fixed: 1. Horizontal rule replacement destroyed mermaid frontmatter: getPrettifiedContent() globally replaced \n---\n with \n
\n, including inside fenced code blocks. Added replaceHorizontalRulesOutsideCodeBlocks() to skip code fences. 2. cleanMetaData() stripped --- separators from content: When a note has YAML front matter, components(separatedBy: "---") splits on ALL occurrences, and list.joined() rejoined without the separator, destroying --- inside mermaid blocks. Fixed by using list.joined(separator: "---"). 3. Mermaid v11 API and rendering improvements: - Updated from deprecated mermaid.init() to mermaid.run() (async) - Bundled @mermaid-js/layout-elk plugin for ELK layout support - Added YAML frontmatter indentation normalization (tabs → spaces) - Changed useMaxWidth from false to true (diagrams fit preview width) - Added error handling with visible error messages on render failure - Removed async script loading to prevent race conditions Co-Authored-By: Claude Opus 4.6 --- FSNotesCore/Business/Note.swift | 35 +++++++++++-- Resources/MPreview.bundle/index.html | 52 +++++++++++++++++-- .../MPreview.bundle/js/elk-layout.min.js | 27 ++++++++++ 3 files changed, 106 insertions(+), 8 deletions(-) create mode 100644 Resources/MPreview.bundle/js/elk-layout.min.js diff --git a/FSNotesCore/Business/Note.swift b/FSNotesCore/Business/Note.swift index 5bf24a7cb..9ab776009 100644 --- a/FSNotesCore/Business/Note.swift +++ b/FSNotesCore/Business/Note.swift @@ -879,7 +879,7 @@ public class Note: NSObject { result += "
\n\n" } - result += list.joined() + result += list.joined(separator: "---") return result } @@ -892,15 +892,42 @@ public class Note: NSObject { #if IOS_APP || os(OSX) let mutable = NotesTextProcessor.convertAppTags(in: self.content.unloadAttachments(), codeBlockRanges: codeBlockRangesCache) let content = NotesTextProcessor.convertAppLinks(in: mutable, codeBlockRanges: codeBlockRangesCache) - let result = cleanMetaData(content: content.string) - .replacingOccurrences(of: "\n---\n", with: "\n
\n") - + let cleaned = cleanMetaData(content: content.string) + let result = Note.replaceHorizontalRulesOutsideCodeBlocks(cleaned) + return result #else return cleanMetaData(content: self.content.string) #endif } + /// Replace `\n---\n` with `\n
\n` only outside fenced code blocks, + /// so that `---` inside e.g. mermaid YAML frontmatter is preserved. + private static func replaceHorizontalRulesOutsideCodeBlocks(_ text: String) -> String { + let pattern = "(?<=\\n|\\A)```[^\\n]*\\n[\\s\\S]*?\\n```(?=\\n|\\Z)" + guard let regex = try? NSRegularExpression(pattern: pattern) else { + return text.replacingOccurrences(of: "\n---\n", with: "\n
\n") + } + + let nsText = text as NSString + let codeRanges = regex.matches(in: text, range: NSRange(location: 0, length: nsText.length)).map { $0.range } + + var result = "" + var currentIndex = 0 + + for codeRange in codeRanges { + let beforeRange = NSRange(location: currentIndex, length: codeRange.location - currentIndex) + result += nsText.substring(with: beforeRange).replacingOccurrences(of: "\n---\n", with: "\n
\n") + result += nsText.substring(with: codeRange) + currentIndex = codeRange.location + codeRange.length + } + + let remainingRange = NSRange(location: currentIndex, length: nsText.length - currentIndex) + result += nsText.substring(with: remainingRange).replacingOccurrences(of: "\n---\n", with: "\n
\n") + + return result + } + public func overwrite(url: URL) { self.url = url diff --git a/Resources/MPreview.bundle/index.html b/Resources/MPreview.bundle/index.html index a14c707af..78a6955fb 100644 --- a/Resources/MPreview.bundle/index.html +++ b/Resources/MPreview.bundle/index.html @@ -31,7 +31,8 @@ - + +