diff --git a/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor.cs b/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor.cs
index cb319ed4864..7a0e5a9106f 100644
--- a/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor.cs
+++ b/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor.cs
@@ -471,7 +471,7 @@ private void LoadLogs(ConsoleLogsSubscription newConsoleLogsSubscription)
// Console logs are filtered in the UI by the timestamp of the log entry.
var timestampFilterDate = GetFilteredDateFromRemove();
- var logParser = new LogParser();
+ var logParser = new LogParser(ConsoleColor.Black);
await foreach (var batch in subscription.ConfigureAwait(true))
{
if (batch.Count is 0)
diff --git a/src/Aspire.Dashboard/ConsoleLogs/AnsiParser.cs b/src/Aspire.Dashboard/ConsoleLogs/AnsiParser.cs
index b037a023946..2d6e7b3f357 100644
--- a/src/Aspire.Dashboard/ConsoleLogs/AnsiParser.cs
+++ b/src/Aspire.Dashboard/ConsoleLogs/AnsiParser.cs
@@ -64,7 +64,7 @@ public static string StripControlSequences(string text)
return outputBuilder?.ToString() ?? text;
}
- public static ConversionResult ConvertToHtml(string? text, ParserState? priorResidualState = null)
+ public static ConversionResult ConvertToHtml(string? text, ParserState? priorResidualState = null, ConsoleColor? defaultBackgroundColor = null)
{
var textStartIndex = -1;
var textLength = 0;
@@ -147,7 +147,7 @@ public static ConversionResult ConvertToHtml(string? text, ParserState? priorRes
// Ignore everything else and don't write sequence to the output.
if (finalByte == DisplayAttributesFinalByte)
{
- ProcessParameters(ref newState, parameters);
+ ProcessParameters(defaultBackgroundColor, ref newState, parameters);
}
continue;
@@ -197,7 +197,7 @@ public static ConversionResult ConvertToHtml(string? text, ParserState? priorRes
return new(outputBuilder.ToString(), currentState);
}
- private static void ProcessParameters(ref ParserState newState, int[] parameters)
+ private static void ProcessParameters(ConsoleColor? defaultBackgroundColor, ref ParserState newState, int[] parameters)
{
for (var i = 0; i < parameters.Length; i++)
{
@@ -228,7 +228,9 @@ private static void ProcessParameters(ref ParserState newState, int[] parameters
}
else if (TryGetBackgroundColor(parameter, out color))
{
- newState.BackgroundColor = color;
+ // Don't set the background color if it matches the default background color.
+ // Skipping setting it improves appearance when row mouseover slightly changes color.
+ newState.BackgroundColor = (color != defaultBackgroundColor) ? color : null;
}
else if (parameter == DefaultBackgroundCode)
{
@@ -516,14 +518,14 @@ private static string ProcessStateChange(ParserState currentState, ParserState n
{
return state.ForegroundColor switch
{
- ConsoleColor.Black => state.Bright ? "ansi-fg-brightblack" : "ansi-fg-black",
- ConsoleColor.Blue => state.Bright ? "ansi-fg-brightblue" : "ansi-fg-blue",
- ConsoleColor.Cyan => state.Bright ? "ansi-fg-brightcyan" : "ansi-fg-cyan",
- ConsoleColor.Green => state.Bright ? "ansi-fg-brightgreen" : "ansi-fg-green",
+ ConsoleColor.Black => state.Bright ? "ansi-fg-brightblack" : "ansi-fg-black",
+ ConsoleColor.Blue => state.Bright ? "ansi-fg-brightblue" : "ansi-fg-blue",
+ ConsoleColor.Cyan => state.Bright ? "ansi-fg-brightcyan" : "ansi-fg-cyan",
+ ConsoleColor.Green => state.Bright ? "ansi-fg-brightgreen" : "ansi-fg-green",
ConsoleColor.Magenta => state.Bright ? "ansi-fg-brightmagenta" : "ansi-fg-magenta",
- ConsoleColor.Red => state.Bright ? "ansi-fg-brightred" : "ansi-fg-red",
- ConsoleColor.White => state.Bright ? "ansi-fg-brightwhite" : "ansi-fg-white",
- ConsoleColor.Yellow => state.Bright ? "ansi-fg-brightyellow" : "ansi-fg-yellow",
+ ConsoleColor.Red => state.Bright ? "ansi-fg-brightred" : "ansi-fg-red",
+ ConsoleColor.White => state.Bright ? "ansi-fg-brightwhite" : "ansi-fg-white",
+ ConsoleColor.Yellow => state.Bright ? "ansi-fg-brightyellow" : "ansi-fg-yellow",
_ => ""
};
}
@@ -532,14 +534,14 @@ private static string ProcessStateChange(ParserState currentState, ParserState n
{
return state.BackgroundColor switch
{
- ConsoleColor.Black => "ansi-bg-black",
- ConsoleColor.Blue => "ansi-bg-blue",
- ConsoleColor.Cyan => "ansi-bg-cyan",
- ConsoleColor.Green => "ansi-bg-green",
+ ConsoleColor.Black => "ansi-bg-black",
+ ConsoleColor.Blue => "ansi-bg-blue",
+ ConsoleColor.Cyan => "ansi-bg-cyan",
+ ConsoleColor.Green => "ansi-bg-green",
ConsoleColor.Magenta => "ansi-bg-magenta",
- ConsoleColor.Red => "ansi-bg-red",
- ConsoleColor.White => "ansi-bg-white",
- ConsoleColor.Yellow => "ansi-bg-yellow",
+ ConsoleColor.Red => "ansi-bg-red",
+ ConsoleColor.White => "ansi-bg-white",
+ ConsoleColor.Yellow => "ansi-bg-yellow",
_ => ""
};
}
diff --git a/src/Aspire.Dashboard/ConsoleLogs/LogParser.cs b/src/Aspire.Dashboard/ConsoleLogs/LogParser.cs
index cdba273e7f4..537cc36f753 100644
--- a/src/Aspire.Dashboard/ConsoleLogs/LogParser.cs
+++ b/src/Aspire.Dashboard/ConsoleLogs/LogParser.cs
@@ -8,8 +8,14 @@ namespace Aspire.Dashboard.ConsoleLogs;
internal sealed class LogParser
{
+ private readonly ConsoleColor _defaultBackgroundColor;
private AnsiParser.ParserState? _residualState;
+ public LogParser(ConsoleColor defaultBackgroundColor)
+ {
+ _defaultBackgroundColor = defaultBackgroundColor;
+ }
+
public LogEntry CreateLogEntry(string rawText, bool isErrorOutput)
{
// Several steps to do here:
@@ -39,7 +45,7 @@ public LogEntry CreateLogEntry(string rawText, bool isErrorOutput)
var updatedText = WebUtility.HtmlEncode(s);
// 3b. Parse the content to look for ANSI Control Sequences and color them if possible
- var conversionResult = AnsiParser.ConvertToHtml(updatedText, _residualState);
+ var conversionResult = AnsiParser.ConvertToHtml(updatedText, _residualState, _defaultBackgroundColor);
updatedText = conversionResult.ConvertedText;
_residualState = conversionResult.ResidualState;
diff --git a/tests/Aspire.Dashboard.Tests/ConsoleLogsTests/LogEntriesTests.cs b/tests/Aspire.Dashboard.Tests/ConsoleLogsTests/LogEntriesTests.cs
index 7fc31b63cb1..c7619d9287a 100644
--- a/tests/Aspire.Dashboard.Tests/ConsoleLogsTests/LogEntriesTests.cs
+++ b/tests/Aspire.Dashboard.Tests/ConsoleLogsTests/LogEntriesTests.cs
@@ -18,7 +18,7 @@ private static LogEntries CreateLogEntries(int? maximumEntryCount = null, int? b
private static void AddLogLine(LogEntries logEntries, string content, bool isError)
{
- var logParser = new LogParser();
+ var logParser = new LogParser(ConsoleColor.Black);
var logEntry = logParser.CreateLogEntry(content, isError);
logEntries.InsertSorted(logEntry);
}
@@ -268,7 +268,7 @@ public void InsertSorted_TrimsToMaximumEntryCount_OutOfOrder()
public void CreateLogEntry_AnsiAndUrl_HasUrlAnchor()
{
// Arrange
- var parser = new LogParser();
+ var parser = new LogParser(ConsoleColor.Black);
// Act
var entry = parser.CreateLogEntry("\x1b[36mhttps://www.example.com\u001b[0m", isErrorOutput: false);
@@ -276,4 +276,19 @@ public void CreateLogEntry_AnsiAndUrl_HasUrlAnchor()
// Assert
Assert.Equal("https://www.example.com", entry.Content);
}
+
+ [Theory]
+ [InlineData(ConsoleColor.Black, @"info: LoggerName")]
+ [InlineData(ConsoleColor.Blue, @"info: LoggerName")]
+ public void CreateLogEntry_DefaultBackgroundColor_SkipMatchingColor(ConsoleColor defaultBackgroundColor, string output)
+ {
+ // Arrange
+ var parser = new LogParser(defaultBackgroundColor);
+
+ // Act
+ var entry = parser.CreateLogEntry("\u001b[40m\u001b[32minfo\u001b[39m\u001b[22m\u001b[49m: LoggerName", isErrorOutput: false);
+
+ // Assert
+ Assert.Equal(output, entry.Content);
+ }
}