Last active
February 10, 2026 10:10
-
-
Save karunru/512b78b430fad751d2466b72da4a4893 to your computer and use it in GitHub Desktop.
mdview: Markdown viewer with mermaid-ascii support (glow wrapper)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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