Parse Markdown and turn it into nicely-formatted text for terminal display
Project description
md2term
A beautiful markdown-to-terminal converter that renders markdown with rich formatting, syntax highlighting, and proper terminal layout.
It supports streaming so you can pipe your favorite LLM tool to it, like this:
[!NOTE] This software was created almost entirely by AI with Cursor and Claude 4 Sonnet.
Installation
Install md2term using uv:
uv tool install md2term
Or with pip or pipx:
pip install md2term
# or
pipx install md2term
Verify the installation:
md2term --version
Usage
Command Line
# Convert a markdown file
md2term README.md
# Read from stdin
cat README.md | md2term
# Pipe from other commands
curl -s https://raw.githubusercontent.com/user/repo/main/README.md | md2term
# Or commands with slow output
llm 'tell me long a story about cheesecakes using markdown formatting' | md2term
# Override terminal width
md2term --width 100 README.md
# Show version
md2term --version
# Show help
md2term --help
Python Library
You can also use md2term as a Python library for integrating markdown rendering into your applications:
from rich.console import Console
from md2term import convert, StreamingRenderer
# Simple conversion
markdown_text = "# Hello\n\nThis is **bold** text."
convert(markdown_text)
# Streaming usage (great for LLM applications)
console = Console(force_terminal=True)
renderer = StreamingRenderer(console)
try:
# Add content incrementally
renderer.add_text("# Streaming Example\n\n")
renderer.add_text("This content appears **immediately** as it's added.\n")
renderer.add_text("\n```python\nprint('Hello, World!')\n```\n")
finally:
# Always finalize to ensure complete rendering
renderer.finalize()
The streaming functionality is particularly useful for:
- LLM/AI applications that generate content in real-time
- Processing large files with immediate visual feedback
- Building interactive CLI tools with progressive output
See example_streaming.py for more detailed examples and patterns.
Examples
For a comprehensive example of markdown features, see example.md in this repository.
Design Decisions
Code Block Handling
The program uses a smart approach to handle multi-line code blocks:
- Streaming Processing: For stdin input, the program processes content in chunks, buffering until it encounters blank lines (when not in a code block)
- Code Fence Detection: Detects triple backticks (
\```) to track when we're inside code blocks - No Backtracking: Instead of clearing previous lines, the program assumes that triple backticks always indicate the start/end of code blocks
This approach is efficient and works well for typical markdown usage patterns.
Color Scheme
- H1: Bright cyan with rules above and below, centered
- H2: Bright blue with rule below
- H3: Bright magenta
- H4: Bright yellow
- H5: Bright green
- H6: Bright white
- Code spans: Red text on dark gray background
- Links: Blue underlined text with URL in parentheses
- Lists: Yellow bullets (•) for unordered, cyan numbers for ordered
- Blockquotes: Blue italic text in a panel
Terminal Width Handling
The program automatically detects terminal width and wraps text accordingly. You can override this with the --width option for testing or specific formatting needs.
Development
To install for development:
# Clone the repository
git clone https://github.com/statico/md2term
cd md2term
# Install in development mode
uv tool install --editable .
# Or install dependencies for local development
uv sync
uv run md2term README.md
Running Tests
The project uses pytest with snapshot testing via syrupy to ensure consistent output formatting:
# Install test dependencies
uv sync --group test
# Run all tests
uv run pytest
# Run tests with coverage
uv run pytest --cov=md2term
# Run specific test file
uv run pytest tests/test_md2term.py
# Run specific test
uv run pytest tests/test_md2term.py::TestMarkdownFeatures::test_headings_all_levels
Snapshot Testing
The tests use snapshot testing to verify that markdown rendering produces consistent terminal output. Snapshots capture the exact ANSI escape sequences and formatting that would appear in the terminal.
# Update snapshots when output changes (after verifying changes are correct)
uv run pytest --snapshot-update
# Review snapshot differences
uv run pytest --snapshot-details
Important: When modifying rendering logic, always:
- Run tests to see what changed
- Manually verify the output looks correct with
uv run md2term example.md - Update snapshots only if the changes are intentional and correct
The snapshot file is located at tests/__snapshots__/test_md2term.ambr and contains the expected terminal output for various markdown inputs.
Publishing
To publish a new version to PyPI:
# Update version in pyproject.toml
# Build the package
uv build
# Publish to PyPI (requires PyPI credentials)
uv publish
License
This project is open source. Feel free to use and modify as needed.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file md2term-1.0.0.tar.gz.
File metadata
- Download URL: md2term-1.0.0.tar.gz
- Upload date:
- Size: 70.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d3acd37970d60d5144b827c29edba1ef49d1dc389256d7957eefd77a6a466a04
|
|
| MD5 |
e4132c15c06456964b0117b77733bba7
|
|
| BLAKE2b-256 |
b24f5da26bcd4f89e6cd1dc2dcab5fe5104633ccc64d7b863c5ac12db8cf649a
|
Provenance
The following attestation bundles were made for md2term-1.0.0.tar.gz:
Publisher:
publish.yml on statico/md2term
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
md2term-1.0.0.tar.gz -
Subject digest:
d3acd37970d60d5144b827c29edba1ef49d1dc389256d7957eefd77a6a466a04 - Sigstore transparency entry: 219893637
- Sigstore integration time:
-
Permalink:
statico/md2term@408b6217ac4a8a5d88abc54509e524f71fe3fe58 -
Branch / Tag:
refs/tags/1.0.0 - Owner: https://github.com/statico
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@408b6217ac4a8a5d88abc54509e524f71fe3fe58 -
Trigger Event:
release
-
Statement type:
File details
Details for the file md2term-1.0.0-py3-none-any.whl.
File metadata
- Download URL: md2term-1.0.0-py3-none-any.whl
- Upload date:
- Size: 15.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
09398973ea02c01d6d00df59db2dc4c54119523d87f520a0c90a03e695efc53c
|
|
| MD5 |
3e89b2b98087525f4fc460aef0cc81fc
|
|
| BLAKE2b-256 |
39c97029150f0b6d451dabdc8c652985e2409060696d78b8202209d3a4f6b186
|
Provenance
The following attestation bundles were made for md2term-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on statico/md2term
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
md2term-1.0.0-py3-none-any.whl -
Subject digest:
09398973ea02c01d6d00df59db2dc4c54119523d87f520a0c90a03e695efc53c - Sigstore transparency entry: 219893639
- Sigstore integration time:
-
Permalink:
statico/md2term@408b6217ac4a8a5d88abc54509e524f71fe3fe58 -
Branch / Tag:
refs/tags/1.0.0 - Owner: https://github.com/statico
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@408b6217ac4a8a5d88abc54509e524f71fe3fe58 -
Trigger Event:
release
-
Statement type: