A streaming markdown renderer for modern terminals
Perfect for rendering LLM output in real-time.
- π‘ Streaming Rendering - Render markdown as it arrives, line by line
- π¨ Syntax Highlighting - Beautiful code blocks powered by Pygments
- π Tables - Full support for GitHub-flavored markdown tables
- π Lists - Ordered, unordered, and nested lists with smart indentation
- π» Code Blocks - Fenced code blocks with language detection and clipboard support
- π Think Blocks - Special rendering for
<think>tags (great for LLM chain-of-thought) - π Hyperlinks - OSC 8 clickable links in supported terminals
- π Clipboard - OSC 52 clipboard integration for code blocks
- ποΈ Configurable - Customize colors, styles, and features via TOML config
- β‘ Fast - Lightweight and performant, minimal dependencies
uvx termflowpip install termflow-mdgit clone https://github.com/your-username/termflow.git
cd termflow
pip install -e ".[dev]"# Render a file
tf README.md
# Pipe markdown content
echo "# Hello World" | tf
# Pipe from LLM output
curl -s https://api.example.com/chat | tf
# Set terminal width
tf -w 100 document.md
# Use a color preset
tf --style dracula README.md
# Use a syntax highlighting theme
tf --syntax-style nord file.md
# Disable clipboard integration
tf --no-clipboard document.md
# List available syntax styles
tf --list-syntax-stylesusage: tf [-h] [-w N] [-c PATH] [--style {default,dracula,nord,gruvbox}]
[--syntax-style NAME] [--list-syntax-styles] [--no-clipboard]
[--no-hyperlinks] [--no-pretty] [-V]
[file]
options:
-h, --help show this help message and exit
-w, --width N Terminal width (default: auto-detect)
-c, --config PATH Path to config file
--style PRESET Color style preset (default, dracula, nord, gruvbox)
--syntax-style NAME Pygments syntax highlighting style
--list-syntax-styles List available syntax highlighting styles
--no-clipboard Disable OSC 52 clipboard for code blocks
--no-hyperlinks Disable OSC 8 hyperlinks
--no-pretty Disable pretty code block borders
-V, --version show program's version number and exit
from termflow import Parser, Renderer, render_markdown
# Quick rendering to stdout
render_markdown("# Hello World!")
# Render to a file or buffer
from io import StringIO
output = StringIO()
render_markdown("# Hello\n\nThis is **bold** text.", output=output)
print(output.getvalue())For real-time rendering of streaming content (e.g., LLM responses):
from termflow import Parser, Renderer
import sys
# Create parser and renderer
parser = Parser()
renderer = Renderer(output=sys.stdout, width=80)
# Process markdown line by line as it streams in
for line in markdown_stream:
events = parser.parse_line(line)
renderer.render_all(events)
# Finalize to close any open blocks
renderer.render_all(parser.finalize())from termflow import Renderer, RenderStyle, RenderFeatures
from io import StringIO
# Use a preset style
style = RenderStyle.dracula() # or .nord(), .gruvbox()
# Or create a custom style
style = RenderStyle(
bright="#87ceeb", # Main accent color
head="#98fb98", # Heading color
symbol="#dda0dd", # Bullets, borders
link="#87cefa", # Link color
)
# Configure features
features = RenderFeatures(
clipboard=True, # OSC 52 clipboard support
hyperlinks=True, # OSC 8 clickable links
pretty_pad=True, # Pretty code block borders
)
# Create renderer with custom config
output = StringIO()
renderer = Renderer(
output=output,
width=100,
style=style,
features=features,
)Create a config file at ~/.config/termflow/config.toml:
# Terminal width (null = auto-detect)
width = null
max_width = 120
# Pygments syntax highlighting style
syntax_style = "monokai"
# Color scheme
[style]
bright = "#87ceeb" # Main accent color
head = "#98fb98" # Heading color
symbol = "#dda0dd" # Bullets, table borders, code block borders
grey = "#808080" # Muted text
dark = "#404040" # Dark accents
mid = "#a0a0a0" # Medium text
light = "#d0d0d0" # Light accents
link = "#87cefa" # Hyperlink color
error = "#ff6b6b" # Error messages
# Feature toggles
[features]
clipboard = true # OSC 52 clipboard for code blocks
hyperlinks = true # OSC 8 clickable links
pretty_pad = true # Pretty unicode borders on code blocksYou can also set the config path via environment variable:
export TERMFLOW_CONFIG=/path/to/config.tomltermflow includes several built-in color presets:
| Preset | Description |
|---|---|
default |
Soft pastel colors |
dracula |
Purple-tinted dark theme |
nord |
Arctic, bluish color palette |
gruvbox |
Retro, earthy colors |
Use them via CLI:
tf --style dracula README.mdOr programmatically:
from termflow import RenderStyle
style = RenderStyle.dracula()
style = RenderStyle.nord()
style = RenderStyle.gruvbox()termflow has special support for <think> blocks, commonly used in LLM
chain-of-thought prompting:
<think>
Let me reason through this step by step...
1. First, I'll analyze the problem
2. Then, I'll formulate a solution
</think>
Here's my answer based on my reasoning above.Think blocks are rendered with a distinct style to visually separate the model's reasoning from its final response.
termflow is a Python port of streamdown-rs, a high-performance streaming markdown renderer written in Rust. This project brings the same streaming rendering capabilities to the Python ecosystem with a clean, Pythonic API.
Contributions are welcome! Please feel free to submit a Pull Request.
# Clone and install for development
git clone https://github.com/your-username/termflow.git
cd termflow
pip install -e ".[dev]"
# Run tests
pytest tests/ -v
# Run linter
ruff check .
ruff format .This project is licensed under the MIT License - see the LICENSE file for details.
Made with β€οΈ for the terminal