How the nGPT Markdown Rendering System Handles Elements and Styling Customizations

The nGPT markdown rendering system leverages the Rich library to stream live Markdown in a bordered panel, automatically truncating content to fit terminal dimensions while centralizing color styling through a dedicated configuration dictionary.

The nazdridoy/ngpt repository implements a sophisticated markdown rendering system that transforms AI responses into beautifully formatted terminal output. This system combines the rich library's capabilities with custom truncation logic and centralized color management to deliver a seamless streaming experience.

Architecture of the Markdown Rendering Pipeline

The markdown rendering system in ngpt/ui/renderers.py processes AI responses through a six-stage pipeline that balances performance with visual polish.

Creating the Rich Console

The pipeline begins by instantiating a Console() object from the rich library. This console serves as the low-level rendering target and automatically handles ANSI-color support detection across different terminal environments.

Wrapping Content in a Panel

The system wraps the Markdown content inside a Panel with a rounded box style. The implementation uses Panel(Markdown(""), title=panel_title, box=rich.box.ROUNDED, border_style="cyan") to create a distinct visual container with a cyan border and customizable header.

Initializing the Live Display

To prevent flickering during streaming, the code starts a Live display with Live(md_obj, console=console, refresh_per_second=10, auto_refresh=False). This maintains the panel on-screen while its contents update in-place at a controlled refresh rate.

Handling Streaming Chunks

The update_content() function inside renderers.py processes each incoming chunk by replacing the panel's renderable with a fresh Markdown object. The system also implements intelligent truncation: it calculates display_height using shutil.get_terminal_size() and slices the markdown text when it exceeds terminal capacity, showing only the most recent lines based on a max(10, min(30, ...)) formula.

Synchronizing with the Spinner

While the LLM generates output, a background spinner runs via setup_spinner() in renderers.py and ngpt/ui/tui.py. The create_spinner_handling_callback() function ensures the first piece of content automatically stops the spinner, clears its line, and hands control to the live panel.

Applying Color Styling

All color codes are centralized in ngpt/ui/colors.py within the COLORS dictionary. The rendering code references COLORS['cyan'], COLORS['reset'], and other keys, ensuring that modifying a single value in this dictionary propagates to the markdown header, spinner text, and border styles.

Element-Level Handling in the Markdown Rendering System

The markdown rendering system delegates specific element parsing to rich.markdown.Markdown, which translates syntax into Rich Text objects that inherit the console's color configuration.

Structural Elements

Headers, lists, code blocks, tables, and block-quotes are parsed natively by the Rich library. The nGPT system does not intercept these elements, allowing the underlying library to handle the complex AST translation while the panel container manages the visual presentation.

Inline Formatting

Bold, italic, and hyperlink styling are supported out-of-the-box by Rich. While nGPT does not add extra processing layers for inline elements, you can influence the visual result by tweaking the console theme or the COLORS dictionary in colors.py.

Custom Panel Title

The panel header displaying "🤖 nGPT" is created with Text(..., style="cyan bold"), giving it a distinct appearance separate from the content. This styling can be modified by editing the panel_title variable within the prettify_streaming_markdown() function in renderers.py.

Dynamic Styling Customizations

The markdown rendering system offers several extension points for customizing the visual experience without modifying core streaming logic.

Panel Appearance

You can modify the border shape, color, and spacing by adjusting the Panel constructor parameters in prettify_streaming_markdown(). Changing box=rich.box.ROUNDED to alternatives like rich.box.DOUBLE or modifying border_style="cyan" to another color key alters the container aesthetics.

Terminal-Size-Aware Truncation

The system adapts to viewport constraints by calculating display height via shutil.get_terminal_size(). You can customize the truncation limits by editing the formula max(10, min(30, int(term_height * 0.7))) in renderers.py, adjusting the minimum (10), maximum (30), or percentage multiplier (0.7) to control how much content remains visible.

Color Palette Management

All UI colors are centralized in the COLORS dictionary within ngpt/ui/colors.py. Editing values such as "cyan": "\033[96m" instantly propagates changes to the panel border, header text, and spinner animations across all CLI modes.

Spinner Styling

The spinner text color is passed via color=COLORS['cyan'] in the setup_spinner() function. Modifying the referenced color key changes the spinner appearance independently of the markdown panel rendering logic.

Integration with CLI Modes

Every interactive mode in nGPT—including chat, text, code, and rewrite—imports the prettify_streaming_markdown function from ngpt/ui/renderers.py to provide consistent visual output.

Markdown Mode Operation

When the user does not pass the --plaintext flag, the CLI mode invokes:

live_display, stream_callback, setup_spinner = prettify_streaming_markdown()

This returns a tuple containing the live display object, a callback function for streaming chunks, and a spinner setup function. The mode then starts the spinner using setup_spinner(), which runs while the LLM generates output. As chunks arrive, they are fed to stream_callback, triggering live Markdown updates within the bordered panel.

Plaintext Fallback

If the user specifies --plaintext, the spinner logic switches to setup_plaintext_spinner, and responses print as raw text without Rich formatting. This bypasses the prettify_streaming_markdown pipeline entirely, providing unstyled output for environments where ANSI codes are unsupported or undesired.

Code Examples

Setting Up Live Markdown Rendering

The following simplified example demonstrates how to initialize the rendering pipeline programmatically:

from ngpt.ui.renderers import prettify_streaming_markdown
import threading

live, update, start_spinner = prettify_streaming_markdown()
stop_spinner = start_spinner(threading.Event(), "Thinking…")

# Simulated streaming loop

for chunk in streaming_response():
    update(chunk)                     # updates the live panel

stop_spinner()                        # stops the spinner when done

The update function handles content truncation and panel refreshes automatically, ensuring the display remains responsive even during high-volume streaming.

Changing the Panel Border Color

To customize the visual appearance, modify the color definitions in ngpt/ui/colors.py:

COLORS["cyan"] = "\033[94m"   # a deeper blue

Because prettify_streaming_markdown references border_style="cyan" when constructing the Panel, this change immediately affects the border color across all CLI modes.

Customizing Truncation Limits

Adjust the viewport constraints in ngpt/ui/renderers.py to change how much content remains visible:

display_height = max(5, min(40, int(term_height * 0.7)))  # new limits

This modification allows the panel to display between 5 and 40 lines of content, adapting dynamically to terminal resizing while maintaining the 70% height ratio.

Key Files in the Markdown Rendering System

The markdown rendering system spans several modules that handle distinct responsibilities:

  • ngpt/ui/renderers.py – Contains the core streaming logic, including prettify_streaming_markdown(), update_content(), and spinner coordination via setup_spinner() and create_spinner_handling_callback().

  • ngpt/ui/colors.py – Centralizes the COLORS dictionary that defines ANSI escape sequences for all UI elements, enabling system-wide color changes from a single location.

  • ngpt/ui/tui.py – Implements the background spinner animation used while waiting for LLM responses.

  • ngpt/cli/modes/*.py (e.g., chat.py, text.py) – CLI entry points that import prettify_streaming_markdown and handle the --plaintext flag to toggle between styled and raw output.

  • ngpt/api/client.py – API wrapper that sets markdown_format=True to encourage the LLM to return Markdown-formatted responses compatible with the rendering system.

Summary

  • The markdown rendering system in nazdridoy/ngpt leverages the rich library to stream live Markdown within a bordered, rounded panel that updates in real-time.
  • Centralized color management via ngpt/ui/colors.py allows system-wide styling changes by modifying a single dictionary.
  • Intelligent truncation in ngpt/ui/renderers.py ensures content fits within terminal viewports by calculating dynamic height limits based on shutil.get_terminal_size().
  • Spinner synchronization provides visual feedback during LLM generation, automatically transitioning to the live panel when content arrives.
  • All interactive CLI modes share the same rendering pipeline through the prettify_streaming_markdown() function, with a --plaintext fallback for unstyled output.

Frequently Asked Questions

How does the markdown rendering system prevent content from overflowing the terminal?

The system calculates available viewport space using shutil.get_terminal_size() in ngpt/ui/renderers.py. It applies a dynamic height formula—typically max(10, min(30, int(term_height * 0.7)))—to determine how many lines to display. When content exceeds this limit, the update_content() function truncates the text to show only the most recent lines, ensuring the panel never scrolls beyond the visible terminal area.

Can I customize the colors used in the markdown rendering system without modifying the source code?

While the color definitions reside in ngpt/ui/colors.py within the COLORS dictionary, you can modify these values before runtime or fork the repository to change them permanently. Each UI component references these centralized color keys—for example, border_style="cyan" in the panel constructor—so changing "cyan" in the dictionary propagates instantly to borders, headers, and spinners across all CLI modes.

What happens to the markdown rendering system when I use the --plaintext flag?

When you invoke nGPT with --plaintext, the application bypasses the Rich-based pipeline entirely. Instead of calling prettify_streaming_markdown(), the CLI modes use setup_plaintext_spinner for loading indicators and print raw text chunks directly to stdout without Panel containers, border styling, or Markdown parsing. This provides unformatted output suitable for piping to other programs or terminals without ANSI support.

How does the markdown rendering system handle code blocks and tables within streaming content?

The system delegates element parsing to rich.markdown.Markdown, which automatically converts standard Markdown syntax—including fenced code blocks, tables, blockquotes, and headers—into Rich Text objects. As the update() function in ngpt/ui/renderers.py receives new chunks, it reconstructs the Markdown object, allowing Rich to re-render the entire document with proper syntax highlighting and table formatting within the live panel boundaries.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →