Skip to content

feat(tui): rich display mode with type glyphs (#15)#17

Merged
sunng87 merged 5 commits into
mainfrom
feat/15-tui-rich-mode
Jul 14, 2026
Merged

feat(tui): rich display mode with type glyphs (#15)#17
sunng87 merged 5 commits into
mainfrom
feat/15-tui-rich-mode

Conversation

@sunng87

@sunng87 sunng87 commented Jul 13, 2026

Copy link
Copy Markdown
Owner

First slice of #15: a TUI-only rich display mode that renders structured messages instead of the flat line view. Toggle with `r`, or start with `--tui-rich`. The default line view and stdout output are unchanged.

What it does

  • `DataRow` → a per-message key/value table: one `column = value` row per field (field names in blue, value default, type dimmed on the right).

  • `RowDescription` → a typed column list (`name typename`).

  • Type glyphs — each type shows an icon-font (Nerd Font) glyph plus its name, keyed on the PostgreSQL OID, coloured by category:

    Kind OIDs glyph
    bool 16 ✓ check
    int2/4/8 20, 21, 23 # hashtag
    float4/8, numeric 700, 701, 1700 calculator
    text/varchar/bpchar/name/char 18, 19, 25, 1042, 1043 font
    json/jsonb 114, 3802 code
    uuid 2950 fingerprint
    bytea 17 database
    date 1082 calendar
    time/timestamp*/interval 1083, 1114, 1184, 1186, 1266 clock

    Glyphs are always on in rich mode (Nerd Font assumed); the textual type name is always shown alongside, so nothing is hidden on a non-Nerd-Font terminal.

How it's wired

The decoder now emits structured detail alongside the line text:

  • New `Output::Rich { text, detail }`; `EventDetail` carries `RowDescription` fields and `DataRow` columns (name, type OID, value). stdout/capture consumers print `text` and ignore `detail`, so the line view is byte-for-byte identical (replay tests unchanged).
  • `DataRow` parsing is refactored into `data_row_columns()`, shared by both the line formatter and the structured detail.

RowDescription caching

The cached `RowDescription` (`Direction::row_desc`) is not cleared at `ReadyForQuery`. In the extended protocol a statement/portal is described once but executed across many `ReadyForQuery` cycles, so the columns must outlive a single command cycle — otherwise later-cycle `DataRow`s would lose their field names. It is only cleared by `NoData`. The rich `DataRow` detail is emitted only while a description is cached.

The TUI message windowing is generalized to count display rows per event (wrap and rich mode both let one event span many rows), so a key/value table is never clipped mid-item.

Out of scope for this slice (future #15 work)

Tests

  • New decode-side tests for `data_row_columns` (text/binary/null/truncation) and `format_columns`.
  • TUI tests for the glyph map, type spans, row-aware windowing, and that the rich header omits the row content.
  • All existing line-view / replay tests unchanged. `cargo test` 26 passed, `cargo clippy` clean.

Refs #15.

sunng87 added 5 commits July 13, 2026 16:07
Add a TUI-only rich display mode that renders structured messages
instead of the flat line view: each DataRow becomes a per-message
key/value table (column = value, with its type) and a RowDescription
becomes a typed column list. Toggle with `r`, or start with --tui-rich.
The default line view is unchanged.

The decoder now carries structured detail alongside the line text so the
TUI can draw these messages:

- New Output::Rich { text, detail }; EventDetail captures RowDescription
  fields and DataRow columns (name, type_oid, value). stdout/capture
  consumers print `text` and ignore `detail`.
- DataRow parsing is refactored into data_row_columns(), shared by both
  the line formatter and the structured detail. Line-view text is
  byte-for-byte identical (replay tests unchanged).
- RowDescription stays cached per connection (Direction::row_desc) and
  the rich DataRow detail is emitted only while one is cached. The cache
  is intentionally NOT cleared at ReadyForQuery: in the extended
  protocol a statement/portal is described once but executed across many
  ReadyForQuery cycles, so columns must outlive a single command cycle.

The TUI message windowing is generalized to count display rows per event
(wrap and rich mode both let one event span many rows), so a key/value
table is never clipped mid-item. Type names use a safe textual fallback
keyed on OID (no glyph/font assumptions), per #15's guidance ahead of an
optional glyph layer.

Refs #15.
The rich header used to repeat the line-view content (`{ id=1, ... }`)
above the key/value table that already showed it. Now the header carries
only the timestamp/direction/kind, so each value appears once.

Field names move from cyan (which collides with the F→B direction
colour) to bold yellow, so the key column reads as a header and stays
distinct from the cyan/magenta direction colours.

build_line is factored to share a parser (parse_line) with a new
build_header_line that drops the trailing content.
In rich mode each column type now shows an icon-font glyph plus its
textual name, keyed on the PostgreSQL OID:

  # int4   A text   ✓ bool   { json   ⏾ uuid   …

Glyphs use Nerd Font / Font Awesome solid codepoints (bool->check,
int->hashtag, float/numeric->calculator, text->font, json->code,
uuid->fingerprint, bytea->database, date->calendar, time->clock).
They are coloured by category and the textual type name is always shown
alongside, so a terminal without a Nerd Font never loses the type -- it
just sees tofu where the glyph would be.

Disable glyphs with `i` at runtime, `--no-glyphs` at startup, or the
TAPGRES_NO_GLYPHS env var.

Plumbing: rendering options are bundled into a `View { rich, wrap,
glyphs }` struct so the draw/window functions take one argument instead
of three bools. Field names move to blue (distinct from the
cyan/magenta direction colours).

Refs #15.
Glyphs were behind a mode (i key, --no-glyphs, TAPGRES_NO_GLYPHS). Per
#15 the Nerd Font is an assumed prerequisite for rich mode, so drop the
toggle entirely: type icons now always render alongside the type name.

Removed: the glyphs field and App/run plumbing, the --no-glyphs flag and
TAPGRES_NO_GLYPHS env, the i keybinding, the footer indicator, and the
glyphs flag on View/type_spans. type_spans(oid) and render_rich(text,
detail) no longer take an options argument.
@sunng87
sunng87 merged commit e497e3e into main Jul 14, 2026
2 checks passed
@sunng87
sunng87 deleted the feat/15-tui-rich-mode branch July 14, 2026 02:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TUI rich display mode: type glyphs for descriptions and tables for data rows

1 participant