Skip to main content

A streaming markdown renderer for modern terminals

Project description

termflow 🌊

A streaming markdown renderer for modern terminals

PyPI version Python versions License: MIT Build status

Perfect for rendering LLM output in real-time.


✨ Features

  • 📡 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

🚀 Quick Installation

Using uvx (recommended)

uvx termflow

Using pip

pip install termflow-md

From source

git clone https://github.com/your-username/termflow.git
cd termflow
pip install -e ".[dev]"

📖 Usage

Command Line

# 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-styles

CLI Options

usage: 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

Programmatic Usage

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())

Streaming Mode

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())

Custom Styling

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,
)

🔧 Configuration

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 blocks

You can also set the config path via environment variable:

export TERMFLOW_CONFIG=/path/to/config.toml

🎨 Style Presets

termflow 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.md

Or programmatically:

from termflow import RenderStyle

style = RenderStyle.dracula()
style = RenderStyle.nord()
style = RenderStyle.gruvbox()

💭 Think Block Support

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.

🦀 Origin

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.

🤝 Contributing

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 .

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.


Made with ❤️ for the terminal

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

termflow_md-0.1.11.tar.gz (115.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

termflow_md-0.1.11-py3-none-any.whl (58.7 kB view details)

Uploaded Python 3

File details

Details for the file termflow_md-0.1.11.tar.gz.

File metadata

  • Download URL: termflow_md-0.1.11.tar.gz
  • Upload date:
  • Size: 115.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for termflow_md-0.1.11.tar.gz
Algorithm Hash digest
SHA256 8e18f937ca136801160dddd4b837499e028c6573aebb4da7aa74ec1fa57cc87f
MD5 74e3d4c894240956d79196a1caf110f0
BLAKE2b-256 219d85297bbe0277905c45428422335454ce4131a72d98b7c613d0b3ec0fb7b7

See more details on using hashes here.

File details

Details for the file termflow_md-0.1.11-py3-none-any.whl.

File metadata

  • Download URL: termflow_md-0.1.11-py3-none-any.whl
  • Upload date:
  • Size: 58.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for termflow_md-0.1.11-py3-none-any.whl
Algorithm Hash digest
SHA256 4fd6f375b4a06aa06987dfc80d2910fed2b27e9576c50d49c7f3903c67195b2a
MD5 918b223c62bf1288178670ed3226bbc0
BLAKE2b-256 15490fa8324879351c59ceb169f443c578fd6f8272ee3e71268f0b9140589614

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page