# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project

`gilt` is a Rust port of Python's [rich](https://github.com/Textualize/rich) library — terminal formatting (styles, tables, trees, syntax highlighting, progress bars, live displays, markdown) emitted as ANSI escape sequences. It is a published crate (`gilt` on crates.io, currently v1.4.1) with a workspace member `gilt-derive` (proc-macro crate) under `crates/gilt-derive/`. MSRV is **1.82.0** (relies on `std::sync::LazyLock`).

API/behavior parity with upstream Python `rich` is an explicit goal — when porting or fixing a widget, the reference behavior is rich's. The rich source is not checked out here; the porting reference is **`../research_doc/`** — 30 module-by-module analysis docs of rich's source (segment, style, color, text, cells/measure, protocols, console, table, …) with Python→Rust patterns. Consult the matching doc before porting or changing a widget's behavior. Parity gaps against rich's test suite are tracked in `.review/04-test-parity.md`.

This repo is one of several sibling crates in the parent `rusty_rich/` workspace that together port the rich/Textual stack to Rust: `../gilt-tui` (a Textual port — CSS-styled TUI framework built on gilt) + `../gilt-tui-macros`, `../anstyle`, and `../textual_research/` (the Textual-analysis counterpart to `research_doc/`, feeding gilt-tui). Changes to gilt's public API can affect gilt-tui.

## Commands

The `justfile` defines the canonical workflows; `cargo test --lib` is what CI runs.

```bash
# Tests — prefer nextest, but it does NOT run doctests (run those with cargo test --doc)
cargo nextest run --lib                 # fast unit-test run (2300+ inline #[test])
cargo nextest run --lib <substring>     # run a single test / filtered subset
cargo test --doc                        # doctests — there are many in lib.rs; nextest skips these
cargo test --lib                        # what CI uses

just check         # fmt --check + clippy --all-features + test --lib + test --doc + doc + missing-docs
just check-all     # tests across default / no-default / all features + doctests + clippy
just check-minimal # cargo test --lib --no-default-features

# Lint / format / docs
cargo clippy --all-features -- -D warnings   # CI treats warnings as errors
cargo fmt                                     # apply; `just fmt-check` to verify
cargo doc --no-deps --all-features            # CI builds docs with RUSTDOCFLAGS=-D warnings

# Benchmarks (criterion, ~80 benches) — use the release-fast profile for local runs
cargo bench                                   # benches/benchmarks.rs + benches/live_threaded.rs

# Examples (107 of them under examples/)
cargo run --example showcase --all-features
cargo run --example derive_table --features derive
```

CI matrix (`.github/workflows/ci.yml`) gates on: `cargo test --lib` (stable + nightly), all-features tests, doctests, clippy `-D warnings` (base + features), `cargo fmt --check`, `cargo doc -D warnings`, **MSRV 1.82.0 `cargo check`**, and a **wasm32-unknown-unknown** build (`--no-default-features --features json,markdown,syntax`). Keep all of these green — especially MSRV and WASM, which are easy to break accidentally.

## Architecture

### The rendering pipeline (the core abstraction)

Everything renders through three types in `src/`:

- **`Renderable`** (trait, `console.rs`): a single method `gilt_console(&self, &Console, &ConsoleOptions) -> Vec<Segment>`. Every widget (`Table`, `Panel`, `Tree`, `Text`, even `str`/`String`) implements this. To add a widget, implement `Renderable`.
- **`Segment`** (`segment.rs`): the atomic unit of output — text + optional `Style` + optional control code. Width is computed in terminal *cells* (see Unicode below). All rendering flows through `Vec<Segment>`.
- **`Console`** (`console.rs` + sibling files): the orchestrator. Holds terminal capabilities (size, color system, markup on/off), drives `render()` → segments → ANSI string, and handles capture/record/export.

Flow: `console.print(&widget)` → `widget.gilt_console(...)` produces `Vec<Segment>` → console serializes segments to ANSI (or HTML/SVG when recording).

### Console is split across files via `#[path]` + multiple `impl Console` blocks

`console.rs` is the hub; methods are partitioned into sibling files included with `#[path = "..."] mod ...;`, each adding another `impl Console` block:
- `console_builder.rs` — `ConsoleBuilder`
- `console_capture.rs` — `begin_capture` / `end_capture`
- `console_render.rs` — `print`/`log`/`rule`/`render`/`measure`/`save_*` etc.
- `console_export.rs` — `export_text` / `export_html` / `export_svg`
- `console_tests.rs` — tests

When adding a `Console` method, put it in the file matching its concern and keep it inside that file's `impl Console` block — don't bloat `console.rs`.

### Module organization & backward-compat re-exports

Modules were reorganized into subdirectories (`color/`, `text/`, `utils/`, `widgets/`, `live/`, `progress/`, `error/`) but **`lib.rs` re-exports them at their old crate-root paths** (e.g. `pub use color::{accessibility, theme, ...}`, `pub use utils::{box_chars, emoji, inspect, ...}`, `pub use widgets::table`). So `gilt::theme`, `gilt::box_chars`, `gilt::table` still resolve. When moving a module, preserve its public path with a re-export rather than breaking it.

- `prelude.rs` — the curated `use gilt::prelude::*;` surface; add commonly-used new public types here.
- `text/` — `Text` (rich text = plain string + styled spans), wrapping, markup. `markup.rs` parses `[tag]...[/tag]` BBCode-style markup.
- `widgets/table/` — `Table` split into `core/render/column/row`.
- `progress/` and `progress/columns/` — multi-task progress with pluggable columns.
- `color/` — `Color`, `ColorSystem`, `ColorTriplet`, palettes, themes, WCAG `accessibility`.

### Feature gating

Default features: `json`, `markdown`, `syntax`, `interactive`, `logging`. Heavy/optional deps are behind features — `derive`, `tracing`, `miette`, `eyre`, `anstyle`, `csv`, `readline`, `async`, `http` (see `Cargo.toml`). Feature-gated modules use `#[cfg(feature = "...")]` on both the `pub mod` and the re-export. **Any new public item must compile under `--no-default-features` and on `wasm32`** — no `libc`/`crossterm`/terminal-syscall deps (that's how WASM support is kept).

### Derive macros

`crates/gilt-derive/` provides 7 derives (`Table`, `Panel`, `Tree`, `Columns`, `Rule`, `Inspect`, `Renderable`). Non-colliding ones are re-exported at the crate root (`gilt::Table`); the three that collide with widget type names (`Columns`, `Inspect`, `Rule`) live ONLY under `gilt::derives::*`. The derive crate version is pinned to the main crate version in `Cargo.toml`.

## Conventions

- **`Style::parse(&str) -> Style` is lossy/infallible** (not `Result`) — invalid tokens are silently dropped. This is a deliberate v1 ergonomic choice (see `MIGRATION_v1.md`). Markup parsing (`Text::from_markup`) does return `Result`.
- **Unicode correctness matters and is non-trivial.** Cell width uses `unicode-width`; cluster iteration uses `unicode-segmentation` (UAX #29 grapheme clusters). Truncation/cropping snaps to grapheme boundaries (never splits a ZWJ emoji cluster). v1.4 was a dedicated Unicode-correctness pass — `cells.rs` (`cell_len`, `set_cell_size`, `get_character_cell_size`) is the central width logic. The README "Unicode handling" section documents exactly what is in/out of scope; honor those boundaries.
- **Tests live both inline (`#[cfg(test)]` in `src/`, the bulk) and in `tests/`** (`tests/unit/`, `proptests.rs` with proptest, `integration.rs`, per-widget `*_unit.rs`). New widget logic should add inline unit tests; cross-cutting behavior goes in `tests/`.
- `CHANGELOG.md` is detailed and kept current per release; `MIGRATION_v1.md` documents the 0.13→1.0 API changes. Update the changelog when shipping user-visible changes.
- Release is automated via `just release <semver>` (bumps `Cargo.toml`, runs checks, tags, pushes, `gh release create`, `cargo publish`).

## Reference material

- **`../research_doc/`** — the canonical rich-port reference (start at `00-SUMMARY.md` / `07-QUICK-REFERENCE.md` / `INDEX.md`). Use it to find rich's exact behavior when porting or fixing a widget.
- **`.review/`** (coverage map, perf reports, code-quality, parity audit, v0.11 design) and **`thoughts/`** (research, audits, continuity ledgers) — in-repo prior analysis; consult before large refactors or perf work rather than re-deriving. Excluded from the published crate (`exclude` in `Cargo.toml`).
