Skip to content

Render mermaid and katex files#81

Open
Copilot wants to merge 15 commits intomainfrom
copilot/render-mermaid-diagram-file
Open

Render mermaid and katex files#81
Copilot wants to merge 15 commits intomainfrom
copilot/render-mermaid-diagram-file

Conversation

Copy link
Contributor

Copilot AI commented Mar 19, 2026

.mmd and .mermaid files opened in MarkEdit render as standalone Mermaid diagrams, and .tex files render as standalone KaTeX math — rather than treating them as Markdown. Standalone file rendering works automatically with no user option required, gated behind __FULL_BUILD__.

Changes

  • src/render.ts — Add async renderMermaid(content, lineInfo) that wraps raw content in <div class="mermaid"> with HTML escaping, bypassing the markdown-it pipeline. Add async renderKatex(content, lineInfo) that renders raw LaTeX content via katex.renderToString() with displayMode: true and throwOnError: false, wrapping the output in <div class="katex">. Both delegate to a shared private renderStandalone(className, innerHtml, lineInfo) helper that awaits pluginsReady and handles div wrapping with optional data-line-from/data-line-to attributes for scroll sync. When lineInfo is enabled, the line count is read internally from CodeMirror's MarkEdit.editorView.state.doc.lines.
  • src/view.ts — Add a shared getFileExtension() helper using MarkEdit.getFileInfo() for case-insensitive extension extraction. Add isMermaidFile() to detect .mmd and .mermaid, and isKatexFile() to detect .tex. Route these files to renderMermaid() or renderKatex() respectively in getRenderedHtml(), gated behind __FULL_BUILD__. Pass lineInfo through for scroll sync support.
  • README.md — Removed the previously added renderFileAsMermaid setting (standalone rendering is now automatic).
  • tests/render.test.ts — Tests for renderMermaid (wrapping, escaping, trimming, line info attributes) and renderKatex (wrapping, KaTeX output, error handling, trimming, line info attributes, markdown compatibility). Tests use vi.mock('markedit-api') to properly mock the module for editorView line counting.

The rendered mermaid div is picked up by the existing handlePostRender()mermaid.run() pipeline, and KaTeX output is already rendered HTML from katex.renderToString(), so preview, copy, and export all work consistently. Scroll sync works in preview mode via the data-line-from/data-line-to attributes on the wrapper divs (line count derived internally from CodeMirror's editor state), while copy/export omit them. Falls back to normal Markdown rendering if getFileInfo is unavailable or the file extension doesn't match.

Original prompt

This section details on the original issue you should resolve

<issue_title>Render Mermaid diagram file</issue_title>
<issue_description>## Goals

__FULL_BUILD__ builds already support Mermaid rendering within Markdown content.

Following the recent update allowing direct opening of .mmd files (MarkEdit-app/MarkEdit#1284), we want to extend this functionality to render Mermaid diagrams directly when opening these files, rather than treating them as Markdown with a secondary language.

Proposal

  1. Use MarkEdit.getFileInfo (https://github.com/MarkEdit-app/MarkEdit-api/blob/main/index.d.ts#L181) to determine the file extension of the current file.
  2. If the file extension is .mmd, render the entire file using MermaidJS.
  3. Do not render .mmd content as Markdown with a secondary language; treat it as a standalone Mermaid diagram.

Requirements

  • Use yarn for dependency management.
  • Do not run yarn build; this change applies only to __FULL_BUILD__ builds.
  • Ensure the change does not affect existing Markdown Mermaid rendering.</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.

- Add renderMermaid() in src/render.ts to wrap raw content in a mermaid div
- Add isMermaidFile() in src/view.ts using MarkEdit.getFileInfo()
- Route .mmd files to renderMermaid() in getRenderedHtml() (__FULL_BUILD__ only)
- Add tests for renderMermaid() function

Co-authored-by: cyanzhong <6745066+cyanzhong@users.noreply.github.com>
Copilot AI changed the title [WIP] Add functionality to render Mermaid diagram files Render .mmd files as standalone Mermaid diagrams Mar 19, 2026
Copilot AI requested a review from cyanzhong March 19, 2026 12:11
src/view.ts Outdated
}

const info = await MarkEdit.getFileInfo();
return info?.filePath?.endsWith('.mmd') ?? false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot case sensitive match?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in 3939e04 by using .toLowerCase().endsWith('.mmd') so it matches .MMD, .Mmd, etc.

Co-authored-by: cyanzhong <6745066+cyanzhong@users.noreply.github.com>
Copilot AI requested a review from cyanzhong March 19, 2026 12:13
@cyanzhong
Copy link
Contributor

@copilot After careful consideration, I think we might want a new option (default true), because it seems multi-markdown also uses "mmd". Or do you have a better way to recognize mermaid diagrams?

Also, we want to support filename extension mermaid too.

Co-authored-by: cyanzhong <6745066+cyanzhong@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 19, 2026

@copilot After careful consideration, I think we might want a new option (default true), because it seems multi-markdown also uses "mmd". Or do you have a better way to recognize merma...

Added a renderMermaidDiagram setting (default true) in bb3121b. When set to false, .mmd files render as normal Markdown. Also added support for the .mermaid extension. The check in isMermaidFile() now matches both .mmd and .mermaid (case-insensitive).

Copilot AI changed the title Render .mmd files as standalone Mermaid diagrams Render .mmd and .mermaid files as standalone Mermaid diagrams Mar 19, 2026
@cyanzhong cyanzhong marked this pull request as ready for review March 19, 2026 12:34
@cyanzhong cyanzhong requested a review from Copilot March 19, 2026 12:34
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables standalone Mermaid rendering when opening .mmd / .mermaid files in MarkEdit Preview (instead of treating them as Markdown), controlled by a new renderMermaidDiagram user setting.

Changes:

  • Added renderMermaid() helper to wrap raw Mermaid content in a <div class="mermaid">…</div> with HTML escaping.
  • Added renderMermaidDiagram setting (default true) to allow opting out (e.g., for MultiMarkdown .mmd usage).
  • Updated preview rendering flow to detect Mermaid files via MarkEdit.getFileInfo() and route them through renderMermaid().
  • Added unit tests for renderMermaid() behavior (wrapping/escaping/trimming).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/render.ts Adds standalone Mermaid HTML renderer utility.
src/settings.ts Introduces renderMermaidDiagram toggle to gate .mmd behavior.
src/view.ts Detects Mermaid file extensions and switches rendering path in full builds.
tests/render.test.ts Adds test coverage for standalone Mermaid rendering and escaping.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

@cyanzhong
Copy link
Contributor

@copilot comment #81 (comment) reads reasonable, but it doesn't look complete, work on this.

Co-authored-by: cyanzhong <6745066+cyanzhong@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 19, 2026

@copilot comment #81 (comment) reads reasonable, but it doesn't look complete, work on this.

Fixed in 28c955f. renderMermaid() now accepts a lineInfo parameter — when enabled, the div includes data-line-from="0" and data-line-to="<lastLine>" covering the full file range. getRenderedHtml() passes its lineInfo through, so preview mode gets scroll sync while copy/export remain attribute-free.

src/render.ts Outdated
export function renderMermaid(content: string, lineInfo = false) {
const trimmed = content.trim();
const escaped = mdit.utils.escapeHtml(trimmed);
const lineAttrs = lineInfo ? ` data-line-from="0" data-line-to="${Math.max(0, trimmed.split('\n').length - 1)}"` : '';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot This isn't always correct. Use state.lineBreak from codemirror or its built-in way to get max lines.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2cdc887. renderMermaid() now accepts an optional lastLine number instead of computing it from the content. The call site in view.ts uses MarkEdit.editorView.state.doc.lines - 1 for the correct line count.

Co-authored-by: cyanzhong <6745066+cyanzhong@users.noreply.github.com>
Copilot AI requested a review from cyanzhong March 19, 2026 13:39
src/view.ts Outdated
}

async function isMermaidFile() {
if (!renderFileAsMermaid || typeof MarkEdit.getFileInfo !== 'function') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot This is bad. How can a user config affects the result of isMermaidFile? Is this your design principles?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the renderFileAsMermaid check out of isMermaidFile() into the call site in 986c6dc. isMermaidFile() now purely detects file type by extension.

Co-authored-by: cyanzhong <6745066+cyanzhong@users.noreply.github.com>
Copilot AI requested a review from cyanzhong March 19, 2026 15:13
@cyanzhong
Copy link
Contributor

@copilot We need to do two things because I've changed my mind:

  1. Let's remove the user option, let mmd and mermaid work on automatically
  2. If the filename extension is "tex", render the whole doc as katex content
  3. Make sure changes are consistent for markdown/mermaid/katex

Try your best.

Co-authored-by: cyanzhong <6745066+cyanzhong@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 20, 2026

@copilot We need to do two things because I've changed my mind:

  1. Let's remove the user option, let mmd and mermaid work on automatically
    ...

Done in 6f84744:

  1. Removed renderFileAsMermaid option — .mmd/.mermaid files now render as Mermaid automatically
  2. Added .tex support — renders the whole document as KaTeX display math via katex.renderToString(content, { displayMode: true, throwOnError: false })
  3. Consistent structure across all three: renderMarkdown/renderMermaid/renderKatex in render.ts, isMermaidFile/isKatexFile using a shared getFileExtension() helper in view.ts, all routed through getRenderedHtml() behind __FULL_BUILD__

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • unpkg.com
    • Triggering command: /usr/bin/curl curl -s REDACTED /rg (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title Render .mmd and .mermaid files as standalone Mermaid diagrams Render .mmd, .mermaid, and .tex files as standalone diagrams Mar 20, 2026
src/render.ts Outdated
Comment on lines +47 to +48
if (lineInfo) {
const lastLine = MarkEdit.editorView.state.doc.lines - 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot So many duplicate code between renderKatex and renderMermaid, try better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted a shared renderStandaloneBlock(className, innerHtml, lineInfo) helper in 68753db — handles pluginsReady, lineInfo attributes, and div wrapping. renderMermaid and renderKatex are now thin wrappers that just prepare their inner HTML.

…plication

Co-authored-by: cyanzhong <6745066+cyanzhong@users.noreply.github.com>
Copilot AI requested a review from cyanzhong March 20, 2026 03:31
@cyanzhong cyanzhong changed the title Render .mmd, .mermaid, and .tex files as standalone diagrams Render .mmd, .mermaid, and .tex files Mar 20, 2026
src/render.ts Outdated

async function renderStandaloneBlock(className: string, innerHtml: string, lineInfo: boolean) {
await pluginsReady;
const lineAttrs = lineInfo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot prefer one-liner as long as not absurdly long.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inlined the lineAttrs ternary into the return statement in 57c75e2.

src/view.ts Outdated
}

async function isMermaidFile() {
const ext = await getFileExtension();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot don't shorten names like ext.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to extension in 57c75e2.

src/render.ts Outdated
return renderStandaloneBlock('katex-block', rendered, lineInfo);
}

async function renderStandaloneBlock(className: string, innerHtml: string, lineInfo: boolean) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Why "block" when you render the entire file as something? Also, why mermaid vs katex-block?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to renderStandalone and changed katex-blockkatex for consistency with mermaid in 57c75e2.

… class, inline lineAttrs, expand ext to extension

Co-authored-by: cyanzhong <6745066+cyanzhong@users.noreply.github.com>
Copilot AI changed the title Render .mmd, .mermaid, and .tex files Render .mmd, .mermaid, and .tex files as standalone diagrams Mar 20, 2026
Copilot AI requested a review from cyanzhong March 20, 2026 05:55
@cyanzhong cyanzhong changed the title Render .mmd, .mermaid, and .tex files as standalone diagrams Render mermaid and katex files Mar 20, 2026
@cyanzhong cyanzhong requested a review from Copilot March 20, 2026 06:21
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds “standalone file rendering” paths so MarkEdit preview treats Mermaid diagram files and LaTeX files as their native formats (instead of Markdown), while keeping existing Markdown rendering behavior intact and gating the feature behind __FULL_BUILD__.

Changes:

  • Add renderMermaid() and renderKatex() to render raw .mmd/.mermaid and .tex files as standalone outputs.
  • Route rendering in getRenderedHtml() based on file extension (via MarkEdit.getFileInfo()), falling back to Markdown rendering when unavailable.
  • Add Vitest coverage for standalone Mermaid/KaTeX rendering and scroll-sync line attributes.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/render.ts Introduces standalone Mermaid/KaTeX rendering helpers and shared wrapper for optional scroll-sync attributes.
src/view.ts Adds extension-based routing to choose Mermaid/KaTeX standalone rendering vs Markdown.
src/utils.ts Adds a file-extension helper used by view.ts for routing.
tests/render.test.ts Adds tests for standalone Mermaid/KaTeX rendering, escaping, trimming, and line attribute behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +28 to +30
export async function getFileExtension(filePath?: string) {
const index = filePath?.lastIndexOf('.');
return index === -1 ? '' : filePath?.slice(index).toLowerCase();
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getFileExtension() is declared async but doesn't await, and it can return undefined when filePath is missing (because index becomes undefined and the ternary falls through to filePath?.slice(...)). This makes the helper's contract unclear and can lead to subtle misuse (e.g., callers forgetting to await). Consider making it a synchronous function that always returns a string (e.g., '' when filePath is falsy or has no dot).

Suggested change
export async function getFileExtension(filePath?: string) {
const index = filePath?.lastIndexOf('.');
return index === -1 ? '' : filePath?.slice(index).toLowerCase();
export function getFileExtension(filePath?: string): string {
if (!filePath) {
return '';
}
const index = filePath.lastIndexOf('.');
if (index < 0 || index === filePath.length - 1) {
return '';
}
return filePath.slice(index).toLowerCase();

Copilot uses AI. Check for mistakes.
Comment on lines +104 to +105
const lineTo = MarkEdit.editorView.state.doc.lines - 1;
const lineAttrs = lineInfo ? ` data-line-from="0" data-line-to="${lineTo}"` : '';
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renderStandalone() reads MarkEdit.editorView.state.doc.lines unconditionally, even when lineInfo is false. This makes renderMermaid()/renderKatex() depend on editorView being available even for copy/export and unit tests where line info is intentionally omitted. Consider only computing lineTo when lineInfo is true (and/or gracefully handling a missing editorView by skipping line attributes).

Suggested change
const lineTo = MarkEdit.editorView.state.doc.lines - 1;
const lineAttrs = lineInfo ? ` data-line-from="0" data-line-to="${lineTo}"` : '';
let lineAttrs = '';
if (lineInfo && MarkEdit.editorView) {
const lineTo = MarkEdit.editorView.state.doc.lines - 1;
lineAttrs = ` data-line-from="0" data-line-to="${lineTo}"`;
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Render Mermaid diagram file

3 participants