diff --git a/packages/devtools_app/lib/src/screens/logging/_message_column.dart b/packages/devtools_app/lib/src/screens/logging/_message_column.dart index 96a6c916e70..32658398274 100644 --- a/packages/devtools_app/lib/src/screens/logging/_message_column.dart +++ b/packages/devtools_app/lib/src/screens/logging/_message_column.dart @@ -74,6 +74,7 @@ class MessageColumn extends ColumnData // The current summary is generally the first 200 chars of details. data.summary!, theme.regularTextStyle, + brightness: theme.brightness, ), if (hasSummary && hasDetails()) TextSpan(text: ' • ', style: theme.subtleTextStyle), @@ -81,6 +82,7 @@ class MessageColumn extends ColumnData ...textSpansFromAnsi( detailsComputed ? data.details! : '', theme.subtleTextStyle, + brightness: theme.brightness, ), ], ), diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_instance_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_instance_display.dart index 8bd7e660189..da85b9110e6 100644 --- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_instance_display.dart +++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_instance_display.dart @@ -171,6 +171,7 @@ class DisplayProvider extends StatelessWidget { children: textSpansFromAnsi( variable.text ?? '', theme.subtleFixedFontStyle, + brightness: theme.brightness, ), ), ), diff --git a/packages/devtools_app/lib/src/shared/console/console.dart b/packages/devtools_app/lib/src/shared/console/console.dart index 30e9913ae67..4a8be68217b 100644 --- a/packages/devtools_app/lib/src/shared/console/console.dart +++ b/packages/devtools_app/lib/src/shared/console/console.dart @@ -196,6 +196,7 @@ class _ConsoleOutputState extends State<_ConsoleOutput> children: textSpansFromAnsi( line.text, theme.regularTextStyle, + brightness: theme.brightness, ), ), ); diff --git a/packages/devtools_app/lib/src/shared/console/widgets/display_provider.dart b/packages/devtools_app/lib/src/shared/console/widgets/display_provider.dart index 91fd7ac7fee..e85209d115b 100644 --- a/packages/devtools_app/lib/src/shared/console/widgets/display_provider.dart +++ b/packages/devtools_app/lib/src/shared/console/widgets/display_provider.dart @@ -75,6 +75,7 @@ class _DisplayProviderState extends State { children: textSpansFromAnsi( widget.variable.text!, theme.subtleFixedFontStyle, + brightness: theme.brightness, ), ), ), diff --git a/packages/devtools_app/lib/src/shared/primitives/utils.dart b/packages/devtools_app/lib/src/shared/primitives/utils.dart index d09c45a3a5b..4f89c2ca592 100644 --- a/packages/devtools_app/lib/src/shared/primitives/utils.dart +++ b/packages/devtools_app/lib/src/shared/primitives/utils.dart @@ -545,7 +545,11 @@ class DebugTimingLogger { } } -List textSpansFromAnsi(String input, TextStyle defaultStyle) { +List textSpansFromAnsi( + String input, + TextStyle defaultStyle, { + required Brightness brightness, +}) { final parser = AnsiParser(input); return parser.parse().map((entry) { final styled = entry.bold || entry.fgColor != null || entry.bgColor != null; @@ -553,8 +557,8 @@ List textSpansFromAnsi(String input, TextStyle defaultStyle) { text: entry.text, style: styled ? TextStyle( - color: ansiToColor(entry.fgColor), - backgroundColor: ansiToColor(entry.bgColor), + color: ansiToColor(entry.fgColor, brightness: brightness), + backgroundColor: ansiToColor(entry.bgColor, brightness: brightness), fontWeight: entry.bold ? FontWeight.bold : FontWeight.normal, ) : defaultStyle, @@ -562,13 +566,27 @@ List textSpansFromAnsi(String input, TextStyle defaultStyle) { }).toList(); } -Color? ansiToColor(List? ansiInput) { +Color? ansiToColor(List? ansiInput, {required Brightness brightness}) { if (ansiInput == null) { return null; } assert(ansiInput.length == 3, 'Ansi color list should contain 3 elements'); - return Color.fromRGBO(ansiInput[0], ansiInput[1], ansiInput[2], 1); + final color = Color.fromRGBO(ansiInput[0], ansiInput[1], ansiInput[2], 1); + return _ansiColorVisibleOnBackground(color, brightness); +} + +/// Adjusts ANSI colors that would be unreadable against the DevTools background. +Color _ansiColorVisibleOnBackground(Color color, Brightness brightness) { + final hsl = HSLColor.fromColor(color); + if (brightness == Brightness.dark) { + if (hsl.lightness < 0.2) { + return hsl.withLightness(0.65).toColor(); + } + } else if (hsl.lightness > 0.85) { + return hsl.withLightness(0.25).toColor(); + } + return color; } /// An extension on [LogicalKeySet] to provide user-facing names for key diff --git a/packages/devtools_app/test/shared/primitives/utils_test.dart b/packages/devtools_app/test/shared/primitives/utils_test.dart index f71c0cae441..b972a1735ee 100644 --- a/packages/devtools_app/test/shared/primitives/utils_test.dart +++ b/packages/devtools_app/test/shared/primitives/utils_test.dart @@ -680,6 +680,35 @@ void main() { equals('http://127.0.0.1:9100'), ); }); + + group('ansiToColor', () { + test('lightens dark colors on dark backgrounds', () { + const black = Color.fromRGBO(0, 0, 0, 1); + final adjusted = ansiToColor([0, 0, 0], brightness: Brightness.dark)!; + expect(adjusted, isNot(equals(black))); + expect(adjusted.computeLuminance(), greaterThan(0.2)); + }); + + test('darkens light colors on light backgrounds', () { + const white = Color.fromRGBO(255, 255, 255, 1); + final adjusted = ansiToColor([255, 255, 255], brightness: Brightness.light)!; + expect(adjusted, isNot(equals(white))); + expect(adjusted.computeLuminance(), lessThan(0.5)); + }); + + test('preserves readable colors', () { + const red = Color.fromRGBO(187, 0, 0, 1); + expect( + ansiToColor([187, 0, 0], brightness: Brightness.dark), + equals(red), + ); + expect( + ansiToColor([187, 0, 0], brightness: Brightness.light), + equals(red), + ); + }); + }); + }); } class _SubtractionResult {