# How the nGPT Markdown Rendering System Handles Elements and Styling Customizations

> Discover how the nGPT markdown rendering system efficiently handles elements and styling. Learn about its use of the Rich library for live streaming, auto-truncation, and centralized color customization.

- Repository: [nazDridoy/ngpt](https://github.com/nazdridoy/ngpt)
- Tags: internals
- Published: 2026-03-07

---

**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`](https://github.com/nazdridoy/ngpt/blob/main/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`](https://github.com/nazdridoy/ngpt/blob/main/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`](https://github.com/nazdridoy/ngpt/blob/main/renderers.py) and [`ngpt/ui/tui.py`](https://github.com/nazdridoy/ngpt/blob/main/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`](https://github.com/nazdridoy/ngpt/blob/main/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`](https://github.com/nazdridoy/ngpt/blob/main/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`](https://github.com/nazdridoy/ngpt/blob/main/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`](https://github.com/nazdridoy/ngpt/blob/main/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`](https://github.com/nazdridoy/ngpt/blob/main/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`](https://github.com/nazdridoy/ngpt/blob/main/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:

```python
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:

```python
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`](https://github.com/nazdridoy/ngpt/blob/main/ngpt/ui/colors.py):

```python
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`](https://github.com/nazdridoy/ngpt/blob/main/ngpt/ui/renderers.py) to change how much content remains visible:

```python
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`](https://github.com/nazdridoy/ngpt/blob/main/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`](https://github.com/nazdridoy/ngpt/blob/main/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`](https://github.com/nazdridoy/ngpt/blob/main/ngpt/ui/tui.py)** – Implements the background spinner animation used while waiting for LLM responses.

- **`ngpt/cli/modes/*.py`** (e.g., [`chat.py`](https://github.com/nazdridoy/ngpt/blob/main/chat.py), [`text.py`](https://github.com/nazdridoy/ngpt/blob/main/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`](https://github.com/nazdridoy/ngpt/blob/main/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`](https://github.com/nazdridoy/ngpt/blob/main/ngpt/ui/colors.py) allows system-wide styling changes by modifying a single dictionary.
- **Intelligent truncation** in [`ngpt/ui/renderers.py`](https://github.com/nazdridoy/ngpt/blob/main/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`](https://github.com/nazdridoy/ngpt/blob/main/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`](https://github.com/nazdridoy/ngpt/blob/main/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`](https://github.com/nazdridoy/ngpt/blob/main/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.