Skip to content

output: restrained colour on a terminal, plain bytes everywhere else - #33

Merged
pyjeebz merged 2 commits into
mainfrom
feat/colour
Nov 11, 2026
Merged

pyjeebz merged 2 commits into
mainfrom
feat/colour

Conversation

@pyjeebz

@pyjeebz pyjeebz commented Nov 9, 2026

Copy link
Copy Markdown
Owner

Closes #26

Why

A little colour makes the table and the stop and doctor reports scannable. Scripts, pipes and agents must see exactly the bytes they see today.

What

  • internal/inspect/colour.go

    • Color and ColorErr switches, one per stream.
    • Mark colours a line's leading ✓ (green), ⚠ (yellow) or ✗ (red). · info stays plain.
    • colourTable dims uptime, and dims the whole row of another user's listener.
    • Colour is applied after tabwriter has aligned the table: columns are found in runes, the way tabwriter counts them, and cut by byte offset. The bytes, the alignment and the last column's OSC 8 links pass through untouched.
  • Wiring: WriteTable, the stop/kill/free result lines in stopAll (✓ to stdout; ✗ and skipped to stderr), the wait --gone line, and doctor findings.

  • escapesWork(f) decides each stream for itself. Colour is off for:

    • a pipe or a file;
    • non-empty NO_COLOR;
    • TERM=dumb;
    • the legacy Windows console. Windows needs WT_SESSION or TERM_PROGRAM, the same gate as table links.

    Table links now share this check, so they also turn off under TERM=dumb.

  • Untouched: --json, the pickers (TableRows) and the header carry no colour.

  • Docs: commands.mdx says when colour is on and off.

  • Not added: a --color=always|never flag. output: restrained colour, respecting NO_COLOR #26 asks for automatic detection only; a flag can follow if wanted.

Evidence

  • Plain output pinned:
    • TestPlainTableUnchanged compares against fixed table text, and the same test passes against main;
    • TestColourOnlyAddsEscapes: the coloured table with links on and an invalid-UTF-8 process name, stripped of SGR and OSC 8 codes, is byte-identical to the plain one.
  • Unit tests:
    • TestColourTable: only uptime is dimmed, the other-user row is dimmed whole, the header is plain;
    • TestMark: each glyph, and each stream separately.
  • Integration: TestColour runs ports doctor on a real PTY. It's coloured on a terminal, has no SGR with NO_COLOR=1, and no escape bytes through a pipe.
  • Live: looked at ports and ports doctor under script; the output is as described.
  • Gate:
    • unit and integration tests pass;
    • golangci-lint reports 0 issues;
    • the Windows and darwin builds succeed;
    • web builds.
  • Risky tier, because it touches internal/inspect/output.go. A fresh-context review gave SHIP WITH NITS; the second commit fixes them:
    • · is no longer dimmed;
    • the table is cut by byte offset, so invalid UTF-8 passes through;
    • the NO_COLOR docs wording is corrected;
    • the links and plain-table tests above were added.

Rollback

Revert the squash commit. No output format changes, and machine output is untouched.


Generated by Claude Code

@pyjeebz
pyjeebz merged commit 371235b into main Nov 11, 2026
5 checks passed
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.

output: restrained colour, respecting NO_COLOR

2 participants