Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

Datui is an interactive terminal user interface (TUI) for exploring and analyzing data files.

Datui is in rapid development—features and interfaces are evolving as built. Expect some changes between releases en route to a stable foundation.

See It In Action

Use arrow keys or vim-style bindings (j/k for up/down, h/l for left/right) to explore interactively.

Basic Navigation Demo

For more demonstrations of Datui’s features, see the Demos page.

Demo Gallery

This page showcases interactive demonstrations of Datui’s features.

Basic Navigation Demo

What it shows:

  • Loading a Parquet file (people.parquet)
  • Scrolling through data using / (or j/k) keys

See Loading Data for more information about file formats and options.

Querying

Querying Demo

What it shows:

  • Opening the query input with /
  • Typing a query: select first_name, last_name, city, salary where salary > 80000
  • Executing the query and seeing filtered results

See Querying Data for detailed query syntax and examples.

Getting Started

This section will help you get started with datui.

Note: This is a stub. Please add your getting started content here.

Installation

Note: This is a stub. Please add installation instructions here.

Quick Start

Note: This is a stub. Please add quick start guide here.

User Guide

Note: This is a stub. Please add user guide content here.

Configuration

Datui supports extensive customization through TOML configuration files. You can customize colors, defaults, performance settings, and more.

Quick Start

Generate a default configuration file:

datui --generate-config

This creates ~/.config/datui/config.toml with all available options and helpful comments.

Configuration File Location

  • Linux: ~/.config/datui/config.toml
  • macOS: ~/.config/datui/config.toml
  • Windows: %APPDATA%\datui\config.toml

Configuration Priority

Settings are applied in this order (later values override earlier ones):

  1. Default values (hardcoded)
  2. Config file (~/.config/datui/config.toml)
  3. Command-line arguments (highest priority)

Configuration Sections

File Loading Defaults

Customize how data files are loaded:

[file_loading]
delimiter = 44        # CSV delimiter (44 = comma)
has_header = true     # Whether files have headers
skip_lines = 0        # Lines to skip at file start
skip_rows = 0         # Rows to skip when reading
compression = "gzip"  # Default compression: "gzip", "zstd", "bzip2", "xz"

Display Settings

Control how data is displayed:

[display]
pages_lookahead = 3   # Pages to buffer ahead (smoother scrolling)
pages_lookback = 3    # Pages to buffer behind
row_numbers = false   # Show row numbers on left side
row_start_index = 1   # Starting index for row numbers (0 or 1)

Example: Enable row numbers starting at 0

[display]
row_numbers = true
row_start_index = 0

Performance Settings

Tune performance and responsiveness:

[performance]
sampling_threshold = 10000   # Sample datasets >= this size
event_poll_interval_ms = 25  # UI polling interval (lower = more responsive)

Memory vs Speed:

  • Increase sampling_threshold to avoid sampling (uses more memory, full accuracy)
  • Decrease sampling_threshold for faster analysis on large datasets (uses less memory)

Color Themes

Customize the entire UI appearance:

[theme.colors]
keybind_hints = "cyan"              # Keybind hints
keybind_labels = "yellow"           # Action labels
primary_chart_series_color = "cyan" # Chart data
secondary_chart_series_color = "dark_gray" # Chart theory
error = "red"                       # Error messages
success = "green"                   # Success indicators
warning = "yellow"                  # Warnings
dimmed = "dark_gray"                # Dimmed elements

Color Formats

Three color formats are supported:

1. Named Colors

keybind_hints = "cyan"
error = "bright_red"
dimmed = "dark_gray"
background = "default"  # Use terminal default background
text_primary = "default"  # Use terminal default text color

Available names:

  • Basic: black, red, green, yellow, blue, magenta, cyan, white
  • Bright: bright_red, bright_green, bright_blue, etc.
  • Grays: gray, dark_gray, light_gray
  • Special: reset or default (uses terminal default colors - works in both light and dark themes)

2. Hex Colors

background = "#1e1e1e"
sidebar_border = "#007acc"
controls_bg = "#2d2d2d"
  • Format: #rrggbb (6 hex digits)
  • Case-insensitive: #FF0000 or #ff0000
  • Automatically adapted to your terminal’s capabilities

3. Indexed Colors

controls_bg = "indexed(236)"  # Example: specific palette entry
surface = "indexed(239)"
  • Direct reference to xterm 256-color palette (0-255)
  • Respects your terminal’s color scheme
  • Useful for matching specific terminal themes

Terminal Compatibility

Colors automatically adapt to your terminal:

  • True color terminals (Alacritty, kitty, iTerm2): Hex colors display exactly
  • 256-color terminals (xterm-256color): Hex converted to nearest palette match
  • Basic terminals (8/16 colors): Colors map to nearest ANSI color
  • Monochrome: Set NO_COLOR=1 to disable colors

Light Theme Support: The default background and text_primary use "default" to inherit your terminal’s default colors. This ensures the app works correctly in both light and dark terminal themes. If you set explicit colors like "black" or "white", they may not work well in light themes.

Available Colors

All UI colors can be customized:

ColorPurposeDefault
keybind_hintsKeybind hints (modals, breadcrumb, correlation matrix)cyan
keybind_labelsAction labels in controls baryellow
primary_chart_series_colorChart data (histogram bars, Q-Q plot data points)cyan
secondary_chart_series_colorChart theory (histogram overlays, Q-Q plot reference line)dark_gray
successSuccess indicators, normal distributionsgreen
errorError messages, outliersred
warningWarnings, skewed distributionsyellow
dimmedDimmed elements, axis linesdark_gray
backgroundMain backgrounddefault (uses terminal default)
surfaceModal/surface backgroundsdefault (uses terminal default)
controls_bgControls bar and table header backgroundsdark_gray
text_primaryPrimary textdefault (uses terminal default)
text_secondarySecondary textdark_gray
text_inverseText on light backgroundsblack
table_headerTable column header textwhite
table_header_bgTable column header backgrounddark_gray
column_separatorVertical line between table columnscyan
table_selectedSelected row stylereversed
sidebar_borderSidebar bordersdark_gray
modal_border_activeActive modal elementsyellow
modal_border_errorError modal bordersred
distribution_normalNormal distribution indicatorgreen
distribution_skewedSkewed distribution indicatoryellow
distribution_otherOther distribution typeswhite
outlier_markerOutlier indicatorsred

Query System

Configure query behavior:

[query]
history_limit = 1000      # Max queries to remember
enable_history = true     # Enable query history

Template Settings

Configure template behavior:

[templates]
auto_apply = false  # Auto-apply most relevant template on file open

Debug Settings

Configure debug overlay:

[debug]
enabled = false             # Show debug overlay by default
show_performance = true     # Show performance metrics
show_query = true           # Show LazyFrame query
show_transformations = true # Show transformation state

Example Configurations

Minimal Configuration

Simple customization for common preferences:

version = "0.2"

[display]
row_numbers = true
row_start_index = 0

[theme.colors]
keybind_hints = "blue"

Dracula Theme

Complete Dracula color scheme using hex colors:

version = "0.2"

[theme.colors]
keybind_hints = "#bd93f9"              # Purple
keybind_labels = "#ff79c6"             # Pink
primary_chart_series_color = "#bd93f9" # Purple
secondary_chart_series_color = "#6272a4" # Comment gray
success = "#50fa7b"                    # Green
error = "#ff5555"                      # Red
warning = "#ffb86c"                    # Orange
dimmed = "#6272a4"                     # Comment gray

background = "#282a36"                 # Background (Dracula dark)
surface = "#44475a"                    # Current line
controls_bg = "#44475a"                # Controls bar

text_primary = "#f8f8f2"               # Foreground
text_secondary = "#6272a4"             # Comment
text_inverse = "#282a36"               # Background (for inverse)

table_header = "#f8f8f2"               # Foreground
table_header_bg = "#44475a"            # Current line
column_separator = "#bd93f9"            # Purple
table_selected = "reversed"

sidebar_border = "#6272a4"             # Comment gray
modal_border_active = "#ff79c6"        # Pink
modal_border_error = "#ff5555"         # Red

distribution_normal = "#50fa7b"         # Green
distribution_skewed = "#ffb86c"         # Orange
distribution_other = "#f8f8f2"          # Foreground
outlier_marker = "#ff5555"              # Red

Performance Tuned

Optimize for large datasets:

version = "0.2"

[display]
pages_lookahead = 5   # More buffering for smoother scrolling
pages_lookback = 5

[performance]
sampling_threshold = 50000  # Sample only very large datasets
event_poll_interval_ms = 16 # ~60 FPS polling (more responsive)

High Contrast Theme

Using named colors for maximum compatibility:

version = "0.2"

[theme.colors]
keybind_hints = "bright_cyan"
keybind_labels = "bright_yellow"
primary_chart_series_color = "bright_cyan"
secondary_chart_series_color = "dark_gray"
error = "bright_red"
success = "bright_green"
warning = "bright_yellow"
dimmed = "dark_gray"

background = "black"
controls_bg = "dark_gray"
text_primary = "bright_white"

Command-Line Overrides

CLI arguments always override config file settings:

# Config has row_numbers = true, but disable for this run:
datui data.csv --row-numbers=false

# Override page buffering:
datui data.csv --pages-lookahead 10

# Override delimiter:
datui data.csv --delimiter=9  # Tab character (ASCII 9)

Managing Configuration

View Current Config

Your config file is at ~/.config/datui/config.toml. Edit it with any text editor:

# Linux/macOS
nano ~/.config/datui/config.toml
vim ~/.config/datui/config.toml
code ~/.config/datui/config.toml

# Windows
notepad %APPDATA%\datui\config.toml

Reset to Defaults

Regenerate the default config file:

datui --generate-config --force

This overwrites your existing config with a fresh template.

Remove Configuration

Simply delete the config file:

# Linux/macOS
rm ~/.config/datui/config.toml

# Windows
del %APPDATA%\datui\config.toml

Datui will use default values when no config file exists.

Troubleshooting

Config Not Loading

If your config isn’t being used:

  1. Check file location: Ensure config is at ~/.config/datui/config.toml
  2. Check syntax: TOML must be valid. Run datui <file> and check for warnings
  3. Check version: Config must start with version = "0.2"
  4. Check validation: Ensure values are in valid ranges (e.g., sampling_threshold > 0)

Invalid Color

If you see an error about invalid colors:

Error: Invalid color value for 'keybind_hints': Unknown color name: 'notacolor'

Solutions:

  • Use valid color names (see list above)
  • Use hex format: #ff0000
  • Use indexed format: indexed(236)
  • Check spelling and case (names are case-insensitive)

Config Parse Error

If TOML parsing fails:

Error: Failed to parse config file: expected newline, found ...

Solutions:

  • Check TOML syntax at https://toml.io/
  • Ensure proper quotes around strings
  • Verify no typos in section names
  • Regenerate config: datui --generate-config --force

Colors Look Wrong

If colors don’t look right:

  1. Check terminal capabilities: Some terminals don’t support true color
  2. Try named colors: More portable than hex colors
  3. Try indexed colors: Match your terminal’s palette exactly
  4. Check NO_COLOR: Unset with unset NO_COLOR if colors are disabled

See Also

Loading Data

Note: This is a stub. Please add content about loading data files (CSV, Parquet, JSON, NDJSON) here.

Querying Data

Note: This is a stub. Please add content about querying data here.

Filtering and Sorting

Note: This is a stub. Please add content about filtering and sorting data here.

Analysis Features

Note: This is a stub. Please add content about statistical analysis features here.

Templates

Note: This is a stub. Please add content about templates here.

Reference

Note: This is a stub. Please add reference documentation here.

Command Line Options

Note: This is a stub. Please add command-line options documentation here.

Keyboard Shortcuts

Note: This is a stub. Please add keyboard shortcuts documentation here.

Query Syntax

Note: This is a stub. Please add query syntax documentation here.

Advanced Topics

Note: This is a stub. Please add advanced topics content here.

Performance Tips

Note: This is a stub. Please add performance tips here.

For Developers

Contributing

Thank you for your interest in contributing to Datui!

After cloning the repo, follow the Setup instructions below to get started.

Setup

Setup Script

TLDR: The entire setup process can be automated by running

python scripts/setup-dev.py

The script will:

Python Virtual Environment

There are Python scripts in the /scripts directory that are used to do things like build test data, documentation, and demo gifs.

Setting up a virtual environment with dependencies for these scripts will ensure you can run them all.

A common convention is to create a virtual environment in the .venv/ directory of the repository. The .gitignore is set up to ignore this location so that files there aren’t added by mistake.

python -m venv .venv

Then activate the virtual environment.

source .venv/bin/activate

Once activated, install dependencies used to run the availble Python scripts.

pip install -r scripts/requirements.txt

You’re now ready to run the tests.

Pre-commit Hooks

To encourage consistency and quality, the CI build checks the source code of the application for formatting and linter warnings.

This project uses pre-commit to manage git pre-commit hooks which automatically run the same code quality checks in your repository before commits are made.

Installing Pre-commit and Hooks

If you used the Setup Script, the pre-commit hooks are already installed.

  1. Install pre-commit:

    If you set up a Python virtual environment using the instructions above then you already have everything you need. Activate it and skip this step.

    Otherwise, install pre-commit using your desired method.

    # Using pip
    pip install pre-commit
    
    # Or using homebrew (macOS)
    brew install pre-commit
    
    # Or using conda
    conda install -c conda-forge pre-commit
    
  2. Install the git hooks:

    pre-commit install
    

    This installs the hooks into .git/hooks/ so they run automatically on commit.

    Note: You only need the pre-commit command accessible when you need to use it to manually run or update the hooks. Once installed into your repo, the hooks themselves do not require pre-commit.

    See the pre-commit documentation for more information about its features.

The following hooks are configured:

  • cargo-fmt: Automatically formats Rust code with cargo fmt

    • If code needs formatting, it will be formatted and the commit will fail
    • Stage the formatted changes and commit again
  • cargo-clippy: Runs cargo clippy --all-targets -- -D warnings

    • Fails if clippy finds any warnings
    • Fix them and commit again

Hooks run automatically when you git commit. If any hook fails, the commit is aborted.

Running Hooks

Run all hooks manually:

pre-commit run --all-files

Run a specific hook:

pre-commit run cargo-fmt --all-files
pre-commit run cargo-clippy --all-files

Skipping Hooks

If you need to skip hooks for a specific commit (not recommended):

git commit --no-verify -m "Emergency fix"

Updating hooks

Update hook versions and configurations:

pre-commit autoupdate

Troubleshooting

Hook not running?

  • Make sure you ran pre-commit install
  • Check .git/hooks/pre-commit exists

Hooks too slow?

  • Only changed files are checked by default
  • Use SKIP=hook-name git commit to skip specific hooks

Adding Configuration Options

For detailed instructions on adding new configuration options to datui, see the dedicated Guide to Adding Configuration Options.

Quick summary:

  1. Add field to appropriate config struct (src/config.rs)
  2. Update Default implementation
  3. Add merge logic in merge() method
  4. Add comments to comment constant (next to struct)
  5. Use the value in application code
  6. Add tests
  7. Update user documentation

The guide includes step-by-step instructions, code examples, merge rules, best practices, and special instructions for adding theme colors.

Running the Tests

Running the tests is done using Cargo’s test command.

cargo test

However, the tests require sample data which are too large to add to the repo. Instead, the data must be generated before the tests can be run.

Generating Sample Data

If you used the Setup Script, the sample data has already been generated. To regenerate the data, see the instructions

The tests will automatically run a Python script to generate the sample files if they do not already exist. However, that script has some dependencies.

To install the dependencies, I recommend following the Python Virtual Environment Setup Instructions from the Contributing section.

Once you have a Python virtual environment set up with the requirements.txt from the scripts/ directory, and activated it, you’re ready to run the tests for the first time.

# activate the virtual environment if sample data is not already generated
source .venv/bin/activte

# run the tests
cargo test

The tests will look for the files and run the generation script if they don’t already exist. Having the virtual environment activated before running tests for the first time ensures the automatic generation goes smoothly.

After the files are built you don’t need to have that environment activated anymore to run tests.

Regenerating or Updating the Sample Data

You can run the data generation script yourself:

python scripts/generate_sample_data.py

The data will not be automatically regenerated in the future. Use the script to regenerate the data when necessary.

Documentation

Datui uses mdBook to build static documentation web pages from markdown files.

The documentation markdown files can be found in the docs subdirectory.

Build Documentation

Install mdBook

If you used the Setup Script, mdBook is already installed.

Building the documentation requires mdbook to be available in your terminal.

I recommend using cargo to install it. It will be available into your ~/.cargo/bin/, where the documentation build script will look for it. You may also add that location to your PATH if you like.

cargo install mdbook

Build

To build the entire documentation site:

scripts/docs/build_all_docs_local.sh

This will populate the book directory with the site’s files.

At the end it will ask you if you would like a server to view the docs, or you can simply open the index.html with your web browser.

To view locally, you can:
  1. Open book/index.html in your browser
  2. Or use a simple HTTP server:
     python3 -m http.server 8000 --directory book
     Then visit: http://localhost:8000

Start a local HTTP server to view the docs? (y/n)

Adding Configuration Options

When adding new configuration options to datui, follow this process:

Process Overview

Adding a new configuration option requires updates in 7 places:

  1. Config struct definition
  2. Default implementation
  3. Merge logic
  4. Comment constants (for generated configs)
  5. Application code usage
  6. Tests
  7. Documentation

Step-by-Step Guide

1. Add Field to Config Struct

Add the new field to the appropriate config struct in src/config.rs:

#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct DisplayConfig {
    pub pages_lookahead: usize,
    pub pages_lookback: usize,
    pub row_numbers: bool,
    pub row_start_index: usize,
    pub font_size: Option<u8>,  // NEW FIELD
}
}

2. Update Default Implementation

Add the default value in the Default trait:

#![allow(unused)]
fn main() {
impl Default for DisplayConfig {
    fn default() -> Self {
        Self {
            pages_lookahead: 3,
            pages_lookback: 3,
            row_numbers: false,
            row_start_index: 1,
            font_size: None,  // NEW: None = use terminal default
        }
    }
}
}

3. Update Merge Logic

Add merge handling in the section’s merge() method:

#![allow(unused)]
fn main() {
impl DisplayConfig {
    pub fn merge(&mut self, other: Self) {
        let default = DisplayConfig::default();
        // ... existing merge logic ...
        
        // NEW: Merge font_size (Option fields)
        if other.font_size.is_some() {
            self.font_size = other.font_size;
        }
    }
}
}

Merge rules:

  • Option fields: If other.field.is_some(), take the value
  • Non-Option fields: If other.field != default.field, take the value

4. Add Comments to Comment Constants

Add comments to the comment constant array right after the struct definition in src/config.rs:

#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct DisplayConfig {
    pub pages_lookahead: usize,
    pub pages_lookback: usize,
    pub row_numbers: bool,
    pub row_start_index: usize,
    pub font_size: Option<u8>,  // NEW FIELD
}

// Field comments for DisplayConfig
const DISPLAY_COMMENTS: &[(&str, &str)] = &[
    // ... existing fields ...
    (
        "font_size",
        "Font size for terminal display (optional)\nSet to null to use terminal default, or 8-16 for explicit size",
    ),
];
}

Note: Comments are defined next to the struct definition. The config template is generated from Rust code defaults, with all fields commented out so users can uncomment to override.

5. Use in Application Code

Access the config value where needed:

#![allow(unused)]
fn main() {
let font_size = config.display.font_size.unwrap_or(terminal_default);
}

Or pass through App if needed globally:

#![allow(unused)]
fn main() {
app.font_size = config.display.font_size;
}

6. Add Tests

Add tests in tests/config_test.rs or tests/config_integration_test.rs:

#![allow(unused)]
fn main() {
#[test]
fn test_font_size_config() {
    let mut config = AppConfig::default();
    config.display.font_size = Some(12);
    
    assert_eq!(config.display.font_size, Some(12));
    assert!(config.validate().is_ok());
}
}

7. Update Documentation

Update documentation:

  • Add to docs/user-guide/configuration.md
  • Mention in README.md if it’s a major feature

Note: Configuration comments are defined in comment constants next to struct definitions (e.g., DISPLAY_COMMENTS, PERFORMANCE_COMMENTS) in src/config.rs. The config template is generated programmatically from these constants.

Implementation Checklist

  • Field added to config struct
  • Default implementation updated
  • Merge logic implemented
  • Comments added to comment constant (next to struct)
  • Used in application code
  • Tests added
  • Documentation updated
  • All tests passing (cargo test)
  • No clippy warnings (cargo clippy)
  • Code formatted (cargo fmt)

Best Practices

Choosing Field Types

  • Option fields: Use Option<T> for optional settings

    #![allow(unused)]
    fn main() {
    pub font_size: Option<u8>,  // None = use default
    }
  • Required fields: Use plain types with sensible defaults

    #![allow(unused)]
    fn main() {
    pub pages_lookahead: usize,  // Always has a value
    }
  • Strings: Use String for text values

    #![allow(unused)]
    fn main() {
    pub delimiter: String,  // CSV delimiter character
    }

Sensible Defaults

Ensure defaults match existing behavior:

#![allow(unused)]
fn main() {
impl Default for DisplayConfig {
    fn default() -> Self {
        Self {
            pages_lookahead: 3,
            pages_lookback: 3,
            row_numbers: false,
            row_start_index: 1,
        }
    }
}
}

Clear Config Comments

Comments in the comment constants should:

  • Explain what the option does
  • Show valid values or ranges
  • Provide examples
  • Note any interactions with other settings

Good example:

#![allow(unused)]
fn main() {
const PERFORMANCE_COMMENTS: &[(&str, &str)] = &[
    (
        "sampling_threshold",
        "Sampling threshold: datasets >= this size will be sampled for statistics\nSet to higher value to avoid sampling, or lower to sample more aggressively",
    ),
];
}

Poor example:

#![allow(unused)]
fn main() {
const PERFORMANCE_COMMENTS: &[(&str, &str)] = &[
    ("sampling_threshold", "Sampling threshold"),
];
}

Validation

Add validation in AppConfig::validate() for constraints:

#![allow(unused)]
fn main() {
fn validate(&self) -> Result<()> {
    // ... existing validation ...
    
    // Validate new field
    if self.performance.sampling_threshold == 0 {
        return Err(eyre!("sampling_threshold must be greater than 0"));
    }
    
    Ok(())
}
}

Testing Edge Cases

Test important scenarios:

  • Missing values (uses default)
  • Invalid ranges (validation catches)
  • Boundary conditions
  • Config merging (CLI overrides config)
  • TOML parsing (valid syntax)

Adding Colors to Theme

When adding new colors to the theme system, follow these additional steps:

1. Add to ColorConfig Struct

#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ColorConfig {
    // ... existing colors ...
    pub new_color: String,  // NEW
}
}

2. Add to ColorConfig Default

#![allow(unused)]
fn main() {
impl Default for ColorConfig {
    fn default() -> Self {
        Self {
            // ... existing colors ...
            new_color: "cyan".to_string(),  // NEW
        }
    }
}
}

3. Add to Validation

#![allow(unused)]
fn main() {
impl ColorConfig {
    fn validate(&self, parser: &ColorParser) -> Result<()> {
        macro_rules! validate_color {
            ($field:expr, $name:expr) => {
                parser
                    .parse($field)
                    .map_err(|e| eyre!("Invalid color value for '{}': {}", $name, e))?;
            };
        }
        
        // ... existing validations ...
        validate_color!(&self.new_color, "new_color");  // NEW
        
        Ok(())
    }
}
}

4. Add to Merge Logic

#![allow(unused)]
fn main() {
impl ColorConfig {
    pub fn merge(&mut self, other: Self) {
        let default = ColorConfig::default();
        // ... existing merge logic ...
        
        if other.new_color != default.new_color {  // NEW
            self.new_color = other.new_color;
        }
    }
}
}

5. Add to Theme Parsing

#![allow(unused)]
fn main() {
impl Theme {
    pub fn from_config(config: &ThemeConfig) -> Result<Self> {
        let parser = ColorParser::new();
        let mut colors = HashMap::new();
        
        // ... existing color parsing ...
        colors.insert(
            "new_color".to_string(),
            parser.parse(&config.colors.new_color)?,
        );  // NEW
        
        Ok(Self { colors })
    }
}
}

6. Add Comments to Comment Constant

#![allow(unused)]
fn main() {
// Field comments for ColorConfig
const COLOR_COMMENTS: &[(&str, &str)] = &[
    // ... existing colors ...
    (
        "new_color",
        "Description of the new color and where it's used",
    ),
];
}

Note: Comments are simple text - they’ll be prefixed with # when generating the config. The field itself will appear as # new_color = "cyan" (commented out).

7. Replace Hardcoded Usage

Find and replace hardcoded colors in widgets:

Before:

#![allow(unused)]
fn main() {
Style::default().fg(Color::Cyan)
}

After:

#![allow(unused)]
fn main() {
Style::default().fg(self.color("new_color"))
// or
Style::default().fg(theme.get("new_color"))
}

Color Naming Conventions

  • Use descriptive names: sidebar_border not sb
  • Be specific: modal_border_active vs sidebar_border (modals vs sidebars)
  • Group logically: distribution_normal, distribution_skewed, distribution_other
  • Consider purpose: text_primary, text_secondary, text_inverse

Common Patterns

Option Field Pattern

#![allow(unused)]
fn main() {
// Config struct
pub struct Config {
    pub optional_field: Option<T>,
}

// Default
impl Default for Config {
    fn default() -> Self {
        Self {
            optional_field: None,  // No default value
        }
    }
}

// Merge
impl Config {
    pub fn merge(&mut self, other: Self) {
        if other.optional_field.is_some() {
            self.optional_field = other.optional_field;
        }
    }
}

// Usage
let value = config.optional_field.unwrap_or(fallback);
}

Required Field Pattern

#![allow(unused)]
fn main() {
// Config struct
pub struct Config {
    pub required_field: usize,
}

// Default
impl Default for Config {
    fn default() -> Self {
        Self {
            required_field: 10,  // Sensible default
        }
    }
}

// Merge
impl Config {
    pub fn merge(&mut self, other: Self) {
        let default = Config::default();
        if other.required_field != default.required_field {
            self.required_field = other.required_field;
        }
    }
}

// Usage
let value = config.required_field;
}

String Field Pattern

#![allow(unused)]
fn main() {
// Config struct
pub struct Config {
    pub mode: String,
}

// Default
impl Default for Config {
    fn default() -> Self {
        Self {
            mode: "auto".to_string(),
        }
    }
}

// Merge
impl Config {
    pub fn merge(&mut self, other: Self) {
        let default = Config::default();
        if other.mode != default.mode {
            self.mode = other.mode;
        }
    }
}

// Validation
fn validate(&self) -> Result<()> {
    match self.mode.as_str() {
        "option1" | "option2" | "option3" => Ok(()),
        _ => Err(eyre!("Invalid mode: {}. Must be one of: option1, option2, option3", self.mode))
    }
}
}

Resources

  • See src/config.rs for existing implementations and comment constants (e.g., PERFORMANCE_COMMENTS, DISPLAY_COMMENTS)
  • See tests/config_test.rs for test examples
  • Run datui --generate-config to see the generated config template (all fields commented out)

Questions?

If you’re unsure about:

  • Which config section to use: Look at similar settings in existing config
  • Merge logic: Follow the patterns in existing merge implementations
  • Validation: Add validation if there are constraints on the value
  • Testing: Look at existing tests for similar config types

Demos

The Datui demo animations are created using vhs, which lets you script and record keystrokes to a terminal app.

Install vhs

See vhs install instructions.

Define Tapes

The vhs application uses .tape files to script keystrokes. See Datui’s here.

Generating the Animations

Run generate-all to use vhs to generate an animated gif file for each tape.

The generate-all script will first run a release build, and then use that version of the application when creating the demos.

scripts/demos/generate-all.sh

The animations will be placed in the demos directory.

During the creation of the documentation, these animations are copied into a demos/ subdirectory of the generated site. From there, the files may be referenced from within the docs.