Skip to content

Spectre integration ignores the host's ANSI/Unicode detection (raw escapes + mojibake on non-ANSI consoles) #46

Description

@carldebilly

Symptom

In a console that does not interpret ANSI (observed in JetBrains Rider's Run tool window), Spectre-rendered output is garbled twice over:

  • ANSI escape sequences are printed literally (?[38;5;12m, ?[0m, …) around the figlet banner and every table cell;
  • table borders render as replacement characters (◆◆◆…) instead of box-drawing glyphs.

Meanwhile the Repl layer itself behaves correctly in the same console: the prompt/help text is plain, no shell-integration marks are emitted, and the spectre sample's terminal command reports Shell integration: off (AnsiUnsupported) — the host's detection is right; only the Spectre output ignores it.

Root cause

SessionAnsiConsole.Create() (src/Repl.Spectre/SessionAnsiConsole.cs) hardcodes the console capabilities instead of consulting the host's detection:

var settings = new AnsiConsoleSettings
{
    Ansi = AnsiSupport.Yes,                     // always, even on a redirected console
    ColorSystem = ColorSystemSupport.TrueColor,
    Out = new SessionAnsiConsoleOutput(),
};

Because the output is a custom TextWriter (session routing), Spectre's own detection never runs either — so escape sequences are always emitted.

The mojibake half: UseSpectreConsole only sets Console.OutputEncoding = Encoding.UTF8 when output is not redirected (src/Repl.Spectre/SpectreReplExtensions.cs), yet Profile.Capabilities.Unicode stays true (default SpectreConsoleOptions.Unicode), so Spectre writes Unicode box-drawing characters into a stream whose encoding cannot carry them.

This is the same "multiple ANSI gates drifting apart" theme that PR #42 consolidated for marks and advanced progress (TerminalAnsiCapability): the Spectre integration is the remaining parallel world.

Proposed fix

Derive the IAnsiConsole profile from the host's decision at creation time (the service is transient, so it resolves within the session context):

  • Ansi ← the shared gate (TerminalAnsiCapability.IsAnsiCapableForTerminalSequences / OutputOptions.IsAnsiEnabled) instead of AnsiSupport.Yes;
  • ColorSystem ← degrade with capabilities (NoColors when ANSI is off; TrueColor only when the terminal is capable);
  • Unicode ← only when the effective output encoding actually transports it (and set the encoding consistently with redirection state);
  • keep IsTerminal as is (already redirection/session-aware).

Suggested red tests: captured/redirected console → Spectre render contains no \x1b[ and falls back to ASCII borders; hosted session advertising ANSI via capability flags → colors preserved (parity with the marks/progress gate).

Notes

  • Repro: run samples/07-spectre from Rider's Run window (or any redirected console) and type terminal or calendar.
  • Out of scope of PR Add OSC 133/633 shell-integration marks in interactive mode #42 (shell-integration marks) by design — the detection layer there already reports the right verdict; this is purely the Spectre profile wiring.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Fields

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions