diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3acf68510d..30dc481d22 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Unreleased
+### Added
+
+- Mapping of ANSI colors to hex codes configurable via `App.ansi_theme_dark` and `App.ansi_theme_light` https://github.com/Textualize/textual/pull/4192
+
### Fixed
- Fixed `TextArea.code_editor` missing recently added attributes https://github.com/Textualize/textual/pull/4172
diff --git a/src/textual/_ansi_theme.py b/src/textual/_ansi_theme.py
new file mode 100644
index 0000000000..f53f40ca78
--- /dev/null
+++ b/src/textual/_ansi_theme.py
@@ -0,0 +1,53 @@
+from rich.terminal_theme import TerminalTheme
+
+MONOKAI = TerminalTheme(
+ (12, 12, 12),
+ (217, 217, 217),
+ [
+ (26, 26, 26),
+ (244, 0, 95),
+ (152, 224, 36),
+ (253, 151, 31),
+ (157, 101, 255),
+ (244, 0, 95),
+ (88, 209, 235),
+ (196, 197, 181),
+ (98, 94, 76),
+ ],
+ [
+ (244, 0, 95),
+ (152, 224, 36),
+ (224, 213, 97),
+ (157, 101, 255),
+ (244, 0, 95),
+ (88, 209, 235),
+ (246, 246, 239),
+ ],
+)
+
+ALABASTER = TerminalTheme(
+ (247, 247, 247),
+ (0, 0, 0),
+ [
+ (0, 0, 0),
+ (170, 55, 49),
+ (68, 140, 39),
+ (203, 144, 0),
+ (50, 92, 192),
+ (122, 62, 157),
+ (0, 131, 178),
+ (247, 247, 247),
+ (119, 119, 119),
+ ],
+ [
+ (240, 80, 80),
+ (96, 203, 0),
+ (255, 188, 93),
+ (0, 122, 204),
+ (230, 76, 230),
+ (0, 170, 203),
+ (247, 247, 247),
+ ],
+)
+
+DEFAULT_TERMINAL_THEME = MONOKAI
diff --git a/src/textual/_styles_cache.py b/src/textual/_styles_cache.py
index 6e6366113e..9d4888e292 100644
--- a/src/textual/_styles_cache.py
+++ b/src/textual/_styles_cache.py
@@ -10,7 +10,9 @@
from rich.text import Text
from . import log
+from ._ansi_theme import DEFAULT_TERMINAL_THEME
from ._border import get_box, render_border_label, render_row
+from ._context import active_app
from ._opacity import _apply_opacity
from ._segment_tools import line_pad, line_trim
from .color import Color
@@ -318,8 +320,14 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]:
Returns:
New list of segments
"""
+ try:
+ app = active_app.get()
+ ansi_theme = app.ansi_theme
+ except LookupError:
+ ansi_theme = DEFAULT_TERMINAL_THEME
+
if styles.tint.a:
- segments = Tint.process_segments(segments, styles.tint)
+ segments = Tint.process_segments(segments, styles.tint, ansi_theme)
if opacity != 1.0:
segments = _apply_opacity(segments, base_background, opacity)
return segments
diff --git a/src/textual/app.py b/src/textual/app.py
index 08c4392fbe..8e19385315 100644
--- a/src/textual/app.py
+++ b/src/textual/app.py
@@ -52,11 +52,11 @@
import rich
import rich.repr
-from rich import terminal_theme
from rich.console import Console, RenderableType
from rich.control import Control
from rich.protocol import is_renderable
from rich.segment import Segment, Segments
+from rich.terminal_theme import TerminalTheme
from . import (
Logger,
@@ -71,6 +71,7 @@
)
from ._animator import DEFAULT_EASING, Animatable, Animator, EasingFunction
from ._ansi_sequences import SYNC_END, SYNC_START
+from ._ansi_theme import ALABASTER, MONOKAI
from ._callback import invoke
from ._compose import compose
from ._compositor import CompositorUpdate
@@ -398,6 +399,12 @@ class MyApp(App[None]):
get focus when the terminal widget has focus.
"""
+ ansi_theme_dark = Reactive(MONOKAI, init=False)
+ """Maps ANSI colors to hex colors using a Rich TerminalTheme object while in dark mode."""
+
+ ansi_theme_light = Reactive(ALABASTER, init=False)
+ """Maps ANSI colors to hex colors using a Rich TerminalTheme object while in light mode."""
+
def __init__(
self,
driver_class: Type[Driver] | None = None,
@@ -422,9 +429,9 @@ def __init__(
super().__init__()
self.features: frozenset[FeatureFlag] = parse_features(os.getenv("TEXTUAL", ""))
- self._filters: list[LineFilter] = [
- ANSIToTruecolor(terminal_theme.DIMMED_MONOKAI)
- ]
+ ansi_theme = self.ansi_theme_dark if self.dark else self.ansi_theme_light
+ self._filters: list[LineFilter] = [ANSIToTruecolor(ansi_theme)]
+
environ = dict(os.environ)
no_color = environ.pop("NO_COLOR", None)
if no_color is not None:
@@ -880,8 +887,40 @@ def watch_dark(self, dark: bool) -> None:
"""
self.set_class(dark, "-dark-mode", update=False)
self.set_class(not dark, "-light-mode", update=False)
+ self._refresh_truecolor_filter(self.ansi_theme)
self.call_later(self.refresh_css)
+ def watch_ansi_theme_dark(self, theme: TerminalTheme) -> None:
+ if self.dark:
+ self._refresh_truecolor_filter(theme)
+ self.call_later(self.refresh_css)
+
+ def watch_ansi_theme_light(self, theme: TerminalTheme) -> None:
+ if not self.dark:
+ self._refresh_truecolor_filter(theme)
+ self.call_later(self.refresh_css)
+
+ @property
+ def ansi_theme(self) -> TerminalTheme:
+ """The ANSI TerminalTheme currently being used.
+
+ Defines how colors defined as ANSI (e.g. `magenta`) inside Rich renderables
+ are mapped to hex codes.
+ """
+ return self.ansi_theme_dark if self.dark else self.ansi_theme_light
+
+ def _refresh_truecolor_filter(self, theme: TerminalTheme) -> None:
+ """Update the ANSI to Truecolor filter, if available, with a new theme mapping.
+
+ Args:
+ theme: The new terminal theme to use for mapping ANSI to truecolor.
+ """
+ filters = self._filters
+ for index, filter in enumerate(filters):
+ if isinstance(filter, ANSIToTruecolor):
+ filters[index] = ANSIToTruecolor(theme)
+ return
+
def get_driver_class(self) -> Type[Driver]:
"""Get a driver class for this platform.
diff --git a/src/textual/command.py b/src/textual/command.py
index 94d3aa07dc..c7c240fcaa 100644
--- a/src/textual/command.py
+++ b/src/textual/command.py
@@ -25,7 +25,6 @@
import rich.repr
from rich.align import Align
from rich.console import Group, RenderableType
-from rich.emoji import Emoji
from rich.style import Style
from rich.text import Text
from typing_extensions import Final, TypeAlias
@@ -384,13 +383,14 @@ class SearchIcon(Static, inherit_css=False):
DEFAULT_CSS = """
SearchIcon {
+ color: #000; /* required for snapshot tests */
margin-left: 1;
margin-top: 1;
width: 2;
}
"""
- icon: var[str] = var(Emoji.replace(":magnifying_glass_tilted_right:"))
+ icon: var[str] = var("๐")
"""The icon to display."""
def render(self) -> RenderableType:
diff --git a/src/textual/filter.py b/src/textual/filter.py
index 7494d9a52a..2963689ebd 100644
--- a/src/textual/filter.py
+++ b/src/textual/filter.py
@@ -188,7 +188,7 @@ def __init__(self, terminal_theme: TerminalTheme):
Args:
terminal_theme: A rich terminal theme.
"""
- self.terminal_theme = terminal_theme
+ self._terminal_theme = terminal_theme
@lru_cache(1024)
def truecolor_style(self, style: Style) -> Style:
@@ -200,7 +200,7 @@ def truecolor_style(self, style: Style) -> Style:
Returns:
New style.
"""
- terminal_theme = self.terminal_theme
+ terminal_theme = self._terminal_theme
color = style.color
if color is not None and color.is_system_defined:
color = RichColor.from_rgb(
@@ -211,6 +211,7 @@ def truecolor_style(self, style: Style) -> Style:
bgcolor = RichColor.from_rgb(
*bgcolor.get_truecolor(terminal_theme, foreground=False)
)
+
return style + Style.from_color(color, bgcolor)
def apply(self, segments: list[Segment], background: Color) -> list[Segment]:
diff --git a/src/textual/renderables/tint.py b/src/textual/renderables/tint.py
index 358bbca3fc..a11450e2e3 100644
--- a/src/textual/renderables/tint.py
+++ b/src/textual/renderables/tint.py
@@ -2,10 +2,10 @@
from typing import Iterable
-from rich import terminal_theme
-from rich.console import Console, ConsoleOptions, RenderableType, RenderResult
+from rich.console import RenderableType
from rich.segment import Segment
from rich.style import Style
+from rich.terminal_theme import TerminalTheme
from ..color import Color
from ..filter import ANSIToTruecolor
@@ -30,13 +30,14 @@ def __init__(
@classmethod
def process_segments(
- cls, segments: Iterable[Segment], color: Color
+ cls, segments: Iterable[Segment], color: Color, ansi_theme: TerminalTheme
) -> Iterable[Segment]:
"""Apply tint to segments.
Args:
segments: Incoming segments.
color: Color of tint.
+ ansi_theme: The TerminalTheme defining how to map ansi colors to hex.
Returns:
Segments with applied tint.
@@ -45,7 +46,7 @@ def process_segments(
style_from_color = Style.from_color
_Segment = Segment
- truecolor_style = ANSIToTruecolor(terminal_theme.DIMMED_MONOKAI).truecolor_style
+ truecolor_style = ANSIToTruecolor(ansi_theme).truecolor_style
NULL_STYLE = Style()
for segment in segments:
@@ -73,10 +74,3 @@ def process_segments(
),
control,
)
-
- def __rich_console__(
- self, console: Console, options: ConsoleOptions
- ) -> RenderResult:
- segments = console.render(self.renderable, options)
- color = self.color
- return self.process_segments(segments, color)
diff --git a/tests/renderables/test_tint.py b/tests/renderables/test_tint.py
index 52e54aa60c..6c1b7c2525 100644
--- a/tests/renderables/test_tint.py
+++ b/tests/renderables/test_tint.py
@@ -1,8 +1,11 @@
import io
from rich.console import Console
+from rich.segment import Segments
+from rich.terminal_theme import DIMMED_MONOKAI
from rich.text import Text
+from textual._ansi_theme import DEFAULT_TERMINAL_THEME
from textual.color import Color
from textual.renderables.tint import Tint
@@ -10,8 +13,36 @@
def test_tint():
console = Console(file=io.StringIO(), force_terminal=True, color_system="truecolor")
renderable = Text.from_markup("[#aabbcc on #112233]foo")
- console.print(Tint(renderable, Color(0, 100, 0, 0.5)))
+ segments = list(console.render(renderable))
+ console.print(
+ Segments(
+ Tint.process_segments(
+ segments=segments,
+ color=Color(0, 100, 0, 0.5),
+ ansi_theme=DEFAULT_TERMINAL_THEME,
+ )
+ )
+ )
output = console.file.getvalue()
print(repr(output))
expected = "\x1b[38;2;85;143;102;48;2;8;67;25mfoo\x1b[0m\n"
assert output == expected
+
+
+def test_tint_ansi_mapping():
+ console = Console(file=io.StringIO(), force_terminal=True, color_system="truecolor")
+ renderable = Text.from_markup("[red on yellow]foo")
+ segments = list(console.render(renderable))
+ console.print(
+ Segments(
+ Tint.process_segments(
+ segments=segments,
+ color=Color(0, 100, 0, 0.5),
+ ansi_theme=DIMMED_MONOKAI,
+ )
+ )
+ )
+ output = console.file.getvalue()
+ print(repr(output))
+ expected = "\x1b[38;2;95;81;36;48;2;98;133;26mfoo\x1b[0m\n"
+ assert output == expected
diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
index b6f4122b2f..efefa50e4a 100644
--- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
+++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
@@ -161,6 +161,346 @@
'''
# ---
+# name: test_ansi_color_mapping[False]
+ '''
+
+
+ '''
+# ---
+# name: test_ansi_color_mapping[True]
+ '''
+
+
+ '''
+# ---
# name: test_auto_fr
'''
@@ -2992,137 +3332,137 @@
font-weight: 700;
}
- .terminal-174430999-matrix {
+ .terminal-454793765-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-174430999-title {
+ .terminal-454793765-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-174430999-r1 { fill: #a2a2a2 }
- .terminal-174430999-r2 { fill: #c5c8c6 }
- .terminal-174430999-r3 { fill: #004578 }
- .terminal-174430999-r4 { fill: #e2e3e3 }
- .terminal-174430999-r5 { fill: #00ff00 }
- .terminal-174430999-r6 { fill: #24292f }
- .terminal-174430999-r7 { fill: #1e1e1e }
- .terminal-174430999-r8 { fill: #fea62b;font-weight: bold }
+ .terminal-454793765-r1 { fill: #a2a2a2 }
+ .terminal-454793765-r2 { fill: #c5c8c6 }
+ .terminal-454793765-r3 { fill: #004578 }
+ .terminal-454793765-r4 { fill: #e2e3e3 }
+ .terminal-454793765-r5 { fill: #00ff00 }
+ .terminal-454793765-r6 { fill: #000000 }
+ .terminal-454793765-r7 { fill: #1e1e1e }
+ .terminal-454793765-r8 { fill: #fea62b;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- CommandPaletteApp
+ CommandPaletteApp
-
+
-
-
-
-
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-
- ๐A
-
-
- This is a test of this code 9
- This is a test of this code 8
- This is a test of this code 7
- This is a test of this code 6
- This is a test of this code 5
- This is a test of this code 4
- This is a test of this code 3
- This is a test of this code 2
- This is a test of this code 1
- This is a test of this code 0
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-
-
-
-
+
+
+
+
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ ๐A
+
+
+ This is a test of this code 9
+ This is a test of this code 8
+ This is a test of this code 7
+ This is a test of this code 6
+ This is a test of this code 5
+ This is a test of this code 4
+ This is a test of this code 3
+ This is a test of this code 2
+ This is a test of this code 1
+ This is a test of this code 0
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+
+
+
@@ -3153,137 +3493,137 @@
font-weight: 700;
}
- .terminal-3083317776-matrix {
+ .terminal-929804574-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3083317776-title {
+ .terminal-929804574-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3083317776-r1 { fill: #a2a2a2 }
- .terminal-3083317776-r2 { fill: #c5c8c6 }
- .terminal-3083317776-r3 { fill: #004578 }
- .terminal-3083317776-r4 { fill: #e2e3e3 }
- .terminal-3083317776-r5 { fill: #00ff00 }
- .terminal-3083317776-r6 { fill: #24292f }
- .terminal-3083317776-r7 { fill: #1e1e1e }
- .terminal-3083317776-r8 { fill: #777a7e }
+ .terminal-929804574-r1 { fill: #a2a2a2 }
+ .terminal-929804574-r2 { fill: #c5c8c6 }
+ .terminal-929804574-r3 { fill: #004578 }
+ .terminal-929804574-r4 { fill: #e2e3e3 }
+ .terminal-929804574-r5 { fill: #00ff00 }
+ .terminal-929804574-r6 { fill: #000000 }
+ .terminal-929804574-r7 { fill: #1e1e1e }
+ .terminal-929804574-r8 { fill: #777a7e }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- CommandPaletteApp
+ CommandPaletteApp
-
+
-
-
-
-
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-
- ๐Command Palette Search...
-
-
- This is a test of this code 0
- This is a test of this code 1
- This is a test of this code 2
- This is a test of this code 3
- This is a test of this code 4
- This is a test of this code 5
- This is a test of this code 6
- This is a test of this code 7
- This is a test of this code 8
- This is a test of this code 9
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-
-
-
-
+
+
+
+
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ ๐Command Palette Search...
+
+
+ This is a test of this code 0
+ This is a test of this code 1
+ This is a test of this code 2
+ This is a test of this code 3
+ This is a test of this code 4
+ This is a test of this code 5
+ This is a test of this code 6
+ This is a test of this code 7
+ This is a test of this code 8
+ This is a test of this code 9
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+
+
+
@@ -5008,141 +5348,141 @@
font-weight: 700;
}
- .terminal-1056312446-matrix {
+ .terminal-2586053582-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1056312446-title {
+ .terminal-2586053582-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1056312446-r1 { fill: #e1e1e1 }
- .terminal-1056312446-r2 { fill: #c5c8c6 }
- .terminal-1056312446-r3 { fill: #fea62b }
- .terminal-1056312446-r4 { fill: #fea62b;font-weight: bold }
- .terminal-1056312446-r5 { fill: #fea62b;font-weight: bold;font-style: italic; }
- .terminal-1056312446-r6 { fill: #be3f48;font-weight: bold }
- .terminal-1056312446-r7 { fill: #1e1e1e }
- .terminal-1056312446-r8 { fill: #1e1e1e;text-decoration: underline; }
- .terminal-1056312446-r9 { fill: #fea62b;text-decoration: underline; }
- .terminal-1056312446-r10 { fill: #3a3d43;text-decoration: underline; }
- .terminal-1056312446-r11 { fill: #4ebf71 }
- .terminal-1056312446-r12 { fill: #b93c5b }
+ .terminal-2586053582-r1 { fill: #e1e1e1 }
+ .terminal-2586053582-r2 { fill: #c5c8c6 }
+ .terminal-2586053582-r3 { fill: #fea62b }
+ .terminal-2586053582-r4 { fill: #fea62b;font-weight: bold }
+ .terminal-2586053582-r5 { fill: #fea62b;font-weight: bold;font-style: italic; }
+ .terminal-2586053582-r6 { fill: #f4005f;font-weight: bold }
+ .terminal-2586053582-r7 { fill: #1e1e1e }
+ .terminal-2586053582-r8 { fill: #1e1e1e;text-decoration: underline; }
+ .terminal-2586053582-r9 { fill: #fea62b;text-decoration: underline; }
+ .terminal-2586053582-r10 { fill: #1a1a1a;text-decoration: underline; }
+ .terminal-2586053582-r11 { fill: #4ebf71 }
+ .terminal-2586053582-r12 { fill: #b93c5b }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- BorderSubTitleAlignAll
+ BorderSubTitleAlignAll
-
-
-
-
-
- โBorder titleโโญโLefโฆโโฎโโโโโLeftโโโโโ
- โThis is the story ofโโa Pythonโโdeveloper thatโ
- โBorder subtitleโโฐโCenโฆโโฏโโโโโ@@@โโโโโโ
-
-
-
-
-
- +--------------+โTitleโโโโโโโโโโโโโโโโโ
- |had to fill up|nine labelsand ended up redoing it
- +-Left-------+โโโโโโโโโโโโโโSubtitleโ
-
-
-
-
- โTitle, but really loooโฆโ
- โTitle, but rโฆโโTitle, but reallโฆโ
- because the first tryhad some labelsthat were too long.
- โSubtitle, buโฆโโSubtitle, but reโฆโ
- โSubtitle, but really lโฆโ
-
+
+
+
+
+
+ โBorder titleโโญโLefโฆโโฎโโโโโLeftโโโโโ
+ โThis is the story ofโโa Pythonโโdeveloper thatโ
+ โBorder subtitleโโฐโCenโฆโโฏโโโโโ@@@โโโโโโ
+
+
+
+
+
+ +--------------+โTitleโโโโโโโโโโโโโโโโโ
+ |had to fill up|nine labelsand ended up redoing it
+ +-Left-------+โโโโโโโโโโโโโโSubtitleโ
+
+
+
+
+ โTitle, but really loooโฆโ
+ โTitle, but rโฆโโTitle, but reallโฆโ
+ because the first tryhad some labelsthat were too long.
+ โSubtitle, buโฆโโSubtitle, but reโฆโ
+ โSubtitle, but really lโฆโ
+
@@ -16416,137 +16756,137 @@
font-weight: 700;
}
- .terminal-905695451-matrix {
+ .terminal-3159150741-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-905695451-title {
+ .terminal-3159150741-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-905695451-r1 { fill: #e1e1e1 }
- .terminal-905695451-r2 { fill: #c5c8c6 }
- .terminal-905695451-r3 { fill: #dde6ed;font-weight: bold }
- .terminal-905695451-r4 { fill: #dde6ed }
- .terminal-905695451-r5 { fill: #fea62b;font-weight: bold;font-style: italic; }
- .terminal-905695451-r6 { fill: #e1e2e3 }
- .terminal-905695451-r7 { fill: #be3f48 }
- .terminal-905695451-r8 { fill: #be3f48;font-weight: bold;font-style: italic; }
+ .terminal-3159150741-r1 { fill: #e1e1e1 }
+ .terminal-3159150741-r2 { fill: #c5c8c6 }
+ .terminal-3159150741-r3 { fill: #dde6ed;font-weight: bold }
+ .terminal-3159150741-r4 { fill: #dde6ed }
+ .terminal-3159150741-r5 { fill: #fea62b;font-weight: bold;font-style: italic; }
+ .terminal-3159150741-r6 { fill: #e1e2e3 }
+ .terminal-3159150741-r7 { fill: #f4005f }
+ .terminal-3159150741-r8 { fill: #f4005f;font-weight: bold;font-style: italic; }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- DataTableCursorStyles
+ DataTableCursorStyles
-
-
-
- Foreground is 'css', background is 'css':
- Movies
- Severance
- Foundation
- Dark
-
- Foreground is 'css', background is 'renderable':
- Movies
- Severance
- Foundation
- Dark
-
- Foreground is 'renderable', background is 'renderable':
- Movies
- Severance
- Foundation
- Dark
-
- Foreground is 'renderable', background is 'css':
- Movies
- Severance
- Foundation
- Dark
+
+
+
+ Foreground is 'css', background is 'css':
+ Movies
+ Severance
+ Foundation
+ Dark
+
+ Foreground is 'css', background is 'renderable':
+ Movies
+ Severance
+ Foundation
+ Dark
+
+ Foreground is 'renderable', background is 'renderable':
+ Movies
+ Severance
+ Foundation
+ Dark
+
+ Foreground is 'renderable', background is 'css':
+ Movies
+ Severance
+ Foundation
+ Dark
@@ -25542,138 +25882,138 @@
font-weight: 700;
}
- .terminal-2568645244-matrix {
+ .terminal-43804987-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2568645244-title {
+ .terminal-43804987-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2568645244-r1 { fill: #1e1e1e }
- .terminal-2568645244-r2 { fill: #0178d4 }
- .terminal-2568645244-r3 { fill: #c5c8c6 }
- .terminal-2568645244-r4 { fill: #ddedf9;font-weight: bold }
- .terminal-2568645244-r5 { fill: #e2e2e2;font-weight: bold }
- .terminal-2568645244-r6 { fill: #e2e2e2 }
- .terminal-2568645244-r7 { fill: #434343 }
- .terminal-2568645244-r8 { fill: #be3f48 }
- .terminal-2568645244-r9 { fill: #e1e1e1 }
+ .terminal-43804987-r1 { fill: #1e1e1e }
+ .terminal-43804987-r2 { fill: #0178d4 }
+ .terminal-43804987-r3 { fill: #c5c8c6 }
+ .terminal-43804987-r4 { fill: #ddedf9;font-weight: bold }
+ .terminal-43804987-r5 { fill: #e2e2e2;font-weight: bold }
+ .terminal-43804987-r6 { fill: #e2e2e2 }
+ .terminal-43804987-r7 { fill: #434343 }
+ .terminal-43804987-r8 { fill: #f4005f }
+ .terminal-43804987-r9 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- OptionListApp
+ OptionListApp
-
+
-
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โOneโโOneโโOneโ
- โTwoโโTwoโโTwoโ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โThreeโโThreeโโThreeโ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ โOneโโOneโโOneโ
+ โTwoโโTwoโโTwoโ
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ โThreeโโThreeโโThreeโ
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -27419,67 +27759,67 @@
font-weight: 700;
}
- .terminal-4031343223-matrix {
+ .terminal-2341104189-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-4031343223-title {
+ .terminal-2341104189-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-4031343223-r1 { fill: #ffdddd;font-weight: bold }
- .terminal-4031343223-r2 { fill: #879a3b }
- .terminal-4031343223-r3 { fill: #ddeedd }
- .terminal-4031343223-r4 { fill: #c5c8c6 }
- .terminal-4031343223-r5 { fill: #ddddff }
- .terminal-4031343223-r6 { fill: #e1e1e1 }
+ .terminal-2341104189-r1 { fill: #ffdddd;font-weight: bold }
+ .terminal-2341104189-r2 { fill: #98e024 }
+ .terminal-2341104189-r3 { fill: #ddeedd }
+ .terminal-2341104189-r4 { fill: #c5c8c6 }
+ .terminal-2341104189-r5 { fill: #ddddff }
+ .terminal-2341104189-r6 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MyApp
+ MyApp
-
+
-
- ['This is a string that has some chars']
-
- This should be 1 cell away from ^
-
-
-
+
+ ['This is a string that has some chars']
+
+ This should be 1 cell away from ^
+
+
+
@@ -29101,140 +29441,140 @@
font-weight: 700;
}
- .terminal-386310630-matrix {
+ .terminal-2351407483-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-386310630-title {
+ .terminal-2351407483-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-386310630-r1 { fill: #e1e1e1 }
- .terminal-386310630-r2 { fill: #c5c8c6 }
- .terminal-386310630-r3 { fill: #1e1e1e }
- .terminal-386310630-r4 { fill: #0178d4 }
- .terminal-386310630-r5 { fill: #575757 }
- .terminal-386310630-r6 { fill: #262626;font-weight: bold }
- .terminal-386310630-r7 { fill: #e2e2e2 }
- .terminal-386310630-r8 { fill: #e2e2e2;text-decoration: underline; }
- .terminal-386310630-r9 { fill: #434343 }
- .terminal-386310630-r10 { fill: #4ebf71;font-weight: bold }
- .terminal-386310630-r11 { fill: #be3f48;font-weight: bold;font-style: italic; }
+ .terminal-2351407483-r1 { fill: #e1e1e1 }
+ .terminal-2351407483-r2 { fill: #c5c8c6 }
+ .terminal-2351407483-r3 { fill: #1e1e1e }
+ .terminal-2351407483-r4 { fill: #0178d4 }
+ .terminal-2351407483-r5 { fill: #575757 }
+ .terminal-2351407483-r6 { fill: #262626;font-weight: bold }
+ .terminal-2351407483-r7 { fill: #e2e2e2 }
+ .terminal-2351407483-r8 { fill: #e2e2e2;text-decoration: underline; }
+ .terminal-2351407483-r9 { fill: #434343 }
+ .terminal-2351407483-r10 { fill: #4ebf71;font-weight: bold }
+ .terminal-2351407483-r11 { fill: #f4005f;font-weight: bold;font-style: italic; }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- RadioChoicesApp
+ RadioChoicesApp
-
+
-
-
-
-
-
-
-
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โโโโBattlestar Galacticaโโโโโ Amandaโ
- โโโโ Dune 1984โโโโโ Connor MacLeodโ
- โโโโ Dune 2021โโโโโ Duncan MacLeodโ
- โโโโ Serenityโโโโโ Heather MacLeodโ
- โโโโ Star Trek: The Motion Picturโโโโโ Joe Dawsonโ
- โโโโ Star Wars: A New Hopeโโโโโ Kurgan, Theโ
- โโโโ The Last Starfighterโโโโโ Methosโ
- โโโโ Total Recall ๐ ๐ดโโโโโ Rachel Ellensteinโ
- โโโโ Wing Commanderโโโโโ Ramรญrezโ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-
-
-
-
-
-
+
+
+
+
+
+
+
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ โโโโBattlestar Galacticaโโโโโ Amandaโ
+ โโโโ Dune 1984โโโโโ Connor MacLeodโ
+ โโโโ Dune 2021โโโโโ Duncan MacLeodโ
+ โโโโ Serenityโโโโโ Heather MacLeodโ
+ โโโโ Star Trek: The Motion Picturโโโโโ Joe Dawsonโ
+ โโโโ Star Wars: A New Hopeโโโโโ Kurgan, Theโ
+ โโโโ The Last Starfighterโโโโโ Methosโ
+ โโโโ Total Recall ๐ ๐ดโโโโโ Rachel Ellensteinโ
+ โโโโ Wing Commanderโโโโโ Ramรญrezโ
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+
+
+
+
+
@@ -30528,137 +30868,137 @@
font-weight: 700;
}
- .terminal-565918768-matrix {
+ .terminal-1340160965-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-565918768-title {
+ .terminal-1340160965-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-565918768-r1 { fill: #e1e1e1 }
- .terminal-565918768-r2 { fill: #c5c8c6 }
- .terminal-565918768-r3 { fill: #004578 }
- .terminal-565918768-r4 { fill: #23568b }
- .terminal-565918768-r5 { fill: #fea62b }
- .terminal-565918768-r6 { fill: #be3f48 }
- .terminal-565918768-r7 { fill: #14191f }
+ .terminal-1340160965-r1 { fill: #e1e1e1 }
+ .terminal-1340160965-r2 { fill: #c5c8c6 }
+ .terminal-1340160965-r3 { fill: #004578 }
+ .terminal-1340160965-r4 { fill: #23568b }
+ .terminal-1340160965-r5 { fill: #fea62b }
+ .terminal-1340160965-r6 { fill: #f4005f }
+ .terminal-1340160965-r7 { fill: #14191f }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MyApp
+ MyApp
-
+
-
- SPAM
- SPAM
- SPAM
- โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
- โSPAMโ
- โSPAMโ
- โSPAMโ
- โSPAMโ
- โSPAMโ
- โSPAMโโโ
- โSPAMโ
- โSPAMโ
- โโญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎโ
- โโ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@>>bullseye<<@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@โโ
- โโโโโโ
- โโโโโโ
- โโโโ
- โโโโ
- โโโโ
- โโโโ
- โโโโ
- โโโโ
- โโโโ
- โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+
+ SPAM
+ SPAM
+ SPAM
+ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
+ โSPAMโ
+ โSPAMโ
+ โSPAMโ
+ โSPAMโ
+ โSPAMโ
+ โSPAMโโโ
+ โSPAMโ
+ โSPAMโ
+ โโญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎโ
+ โโ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@>>bullseye<<@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@โโ
+ โโโโโโ
+ โโโโโโ
+ โโโโ
+ โโโโ
+ โโโโ
+ โโโโ
+ โโโโ
+ โโโโ
+ โโโโ
+ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
@@ -32133,141 +32473,141 @@
font-weight: 700;
}
- .terminal-1048388837-matrix {
+ .terminal-1590895671-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1048388837-title {
+ .terminal-1590895671-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1048388837-r1 { fill: #c5c8c6 }
- .terminal-1048388837-r2 { fill: #e3e3e3 }
- .terminal-1048388837-r3 { fill: #e1e1e1 }
- .terminal-1048388837-r4 { fill: #0178d4 }
- .terminal-1048388837-r5 { fill: #e1e1e1;font-weight: bold }
- .terminal-1048388837-r6 { fill: #575757 }
- .terminal-1048388837-r7 { fill: #4ebf71;font-weight: bold }
- .terminal-1048388837-r8 { fill: #ddedf9;font-weight: bold }
- .terminal-1048388837-r9 { fill: #879a3b }
- .terminal-1048388837-r10 { fill: #262626;font-weight: bold }
- .terminal-1048388837-r11 { fill: #e2e2e2 }
- .terminal-1048388837-r12 { fill: #ddedf9 }
+ .terminal-1590895671-r1 { fill: #c5c8c6 }
+ .terminal-1590895671-r2 { fill: #e3e3e3 }
+ .terminal-1590895671-r3 { fill: #e1e1e1 }
+ .terminal-1590895671-r4 { fill: #0178d4 }
+ .terminal-1590895671-r5 { fill: #e1e1e1;font-weight: bold }
+ .terminal-1590895671-r6 { fill: #575757 }
+ .terminal-1590895671-r7 { fill: #4ebf71;font-weight: bold }
+ .terminal-1590895671-r8 { fill: #ddedf9;font-weight: bold }
+ .terminal-1590895671-r9 { fill: #98e024 }
+ .terminal-1590895671-r10 { fill: #262626;font-weight: bold }
+ .terminal-1590895671-r11 { fill: #e2e2e2 }
+ .terminal-1590895671-r12 { fill: #ddedf9 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- SelectionListApp
+ SelectionListApp
-
+
-
- โญSelectionListApp
-
-
- โโ Shall we play some games? โโโโโ Selected games โโโโโโโโโโโโโโ
- โโโ[โ
- โโXโFalken's Mazeโโ'secret_back_door',โ
- โโXโBlack Jackโโ'a_nice_game_of_chess',โ
- โโXโGin Rummyโโ'fighter_combat'โ
- โโXโHeartsโโ]โ
- โโXโBridgeโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โโXโCheckersโ
- โโXโChessโ
- โโXโPokerโ
- โโXโFighter Combatโ
- โโ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-
-
-
-
-
-
-
+
+ โญSelectionListApp
+
+
+ โโ Shall we play some games? โโโโโ Selected games โโโโโโโโโโโโโโ
+ โโโ[โ
+ โโXโFalken's Mazeโโ'secret_back_door',โ
+ โโXโBlack Jackโโ'a_nice_game_of_chess',โ
+ โโXโGin Rummyโโ'fighter_combat'โ
+ โโXโHeartsโโ]โ
+ โโXโBridgeโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ โโXโCheckersโ
+ โโXโChessโ
+ โโXโPokerโ
+ โโXโFighter Combatโ
+ โโ
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+
+
+
+
+
+
@@ -34218,136 +34558,136 @@
font-weight: 700;
}
- .terminal-1404658517-matrix {
+ .terminal-2580112047-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1404658517-title {
+ .terminal-2580112047-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1404658517-r1 { fill: #e1e1e1 }
- .terminal-1404658517-r2 { fill: #c5c8c6 }
- .terminal-1404658517-r3 { fill: #e1e1e1;font-weight: bold }
- .terminal-1404658517-r4 { fill: #879a3b;font-weight: bold;font-style: italic; }
- .terminal-1404658517-r5 { fill: #855c8d;font-weight: bold }
- .terminal-1404658517-r6 { fill: #e1e1e1;font-style: italic; }
- .terminal-1404658517-r7 { fill: #e1e1e1;text-decoration: underline; }
+ .terminal-2580112047-r1 { fill: #e1e1e1 }
+ .terminal-2580112047-r2 { fill: #c5c8c6 }
+ .terminal-2580112047-r3 { fill: #e1e1e1;font-weight: bold }
+ .terminal-2580112047-r4 { fill: #98e024;font-weight: bold;font-style: italic; }
+ .terminal-2580112047-r5 { fill: #f4005f;font-weight: bold }
+ .terminal-2580112047-r6 { fill: #e1e1e1;font-style: italic; }
+ .terminal-2580112047-r7 { fill: #e1e1e1;text-decoration: underline; }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- TableStaticApp
+ TableStaticApp
-
+
-
- โโโโโโโโโโโโโโโโณโโโโโโโโโณโโโโโโโโโโโโ
- โFooโBar โbaz โ
- โกโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฉ
- โHello World!โItalicโUnderlineโ
- โโโโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโ
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ โโโโโโโโโโโโโโโโณโโโโโโโโโณโโโโโโโโโโโโ
+ โFooโBar โbaz โ
+ โกโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฉ
+ โHello World!โItalicโUnderlineโ
+ โโโโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -39019,146 +39359,146 @@
font-weight: 700;
}
- .terminal-3972235479-matrix {
+ .terminal-2882699257-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3972235479-title {
+ .terminal-2882699257-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3972235479-r1 { fill: #c5c8c6 }
- .terminal-3972235479-r2 { fill: #e3e3e3 }
- .terminal-3972235479-r3 { fill: #e1e1e1 }
- .terminal-3972235479-r4 { fill: #e1e1e1;text-decoration: underline; }
- .terminal-3972235479-r5 { fill: #e1e1e1;font-weight: bold }
- .terminal-3972235479-r6 { fill: #e1e1e1;font-style: italic; }
- .terminal-3972235479-r7 { fill: #855c8d;font-weight: bold }
- .terminal-3972235479-r8 { fill: #c5a635 }
- .terminal-3972235479-r9 { fill: #879a3b }
- .terminal-3972235479-r10 { fill: #0f722f;font-style: italic; }
- .terminal-3972235479-r11 { fill: #ffcf56 }
- .terminal-3972235479-r12 { fill: #e76580 }
- .terminal-3972235479-r13 { fill: #fea62b;font-weight: bold }
- .terminal-3972235479-r14 { fill: #f5e5e9;font-weight: bold }
- .terminal-3972235479-r15 { fill: #b86b00 }
- .terminal-3972235479-r16 { fill: #780028 }
+ .terminal-2882699257-r1 { fill: #c5c8c6 }
+ .terminal-2882699257-r2 { fill: #e3e3e3 }
+ .terminal-2882699257-r3 { fill: #e1e1e1 }
+ .terminal-2882699257-r4 { fill: #e1e1e1;text-decoration: underline; }
+ .terminal-2882699257-r5 { fill: #e1e1e1;font-weight: bold }
+ .terminal-2882699257-r6 { fill: #e1e1e1;font-style: italic; }
+ .terminal-2882699257-r7 { fill: #f4005f;font-weight: bold }
+ .terminal-2882699257-r8 { fill: #fd971f }
+ .terminal-2882699257-r9 { fill: #98e024 }
+ .terminal-2882699257-r10 { fill: #98e024;font-style: italic; }
+ .terminal-2882699257-r11 { fill: #ffcf56 }
+ .terminal-2882699257-r12 { fill: #e76580 }
+ .terminal-2882699257-r13 { fill: #fea62b;font-weight: bold }
+ .terminal-2882699257-r14 { fill: #f5e5e9;font-weight: bold }
+ .terminal-2882699257-r15 { fill: #b86b00 }
+ .terminal-2882699257-r16 { fill: #780028 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- Textual Keys
+ Textual Keys
-
+
-
- โญTextual Keys
- โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
- โPress some keys!โ
- โโ
- โTo quit the app press ctrl+ctwice or press the Quit button below.โ
- โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
- Key(key='a', character='a', name='a', is_printable=True)
- Key(key='b', character='b', name='b', is_printable=True)
-
-
-
-
-
-
-
-
-
-
-
-
-
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- ClearQuit
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ โญTextual Keys
+ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
+ โPress some keys!โ
+ โโ
+ โTo quit the app press ctrl+ctwice or press the Quit button below.โ
+ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+ Key(key='a', character='a', name='a', is_printable=True)
+ Key(key='b', character='b', name='b', is_printable=True)
+
+
+
+
+
+
+
+
+
+
+
+
+
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ ClearQuit
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
diff --git a/tests/snapshot_tests/snapshot_apps/ansi_mapping.py b/tests/snapshot_tests/snapshot_apps/ansi_mapping.py
new file mode 100644
index 0000000000..05e4669cd2
--- /dev/null
+++ b/tests/snapshot_tests/snapshot_apps/ansi_mapping.py
@@ -0,0 +1,25 @@
+from textual.app import App, ComposeResult
+from textual.widgets import Label
+
+
+class AnsiMappingApp(App[None]):
+ def compose(self) -> ComposeResult:
+ ansi_colors = [
+ "red",
+ "green",
+ "yellow",
+ "blue",
+ "magenta",
+ "cyan",
+ "white",
+ "black",
+ ]
+ yield Label("[fg on bg]Foreground & background[/]")
+ for color in ansi_colors:
+ yield Label(f"[{color}]{color}[/]")
+ yield Label(f"[dim {color}]dim {color}[/]")
+
+
+app = AnsiMappingApp()
+if __name__ == "__main__":
+ app.run()
diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py
index 00fedf3e0b..ece28e9cb2 100644
--- a/tests/snapshot_tests/test_snapshots.py
+++ b/tests/snapshot_tests/test_snapshots.py
@@ -1068,6 +1068,16 @@ def test_input_percentage_width(snap_compare):
assert snap_compare(SNAPSHOT_APPS_DIR / "input_percentage_width.py")
+@pytest.mark.parametrize("dark", [True, False])
+def test_ansi_color_mapping(snap_compare, dark):
+ """Test how ANSI colors in Rich renderables are mapped to hex colors."""
+
+ def setup(pilot):
+ pilot.app.dark = dark
+
+ assert snap_compare(SNAPSHOT_APPS_DIR / "ansi_mapping.py", run_before=setup)
+
+
def test_pretty_grid_gutter_interaction(snap_compare):
"""Regression test for https://github.com/Textualize/textual/pull/4219."""
assert snap_compare(
diff --git a/tests/test_app.py b/tests/test_app.py
index e5afed1e2a..3e3927176f 100644
--- a/tests/test_app.py
+++ b/tests/test_app.py
@@ -1,5 +1,7 @@
import contextlib
+from rich.terminal_theme import DIMMED_MONOKAI, MONOKAI, NIGHT_OWLISH
+
from textual.app import App, ComposeResult
from textual.widgets import Button, Input
@@ -117,3 +119,24 @@ async def test_no_return_code_while_running():
app = App()
async with app.run_test():
assert app.return_code is None
+
+
+async def test_ansi_theme():
+ app = App()
+ async with app.run_test():
+ app.ansi_theme_dark = NIGHT_OWLISH
+ assert app.ansi_theme == NIGHT_OWLISH
+
+ app.dark = False
+ assert app.ansi_theme != NIGHT_OWLISH
+
+ app.ansi_theme_light = MONOKAI
+ assert app.ansi_theme == MONOKAI
+
+ # Ensure if we change the dark theme while on light mode,
+ # then change back to dark mode, the dark theme is updated.
+ app.ansi_theme_dark = DIMMED_MONOKAI
+ assert app.ansi_theme == MONOKAI
+
+ app.dark = True
+ assert app.ansi_theme == DIMMED_MONOKAI