Skip to content

Fix console output rendering - #102

Open
graeme wants to merge 7 commits into
mainfrom
fix-console-output-rendering
Open

Fix console output rendering#102
graeme wants to merge 7 commits into
mainfrom
fix-console-output-rendering

Conversation

@graeme

@graeme graeme commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

PR: Render brew colour output in the console

Summary

Homebrew strips ANSI colour when its stdout isn't a TTY, and a Pipe never is. So the console panel only ever showed flat monospace text. This PR forces colour on and renders it, giving the console the same colour cues (blue ==> headers, green success, yellow warnings, red errors) a user sees in Terminal.

Changes

  • Force colour emissionBrewCommandService sets HOMEBREW_COLOR=1 and CLICOLOR_FORCE=1 on the subprocess only when a console sink is observing the command, leaving parsed read commands (brew config, --json) untouched so their output stays clean.
  • ANSI SGR parser — new UI-free ANSIParser in BrewCore turns escape codes into styled spans (foreground colour + bold) via a semantic ANSIColor enum; unsupported, 256-colour/truecolour, and cursor/screen-control sequences are consumed rather than surfaced as literal text.
  • Rendering + exportANSIConsoleText maps parsed colours onto the design-system palette and ConsoleBody renders an AttributedString; uncoloured spans keep the stream default so untouched lines look identical. Exported/copied logs run through ANSIParser.plainText to stay plain text.

Why this split

This is Stage 1 (colours only) of the "make console output render like a terminal" feedback. It's low-risk and self-contained. The live download/progress "UI" needs a real PTY and terminal emulation — deliberately deferred to a follow-up.

Testing

  • scripts/test ‚ — full package suite green
  • 22 unit tests for the ANSI parser (colours, bold, resets, malformed/Unicode input)
  • Rendering + export-stripping tests in BrewFeatureConsoleTests
  • swift build clean

PR checklist

  • Have you followed this repository's contribution and workflow guidance?
  • Have you explained what changed and why this should land now?
  • Have you run relevant local checks for the changed scope?
  • Are changes scoped and free of unrelated modifications?

  • AI was used to generate or assist with generating this PR.
  • Claude Code wrote the parser, wiring, and tests; the author reviewed the diff and confirmed the full test suite passes locally.

Follow-ups

  • Stage 2: PTY-backed runner to get the live download/progress UI, plus \r/cursor handling to render in-place progress updates.

graeme and others added 3 commits July 28, 2026 21:05
Homebrew strips ANSI colour when stdout isn't a TTY, which a Pipe never
is, so the console panel only ever saw plain text. Set HOMEBREW_COLOR and
CLICOLOR_FORCE on the subprocess environment when a console sink is
observing the command, leaving parsed read commands (brew config, --json)
untouched so their output stays clean for parsing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Parses the colour escape codes brew now emits into styled spans the
console can render. Kept UI-free with a semantic ANSIColor enum so BrewCore
gains no SwiftUI dependency; the view layer maps colours onto its palette.
Scope is per-line, matching the console's one-row-per-line model. Cursor
and screen-control sequences (the live progress UI) are stripped for now —
interpreting them is a later TTY-backed step. Malformed and unsupported
sequences are consumed rather than surfaced as literal text.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Map the parsed ANSI spans onto the console palette so brew's coloured
output (blue ==> headers, green success, yellow warnings, red errors)
renders in the panel instead of plain monospace. Uncoloured spans keep the
stream's default colour, so lines without codes look unchanged. Exported
and copied logs run through ANSIParser.plainText so on-disk output stays
plain text.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
graeme and others added 2 commits July 28, 2026 22:19
Remove comments that merely restated the code, keeping public API docs and
the non-obvious ANSI-protocol facts (escape terminators, CSI final-byte
range, reset semantics, extended-colour argument counts) that a future
editor could break without understanding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Test names starting with a digit trip SwiftLint's identifier_name rule;
prefix the 256-colour test so it starts with a letter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@graeme
graeme marked this pull request as ready for review July 28, 2026 12:28
Copilot AI review requested due to automatic review settings July 28, 2026 12:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds ANSI SGR parsing and rendering so Homebrew’s colored output can be displayed in BrewUI’s console panel (instead of being stripped when stdout is not a TTY), while ensuring exported/copied logs remain plain text.

Changes:

  • Forces color emission for streamed console runs by injecting HOMEBREW_COLOR=1 and CLICOLOR_FORCE=1 only when a console output sink is active.
  • Introduces a UI-free ANSIParser in BrewCore that converts ANSI sequences into styled spans (foreground + bold) and strips unsupported/control sequences.
  • Updates console rendering to use AttributedString runs and updates export logic/tests to strip ANSI codes.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
Tests/BrewFeatureConsoleTests/CommandJobExportTests.swift Adds coverage to ensure exported output strips ANSI sequences.
Tests/BrewFeatureConsoleTests/ANSIConsoleTextTests.swift Adds tests verifying AttributedString rendering and run segmentation.
Tests/BrewCoreTests/ANSIParserTests.swift Adds comprehensive unit tests for parsing SGR, resets, unsupported sequences, and plain-text stripping.
Sources/BrewFeatureConsole/Views/ConsoleBody.swift Switches console rows to render attributed text rather than raw strings.
Sources/BrewFeatureConsole/Views/ANSIConsoleText.swift Maps parsed ANSI spans to design-system colors and bold monospaced font runs.
Sources/BrewFeatureConsole/ViewModels/CommandJob+Presentation.swift Strips ANSI before producing clipboard/export text.
Sources/BrewCore/Operations/ANSIParser.swift Adds the ANSI parser and style/span model in the core layer.
Sources/BrewCLI/BrewCommandService.swift Forces color env vars only for streamed console output (TaskLocal sink present).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Sources/BrewCore/Operations/ANSIParser.swift
graeme and others added 2 commits July 28, 2026 22:39
ESC[48;5;n and ESC[48;2;r;g;b were unhandled, so their arguments fell
through and were parsed as independent SGR codes — a background palette
index like 31 wrongly set a red foreground. Consume 48's arguments the
same way as 38 without mutating the modelled style, and cover both the
256-colour and truecolour background forms with tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

3 participants