From 0509cf89489bf17ac322739134a7e4be502d3122 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 16 Apr 2023 12:31:39 +0100 Subject: [PATCH] Border style (#2292) * border styles * docs for border styles * fix tests * tests * tests and docs * changelog * implement auto * style information fix --- CHANGELOG.md | 3 + docs/examples/styles/border_title_colors.css | 16 + docs/examples/styles/border_title_colors.py | 19 + docs/snippets/border_title_color.md | 18 + docs/snippets/see_also_border.md | 9 + docs/styles/align.md | 7 +- docs/styles/border.md | 30 +- docs/styles/border_subtitle_align.md | 4 + docs/styles/border_subtitle_background.md | 34 + docs/styles/border_subtitle_color.md | 33 + docs/styles/border_subtitle_style.md | 35 + docs/styles/border_title_align.md | 4 + docs/styles/border_title_background.md | 33 + docs/styles/border_title_color.md | 31 + docs/styles/border_title_style.md | 35 + docs/styles/outline.md | 2 +- docs/styles/text_style.md | 2 +- mkdocs-nav.yml | 10 +- src/textual/_border.py | 51 +- src/textual/_styles_cache.py | 58 +- src/textual/css/_styles_builder.py | 8 + src/textual/css/styles.py | 36 + src/textual/css/types.py | 1 + src/textual/dom.py | 46 + .../__snapshots__/test_snapshots.ambr | 2480 +++++++++-------- tests/test_border.py | 17 +- tests/test_styles_cache.py | 32 +- 27 files changed, 1807 insertions(+), 1247 deletions(-) create mode 100644 docs/examples/styles/border_title_colors.css create mode 100644 docs/examples/styles/border_title_colors.py create mode 100644 docs/snippets/border_title_color.md create mode 100644 docs/snippets/see_also_border.md create mode 100644 docs/styles/border_subtitle_background.md create mode 100644 docs/styles/border_subtitle_color.md create mode 100644 docs/styles/border_subtitle_style.md create mode 100644 docs/styles/border_title_background.md create mode 100644 docs/styles/border_title_color.md create mode 100644 docs/styles/border_title_style.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 84fa8fff9..3b7a424da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - option `--port` to the command `textual console` to specify which port the console should connect to https://github.com/Textualize/textual/pull/2258 - `Widget.scroll_to_center` method to scroll children to the center of container widget https://github.com/Textualize/textual/pull/2255 and https://github.com/Textualize/textual/pull/2276 - Added `TabActivated` message to `TabbedContent` https://github.com/Textualize/textual/pull/2260 +- Added "panel" border style https://github.com/Textualize/textual/pull/2292 +- Added `border-title-color`, `border-title-background`, `border-title-style` rules https://github.com/Textualize/textual/issues/2289 +- Added `border-subtitle-color`, `border-subtitle-background`, `border-subtitle-style` rules https://github.com/Textualize/textual/issues/2289 ### Fixed diff --git a/docs/examples/styles/border_title_colors.css b/docs/examples/styles/border_title_colors.css new file mode 100644 index 000000000..0ab890944 --- /dev/null +++ b/docs/examples/styles/border_title_colors.css @@ -0,0 +1,16 @@ +Screen { + align: center middle; +} + +Label { + padding: 4 8; + border: heavy red; + + border-title-color: green; + border-title-background: white; + border-title-style: bold; + + border-subtitle-color: magenta; + border-subtitle-background: yellow; + border-subtitle-style: italic; +} diff --git a/docs/examples/styles/border_title_colors.py b/docs/examples/styles/border_title_colors.py new file mode 100644 index 000000000..1af74ccc6 --- /dev/null +++ b/docs/examples/styles/border_title_colors.py @@ -0,0 +1,19 @@ +from textual.app import App, ComposeResult +from textual.widgets import Label + + +class BorderTitleApp(App): + CSS_PATH = "border_title_colors.css" + + def compose(self) -> ComposeResult: + yield Label("Hello, World!") + + def on_mount(self) -> None: + label = self.query_one(Label) + label.border_title = "Textual Rocks" + label.border_subtitle = "Textual Rocks" + + +if __name__ == "__main__": + app = BorderTitleApp() + app.run() diff --git a/docs/snippets/border_title_color.md b/docs/snippets/border_title_color.md new file mode 100644 index 000000000..c2a69052c --- /dev/null +++ b/docs/snippets/border_title_color.md @@ -0,0 +1,18 @@ +The following examples demonstrates customization of the border color and text style rules. + +=== "Output" + + ```{.textual path="docs/examples/styles/border_title_colors.py"} + ``` + +=== "border_title_colors.py" + + ```python + --8<-- "docs/examples/styles/border_title_colors.py" + ``` + +=== "border_title_colors.css" + + ```sass + --8<-- "docs/examples/styles/border_title_colors.css" + ``` diff --git a/docs/snippets/see_also_border.md b/docs/snippets/see_also_border.md new file mode 100644 index 000000000..83b09dbe3 --- /dev/null +++ b/docs/snippets/see_also_border.md @@ -0,0 +1,9 @@ +- [`border-title-align`](./border_title_align.md) to set the title's alignment. +- [`border-title-color`](./border_subtitle_color.md) to set the title's color. +- [`border-title-background`](./border_subtitle_background.md) to set the title's background color. +- [`border-title-style`](./border_subtitle_style.md) to set the title's text style. + +- [`border-subtitle-align`](./border_subtitle_align.md) to set the sub-title's alignment. +- [`border-subtitle-color`](./border_subtitle_color.md) to set the sub-title's color. +- [`border-subtitle-background`](./border_subtitle_background.md) to set the sub-title's background color. +- [`border-subtitle-style`](./border_subtitle_style.md) to set the sub-title's text style. diff --git a/docs/styles/align.md b/docs/styles/align.md index 76835389c..c32d004aa 100644 --- a/docs/styles/align.md +++ b/docs/styles/align.md @@ -13,12 +13,7 @@ align-vertical: <vertical>; The `align` style takes a [``](../../css_types/horizontal) followed by a [``](../../css_types/vertical). -You can specify the alignment of children on both the horizontal and vertical axes at the same time, -or on each of the axis separately. -To specify alignment on a single axis, use the respective style and type: - - - `align-horizontal` takes a [``](../../css_types/horizontal) and does alignment along the horizontal axis; and - - `align-vertical` takes a [``](../../css_types/vertical) and does alignment along the vertical axis. +You can also set the alignment for each axis individually with `align-horizontal` and `align-vertical`. ## Examples diff --git a/docs/styles/border.md b/docs/styles/border.md index 267c16347..6463c871d 100644 --- a/docs/styles/border.md +++ b/docs/styles/border.md @@ -2,9 +2,11 @@ The `border` style enables the drawing of a box around a widget. +A border style may also be applied to individual edges with `border-top`, `border-right`, `border-bottom`, and `border-left`. + !!! note - Due to a Textual limitation, [`border`](./border.md) and [`outline`](./outline.md) cannot coexist in the same edge of a widget. + [`border`](./border.md) and [`outline`](./outline.md) cannot coexist in the same edge of a widget. ## Syntax @@ -17,31 +19,10 @@ border-bottom: [<border>] [<border>] [<color> [<percentage>]]; --8<-- "docs/snippets/syntax_block_end.md" -The `border` style accepts an optional [``](../../css_types/border) that sets the visual style of the widget border, an optional [``](../../css_types/color) to set the color of the border, and an optional [``](../../css_types/percentage) to specify the color transparency. +In CSS, the border is set with a [border style](./border.md) and a color. Both are optional. An optional percentage may be added to blend the border with the background color. -Borders may also be set individually for the four edges of a widget with the `border-top`, `border-right`, `border-bottom` and `border-left` rules. +In Python, the border is set with a tuple of [border style](./border.md) and a color. -### Multiple edge rules - -If multiple border styles target the same edge, the last style that targets a specific edge is the one that is applied to that edge. -For example, consider the CSS below: - -```sass -Static { - border-top: dashed red; - border: solid green; /* overrides the border-top rule above */ - /* Change the border but just for the bottom edge: */ - border-bottom: double blue; -} -``` - -The CSS snippet above will add a solid green border around `Static` widgets, except for the bottom edge, which will be double blue. - -### Defaults - -If `` is specified but `` is not, it defaults to `"solid"`. -If `` is specified but ``is not, it defaults to green (RGB color `"#00FF00"`). -If `` is not specified it defaults to `100%`. ## Border command @@ -128,3 +109,4 @@ widget.styles.border_left = ("outer", "red") - [`box-sizing`](./box_sizing.md) to specify how to account for the border in a widget's dimensions. - [`outline`](./outline.md) to add an outline around the content of a widget. +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_subtitle_align.md b/docs/styles/border_subtitle_align.md index d46c9f1e2..148918ca6 100644 --- a/docs/styles/border_subtitle_align.md +++ b/docs/styles/border_subtitle_align.md @@ -60,3 +60,7 @@ widget.styles.border_subtitle_align = "left" widget.styles.border_subtitle_align = "center" widget.styles.border_subtitle_align = "right" ``` + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_subtitle_background.md b/docs/styles/border_subtitle_background.md new file mode 100644 index 000000000..8d60b3e15 --- /dev/null +++ b/docs/styles/border_subtitle_background.md @@ -0,0 +1,34 @@ +# Border-subtitle-background + +The `border-subtitle-background` style sets the *background* color of the [border_subtitle][textual.widget.Widget.border_subtitle]. + +## Syntax + +--8<-- "docs/snippets/syntax_block_start.md" +border-subtitle-background: (<color> | auto) [<percentage>]; +--8<-- "docs/snippets/syntax_block_end.md" + + + +## Example + +--8<-- "docs/snippets/border_title_color.md" + + + +## CSS + +```sass +border-subtitle-background: blue; +``` + +## Python + +```python +widget.styles.border_subtitle_background = "blue" +``` + + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_subtitle_color.md b/docs/styles/border_subtitle_color.md new file mode 100644 index 000000000..388011159 --- /dev/null +++ b/docs/styles/border_subtitle_color.md @@ -0,0 +1,33 @@ +# Border-subtitle-color + +The `border-subtitle-color` style sets the color of the [border_subtitle][textual.widget.Widget.border_subtitle]. + +## Syntax + +--8<-- "docs/snippets/syntax_block_start.md" +border-subtitle-color: (<color> | auto) [<percentage>]; +--8<-- "docs/snippets/syntax_block_end.md" + + +## Example + +--8<-- "docs/snippets/border_title_color.md" + + + +## CSS + +```sass +border-subtitle-color: red; +``` + +## Python + +```python +widget.styles.border_subtitle_color = "red" +``` + + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_subtitle_style.md b/docs/styles/border_subtitle_style.md new file mode 100644 index 000000000..3b826568f --- /dev/null +++ b/docs/styles/border_subtitle_style.md @@ -0,0 +1,35 @@ +# Border-subtitle-style + +The `border-subtitle-style` style sets the text style of the [border_subtitle][textual.widget.Widget.border_subtitle]. + + +## Syntax + +--8<-- "docs/snippets/syntax_block_start.md" +border-subtitle-style: <text-style>; +--8<-- "docs/snippets/syntax_block_end.md" + + +## Example + +--8<-- "docs/snippets/border_title_color.md" + + +## CSS + +```sass +border-subtitle-style: bold underline; +``` + +## Python + +```python +widget.styles.border_subtitle_style = "bold underline" +``` + + + + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_title_align.md b/docs/styles/border_title_align.md index e410a771c..5f754fe39 100644 --- a/docs/styles/border_title_align.md +++ b/docs/styles/border_title_align.md @@ -60,3 +60,7 @@ widget.styles.border_title_align = "left" widget.styles.border_title_align = "center" widget.styles.border_title_align = "right" ``` + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_title_background.md b/docs/styles/border_title_background.md new file mode 100644 index 000000000..19d33e578 --- /dev/null +++ b/docs/styles/border_title_background.md @@ -0,0 +1,33 @@ +# Border-title-background + +The `border-title-background` style sets the *background* color of the [border_title][textual.widget.Widget.border_title]. + +## Syntax + +--8<-- "docs/snippets/syntax_block_start.md" +border-title-background: (<color> | auto) [<percentage>]; +--8<-- "docs/snippets/syntax_block_end.md" + + + +## Example + +--8<-- "docs/snippets/border_title_color.md" + + +## CSS + +```sass +border-title-background: blue; +``` + +## Python + +```python +widget.styles.border_title_background = "blue" +``` + + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_title_color.md b/docs/styles/border_title_color.md new file mode 100644 index 000000000..2272de9c6 --- /dev/null +++ b/docs/styles/border_title_color.md @@ -0,0 +1,31 @@ +# Border-title-color + +The `border-title-color` style sets the color of the [border_title][textual.widget.Widget.border_title]. + +## Syntax + +--8<-- "docs/snippets/syntax_block_start.md" +border-title-color: (<color> | auto) [<percentage>]; +--8<-- "docs/snippets/syntax_block_end.md" + +## Example + +--8<-- "docs/snippets/border_title_color.md" + + +## CSS + +```sass +border-title-color: red; +``` + +## Python + +```python +widget.styles.border_title_color = "red" +``` + + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_title_style.md b/docs/styles/border_title_style.md new file mode 100644 index 000000000..09e8319cc --- /dev/null +++ b/docs/styles/border_title_style.md @@ -0,0 +1,35 @@ +# Border-title-style + +The `border-title-style` style sets the text style of the [border_title][textual.widget.Widget.border_title]. + + +## Syntax + +--8<-- "docs/snippets/syntax_block_start.md" +border-title-style: <text-style>; +--8<-- "docs/snippets/syntax_block_end.md" + + + +## Example + +--8<-- "docs/snippets/border_title_color.md" + + +## CSS + +```sass +border-title-style: bold underline; +``` + +## Python + +```python +widget.styles.border_title_style = "bold underline" +``` + + + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/outline.md b/docs/styles/outline.md index 5e5155f89..650619b05 100644 --- a/docs/styles/outline.md +++ b/docs/styles/outline.md @@ -4,7 +4,7 @@ The `outline` style enables the drawing of a box around the content of a widget, !!! note - Due to a Textual limitation, [`border`](./border.md) and [`outline`](./outline.md) cannot coexist in the same edge of a widget. + [`border`](./border.md) and [`outline`](./outline.md) cannot coexist in the same edge of a widget. ## Syntax diff --git a/docs/styles/text_style.md b/docs/styles/text_style.md index a0ee02f90..8e9cb4775 100644 --- a/docs/styles/text_style.md +++ b/docs/styles/text_style.md @@ -1,6 +1,6 @@ # Text-style -The `text-style` sets the style for the text in a widget. +The `text-style` style sets the style for the text in a widget. ## Syntax diff --git a/mkdocs-nav.yml b/mkdocs-nav.yml index c29934582..7b77d6f43 100644 --- a/mkdocs-nav.yml +++ b/mkdocs-nav.yml @@ -64,17 +64,23 @@ nav: - "events/screen_suspend.md" - "events/show.md" - Styles: - - "styles/index.md" - "styles/align.md" - "styles/background.md" - - "styles/border.md" - "styles/border_subtitle_align.md" + - "styles/border_subtitle_background.md" + - "styles/border_subtitle_color.md" + - "styles/border_subtitle_style.md" - "styles/border_title_align.md" + - "styles/border_title_background.md" + - "styles/border_title_color.md" + - "styles/border_title_style.md" + - "styles/border.md" - "styles/box_sizing.md" - "styles/color.md" - "styles/content_align.md" - "styles/display.md" - "styles/dock.md" + - "styles/index.md" - Grid: - "styles/grid/index.md" - "styles/grid/column_span.md" diff --git a/src/textual/_border.py b/src/textual/_border.py index 3b3bf2670..5cbb61dee 100644 --- a/src/textual/_border.py +++ b/src/textual/_border.py @@ -102,9 +102,14 @@ BORDER_CHARS: dict[ ("▊", " ", "▎"), ("▊", "▁", "▎"), ), + "panel": ( + ("▊", "█", "▎"), + ("▊", " ", "▎"), + ("▊", "▁", "▎"), + ), "wide": ( ("▁", "▁", "▁"), - ("▎", " ", "▋"), + ("▎", " ", "▊"), ("▔", "▔", "▔"), ), } @@ -195,6 +200,11 @@ BORDER_LOCATIONS: dict[ (2, 0, 1), (2, 0, 1), ), + "panel": ( + (2, 0, 1), + (2, 0, 1), + (2, 0, 1), + ), "wide": ( (1, 1, 1), (0, 1, 3), @@ -202,6 +212,11 @@ BORDER_LOCATIONS: dict[ ), } +# Some borders (such as panel) require that the title (and subtitle) be draw in reverse. +# This is a mapping of the border type on to a tuple for the top and bottom borders, to indicate +# reverse colors is required. +BORDER_TITLE_FLIP: dict[str, tuple[bool, bool]] = {"panel": (True, False)} + # In a similar fashion, we extract the border _label_ locations for easier access when # rendering a border label. # The values are a pair with (title location, subtitle location). @@ -283,7 +298,7 @@ def get_box( def render_border_label( - label: Text, + label: tuple[Text, Style], is_title: bool, name: EdgeType, width: int, @@ -300,7 +315,7 @@ def render_border_label( account the inner, outer, and border-specific, styles. Args: - label: The label to display (that may contain markup). + label: Tuple of label and style to render in the border. is_title: Whether we are rendering the title (`True`) or the subtitle (`False`). name: Name of the box type. width: The width, in cells, of the space available for the whole edge. @@ -323,14 +338,21 @@ def render_border_label( # How many cells do we need to reserve for surrounding blanks and corners? corners_needed = has_left_corner + has_right_corner cells_reserved = 2 * corners_needed - if not label.cell_len or width <= cells_reserved: + + text_label, label_style = label + if not text_label.cell_len or width <= cells_reserved: return - text_label = label.copy() + text_label = text_label.copy() text_label.truncate(width - cells_reserved, overflow="ellipsis") - segments = text_label.render(console) + if has_left_corner: + text_label.pad_left(1) + if has_right_corner: + text_label.pad_right(1) + text_label.stylize_before(label_style) label_style_location = BORDER_LABEL_LOCATIONS[name][0 if is_title else 1] + flip_top, flip_bottom = BORDER_TITLE_FLIP.get(name, (False, False)) inner = inner_style + style outer = outer_style + style @@ -347,15 +369,14 @@ def render_border_label( else: assert False - styled_segments = [ - Segment(segment.text, base_style + segment.style) for segment in segments - ] - blank = Segment(" ", base_style) - if has_left_corner: - yield blank - yield from styled_segments - if has_right_corner: - yield blank + if (flip_top and is_title) or (flip_bottom and not is_title): + base_style = base_style.without_color + Style.from_color( + base_style.bgcolor, base_style.color + ) + + text_label.stylize_before(base_style + label_style) + segments = text_label.render(console) + yield from segments def render_row( diff --git a/src/textual/_styles_cache.py b/src/textual/_styles_cache.py index 8092f0c45..a623c53f4 100644 --- a/src/textual/_styles_cache.py +++ b/src/textual/_styles_cache.py @@ -107,6 +107,10 @@ class StylesCache: Returns: Rendered lines. """ + + border_title = widget._border_title + border_subtitle = widget._border_subtitle + base_background, background = widget.background_colors styles = widget.styles strips = self.render( @@ -116,8 +120,22 @@ class StylesCache: background, widget.render_line, widget.app.console, - widget._border_title, - widget._border_subtitle, + ( + None + if border_title is None + else ( + border_title, + *widget._get_title_style_information(base_background), + ) + ), + ( + None + if border_subtitle is None + else ( + border_subtitle, + *widget._get_subtitle_style_information(base_background), + ) + ), content_size=widget.content_region.size, padding=styles.padding, crop=crop, @@ -147,8 +165,8 @@ class StylesCache: background: Color, render_content_line: RenderLineCallback, console: Console, - border_title: Text | None, - border_subtitle: Text | None, + border_title: tuple[Text, Color, Color, Style] | None, + border_subtitle: tuple[Text, Color, Color, Style] | None, content_size: Size | None = None, padding: Spacing | None = None, crop: Region | None = None, @@ -163,8 +181,8 @@ class StylesCache: background: Background color of widget. render_content_line: Callback to render content line. console: The console in use by the app. - border_title: The title for the widget border. - border_subtitle: The subtitle for the widget border. + border_title: Optional tuple of (title, color, background, style). + border_subtitle: Optional tuple of (subtitle, color, background, style). content_size: Size of content or None to assume full size. padding: Override padding from Styles, or None to use styles.padding. crop: Region to crop to. @@ -229,8 +247,8 @@ class StylesCache: background: Color, render_content_line: Callable[[int], Strip], console: Console, - border_title: Text | None, - border_subtitle: Text | None, + border_title: tuple[Text, Color, Color, Style] | None, + border_subtitle: tuple[Text, Color, Color, Style] | None, ) -> Strip: """Render a styled line. @@ -244,8 +262,8 @@ class StylesCache: background: Background color of widget. render_content_line: Callback to render a line of content. console: The console in use by the app. - border_title: The title for the widget border. - border_subtitle: The subtitle for the widget border. + border_title: Optional tuple of (title, color, background, style). + border_subtitle: Optional tuple of (subtitle, color, background, style). Returns: A line of segments. @@ -305,10 +323,26 @@ class StylesCache: has_left = border_left != "" has_right = border_right != "" border_label = border_title if is_top else border_subtitle + if border_label is None: + render_label = None + else: + label, label_color, label_background, style = border_label + base_label_background = base_background + background + style += Style.from_color( + ( + (base_label_background + label_color).rich_color + if label_color.a + else None + ), + (base_label_background + label_background).rich_color + if label_background.a + else None, + ) + render_label = (label, style) # Try to save time with expensive call to `render_border_label`: - if border_label: + if render_label: label_segments = render_border_label( - border_label, + render_label, is_top, border_edge_type, width - 2, diff --git a/src/textual/css/_styles_builder.py b/src/textual/css/_styles_builder.py index eb8546234..4e6ddcc1d 100644 --- a/src/textual/css/_styles_builder.py +++ b/src/textual/css/_styles_builder.py @@ -640,6 +640,11 @@ class StylesBuilder: process_link_hover_color = process_color process_link_hover_background = process_color + process_border_title_color = process_color + process_border_title_background = process_color + process_border_subtitle_color = process_color + process_border_subtitle_background = process_color + def process_text_style(self, name: str, tokens: list[Token]) -> None: for token in tokens: value = token.value @@ -656,6 +661,9 @@ class StylesBuilder: process_link_style = process_text_style process_link_hover_style = process_text_style + process_border_title_style = process_text_style + process_border_subtitle_style = process_text_style + def process_text_align(self, name: str, tokens: list[Token]) -> None: """Process a text-align declaration""" if not tokens: diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py index e751033ae..2dcbb181e 100644 --- a/src/textual/css/styles.py +++ b/src/textual/css/styles.py @@ -167,6 +167,16 @@ class RulesMap(TypedDict, total=False): link_hover_background: Color link_hover_style: Style + auto_border_title_color: bool + border_title_color: Color + border_title_background: Color + border_title_style: Style + + auto_border_subtitle_color: bool + border_subtitle_color: Color + border_subtitle_background: Color + border_subtitle_style: Style + RULE_NAMES = list(RulesMap.__annotations__.keys()) RULE_NAMES_SET = frozenset(RULE_NAMES) @@ -321,6 +331,16 @@ class StylesBase(ABC): link_hover_background = ColorProperty("transparent") link_hover_style = StyleFlagsProperty() + auto_border_title_color = BooleanProperty(default=False) + border_title_color = ColorProperty(Color(255, 255, 255, 0)) + border_title_background = ColorProperty(Color(0, 0, 0, 0)) + border_title_style = StyleFlagsProperty() + + auto_border_subtitle_color = BooleanProperty(default=False) + border_subtitle_color = ColorProperty(Color(255, 255, 255, 0)) + border_subtitle_background = ColorProperty(Color(0, 0, 0, 0)) + border_subtitle_style = StyleFlagsProperty() + def __textual_animation__( self, attribute: str, @@ -990,6 +1010,22 @@ class Styles(StylesBase): if "link_hover_style" in rules: append_declaration("link-hover-style", str(self.link_hover_style)) + if "border_title_color" in rules: + append_declaration("title-color", self.border_title_color.css) + if "border_title_background" in rules: + append_declaration("title-background", self.border_title_background.css) + if "border_title_style" in rules: + append_declaration("title-text-style", str(self.border_title_style)) + + if "border_subtitle_color" in rules: + append_declaration("subtitle-color", self.border_subtitle_color.css) + if "border_subtitle_background" in rules: + append_declaration( + "subtitle-background", self.border_subtitle_background.css + ) + if "border_subtitle_text_style" in rules: + append_declaration("subtitle-text-style", str(self.border_subtitle_style)) + lines.sort() return lines diff --git a/src/textual/css/types.py b/src/textual/css/types.py index 48f8433b3..395b549e0 100644 --- a/src/textual/css/types.py +++ b/src/textual/css/types.py @@ -25,6 +25,7 @@ EdgeType = Literal[ "hkey", "vkey", "tall", + "panel", "wide", ] Visibility = Literal["visible", "hidden", "initial", "inherit"] diff --git a/src/textual/dom.py b/src/textual/dom.py index 1e7282748..430ebeb43 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -765,6 +765,52 @@ class DOMNode(MessagePump): ) return style + def _get_title_style_information( + self, background: Color + ) -> tuple[Color, Color, Style]: + """Get a Rich Style object for for titles. + + Args: + background: The background color. + + Returns: + A Rich style. + + """ + styles = self.styles + if styles.auto_border_title_color: + color = background.get_contrast_text(styles.border_title_color.a) + else: + color = styles.border_title_color + return ( + color, + styles.border_title_background, + styles.border_title_style, + ) + + def _get_subtitle_style_information( + self, background: Color + ) -> tuple[Color, Color, Style]: + """Get a Rich Style object for for titles. + + Args: + background: The background color. + + Returns: + A Rich style. + + """ + styles = self.styles + if styles.auto_border_subtitle_color: + color = background.get_contrast_text(styles.border_subtitle_color.a) + else: + color = styles.border_subtitle_color + return ( + color, + styles.border_subtitle_background, + styles.border_subtitle_style, + ) + @property def background_colors(self) -> tuple[Color, Color]: """The background color and the color of the parent's background. diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index b591ed28a..5cd124486 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -893,140 +893,141 @@ font-weight: 700; } - .terminal-3953159668-matrix { + .terminal-4039043553-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3953159668-title { + .terminal-4039043553-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3953159668-r1 { fill: #05080f } - .terminal-3953159668-r2 { fill: #e1e1e1 } - .terminal-3953159668-r3 { fill: #c5c8c6 } - .terminal-3953159668-r4 { fill: #1e2226;font-weight: bold } - .terminal-3953159668-r5 { fill: #35393d } - .terminal-3953159668-r6 { fill: #454a50 } - .terminal-3953159668-r7 { fill: #fea62b } - .terminal-3953159668-r8 { fill: #e2e3e3;font-weight: bold } - .terminal-3953159668-r9 { fill: #000000 } - .terminal-3953159668-r10 { fill: #e2e3e3 } + .terminal-4039043553-r1 { fill: #05080f } + .terminal-4039043553-r2 { fill: #e1e1e1 } + .terminal-4039043553-r3 { fill: #c5c8c6 } + .terminal-4039043553-r4 { fill: #1e2226;font-weight: bold } + .terminal-4039043553-r5 { fill: #35393d } + .terminal-4039043553-r6 { fill: #454a50 } + .terminal-4039043553-r7 { fill: #fea62b } + .terminal-4039043553-r8 { fill: #e2e3e3;font-weight: bold } + .terminal-4039043553-r9 { fill: #000000 } + .terminal-4039043553-r10 { fill: #e2e3e3 } + .terminal-4039043553-r11 { fill: #14191f } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - BorderApp + BorderApp - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - ascii - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔+-------------------ascii--------------------+ - none|| - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|| - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|I must not fear.| - hidden|Fear is the mind-killer.| - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|Fear is the little-death that brings | - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|total obliteration.| - blank|I will face my fear.| - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|I will permit it to pass over me and | - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|through me.| - round|And when it has gone past, I will turn| - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|the inner eye to see its path.| - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|Where the fear has gone there will be | - solid|nothing. Only I will remain.| - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|| - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|| - double+----------------------------------------------+ - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - dashed - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + ascii + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔+------------------- ascii --------------------+ + none|| + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|| + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|I must not fear.| + hidden|Fear is the mind-killer.| + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|Fear is the little-death that brings | + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|total obliteration.| + blank|I will face my fear.| + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▅▅|I will permit it to pass over me and | + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|through me.| + round|And when it has gone past, I will turn| + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|the inner eye to see its path.| + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|Where the fear has gone there will be | + solid|nothing. Only I will remain.| + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|| + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|| + double+----------------------------------------------+ + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + dashed + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ @@ -1901,246 +1902,246 @@ font-weight: 700; } - .terminal-1921368926-matrix { + .terminal-797557575-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1921368926-title { + .terminal-797557575-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1921368926-r1 { fill: #c5c8c6 } - .terminal-1921368926-r2 { fill: #e1e1e1 } - .terminal-1921368926-r3 { fill: #454a50 } - .terminal-1921368926-r4 { fill: #e2e3e3;font-weight: bold } - .terminal-1921368926-r5 { fill: #24292f;font-weight: bold } - .terminal-1921368926-r6 { fill: #000000 } - .terminal-1921368926-r7 { fill: #004578 } - .terminal-1921368926-r8 { fill: #121212 } - .terminal-1921368926-r9 { fill: #e2e3e3 } - .terminal-1921368926-r10 { fill: #0053aa } - .terminal-1921368926-r11 { fill: #dde8f3;font-weight: bold } - .terminal-1921368926-r12 { fill: #ffff00;font-weight: bold } - .terminal-1921368926-r13 { fill: #24292f } + .terminal-797557575-r1 { fill: #c5c8c6 } + .terminal-797557575-r2 { fill: #e1e1e1 } + .terminal-797557575-r3 { fill: #454a50 } + .terminal-797557575-r4 { fill: #e2e3e3;font-weight: bold } + .terminal-797557575-r5 { fill: #24292f;font-weight: bold } + .terminal-797557575-r6 { fill: #000000 } + .terminal-797557575-r7 { fill: #004578 } + .terminal-797557575-r8 { fill: #121212 } + .terminal-797557575-r9 { fill: #e2e3e3 } + .terminal-797557575-r10 { fill: #0053aa } + .terminal-797557575-r11 { fill: #dde8f3;font-weight: bold } + .terminal-797557575-r12 { fill: #ffff00;font-weight: bold } + .terminal-797557575-r13 { fill: #24292f } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - ContentSwitcherApp + ContentSwitcherApp - + - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - DataTableMarkdown - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ───────────────────────────────────────── - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - Three Flavours Cornetto - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - The Three Flavours Cornetto  - trilogy is an anthology series  - of Britishcomedic genre films  - directed by Edgar Wright. - - Shaun of the Dead - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - UK  - Release  - FlavourDateDirector -  ━━━━━━━━━━━━━━━━━━━━━━━━━━━  - Strawbe…2004-04…Edgar  - Wright - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - Hot Fuzz - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - UK  - Release  - FlavourDateDirector -  ━━━━━━━━━━━━━━━━━━━━━━━━━━━  - Classico2007-02…Edgar  - Wright - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - The World's End - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - UK  - Release  - FlavourDateDirector - ───────────────────────────────────────── + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + DataTableMarkdown + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ───────────────────────────────────────── + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + Three Flavours Cornetto + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + The Three Flavours Cornetto  + trilogy is an anthology series  + of Britishcomedic genre films  + directed by Edgar Wright. + + Shaun of the Dead + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + UK  + Release  + FlavourDateDirector +  ━━━━━━━━━━━━━━━━━━━━━━━━━━━  + Strawbe…2004-04…Edgar  + Wright + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + Hot Fuzz + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + UK  + Release  + FlavourDateDirector +  ━━━━━━━━━━━━━━━━━━━━━━━━━━━  + Classico2007-02…Edgar  + Wright + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + The World's End + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + UK  + Release  + FlavourDateDirector + ───────────────────────────────────────── @@ -3119,133 +3120,133 @@ font-weight: 700; } - .terminal-1917293938-matrix { + .terminal-1717278065-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1917293938-title { + .terminal-1717278065-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1917293938-r1 { fill: #c5c8c6 } - .terminal-1917293938-r2 { fill: #0178d4 } - .terminal-1917293938-r3 { fill: #e1e1e1 } - .terminal-1917293938-r4 { fill: #1e1e1e } + .terminal-1717278065-r1 { fill: #c5c8c6 } + .terminal-1717278065-r2 { fill: #0178d4 } + .terminal-1717278065-r3 { fill: #e1e1e1 } + .terminal-1717278065-r4 { fill: #1e1e1e } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - AllBordersApp + AllBordersApp - + - - +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - |ascii|blankdashed - +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - - - ══════════════════━━━━━━━━━━━━━━━━━━ - doubleheavyhidden/none - ══════════════════━━━━━━━━━━━━━━━━━━ - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ - hkeyinnerouter - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ - - - ────────────────────────────────────▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - roundsolidtall - ────────────────────────────────────▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - - ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - thickvkeywide - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + |ascii|blankdashed + +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + + + ══════════════════━━━━━━━━━━━━━━━━━━ + doubleheavyhidden/none + ══════════════════━━━━━━━━━━━━━━━━━━ + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ + hkeyinnerouter + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ + + + ────────────────────────────────────▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + roundsolidtall + ────────────────────────────────────▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + thickvkeywide + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ @@ -3276,141 +3277,141 @@ font-weight: 700; } - .terminal-3714773288-matrix { + .terminal-1997861159-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3714773288-title { + .terminal-1997861159-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3714773288-r1 { fill: #e1e1e1 } - .terminal-3714773288-r2 { fill: #c5c8c6 } - .terminal-3714773288-r3 { fill: #fea62b } - .terminal-3714773288-r4 { fill: #fea62b;font-weight: bold } - .terminal-3714773288-r5 { fill: #fea62b;font-weight: bold;font-style: italic; } - .terminal-3714773288-r6 { fill: #cc555a;font-weight: bold } - .terminal-3714773288-r7 { fill: #1e1e1e } - .terminal-3714773288-r8 { fill: #1e1e1e;text-decoration: underline; } - .terminal-3714773288-r9 { fill: #fea62b;text-decoration: underline; } - .terminal-3714773288-r10 { fill: #4b4e55;text-decoration: underline; } - .terminal-3714773288-r11 { fill: #4ebf71 } - .terminal-3714773288-r12 { fill: #b93c5b } + .terminal-1997861159-r1 { fill: #e1e1e1 } + .terminal-1997861159-r2 { fill: #c5c8c6 } + .terminal-1997861159-r3 { fill: #fea62b } + .terminal-1997861159-r4 { fill: #fea62b;font-weight: bold } + .terminal-1997861159-r5 { fill: #fea62b;font-weight: bold;font-style: italic; } + .terminal-1997861159-r6 { fill: #cc555a;font-weight: bold } + .terminal-1997861159-r7 { fill: #1e1e1e } + .terminal-1997861159-r8 { fill: #1e1e1e;text-decoration: underline; } + .terminal-1997861159-r9 { fill: #fea62b;text-decoration: underline; } + .terminal-1997861159-r10 { fill: #4b4e55;text-decoration: underline; } + .terminal-1997861159-r11 { fill: #4ebf71 } + .terminal-1997861159-r12 { fill: #b93c5b } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - BorderSubTitleAlignAll + BorderSubTitleAlignAll - + - - - - Border titleLef…▁▁▁▁Left▁▁▁▁ - This is the story ofa Pythondeveloper that - Border subtitleCen…▔▔▔▔@@@▔▔▔▔▔ - - - - - - +--------------+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 titleLef…▁▁▁▁Left▁▁▁▁ + This is the story ofa Pythondeveloper that + Border subtitleCen…▔▔▔▔@@@▔▔▔▔▔ + + + + + + +--------------+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… + @@ -3441,134 +3442,134 @@ font-weight: 700; } - .terminal-1044533580-matrix { + .terminal-1601354540-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1044533580-title { + .terminal-1601354540-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1044533580-r1 { fill: #e1e1e1 } - .terminal-1044533580-r2 { fill: #c5c8c6 } - .terminal-1044533580-r3 { fill: #fea62b } - .terminal-1044533580-r4 { fill: #ffffff } - .terminal-1044533580-r5 { fill: #1e1e1e } + .terminal-1601354540-r1 { fill: #e1e1e1 } + .terminal-1601354540-r2 { fill: #c5c8c6 } + .terminal-1601354540-r3 { fill: #fea62b } + .terminal-1601354540-r4 { fill: #ffffff } + .terminal-1601354540-r5 { fill: #1e1e1e } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - BorderSubtitleAlignApp + BorderSubtitleAlignApp - - - - - ──────────────────────────────────────────────────────────────────────────── - - My subtitle is on the left. - - < Left─────────────────────────────────────────────────────────────────── - - ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - - My subtitle is centered - - ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍Centered!╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - My subtitle is on the right - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁Right > - - - - - + + + + + ──────────────────────────────────────────────────────────────────────────── + + My subtitle is on the left. + +  < Left ─────────────────────────────────────────────────────────────────── + + ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + + My subtitle is centered + + ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ Centered! ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + My subtitle is on the right + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ Right >  + + + + + @@ -3599,134 +3600,292 @@ font-weight: 700; } - .terminal-304237721-matrix { + .terminal-2047325817-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-304237721-title { + .terminal-2047325817-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-304237721-r1 { fill: #e1e1e1 } - .terminal-304237721-r2 { fill: #c5c8c6 } - .terminal-304237721-r3 { fill: #fea62b } - .terminal-304237721-r4 { fill: #ffffff } - .terminal-304237721-r5 { fill: #1e1e1e } + .terminal-2047325817-r1 { fill: #e1e1e1 } + .terminal-2047325817-r2 { fill: #c5c8c6 } + .terminal-2047325817-r3 { fill: #fea62b } + .terminal-2047325817-r4 { fill: #ffffff } + .terminal-2047325817-r5 { fill: #1e1e1e } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - BorderTitleAlignApp + BorderTitleAlignApp - - - - - < Left─────────────────────────────────────────────────────────────────── - - My title is on the left. - - ──────────────────────────────────────────────────────────────────────────── - - ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍Centered!╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - - My title is centered - - ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔Right > - - My title is on the right - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - - - - + + + + +  < Left ─────────────────────────────────────────────────────────────────── + + My title is on the left. + + ──────────────────────────────────────────────────────────────────────────── + + ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ Centered! ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + + My title is centered + + ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ Right >  + + My title is on the right + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + + + + + + + ''' +# --- +# name: test_css_property[border_title_colors.py] + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BorderTitleApp + + + + + + + + + + + + + + + +  Textual Rocks ━━━━━━━━━━━━━ + + + + + Hello, World! + + + + + ━━━━━━━━━━━━━ Textual Rocks  + + + + + + @@ -3757,132 +3916,132 @@ font-weight: 700; } - .terminal-1232593861-matrix { + .terminal-3266307003-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1232593861-title { + .terminal-3266307003-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1232593861-r1 { fill: #000000 } - .terminal-1232593861-r2 { fill: #c5c8c6 } - .terminal-1232593861-r3 { fill: #ccccff } + .terminal-3266307003-r1 { fill: #000000 } + .terminal-3266307003-r2 { fill: #c5c8c6 } + .terminal-3266307003-r3 { fill: #ccccff } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - BoxSizingApp + BoxSizingApp - + - - - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - I'm using border-box! - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - I'm using content-box! - - - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - - - - + + + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + I'm using border-box! + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + I'm using content-box! + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + + + + @@ -7529,133 +7688,133 @@ font-weight: 700; } - .terminal-211150573-matrix { + .terminal-3234129636-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-211150573-title { + .terminal-3234129636-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-211150573-r1 { fill: #000000 } - .terminal-211150573-r2 { fill: #c5c8c6 } - .terminal-211150573-r3 { fill: #0000ff } - .terminal-211150573-r4 { fill: #ccccff } + .terminal-3234129636-r1 { fill: #000000 } + .terminal-3234129636-r2 { fill: #c5c8c6 } + .terminal-3234129636-r3 { fill: #0000ff } + .terminal-3234129636-r4 { fill: #ccccff } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MarginApp + MarginApp - + - - - - - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - I must not fear. - Fear is the mind-killer. - Fear is the little-death that brings total obliteration. - I will face my fear. - I will permit it to pass over me and through me. - And when it has gone past, I will turn the inner eye to see  - its path. - Where the fear has gone there will be nothing. Only I will  - remain. - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - - - - - - - + + + + + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + I must not fear. + Fear is the mind-killer. + Fear is the little-death that brings total obliteration. + I will face my fear. + I will permit it to pass over me and through me. + And when it has gone past, I will turn the inner eye to see  + its path. + Where the fear has gone there will be nothing. Only I will  + remain. + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + + + + + + + @@ -8806,133 +8965,133 @@ font-weight: 700; } - .terminal-3260169885-matrix { + .terminal-3556982422-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3260169885-title { + .terminal-3556982422-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3260169885-r1 { fill: #000000 } - .terminal-3260169885-r2 { fill: #c5c8c6 } - .terminal-3260169885-r3 { fill: #008000 } - .terminal-3260169885-r4 { fill: #cce5cc } + .terminal-3556982422-r1 { fill: #000000 } + .terminal-3556982422-r2 { fill: #c5c8c6 } + .terminal-3556982422-r3 { fill: #008000 } + .terminal-3556982422-r4 { fill: #cce5cc } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - OutlineApp + OutlineApp - + - - - - - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ear is the mind-killer. - ear is the little-death that brings total obliteration. -  will face my fear. -  will permit it to pass over me and through me. - nd when it has gone past, I will turn the inner eye to see its - ath. - here the fear has gone there will be nothing. Only I will  - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - - - - - - - - - + + + + + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ear is the mind-killer. + ear is the little-death that brings total obliteration. +  will face my fear. +  will permit it to pass over me and through me. + nd when it has gone past, I will turn the inner eye to see its + ath. + here the fear has gone there will be nothing. Only I will  + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + + + + + + + + + @@ -8963,133 +9122,133 @@ font-weight: 700; } - .terminal-3219552913-matrix { + .terminal-3019471504-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3219552913-title { + .terminal-3019471504-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3219552913-r1 { fill: #c5c8c6 } - .terminal-3219552913-r2 { fill: #0178d4 } - .terminal-3219552913-r3 { fill: #e1e1e1 } - .terminal-3219552913-r4 { fill: #1e1e1e } + .terminal-3019471504-r1 { fill: #c5c8c6 } + .terminal-3019471504-r2 { fill: #0178d4 } + .terminal-3019471504-r3 { fill: #e1e1e1 } + .terminal-3019471504-r4 { fill: #1e1e1e } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - AllOutlinesApp + AllOutlinesApp - + - - +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - |ascii|blankdashed - +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - - - ══════════════════━━━━━━━━━━━━━━━━━━ - doubleheavyhidden/none - ══════════════════━━━━━━━━━━━━━━━━━━ - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ - hkeyinnernone - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ - - - ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀──────────────────────────────────── - outerroundsolid - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄──────────────────────────────────── - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - tallvkeywide - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + |ascii|blankdashed + +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + + + ══════════════════━━━━━━━━━━━━━━━━━━ + doubleheavyhidden/none + ══════════════════━━━━━━━━━━━━━━━━━━ + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ + hkeyinnernone + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ + + + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀──────────────────────────────────── + outerroundsolid + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄──────────────────────────────────── + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + tallvkeywide + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ @@ -9277,136 +9436,136 @@ font-weight: 700; } - .terminal-3616080938-matrix { + .terminal-2990670852-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3616080938-title { + .terminal-2990670852-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3616080938-r1 { fill: #c5c8c6 } - .terminal-3616080938-r2 { fill: #000000 } - .terminal-3616080938-r3 { fill: #008000 } - .terminal-3616080938-r4 { fill: #e5f0e5 } - .terminal-3616080938-r5 { fill: #036a03 } - .terminal-3616080938-r6 { fill: #14191f } + .terminal-2990670852-r1 { fill: #c5c8c6 } + .terminal-2990670852-r2 { fill: #000000 } + .terminal-2990670852-r3 { fill: #008000 } + .terminal-2990670852-r4 { fill: #e5f0e5 } + .terminal-2990670852-r5 { fill: #036a03 } + .terminal-2990670852-r6 { fill: #14191f } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - OverflowApp + OverflowApp - + - - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - I must not fear.I must not fear. - Fear is the mind-killer.Fear is the mind-killer. - Fear is the little-death that Fear is the little-death that  - brings total obliteration.brings total obliteration. - I will face my fear.I will face my fear. - I will permit it to pass over meI will permit it to pass over me  - and through me.and through me. - And when it has gone past, I And when it has gone past, I will  - will turn the inner eye to see turn the inner eye to see its  - its path.▁▁path. - Where the fear has gone there Where the fear has gone there will - will be nothing. Only I will be nothing. Only I will remain. - remain.▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁I must not fear. - I must not fear.Fear is the mind-killer. - Fear is the mind-killer.Fear is the little-death that  - Fear is the little-death that brings total obliteration. - brings total obliteration.I will face my fear. - I will face my fear.I will permit it to pass over me  - I will permit it to pass over meand through me. + + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + I must not fear.I must not fear. + Fear is the mind-killer.Fear is the mind-killer. + Fear is the little-death that Fear is the little-death that  + brings total obliteration.brings total obliteration. + I will face my fear.I will face my fear. + I will permit it to pass over meI will permit it to pass over me  + and through me.and through me. + And when it has gone past, I And when it has gone past, I will  + will turn the inner eye to see turn the inner eye to see its  + its path.▁▁path. + Where the fear has gone there Where the fear has gone there will + will be nothing. Only I will be nothing. Only I will remain. + remain.▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁I must not fear. + I must not fear.Fear is the mind-killer. + Fear is the mind-killer.Fear is the little-death that  + Fear is the little-death that brings total obliteration. + brings total obliteration.I will face my fear. + I will face my fear.I will permit it to pass over me  + I will permit it to pass over meand through me. @@ -13410,169 +13569,169 @@ font-weight: 700; } - .terminal-832370059-matrix { + .terminal-1975973248-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-832370059-title { + .terminal-1975973248-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-832370059-r1 { fill: #c5c8c6 } - .terminal-832370059-r2 { fill: #e3e3e3 } - .terminal-832370059-r3 { fill: #e1e1e1 } - .terminal-832370059-r4 { fill: #23568b } - .terminal-832370059-r5 { fill: #e2e2e2 } - .terminal-832370059-r6 { fill: #004578 } - .terminal-832370059-r7 { fill: #14191f } - .terminal-832370059-r8 { fill: #262626 } - .terminal-832370059-r9 { fill: #e2e2e2;font-weight: bold;text-decoration: underline; } - .terminal-832370059-r10 { fill: #e2e2e2;font-weight: bold } - .terminal-832370059-r11 { fill: #7ae998 } - .terminal-832370059-r12 { fill: #4ebf71;font-weight: bold } - .terminal-832370059-r13 { fill: #008139 } - .terminal-832370059-r14 { fill: #dde8f3;font-weight: bold } - .terminal-832370059-r15 { fill: #ddedf9 } + .terminal-1975973248-r1 { fill: #c5c8c6 } + .terminal-1975973248-r2 { fill: #e3e3e3 } + .terminal-1975973248-r3 { fill: #e1e1e1 } + .terminal-1975973248-r4 { fill: #23568b } + .terminal-1975973248-r5 { fill: #e2e2e2 } + .terminal-1975973248-r6 { fill: #004578 } + .terminal-1975973248-r7 { fill: #14191f } + .terminal-1975973248-r8 { fill: #262626 } + .terminal-1975973248-r9 { fill: #e2e2e2;font-weight: bold;text-decoration: underline; } + .terminal-1975973248-r10 { fill: #e2e2e2;font-weight: bold } + .terminal-1975973248-r11 { fill: #7ae998 } + .terminal-1975973248-r12 { fill: #4ebf71;font-weight: bold } + .terminal-1975973248-r13 { fill: #008139 } + .terminal-1975973248-r14 { fill: #dde8f3;font-weight: bold } + .terminal-1975973248-r15 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - Textual Demo + Textual Demo - + - - Textual Demo - ▅▅ - - TOP - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▃▃ - - Widgets - Textual Demo - - Welcome! Textual is a framework for creating sophisticated - Rich contentapplications with the terminal. - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Start - CSS▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - - - - - - - - - -                           Widgets                            -  CTRL+C  Quit  CTRL+B  Sidebar  CTRL+T  Toggle Dark mode  CTRL+S  Screenshot  F1  Notes  + + Textual Demo + ▅▅ + + TOP + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▃▃ + + Widgets + Textual Demo + + Welcome! Textual is a framework for creating sophisticated + Rich contentapplications with the terminal. + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Start + CSS▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + + + + + + + + + +                           Widgets                            +  CTRL+C  Quit  CTRL+B  Sidebar  CTRL+T  Toggle Dark mode  CTRL+S  Screenshot  F1  Notes  @@ -14107,148 +14266,148 @@ font-weight: 700; } - .terminal-1506359380-matrix { + .terminal-391329861-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1506359380-title { + .terminal-391329861-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1506359380-r1 { fill: #454a50 } - .terminal-1506359380-r2 { fill: #e1e1e1 } - .terminal-1506359380-r3 { fill: #c5c8c6 } - .terminal-1506359380-r4 { fill: #e2e3e3;font-weight: bold } - .terminal-1506359380-r5 { fill: #262626 } - .terminal-1506359380-r6 { fill: #000000 } - .terminal-1506359380-r7 { fill: #e2e2e2 } - .terminal-1506359380-r8 { fill: #e3e3e3 } - .terminal-1506359380-r9 { fill: #14191f } - .terminal-1506359380-r10 { fill: #b93c5b } - .terminal-1506359380-r11 { fill: #121212 } - .terminal-1506359380-r12 { fill: #1e1e1e } - .terminal-1506359380-r13 { fill: #e2e3e3 } - .terminal-1506359380-r14 { fill: #fea62b } - .terminal-1506359380-r15 { fill: #211505;font-weight: bold } - .terminal-1506359380-r16 { fill: #211505 } - .terminal-1506359380-r17 { fill: #dde8f3;font-weight: bold } - .terminal-1506359380-r18 { fill: #ddedf9 } + .terminal-391329861-r1 { fill: #454a50 } + .terminal-391329861-r2 { fill: #e1e1e1 } + .terminal-391329861-r3 { fill: #c5c8c6 } + .terminal-391329861-r4 { fill: #e2e3e3;font-weight: bold } + .terminal-391329861-r5 { fill: #262626 } + .terminal-391329861-r6 { fill: #000000 } + .terminal-391329861-r7 { fill: #e2e2e2 } + .terminal-391329861-r8 { fill: #e3e3e3 } + .terminal-391329861-r9 { fill: #14191f } + .terminal-391329861-r10 { fill: #b93c5b } + .terminal-391329861-r11 { fill: #121212 } + .terminal-391329861-r12 { fill: #1e1e1e } + .terminal-391329861-r13 { fill: #e2e3e3 } + .terminal-391329861-r14 { fill: #fea62b } + .terminal-391329861-r15 { fill: #211505;font-weight: bold } + .terminal-391329861-r16 { fill: #211505 } + .terminal-391329861-r17 { fill: #dde8f3;font-weight: bold } + .terminal-391329861-r18 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - EasingApp + EasingApp - + - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - round▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁Animation Duration:1.0 - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - out_sine - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - out_quint - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁Welcome to Textual! - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - out_quartI must not fear. - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁Fear is the  - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔mind-killer. - out_quadFear is the  - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁little-death that  - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔brings total  - out_expoobliteration.▆▆ - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁I will face my fear. - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔I will permit it to  - out_elasticpass over me and  - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁through me. - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔And when it has gone  - out_cubic - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ CTRL+P  Focus: Duration Input  CTRL+B  Toggle Dark  + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + round▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁Animation Duration:1.0 + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + out_sine + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + out_quint + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁Welcome to Textual! + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + out_quartI must not fear. + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁Fear is the  + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔mind-killer. + out_quadFear is the  + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁little-death that  + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔brings total  + out_expoobliteration.▆▆ + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁I will face my fear. + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔I will permit it to  + out_elasticpass over me and  + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁through me. + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔And when it has gone  + out_cubic + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ CTRL+P  Focus: Duration Input  CTRL+B  Toggle Dark  @@ -17287,140 +17446,140 @@ font-weight: 700; } - .terminal-4078770422-matrix { + .terminal-1636374768-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-4078770422-title { + .terminal-1636374768-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-4078770422-r1 { fill: #e1e1e1 } - .terminal-4078770422-r2 { fill: #121212 } - .terminal-4078770422-r3 { fill: #c5c8c6 } - .terminal-4078770422-r4 { fill: #0053aa } - .terminal-4078770422-r5 { fill: #dde8f3;font-weight: bold } - .terminal-4078770422-r6 { fill: #939393;font-weight: bold } - .terminal-4078770422-r7 { fill: #24292f } - .terminal-4078770422-r8 { fill: #e2e3e3;font-weight: bold } - .terminal-4078770422-r9 { fill: #4ebf71;font-weight: bold } - .terminal-4078770422-r10 { fill: #e1e1e1;font-style: italic; } - .terminal-4078770422-r11 { fill: #e1e1e1;font-weight: bold } + .terminal-1636374768-r1 { fill: #e1e1e1 } + .terminal-1636374768-r2 { fill: #121212 } + .terminal-1636374768-r3 { fill: #c5c8c6 } + .terminal-1636374768-r4 { fill: #0053aa } + .terminal-1636374768-r5 { fill: #dde8f3;font-weight: bold } + .terminal-1636374768-r6 { fill: #939393;font-weight: bold } + .terminal-1636374768-r7 { fill: #24292f } + .terminal-1636374768-r8 { fill: #e2e3e3;font-weight: bold } + .terminal-1636374768-r9 { fill: #4ebf71;font-weight: bold } + .terminal-1636374768-r10 { fill: #e1e1e1;font-style: italic; } + .terminal-1636374768-r11 { fill: #e1e1e1;font-weight: bold } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MarkdownExampleApp + MarkdownExampleApp - + - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - Markdown Document - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - This is an example of Textual's Markdown widget. - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - Features - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Markdown syntax and extensions are supported. - - ● Typography emphasisstronginline code etc. - ● Headers - ● Lists (bullet and ordered) - ● Syntax highlighted code blocks - ● Tables! - - - - + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + Markdown Document + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + This is an example of Textual's Markdown widget. + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + Features + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Markdown syntax and extensions are supported. + + ● Typography emphasisstronginline code etc. + ● Headers + ● Lists (bullet and ordered) + ● Syntax highlighted code blocks + ● Tables! + + + + @@ -17451,145 +17610,145 @@ font-weight: 700; } - .terminal-971113808-matrix { + .terminal-873465137-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-971113808-title { + .terminal-873465137-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-971113808-r1 { fill: #c5c8c6 } - .terminal-971113808-r2 { fill: #24292f } - .terminal-971113808-r3 { fill: #121212 } - .terminal-971113808-r4 { fill: #e1e1e1 } - .terminal-971113808-r5 { fill: #e2e3e3 } - .terminal-971113808-r6 { fill: #96989b } - .terminal-971113808-r7 { fill: #0053aa } - .terminal-971113808-r8 { fill: #008139 } - .terminal-971113808-r9 { fill: #dde8f3;font-weight: bold } - .terminal-971113808-r10 { fill: #939393;font-weight: bold } - .terminal-971113808-r11 { fill: #14191f } - .terminal-971113808-r12 { fill: #e2e3e3;font-weight: bold } - .terminal-971113808-r13 { fill: #4ebf71;font-weight: bold } - .terminal-971113808-r14 { fill: #e1e1e1;font-style: italic; } - .terminal-971113808-r15 { fill: #e1e1e1;font-weight: bold } + .terminal-873465137-r1 { fill: #c5c8c6 } + .terminal-873465137-r2 { fill: #24292f } + .terminal-873465137-r3 { fill: #121212 } + .terminal-873465137-r4 { fill: #e1e1e1 } + .terminal-873465137-r5 { fill: #e2e3e3 } + .terminal-873465137-r6 { fill: #96989b } + .terminal-873465137-r7 { fill: #0053aa } + .terminal-873465137-r8 { fill: #008139 } + .terminal-873465137-r9 { fill: #dde8f3;font-weight: bold } + .terminal-873465137-r10 { fill: #939393;font-weight: bold } + .terminal-873465137-r11 { fill: #14191f } + .terminal-873465137-r12 { fill: #e2e3e3;font-weight: bold } + .terminal-873465137-r13 { fill: #4ebf71;font-weight: bold } + .terminal-873465137-r14 { fill: #e1e1e1;font-style: italic; } + .terminal-873465137-r15 { fill: #e1e1e1;font-weight: bold } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MarkdownExampleApp + MarkdownExampleApp - + - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▼  Markdown Viewer - ├──  FeaturesMarkdown Viewer - ├──  Tables - └──  Code Blocks▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - This is an example of Textual's MarkdownViewer - widget. - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▅▅ - - Features - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Markdown syntax and extensions are supported. - - ● Typography emphasisstronginline code - etc. - ● Headers - ● Lists (bullet and ordered) - ● Syntax highlighted code blocks - ● Tables! - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▼  Markdown Viewer + ├──  FeaturesMarkdown Viewer + ├──  Tables + └──  Code Blocks▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + This is an example of Textual's MarkdownViewer + widget. + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▅▅ + + Features + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Markdown syntax and extensions are supported. + + ● Typography emphasisstronginline code + etc. + ● Headers + ● Lists (bullet and ordered) + ● Syntax highlighted code blocks + ● Tables! + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + @@ -20181,135 +20340,134 @@ font-weight: 700; } - .terminal-1405474793-matrix { + .terminal-2499454553-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1405474793-title { + .terminal-2499454553-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1405474793-r1 { fill: #004578 } - .terminal-1405474793-r2 { fill: #c5c8c6 } - .terminal-1405474793-r3 { fill: #e1e1e1 } - .terminal-1405474793-r4 { fill: #23568b } - .terminal-1405474793-r5 { fill: #14191f } + .terminal-2499454553-r1 { fill: #004578 } + .terminal-2499454553-r2 { fill: #c5c8c6 } + .terminal-2499454553-r3 { fill: #e1e1e1 } + .terminal-2499454553-r4 { fill: #23568b } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MyApp + MyApp - - - - ────────────────────────────────────────────────────────────────────────────── - SPAM - SPAM - SPAM▂▂ - SPAM - SPAM - SPAM - ────────────────────────────────────────────────────────────────────────── - SPAM - SPAM▆▆ - SPAM - SPAM - SPAM - SPAM▁▁ - SPAM - SPAM - SPAM - SPAM - SPAM - SPAM - SPAM - SPAM - SPAM - ────────────────────────────────────────────────────────────────────────────── + + + + ────────────────────────────────────────────────────────────────────────────── + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM▆▆ + SPAM + ────────────────────────────────────────────────────────────────────────────── @@ -20495,134 +20653,134 @@ font-weight: 700; } - .terminal-2452225303-matrix { + .terminal-1647606097-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2452225303-title { + .terminal-1647606097-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2452225303-r1 { fill: #c5c8c6 } - .terminal-2452225303-r2 { fill: #e3e3e3 } - .terminal-2452225303-r3 { fill: #ff0000 } - .terminal-2452225303-r4 { fill: #dde2e8 } - .terminal-2452225303-r5 { fill: #ddedf9 } + .terminal-1647606097-r1 { fill: #c5c8c6 } + .terminal-1647606097-r2 { fill: #e3e3e3 } + .terminal-1647606097-r3 { fill: #ff0000 } + .terminal-1647606097-r4 { fill: #dde2e8 } + .terminal-1647606097-r5 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - ScrollViewTester + ScrollViewTester - - - - ScrollViewTester - 1────────────────────────────────────────────────────────────────────────── - Welcome to line 980 - Welcome to line 981 - Welcome to line 982 - Welcome to line 983 - Welcome to line 984 - Welcome to line 985 - Welcome to line 986 - Welcome to line 987 - Welcome to line 988 - Welcome to line 989 - Welcome to line 990 - Welcome to line 991 - Welcome to line 992 - Welcome to line 993 - Welcome to line 994 - Welcome to line 995 - Welcome to line 996 - Welcome to line 997 - Welcome to line 998 - Welcome to line 999 - ────────────────────────────────────────────────────────────────────────────── + + + + ScrollViewTester +  1 ────────────────────────────────────────────────────────────────────────── + Welcome to line 980 + Welcome to line 981 + Welcome to line 982 + Welcome to line 983 + Welcome to line 984 + Welcome to line 985 + Welcome to line 986 + Welcome to line 987 + Welcome to line 988 + Welcome to line 989 + Welcome to line 990 + Welcome to line 991 + Welcome to line 992 + Welcome to line 993 + Welcome to line 994 + Welcome to line 995 + Welcome to line 996 + Welcome to line 997 + Welcome to line 998 + Welcome to line 999 + ────────────────────────────────────────────────────────────────────────────── @@ -20813,140 +20971,140 @@ font-weight: 700; } - .terminal-4003130388-matrix { + .terminal-1328081937-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-4003130388-title { + .terminal-1328081937-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-4003130388-r1 { fill: #c5c8c6 } - .terminal-4003130388-r2 { fill: #737373 } - .terminal-4003130388-r3 { fill: #e1e1e1;font-weight: bold } - .terminal-4003130388-r4 { fill: #323232 } - .terminal-4003130388-r5 { fill: #0178d4 } - .terminal-4003130388-r6 { fill: #121212 } - .terminal-4003130388-r7 { fill: #0053aa } - .terminal-4003130388-r8 { fill: #dde8f3;font-weight: bold } - .terminal-4003130388-r9 { fill: #e1e1e1 } - .terminal-4003130388-r10 { fill: #ddedf9 } + .terminal-1328081937-r1 { fill: #c5c8c6 } + .terminal-1328081937-r2 { fill: #737373 } + .terminal-1328081937-r3 { fill: #e1e1e1;font-weight: bold } + .terminal-1328081937-r4 { fill: #323232 } + .terminal-1328081937-r5 { fill: #0178d4 } + .terminal-1328081937-r6 { fill: #121212 } + .terminal-1328081937-r7 { fill: #0053aa } + .terminal-1328081937-r8 { fill: #dde8f3;font-weight: bold } + .terminal-1328081937-r9 { fill: #e1e1e1 } + .terminal-1328081937-r10 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - TabbedApp + TabbedApp - + - - - LetoJessicaPaul - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - Lady Jessica - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Bene Gesserit and concubine of Leto, and mother of Paul and Alia. - - - - PaulAlia - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - First child - - - - - - -  L  Leto  J  Jessica  P  Paul  + + + LetoJessicaPaul + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + Lady Jessica + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Bene Gesserit and concubine of Leto, and mother of Paul and Alia. + + + + PaulAlia + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + First child + + + + + + +  L  Leto  J  Jessica  P  Paul  diff --git a/tests/test_border.py b/tests/test_border.py index fe02d5e28..5d2518244 100644 --- a/tests/test_border.py +++ b/tests/test_border.py @@ -105,7 +105,7 @@ def test_render_border_label_empty_label_skipped( assert [] == list( render_border_label( - Text(""), + (Text(""), Style()), True, "round", width, @@ -142,7 +142,7 @@ def test_render_border_label_skipped_if_narrow( assert [] == list( render_border_label( - Text.from_markup(label), + (Text.from_markup(label), Style()), True, "round", width, @@ -180,11 +180,10 @@ def test_render_border_label_wide_plain(label: str): True, True, ) - left, original_text, right = render_border_label(Text.from_markup(label), *args) + segments = render_border_label((Text.from_markup(label), Style()), *args) + (segment,) = segments - assert left == _BLANK_SEGMENT - assert right == _BLANK_SEGMENT - assert original_text == Segment(label, _EMPTY_STYLE) + assert segment == Segment(f" {label} ", None) @pytest.mark.parametrize( @@ -200,7 +199,7 @@ def test_render_border_empty_text_with_markup(label: str): """Test label rendering if there is no text but some markup.""" assert [] == list( render_border_label( - Text.from_markup(label), + (Text.from_markup(label), Style()), True, "round", 999, @@ -222,7 +221,7 @@ def test_render_border_label(): # Implicit test on the number of segments returned: blank1, what, is_up, with_you, blank2 = render_border_label( - Text.from_markup(label), + (Text.from_markup(label), Style()), True, "round", 9999, @@ -251,7 +250,7 @@ def test_render_border_label(): assert with_you == expected_with_you blank1, what, blank2 = render_border_label( - Text.from_markup(label), + (Text.from_markup(label), Style()), True, "round", 5 + 4, # 5 where "What…" fits + 2 for the blank spaces + 2 for the corners. diff --git a/tests/test_styles_cache.py b/tests/test_styles_cache.py index e1cb37c65..6567de4ea 100644 --- a/tests/test_styles_cache.py +++ b/tests/test_styles_cache.py @@ -72,8 +72,8 @@ def test_border(): Color.parse("green"), content.__getitem__, Console(), - "", - "", + None, + None, content_size=Size(3, 3), ) @@ -106,8 +106,8 @@ def test_padding(): Color.parse("green"), content.__getitem__, Console(), - "", - "", + None, + None, content_size=Size(3, 3), ) @@ -141,8 +141,8 @@ def test_padding_border(): Color.parse("green"), content.__getitem__, Console(), - "", - "", + None, + None, content_size=Size(3, 3), ) @@ -177,8 +177,8 @@ def test_outline(): Color.parse("green"), content.__getitem__, Console(), - "", - "", + None, + None, content_size=Size(3, 3), ) @@ -208,8 +208,8 @@ def test_crop(): Color.parse("green"), content.__getitem__, Console(), - "", - "", + None, + None, content_size=Size(3, 3), crop=Region(2, 2, 3, 3), ) @@ -247,8 +247,8 @@ def test_dirty_cache() -> None: Color.parse("green"), get_content_line, Console(), - "", - "", + None, + None, content_size=Size(3, 3), ) assert rendered_lines == [0, 1, 2] @@ -275,8 +275,8 @@ def test_dirty_cache() -> None: Color.parse("green"), get_content_line, Console(), - "", - "", + None, + None, content_size=Size(3, 3), ) assert rendered_lines == [] @@ -294,8 +294,8 @@ def test_dirty_cache() -> None: Color.parse("green"), get_content_line, Console(), - "", - "", + None, + None, content_size=Size(3, 3), ) assert rendered_lines == [0, 1]