src — renderers

Module: src-renderers Cohesion: 0.80 Members: 0

src — renderers

The src/renderers module is a core component responsible for transforming various structured data types into human-readable, terminal-friendly output. It provides a flexible and extensible system for displaying complex information, such as code analysis, test results, diffs, tables, and even SVG charts, directly within a terminal environment.

The module supports two primary display modes:

Architecture Overview

The rendering system is built around a central RenderManager that acts as an orchestrator. When data needs to be displayed, the RenderManager attempts to find a specialized Renderer capable of handling that specific data type. If a specialized renderer is found, it takes over the formatting. Otherwise, the RenderManager falls back to a generic rendering mechanism for basic data structures (strings, numbers, arrays, objects).

This design promotes modularity, allowing new data types and their corresponding display logic to be added without modifying the core rendering pipeline.

graph TD
    A[Data (unknown type)] --> B{RenderManager.render(data)};
    B --1. Find matching renderer--> C{Renderer.canRender(data)};
    C -- Yes --> D[Specialized Renderer.render(data, ctx)];
    C -- No --> E[RenderManager.renderGeneric(data, ctx)];
    D --> F[Formatted Terminal Output];
    E --> F;

Core Components

RenderManager (src/renderers/render-manager.ts)

The RenderManager is a singleton class that manages the lifecycle and dispatch of renderers.

Renderer Interface (src/renderers/types.ts)

All specialized renderers must implement the Renderer interface:

export interface Renderer<T = unknown> {
  readonly id: string;
  readonly name: string;
  readonly priority?: number; class="hl-cmt">// Higher priority renderers are checked first
  canRender(data: unknown): data is T; class="hl-cmt">// Type guard to check data compatibility
  render(data: T, ctx: RenderContext): string; class="hl-cmt">// Renders the data
}

RenderContext (src/renderers/types.ts)

The RenderContext object provides runtime configuration to renderers, allowing them to adapt their output.

export interface RenderContext {
  mode: DisplayMode; class="hl-cmt">// &#39;plain&#39; or &#39;fancy&#39;
  color: boolean;    class="hl-cmt">// Whether ANSI colors should be used
  emoji: boolean;    class="hl-cmt">// Whether emojis should be used
  width: number;     class="hl-cmt">// Terminal width for layout adjustments
  height: number;    class="hl-cmt">// Terminal height
  piped: boolean;    class="hl-cmt">// True if output is being piped (non-interactive)
}

The getDefaultRenderContext() function determines initial context values based on the current terminal environment (e.g., process.stdout.isTTY, process.stdout.columns).

Data Types (src/renderers/types.ts)

This file defines the structured data interfaces that specialized renderers are designed to handle. Each interface includes a type literal property (e.g., type: 'diff') which is used by the is*Data type guards (e.g., isDiffData) to identify the data's structure.

Key data types include:

Specialized Renderers

The renderers module includes several built-in specialized renderers, each designed for a specific data type:

Charts Sub-module (src/renderers/charts/)

This sub-module is dedicated to generating SVG charts and converting them into terminal-displayable images. It leverages D3Node for SVG creation, @resvg/resvg-js for efficient SVG-to-PNG conversion, and terminal-image for rendering PNGs in the terminal.

Chart Types (src/renderers/charts/types.ts)

Defines data structures and options specific to charts:

Chart Generators

Each generator function takes chart-specific data and ChartOptions to produce an SVG string:

Rendering Utilities (src/renderers/charts/render-utils.ts)

Integration and Usage

The src/renderers/index.ts file serves as the main entry point for the module, re-exporting all public components.

The rendering system is utilized by UI components (e.g., ui/components/StructuredOutput.tsx, ui/components/ChatHistory.tsx) to display structured data received from various sources. These components call getRenderManager() and then render() or canRender() to dynamically format data for the terminal.

Contributing to Renderers

To add a new specialized renderer:

  1. Define a new data interface in src/renderers/types.ts (e.g., MyNewData). Ensure it has a unique type literal property.
  2. Create a new renderer file (e.g., src/renderers/my-new-renderer.ts).
  3. Implement the Renderer interface:

  1. Export your new renderer from its file.
  2. Register your renderer by importing it into src/renderers/index.ts and adding it to the initializeRenderers() function. Assign a priority if it needs to be checked before or after other renderers.

To add a new chart type:

  1. Define new data interfaces in src/renderers/charts/types.ts if needed.
  2. Create a new chart generator file (e.g., src/renderers/charts/my-new-chart.ts).
  3. Implement a generateMyNewChartSVG(data: MyNewChartData, options: ChartOptions): string function using D3Node to construct the SVG.
  4. Export the generator from its file.
  5. Optionally, create a convenience renderMyNewChart function in src/renderers/charts/render-utils.ts that wraps your generator and svgToTerminalImage.
  6. Re-export your new chart generator and render utility from src/renderers/charts/index.ts and src/renderers/svg-charts.ts.