Metadata-Version: 2.3
Name: heatgraph
Version: 0.1.0
Summary: A terminal heatgraph renderer for matrix-style data.
Author: Nicholas Wagner
Author-email: Nicholas Wagner <hello@nicholaswagner.dev>
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Requires-Python: >=3.12
Description-Content-Type: text/markdown

# heatgraph

A pure terminal ~~heatmap~~ _heatgraph_ renderer.

`heatgraph` reads a document from stdin and prints a colored heatgraph to stdout.

Zero dependencies. No positional arguments. One job.

Think of it like `jq` for heatmaps — its a heatmap viewer at the end of a Unix pipe.

```sh
cat data.json | jq '.filter things' | uvx heatgraph # use directly with uvx
cat workout-log.md | python scripts/calendar_to_matrix.py | heatgraph # or install locally

```

## Install

```bash
uv pip install -e .
```

The package installs two equivalent binaries: `heatgraph` (canonical) and
`thg` (short alias). Every example below uses `thg` to keep pipes terse —
substitute `heatgraph` freely.

## Quickstart

```bash
# Inline a matrix doc.
echo '{"values":[[1,2,3],[4,5,6]]}' | thg
```

The `scripts/` directory has some fixtures you can use to mock data:

```bash
# Generate a fixture and pipe it through.
python scripts/fixtures/matrix.py --rows 7 --cols 53 --seed 42 | thg

# Streaming pipeline — one frame per line, rendered in place.
./examples/game-of-life.py | thg --follow # use <control + c> to exit
```

Got date-keyed data? See [Working with calendars](#working-with-calendars).

## Philosophy
Unix rules apply.  Do just one thing and do it well.

`heatgraph` reads a structured data schema (aka a document) from stdio and renders a ~~heatmap~~ heatgraph in your terminal.  (see [SCHEMA.md](SCHEMA.md))

```json
{
  "values": [[0.0, 1.5, ...], ...],
  "cols": ["Jan", "Feb", ...],
  "rows": ["Mon", "Wed", ...],
  "meta": { "vmin": 0, "vmax": 10 }
}
```

Anything that isn't already a matrix doc has to be transformed by a producer
upstream. The renderer never grows input-shape awareness — that's what the
pipe is for.

## Working with calendars

Date-keyed data is common enough that we ship a reference producer for it:
`scripts/calendar_to_matrix.py` reads a [calendar doc](SCHEMA.md#calendar-doc)
on stdin and emits a [matrix doc](SCHEMA.md#matrix-doc) on stdout. It is a
plain Python script, not a console binary — invoke it with `python`.

Lets say you keep a `workout-log.md` in your obsidian vault.

```sh
# workout-log.md
# each day you hit the gym, you log the date
2026-04-01
2026-04-02
2026-04-04
2026-04-05
...
```

What's more motivating than data visualization?  Nothing, that's what.
But first we're going to need to turn this markdown into something usable.

```bash
examples/habit-to-cal.py examples/workout-log.md \
  | python scripts/calendar_to_matrix.py \
  | thg --glyphs marked
```

- `examples/habit-to-cal.py` turns the log into a [calendar doc](SCHEMA.md#calendar-doc).
- `scripts/calendar_to_matrix.py` turns that into a [matrix doc](SCHEMA.md#matrix-doc).
- `heatgraph` renders the matrix; `--glyphs marked` uses a habit-tracker-styled character set.

The script is in `scripts/` rather than `src/` on purpose: it's a worked
example of a producer, not core to `heatgraph`. If you have your own
calendar shape, copy the script and adapt it — that is the expected path.

## `heatgraph` options

```text
heatgraph [--theme NAME] [--colors JSON] [--glyphs PRESET|JSON] [--gamma F]
          [--vmin F] [--vmax F] [--spacer S] [--message S] [--legend S]
          [--max-columns N] [--simple] [--follow]
```

| Flag             | Description                                                          |
|------------------|----------------------------------------------------------------------|
| `--theme`        | Named theme (see [Built-in themes](#built-in-themes)) or a path to a theme JSON file.        |
| `--colors`       | JSON list of color specs: `#RRGGBB` hex, `256:N`, or raw ANSI escapes.                       |
| `--glyphs`       | Glyph preset name (see below) or a JSON list of glyphs.                                      |
| `--gamma`        | Gamma correction applied to the colormap.                                                    |
| `--vmin`/`--vmax`| Clamp the colormap range. Defaults: computed from `values` (or `meta`).                     |
| `--spacer`       | Character placed between cells (default: a single space).                                    |
| `--message`      | Footer message printed below the grid. Accepts templates — see below.                        |
| `--legend`       | Right-aligned legend printed alongside the message. Accepts templates — see below.           |
| `--max-columns`  | Cap on output width. Auto-detected from terminal width when omitted.                         |
| `--simple`       | Collapse the colormap to two colors (low/high).                                              |
| `--follow`       | Read NDJSON: one matrix doc per line, render each as a frame in place. See [Streaming](#streaming). |

Precedence for every parameter: **CLI arg > `meta` in the doc > theme value > built-in default**.

### Legend & message templates

`--message` and `--legend` are templates. `heatgraph` expands placeholders at
render time using the active theme, glyphs, and computed statistics — so
producers (and you) can describe the *intent* of a legend without ever
touching ANSI escape sequences:

```bash
# Gradient swatch in the active theme.
... | thg --theme github-dark --legend "less [GRADIENT] more"

# Range and aggregate stats.
... | thg --message "[TOTAL] events, [MIN]–[MAX] per cell"

# Calendar-derived matrices carry meta.from / meta.to automatically.
./examples/gh-user-contributions.py \
  | python scripts/calendar_to_matrix.py \
  | thg --theme github-dark \
        --message "[TOTAL] commits, [FROM] → [TO]" \
        --legend  "less  [GRADIENT]  more"

# Granular swatches for binary / custom legends.
... | thg --legend "[CELL:0] missed   [CELL:4] streak"
```

Available placeholders: `[GRADIENT]`, `[CELL:i]`, `[MIN]`, `[MAX]`, `[TOTAL]`,
`[COUNT]`, `[MEAN]`, `[CELLS]`, `[FROM]`, `[TO]`. See
[`SCHEMA.md`](SCHEMA.md) for full semantics.

Producers can also embed templates in `meta.message` / `meta.legend` so the
pipeline ships with sensible defaults; CLI args still override.

### Built-in themes

- `github-dark` — GitHub contribution graph palette.
- `github-dark-high-contrast` — GitHub's high-contrast dark variant.
- `github-light` — GitHub's light palette.
- `default-dark` — generic dark palette.
- `default-light` — generic light palette.
- `catppuccin-mocha` — Catppuccin Mocha palette.
- `tokyo-night` — Tokyo Night palette.

A custom theme is just a JSON file with optional `colors`, `glyphs`, `gamma`
keys. See `src/heatgraph/themes/*.json` for examples.

### Built-in glyph presets

`blocks`, `campground`, `circles`, `github`, `invis`, `rect`, `jaunty`,
`kome-mit`, `marked`, `patches`, `subtle`, `modern`, `n`, `pentup`,
`squarespace`, `meh`, `diamonds`.

---

## Fixtures

You can generate mock data using the supplied fixtures. Both the `matrix` and
`calendar` document shapes are provided for convenience.

```bash
python scripts/fixtures/matrix.py   [--rows N] [--cols N] [--vmin F] [--vmax F] [--seed N]
python scripts/fixtures/calendar.py [--start YYYY-MM-DD] [--end YYYY-MM-DD]
```

---

## Streaming

Pass `--follow` to switch `heatgraph` from one-shot mode to **NDJSON**: one
complete matrix doc per line. Each line is processed as a new frame, and
`heatgraph` redraws in place using cursor-home + erase-to-end so the terminal
doesn't accumulate scrollback. EOF on stdin terminates cleanly. The
`calendar_to_matrix.py` script accepts the same flag and forwards calendar
docs as matrix docs, one per line.

```bash
# Conway's Game of Life as a streaming demo.
./examples/game-of-life.py | thg --follow

# Calendar producers chain through calendar_to_matrix.py --follow.
some-events-to-cal --follow \
  | python scripts/calendar_to_matrix.py --follow \
  | thg --follow
```

A line that fails JSON parsing or schema validation prints to stderr with its
line number, but does **not** abort the stream — the previous frame stays on
screen until the next valid doc arrives. Producers are expected to emit
compact JSON (`json.dumps(separators=(",",":"))` or `jq -c`) and flush after
each line. See [`SCHEMA.md`](SCHEMA.md#streaming-follow-mode) for the full
contract.

## Errors

`heatgraph` validates its input at the boundary. Malformed input prints a
single-line error to stderr (with a pointer to `SCHEMA.md`) and exits with
status `2`:
