fix(headless): N5 dim only lifecycle/command/portal lines

HeadlessConsoleRenderer dimmed every line uniformly, so chat and
interface text (player-visible content) read the same washed-out weight
as scheduling/session-status noise like "entered world" or "command
rejected: ...". Only lifecycle, command, and portal lines are scheduling
noise; chat and interface text now print at the terminal's default
weight.

ChatAndInterfaceTextPrintAtDefaultWeightNeverDimmed was shown to fail
against the prior dim-everything WriteLine (mutation: dim parameter not
yet threaded through, every call still unconditionally wrapped in the
ANSI dim/reset pair) -- the chat line and interface text both carried
the dim escape sequence.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-09-07 08:13:24 +02:00
parent 70e86c180e
commit e3639a4c18
2 changed files with 96 additions and 13 deletions

View file

@ -28,32 +28,35 @@ internal sealed class HeadlessConsoleRenderer : IRuntimeEventObserver
{
string? line = HeadlessConsoleChatFormatter.Format(delta.Entry);
if (!string.IsNullOrEmpty(line))
WriteLine(line);
WriteLine(line, dim: false);
}
/// <summary>
/// Retail's transient "interface text" (SpewBox, <c>ClientLocal</c>
/// type) never touches <see cref="RuntimeCommunicationState.Chat"/> —
/// see <c>RuntimeCommunicationState.AddText</c> — so it never reaches
/// <see cref="OnChat"/>. <see cref="HeadlessConsoleChatFeedback"/> calls
/// this directly for the SAME text
/// <c>ChatCommandRouter.Submit</c>'s <c>ShowInterfaceText</c> path would
/// otherwise only enqueue into the polled <c>SpewBoxState</c>.
/// <see cref="OnChat"/>. <c>HeadlessConsoleSpewBoxPump</c> calls this
/// directly, once per console tick, for whatever text is newly visible
/// in the polled <see cref="AcDream.Core.Chat.SpewBoxState"/> — the
/// SAME seam the graphical overlay's own SpewBox controller reads, so
/// server- and plugin-driven interface text prints here too, not only
/// the console's own submissions. Default weight (N5) — this is
/// player-visible interface text, not scheduling noise.
/// </summary>
internal void WriteInterfaceText(string text) => WriteLine(text);
internal void WriteInterfaceText(string text) => WriteLine(text, dim: false);
public void OnLifecycle(in RuntimeLifecycleDelta delta)
{
switch (delta.Current)
{
case RuntimeLifecycleState.InWorld:
WriteLine("entered world");
WriteLine("entered world", dim: true);
break;
case RuntimeLifecycleState.Stopping:
WriteLine("disconnecting");
WriteLine("disconnecting", dim: true);
break;
case RuntimeLifecycleState.Faulted:
WriteLine("session faulted");
WriteLine("session faulted", dim: true);
break;
}
}
@ -61,13 +64,21 @@ internal sealed class HeadlessConsoleRenderer : IRuntimeEventObserver
public void OnCommand(in RuntimeCommandDelta delta)
{
if (delta.Status == RuntimeCommandStatus.Rejected)
WriteLine($"command rejected: {delta.Domain} {delta.Text}".TrimEnd());
{
WriteLine(
$"command rejected: {delta.Domain} {delta.Text}".TrimEnd(),
dim: true);
}
}
public void OnPortal(in RuntimePortalDelta delta)
{
if (delta.Portal.IsMaterialized)
WriteLine($"portal -> cell 0x{delta.Portal.DestinationCell:X8}");
{
WriteLine(
$"portal -> cell 0x{delta.Portal.DestinationCell:X8}",
dim: true);
}
}
public void OnEntity(in RuntimeEntityDelta delta)
@ -86,9 +97,15 @@ internal sealed class HeadlessConsoleRenderer : IRuntimeEventObserver
{
}
private void WriteLine(string text)
/// <summary>
/// N5 (2026-09-07 review round): only lifecycle/command/portal lines are
/// dimmed — scheduling and session-status noise, not player-visible
/// content. Chat and interface text print at the terminal's default
/// weight.
/// </summary>
private void WriteLine(string text, bool dim)
{
_output.WriteLine(_useColor ? Dim + text + Reset : text);
_output.WriteLine(_useColor && dim ? Dim + text + Reset : text);
_output.Flush();
}
}