Skip to content

Instantly share code, notes, and snippets.

@karunru
Last active February 10, 2026 10:10
Show Gist options
  • Select an option

  • Save karunru/512b78b430fad751d2466b72da4a4893 to your computer and use it in GitHub Desktop.

Select an option

Save karunru/512b78b430fad751d2466b72da4a4893 to your computer and use it in GitHub Desktop.
mdview: Markdown viewer with mermaid-ascii support (glow wrapper)
#!/usr/bin/env python3
"""Markdown viewer that converts mermaid diagrams to ASCII art via mermaid-ascii, then displays with glow."""
import os
import re
import subprocess
import sys
import tempfile
MERMAID_BLOCK_RE = re.compile(
r"```mermaid\s*\n(.*?)```", re.DOTALL
)
def convert_mermaid(mermaid_src: str) -> str:
"""Convert mermaid source to ASCII art using mermaid-ascii CLI."""
try:
result = subprocess.run(
["mermaid-ascii", "-f", "-"],
input=mermaid_src,
capture_output=True,
text=True,
timeout=30,
)
if result.returncode == 0 and result.stdout.strip():
return result.stdout.rstrip("\n")
except (subprocess.TimeoutExpired, FileNotFoundError):
pass
# Fallback: return original as-is in a code block
return mermaid_src.rstrip("\n")
def process_markdown(content: str) -> tuple[str, bool]:
"""Replace mermaid code blocks with ASCII art. Returns (processed_content, had_mermaid)."""
had_mermaid = False
def replacer(match: re.Match[str]) -> str:
nonlocal had_mermaid
had_mermaid = True
mermaid_src = match.group(1)
ascii_art = convert_mermaid(mermaid_src)
return f"```\n{ascii_art}\n```"
processed = MERMAID_BLOCK_RE.sub(replacer, content)
return processed, had_mermaid
def main() -> None:
# Separate file path from glow options
file_path = None
glow_args: list[str] = []
for arg in sys.argv[1:]:
if file_path is None and not arg.startswith("-") and os.path.isfile(arg):
file_path = arg
else:
glow_args.append(arg)
# Read markdown content
if file_path:
with open(file_path) as f:
content = f.read()
elif not sys.stdin.isatty():
content = sys.stdin.read()
else:
# No file and no stdin: just run glow as-is
os.execvp("glow", ["glow", *glow_args])
processed, had_mermaid = process_markdown(content)
# Disable word-wrap by default unless user explicitly sets -w
if not any(a in ("-w", "--width") for a in glow_args):
glow_args = ["-w", "0", *glow_args]
if not had_mermaid and file_path:
# No mermaid blocks: pass original file directly to glow
os.execvp("glow", ["glow", file_path, *glow_args])
# Write processed content to temp file for glow
with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as tmp:
tmp.write(processed)
tmp_path = tmp.name
try:
result = subprocess.run(["glow", tmp_path, *glow_args])
sys.exit(result.returncode)
finally:
os.unlink(tmp_path)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment