diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e3a58db02..c27597851f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,23 +18,24 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - The DataTable cursor is now scrolled into view when the cursor coordinate is changed programmatically https://github.com/Textualize/textual/issues/2459 - run_worker exclusive parameter is now `False` by default https://github.com/Textualize/textual/pull/2470 - Added `always_update` as an optional argument for `reactive.var` -- `TabbedContent` now takes kwargs `id`, `name`, `classes`, and `disabled`, upon initialization, like other widgets https://github.com/Textualize/textual/pull/2497 +- Made Binding description default to empty string, which is equivalent to show=False https://github.com/Textualize/textual/pull/2501 +- Modified Message to allow it to be used as a dataclass https://github.com/Textualize/textual/pull/2501 ### Added +- Experimental: Added "overlay" rule https://github.com/Textualize/textual/pull/2501 +- Experimental: Added "constrain" rule https://github.com/Textualize/textual/pull/2501 +- Added textual.widgets.Select https://github.com/Textualize/textual/pull/2501 +- Added Region.translate_inside https://github.com/Textualize/textual/pull/2501 +- `TabbedContent` now takes kwargs `id`, `name`, `classes`, and `disabled`, upon initialization, like other widgets https://github.com/Textualize/textual/pull/2497 - Method `DataTable.move_cursor` https://github.com/Textualize/textual/issues/2472 - -### Added - - Added `OptionList.add_options` https://github.com/Textualize/textual/pull/2508 - -### Added - - Added `TreeNode.is_root` https://github.com/Textualize/textual/pull/2510 - Added `TreeNode.remove_children` https://github.com/Textualize/textual/pull/2510 - Added `TreeNode.remove` https://github.com/Textualize/textual/pull/2510 + ## [0.23.0] - 2023-05-03 ### Fixed diff --git a/docs/examples/widgets/select.css b/docs/examples/widgets/select.css new file mode 100644 index 0000000000..2be4244304 --- /dev/null +++ b/docs/examples/widgets/select.css @@ -0,0 +1,11 @@ +Screen { + align: center top; +} + +Select { + width: 60; + margin: 2; +} +Input { + width: 60; +} diff --git a/docs/examples/widgets/select_widget.py b/docs/examples/widgets/select_widget.py new file mode 100644 index 0000000000..73b02c25b9 --- /dev/null +++ b/docs/examples/widgets/select_widget.py @@ -0,0 +1,26 @@ +from textual import on +from textual.app import App, ComposeResult +from textual.widgets import Header, Select + +LINES = """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.""".splitlines() + + +class SelectApp(App): + CSS_PATH = "select.css" + + def compose(self) -> ComposeResult: + yield Header() + yield Select((line, line) for line in LINES) + + @on(Select.Changed) + def select_changed(self, event: Select.Changed) -> None: + self.title = str(event.value) + + +if __name__ == "__main__": + app = SelectApp() + app.run() diff --git a/docs/widget_gallery.md b/docs/widget_gallery.md index fe4ea065dd..04e6fada27 100644 --- a/docs/widget_gallery.md +++ b/docs/widget_gallery.md @@ -188,6 +188,16 @@ A collection of radio buttons, that enforces uniqueness. ```{.textual path="docs/examples/widgets/radio_set.py"} ``` +## Select + +Select from a number of possible options. + +[Select reference](./widgets/select.md){ .md-button .md-button--primary } + +```{.textual path="docs/examples/widgets/select_widget.py" press="tab,enter,down,down"} +``` + + ## Static Displays simple static content. Typically used as a base class. diff --git a/docs/widgets/select.md b/docs/widgets/select.md new file mode 100644 index 0000000000..31a36989c1 --- /dev/null +++ b/docs/widgets/select.md @@ -0,0 +1,90 @@ +# Select + +!!! tip "Added in version 0.24.0" + +A Select widget is a compact control to allow the user to select between a number of possible options. + + +- [X] Focusable +- [ ] Container + + +The options in a select control may be passed in to the constructor or set later with [set_options][textual.widgets.Select.set_options]. +Options should be given as a sequence of tuples consisting of two values: the first is the string (or [Rich Renderable](https://rich.readthedocs.io/en/latest/protocol.html)) to display in the control and list of options, the second is the value of option. + +The value of the currently selected option is stored in the `value` attribute of the widget, and the `value` attribute of the [Changed][textual.widgets.Select.Changed] message. + + +## Typing + +The `Select` control is a typing Generic which allows you to set the type of the option values. +For instance, if the data type for your values is an integer, you would type the widget as follows: + +```python +options = [("First", 1), ("Second", 2)] +my_select: Select[int] = Select(options) +``` + +!!! note + + Typing is entirely optional. + + If you aren't familiar with typing or don't want to worry about it right now, feel free to ignore it. + +## Example + +The following example presents a `Select` with a number of options. + +=== "Output" + + ```{.textual path="docs/examples/widgets/select_widget.py"} + ``` + +=== "Output (expanded)" + + ```{.textual path="docs/examples/widgets/select_widget.py" press="tab,enter,down,down"} + ``` + + +=== "select_widget.py" + + ```python + --8<-- "docs/examples/widgets/select_widget.py" + ``` + +=== "select.css" + + ```sass + --8<-- "docs/examples/widgets/select.css" + ``` + +## Messages + +- [Select.Changed][textual.widgets.Select.Changed] + + +## Reactive attributes + + +| Name | Type | Default | Description | +| ---------- | -------------------- | ------- | ----------------------------------- | +| `expanded` | `bool` | `False` | True to expand the options overlay. | +| `value` | `SelectType \| None` | `None` | Current value of the Select. | + + +## Bindings + +The Select widget defines the following bindings: + +::: textual.widgets.Select.BINDINGS + options: + show_root_heading: false + show_root_toc_entry: false + + +--- + + +::: textual.widgets.Select + options: + heading_level: 2 diff --git a/mkdocs-nav.yml b/mkdocs-nav.yml index 4a0fc2cc4e..6688f3ca3c 100644 --- a/mkdocs-nav.yml +++ b/mkdocs-nav.yml @@ -149,6 +149,7 @@ nav: - "widgets/progress_bar.md" - "widgets/radiobutton.md" - "widgets/radioset.md" + - "widgets/select.md" - "widgets/static.md" - "widgets/switch.md" - "widgets/tabbed_content.md" diff --git a/src/textual/_arrange.py b/src/textual/_arrange.py index f1035688e8..b415d89838 100644 --- a/src/textual/_arrange.py +++ b/src/textual/_arrange.py @@ -35,7 +35,7 @@ def arrange( dock_layers: defaultdict[str, list[Widget]] = defaultdict(list) for child in children: if child.display: - dock_layers[child.styles.layer or "default"].append(child) + dock_layers[child.layer].append(child) width, height = size @@ -121,9 +121,14 @@ def arrange( if placement_offset: layout_placements = [ _WidgetPlacement( - _region + placement_offset, margin, layout_widget, order, fixed + _region + placement_offset, + margin, + layout_widget, + order, + fixed, + overlay, ) - for _region, margin, layout_widget, order, fixed in layout_placements + for _region, margin, layout_widget, order, fixed, overlay in layout_placements ] placements.extend(layout_placements) diff --git a/src/textual/_compositor.py b/src/textual/_compositor.py index 1ec7f5c2a6..61e4f3de87 100644 --- a/src/textual/_compositor.py +++ b/src/textual/_compositor.py @@ -512,6 +512,8 @@ def _arrange_root( add_new_widget = widgets.add layer_order: int = 0 + no_clip = size.region + def add_widget( widget: Widget, virtual_region: Region, @@ -586,12 +588,13 @@ def add_widget( layers_to_index = { layer_name: index for index, layer_name in enumerate(_layers) } + get_layer_index = layers_to_index.get scroll_spacing = arrange_result.scroll_spacing # Add all the widgets - for sub_region, margin, sub_widget, z, fixed in reversed( + for sub_region, margin, sub_widget, z, fixed, overlay in reversed( placements ): layer_index = get_layer_index(sub_widget.layer, 0) @@ -608,13 +611,23 @@ def add_widget( widget_order = order + ((layer_index, z, layer_order),) + if overlay: + constrain = sub_widget.styles.constrain + if constrain != "none": + # Constrain to avoid clipping + widget_region = widget_region.translate_inside( + no_clip, + constrain in ("x", "both"), + constrain in ("y", "both"), + ) + add_widget( sub_widget, sub_region, widget_region, - widget_order, + ((1, 0, 0),) if overlay else widget_order, layer_order, - sub_clip, + no_clip if overlay else sub_clip, visible, ) @@ -991,13 +1004,9 @@ def _render_chops( first_cut, last_cut = render_region.column_span final_cuts = [cut for cut in cuts[y] if (last_cut >= cut >= first_cut)] - if len(final_cuts) <= 2: - # Two cuts, which means the entire line - cut_strips = [strip] - else: - render_x = render_region.x - relative_cuts = [cut - render_x for cut in final_cuts[1:]] - cut_strips = strip.divide(relative_cuts) + render_x = render_region.x + relative_cuts = [cut - render_x for cut in final_cuts[1:]] + cut_strips = strip.divide(relative_cuts) # Since we are painting front to back, the first segments for a cut "wins" get_chops_line = chops_line.get diff --git a/src/textual/_layout.py b/src/textual/_layout.py index 9054435bfb..e0061c1484 100644 --- a/src/textual/_layout.py +++ b/src/textual/_layout.py @@ -36,6 +36,7 @@ def spatial_map(self) -> SpatialMap[WidgetPlacement]: ( placement.region.grow(placement.margin), placement.fixed, + placement.overlay, placement, ) for placement in self.placements @@ -73,6 +74,7 @@ class WidgetPlacement(NamedTuple): widget: Widget order: int = 0 fixed: bool = False + overlay: bool = False class Layout(ABC): diff --git a/src/textual/_spatial_map.py b/src/textual/_spatial_map.py index 48b96ab39c..af38065dd8 100644 --- a/src/textual/_spatial_map.py +++ b/src/textual/_spatial_map.py @@ -57,7 +57,7 @@ def _region_to_grid_coordinates(self, region: Region) -> Iterable[GridCoordinate ) def insert( - self, regions_and_values: Iterable[tuple[Region, bool, ValueType]] + self, regions_and_values: Iterable[tuple[Region, bool, bool, ValueType]] ) -> None: """Insert values into the Spatial map. @@ -71,8 +71,9 @@ def insert( get_grid_list = self._map.__getitem__ _region_to_grid = self._region_to_grid_coordinates total_region = self.total_region - for region, fixed, value in regions_and_values: - total_region = total_region.union(region) + for region, fixed, overlay, value in regions_and_values: + if not overlay: + total_region = total_region.union(region) if fixed: append_fixed(value) else: diff --git a/src/textual/binding.py b/src/textual/binding.py index 36498fd20c..87f2c608b1 100644 --- a/src/textual/binding.py +++ b/src/textual/binding.py @@ -18,7 +18,7 @@ if TYPE_CHECKING: from typing_extensions import TypeAlias -BindingType: TypeAlias = "Binding | tuple[str, str, str]" +BindingType: TypeAlias = "Binding | tuple[str, str] | tuple[str, str, str]" class BindingError(Exception): @@ -41,7 +41,7 @@ class Binding: """Key to bind. This can also be a comma-separated list of keys to map multiple keys to a single action.""" action: str """Action to bind to.""" - description: str + description: str = "" """Description of action.""" show: bool = True """Show the action in Footer, or False to hide.""" @@ -74,9 +74,9 @@ def make_bindings(bindings: Iterable[BindingType]) -> Iterable[Binding]: for binding in bindings: # If it's a tuple of length 3, convert into a Binding first if isinstance(binding, tuple): - if len(binding) != 3: + if len(binding) not in (2, 3): raise BindingError( - f"BINDINGS must contain a tuple of three strings, not {binding!r}" + f"BINDINGS must contain a tuple of two or three strings, not {binding!r}" ) binding = Binding(*binding) @@ -95,7 +95,7 @@ def make_bindings(bindings: Iterable[BindingType]) -> Iterable[Binding]: key=key, action=binding.action, description=binding.description, - show=binding.show, + show=bool(binding.description and binding.show), key_display=binding.key_display, priority=binding.priority, ) @@ -165,7 +165,7 @@ def bind( key, action, description, - show=show, + show=bool(description and show), key_display=key_display, priority=priority, ) diff --git a/src/textual/css/_styles_builder.py b/src/textual/css/_styles_builder.py index f385063119..d5d8048cc6 100644 --- a/src/textual/css/_styles_builder.py +++ b/src/textual/css/_styles_builder.py @@ -39,9 +39,11 @@ VALID_ALIGN_VERTICAL, VALID_BORDER, VALID_BOX_SIZING, + VALID_CONSTRAIN, VALID_DISPLAY, VALID_EDGE, VALID_OVERFLOW, + VALID_OVERLAY, VALID_SCROLLBAR_GUTTER, VALID_STYLE_FLAGS, VALID_TEXT_ALIGN, @@ -1003,6 +1005,30 @@ def process_grid_size(self, name: str, tokens: list[Token]) -> None: else: self.error(name, tokens[0], "expected two integers here") + def process_overlay(self, name: str, tokens: list[Token]) -> None: + try: + value = self._process_enum(name, tokens, VALID_OVERLAY) + except StyleValueError: + self.error( + name, + tokens[0], + string_enum_help_text(name, VALID_OVERLAY, context="css"), + ) + else: + self.styles._rules[name] = value # type: ignore + + def process_constrain(self, name: str, tokens: list[Token]) -> None: + try: + value = self._process_enum(name, tokens, VALID_CONSTRAIN) + except StyleValueError: + self.error( + name, + tokens[0], + string_enum_help_text(name, VALID_CONSTRAIN, context="css"), + ) + else: + self.styles._rules[name] = value # type: ignore + def _get_suggested_property_name_for_rule(self, rule_name: str) -> str | None: """ Returns a valid CSS property "Python" name, or None if no close matches could be found. diff --git a/src/textual/css/constants.py b/src/textual/css/constants.py index 1c7fa9f519..4cfa9580bb 100644 --- a/src/textual/css/constants.py +++ b/src/textual/css/constants.py @@ -69,6 +69,7 @@ "focus", "hover", } - +VALID_OVERLAY: Final = {"none", "screen"} +VALID_CONSTRAIN: Final = {"x", "y", "both", "none"} NULL_SPACING: Final = Spacing.all(0) diff --git a/src/textual/css/query.py b/src/textual/css/query.py index e63177886b..ce966d6b18 100644 --- a/src/textual/css/query.py +++ b/src/textual/css/query.py @@ -153,9 +153,8 @@ def __getitem__(self, index: int | slice) -> QueryType | list[QueryType]: return self.nodes[index] def __rich_repr__(self) -> rich.repr.Result: - yield self.node if self._filters: - yield "filter", " AND ".join( + yield "query", " AND ".join( ",".join(selector.css for selector in selectors) for selectors in self._filters ) @@ -214,6 +213,7 @@ def first( Returns: The matching Widget. """ + _rich_traceback_omit = True if self.nodes: first = self.nodes[0] if expect_type is not None: @@ -250,6 +250,7 @@ def only_one( Returns: The matching Widget. """ + _rich_traceback_omit = True # Call on first to get the first item. Here we'll use all of the # testing and checking it provides. the_one = self.first(expect_type) if expect_type is not None else self.first() diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py index 6eef8b1f19..dc73265da1 100644 --- a/src/textual/css/styles.py +++ b/src/textual/css/styles.py @@ -39,8 +39,10 @@ VALID_ALIGN_HORIZONTAL, VALID_ALIGN_VERTICAL, VALID_BOX_SIZING, + VALID_CONSTRAIN, VALID_DISPLAY, VALID_OVERFLOW, + VALID_OVERLAY, VALID_SCROLLBAR_GUTTER, VALID_TEXT_ALIGN, VALID_VISIBILITY, @@ -52,9 +54,11 @@ AlignHorizontal, AlignVertical, BoxSizing, + Constrain, Display, Edge, Overflow, + Overlay, ScrollbarGutter, Specificity3, Specificity6, @@ -177,6 +181,9 @@ class RulesMap(TypedDict, total=False): border_subtitle_background: Color border_subtitle_style: Style + overlay: Overlay + constrain: Constrain + RULE_NAMES = list(RulesMap.__annotations__.keys()) RULE_NAMES_SET = frozenset(RULE_NAMES) @@ -223,7 +230,7 @@ class StylesBase(ABC): node: DOMNode | None = None display = StringEnumProperty( - VALID_DISPLAY, "block", layout=True, refresh_parent=True + VALID_DISPLAY, "block", layout=True, refresh_parent=True, refresh_children=True ) visibility = StringEnumProperty( VALID_VISIBILITY, "visible", layout=True, refresh_parent=True @@ -341,6 +348,11 @@ class StylesBase(ABC): border_subtitle_background = ColorProperty(Color(0, 0, 0, 0)) border_subtitle_style = StyleFlagsProperty() + overlay = StringEnumProperty( + VALID_OVERLAY, "none", layout=True, refresh_parent=True + ) + constrain = StringEnumProperty(VALID_CONSTRAIN, "none") + def __textual_animation__( self, attribute: str, @@ -1024,7 +1036,10 @@ def append_declaration(name: str, value: str) -> None: ) if "border_subtitle_text_style" in rules: append_declaration("subtitle-text-style", str(self.border_subtitle_style)) - + if "overlay" in rules: + append_declaration("overlay", str(self.overlay)) + if "constrain" in rules: + append_declaration("constrain", str(self.constrain)) lines.sort() return lines diff --git a/src/textual/css/types.py b/src/textual/css/types.py index 395b549e05..c723e0b5fa 100644 --- a/src/textual/css/types.py +++ b/src/textual/css/types.py @@ -37,6 +37,8 @@ Overflow = Literal["scroll", "hidden", "auto"] EdgeStyle = Tuple[EdgeType, Color] TextAlign = Literal["left", "start", "center", "right", "end", "justify"] +Constrain = Literal["none", "x", "y", "both"] +Overlay = Literal["none", "screen"] Specificity3 = Tuple[int, int, int] Specificity6 = Tuple[int, int, int, int, int, int] diff --git a/src/textual/dom.py b/src/textual/dom.py index ef73557f39..e136a55883 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -1058,6 +1058,7 @@ def query_one( Returns: A widget matching the selector. """ + _rich_traceback_omit = True from .css.query import DOMQuery if isinstance(selector, str): diff --git a/src/textual/geometry.py b/src/textual/geometry.py index d111cb4aab..706e264f5f 100644 --- a/src/textual/geometry.py +++ b/src/textual/geometry.py @@ -867,6 +867,44 @@ def split_horizontal(self, cut: int) -> tuple[Region, Region]: Region(x, y + cut, width, height - cut), ) + def translate_inside( + self, container: Region, x_axis: bool = True, y_axis: bool = True + ) -> Region: + """Translate this region, so it fits within a container. + + This will ensure that there is as little overlap as possible. + The top left of the returned region is guaranteed to be within the container. + + ``` + ┌──────────────────┐ ┌──────────────────┐ + │ container │ │ container │ + │ │ │ ┌─────────────┤ + │ │ ──▶ │ │ return │ + │ ┌──────────┴──┐ │ │ │ + │ │ self │ │ │ │ + └───────┤ │ └────┴─────────────┘ + │ │ + └─────────────┘ + ``` + + + Args: + container: A container region. + x_axis: Allow translation of X axis. + y_axis: Allow translation of Y axis. + + Returns: + A new region with same dimensions that fits with inside container. + """ + x1, y1, width1, height1 = container + x2, y2, width2, height2 = self + return Region( + max(min(x2, x1 + width1 - width2), x1) if x_axis else x2, + max(min(y2, y1 + height1 - height2), y1) if y_axis else y2, + width2, + height2, + ) + class Spacing(NamedTuple): """The spacing around a renderable, such as padding and border diff --git a/src/textual/layouts/horizontal.py b/src/textual/layouts/horizontal.py index 5b5c8fdae4..d2fb3146ec 100644 --- a/src/textual/layouts/horizontal.py +++ b/src/textual/layouts/horizontal.py @@ -70,6 +70,7 @@ def arrange( _Region = Region _WidgetPlacement = WidgetPlacement for widget, box_model, margin in zip(children, box_models, margins): + overlay = widget.styles.overlay == "screen" content_width, content_height, box_margin = box_model offset_y = box_margin.top next_x = x + content_width @@ -79,7 +80,10 @@ def arrange( max_height = max( max_height, content_height + offset_y + box_model.margin.bottom ) - add_placement(_WidgetPlacement(region, box_model.margin, widget, 0)) - x = next_x + margin + add_placement( + _WidgetPlacement(region, box_model.margin, widget, 0, False, overlay) + ) + if not overlay: + x = next_x + margin return placements, set(displayed_children) diff --git a/src/textual/layouts/vertical.py b/src/textual/layouts/vertical.py index 148c1854fa..0001efdb69 100644 --- a/src/textual/layouts/vertical.py +++ b/src/textual/layouts/vertical.py @@ -64,13 +64,26 @@ def arrange( _Region = Region _WidgetPlacement = WidgetPlacement + for widget, box_model, margin in zip(children, box_models, margins): + overlay = widget.styles.overlay == "screen" content_width, content_height, box_margin = box_model next_y = y + content_height + region = _Region( box_margin.left, int(y), int(content_width), int(next_y) - int(y) ) - add_placement(_WidgetPlacement(region, box_model.margin, widget, 0)) - y = next_y + margin + add_placement( + _WidgetPlacement( + region, + box_model.margin, + widget, + 0, + False, + overlay, + ) + ) + if not overlay: + y = next_y + margin return placements, set(children) diff --git a/src/textual/message.py b/src/textual/message.py index 2a706b4480..cdb731ad13 100644 --- a/src/textual/message.py +++ b/src/textual/message.py @@ -38,6 +38,10 @@ class Message: namespace: ClassVar[str] = "" # Namespace to disambiguate messages def __init__(self) -> None: + self.__post_init__() + + def __post_init__(self) -> None: + """Allow dataclasses to initialize the object.""" self._sender: MessageTarget | None = active_message_pump.get(None) self.time: float = _time.get_time() self._forwarded = False @@ -48,7 +52,6 @@ def __init__(self) -> None: f"on_{self.namespace}_{name}" if self.namespace else f"on_{name}" ) self._prevent: set[type[Message]] = set() - super().__init__() def __rich_repr__(self) -> rich.repr.Result: yield from () @@ -71,6 +74,7 @@ def __init_subclass__( @property def is_forwarded(self) -> bool: + """Has the message been forwarded?""" return self._forwarded @property diff --git a/src/textual/message_pump.py b/src/textual/message_pump.py index 18578606af..1466f11619 100644 --- a/src/textual/message_pump.py +++ b/src/textual/message_pump.py @@ -648,6 +648,7 @@ def post_message(self, message: Message) -> bool: Returns: `True` if the messages was processed, `False` if it wasn't. """ + _rich_traceback_omit = True if self._closing or self._closed: return False if not self.check_message_enabled(message): diff --git a/src/textual/widget.py b/src/textual/widget.py index b4e9e506ee..4fc364ebf9 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -1569,12 +1569,13 @@ def layers(self) -> tuple[str, ...]: Returns: Tuple of layer names. """ + layers: tuple[str, ...] = ("default",) for node in self.ancestors_with_self: if not isinstance(node, Widget): break if node.styles.has_rule("layers"): - return node.styles.layers - return ("default",) + layers = node.styles.layers + return layers @property def link_style(self) -> Style: @@ -1637,8 +1638,11 @@ def _set_dirty(self, *regions: Region) -> None: self._dirty_regions.clear() self._repaint_regions.clear() self._styles_cache.clear() - self._dirty_regions.add(self.outer_size.region) - self._repaint_regions.add(self.outer_size.region) + + outer_size = self.outer_size + self._dirty_regions.add(outer_size.region) + if outer_size: + self._repaint_regions.add(outer_size.region) def _exchange_repaint_regions(self) -> Collection[Region]: """Get a copy of the regions which need a repaint, and clear internal cache. @@ -2921,6 +2925,13 @@ def post_message(self, message: Message) -> bool: Returns: True if the message was posted, False if this widget was closed / closing. """ + _rich_traceback_omit = True + # Catch a common error. + # This will error anyway, but at least we can offer a helpful message here. + if not hasattr(message, "_prevent"): + raise RuntimeError( + f"{type(message)!r} is missing expected attributes; did you forget to call super().__init__() in the constructor?" + ) if constants.DEBUG and not self.is_running and not message.no_dispatch: try: diff --git a/src/textual/widgets/__init__.py b/src/textual/widgets/__init__.py index ee852564a0..04e00e53ba 100644 --- a/src/textual/widgets/__init__.py +++ b/src/textual/widgets/__init__.py @@ -29,6 +29,7 @@ from ._progress_bar import ProgressBar from ._radio_button import RadioButton from ._radio_set import RadioSet + from ._select import Select from ._static import Static from ._switch import Switch from ._tabbed_content import TabbedContent, TabPane @@ -59,6 +60,7 @@ "ProgressBar", "RadioButton", "RadioSet", + "Select", "Static", "Switch", "Tab", diff --git a/src/textual/widgets/__init__.pyi b/src/textual/widgets/__init__.pyi index 9ceffb7425..27e7f4ed16 100644 --- a/src/textual/widgets/__init__.pyi +++ b/src/textual/widgets/__init__.pyi @@ -19,6 +19,7 @@ from ._pretty import Pretty as Pretty from ._progress_bar import ProgressBar as ProgressBar from ._radio_button import RadioButton as RadioButton from ._radio_set import RadioSet as RadioSet +from ._select import Select as Select from ._static import Static as Static from ._switch import Switch as Switch from ._tabbed_content import TabbedContent as TabbedContent diff --git a/src/textual/widgets/_option_list.py b/src/textual/widgets/_option_list.py index dba669592e..193c0385fd 100644 --- a/src/textual/widgets/_option_list.py +++ b/src/textual/widgets/_option_list.py @@ -10,6 +10,7 @@ from typing import ClassVar, Iterable, NamedTuple from rich.console import RenderableType +from rich.padding import Padding from rich.repr import Result from rich.rule import Rule from rich.style import Style @@ -146,6 +147,7 @@ class OptionList(ScrollView, can_focus=True): """ COMPONENT_CLASSES: ClassVar[set[str]] = { + "option-list--option", "option-list--option-disabled", "option-list--option-highlighted", "option-list--option-highlighted-disabled", @@ -474,6 +476,7 @@ def _refresh_content_tracking(self, force: bool = False) -> None: # also set up the tracking of the actual options. line = 0 option = 0 + padding = self.get_component_styles("option-list--option").padding for content in self._contents: if isinstance(content, Option): # The content is an option, so render out the prompt and @@ -483,7 +486,10 @@ def _refresh_content_tracking(self, force: bool = False) -> None: Strip(prompt_line).apply_style(Style(meta={"option": option})), option, ) - for prompt_line in lines_from(content.prompt, options) + for prompt_line in lines_from( + Padding(content.prompt, padding) if padding else content.prompt, + options, + ) ] # Record the span information for the option. add_span(OptionLineSpan(line, len(new_lines))) @@ -838,8 +844,13 @@ def render_line(self, y: int) -> Strip: # It's a normal option line. return strip.apply_style(self.rich_style) - def scroll_to_highlight(self) -> None: - """Ensure that the highlighted option is in view.""" + def scroll_to_highlight(self, top: bool = False) -> None: + """Ensure that the highlighted option is in view. + + Args: + top: Scroll highlight to top of the list. + + """ highlighted = self.highlighted if highlighted is None: return @@ -856,6 +867,7 @@ def scroll_to_highlight(self) -> None: ), force=True, animate=False, + top=top, ) def validate_highlighted(self, highlighted: int | None) -> int | None: diff --git a/src/textual/widgets/_select.py b/src/textual/widgets/_select.py new file mode 100644 index 0000000000..25564af95d --- /dev/null +++ b/src/textual/widgets/_select.py @@ -0,0 +1,378 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING, Generic, Iterable, Optional, TypeVar + +from rich.console import RenderableType +from rich.text import Text + +from .. import events, on +from ..app import ComposeResult +from ..containers import Horizontal, Vertical +from ..message import Message +from ..reactive import var +from ..widgets import Static +from ._option_list import Option, OptionList + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + + +class SelectOverlay(OptionList): + """The 'pop-up' overlay for the Select control.""" + + BINDINGS = [("escape", "dismiss")] + + DEFAULT_CSS = """ + SelectOverlay { + border: tall $background; + background: $panel; + color: $text; + width: 100%; + padding: 0 1; + } + SelectOverlay > .option-list--option { + padding: 0 1; + } + """ + + @dataclass + class Dismiss(Message): + """Inform ancestor the overlay should be dismissed.""" + + lost_focus: bool = False + """True if the overlay lost focus.""" + + @dataclass + class UpdateSelection(Message): + """Inform ancestor the selection was changed.""" + + option_index: int + """The index of the new selection.""" + + def select(self, index: int | None) -> None: + """Move selection. + + Args: + index: Index of new selection. + """ + self.highlighted = index + self.scroll_to_highlight(top=True) + + def action_dismiss(self) -> None: + """Dismiss the overlay.""" + self.post_message(self.Dismiss()) + + def _on_blur(self, _event: events.Blur) -> None: + """On blur we want to dismiss the overlay.""" + self.post_message(self.Dismiss(lost_focus=True)) + + def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None: + """Inform parent when an option is selected.""" + event.stop() + self.post_message(self.UpdateSelection(event.option_index)) + + +class SelectCurrent(Horizontal): + """Displays the currently selected option.""" + + DEFAULT_CSS = """ + SelectCurrent { + border: tall $background; + background: $boost; + color: $text; + width: 100%; + height: auto; + padding: 0 2; + } + SelectCurrent Static#label { + width: 1fr; + height: auto; + color: $text-disabled; + background: transparent; + } + SelectCurrent.-has-value Static#label { + color: $text; + } + SelectCurrent .arrow { + box-sizing: content-box; + width: 1; + height: 1; + padding: 0 0 0 1; + color: $text-muted; + background: transparent; + } + SelectCurrent .arrow { + box-sizing: content-box; + width: 1; + height: 1; + padding: 0 0 0 1; + color: $text-muted; + background: transparent; + } + """ + + has_value: var[bool] = var(False) + """True if there is a current value, or False if it is None.""" + + class Toggle(Message): + """Request toggle overlay.""" + + def __init__(self, placeholder: str) -> None: + """Initialize the SelectCurrent. + + Args: + placeholder: A string to display when there is nothing selected. + """ + super().__init__() + self.placeholder = placeholder + self.label: RenderableType | None = None + + def update(self, label: RenderableType | None) -> None: + """Update the content in the widget. + + Args: + label: A renderable to display, or `None` for the placeholder. + """ + self.label = label + self.has_value = label is not None + self.query_one("#label", Static).update( + self.placeholder if label is None else label + ) + + def compose(self) -> ComposeResult: + """Compose label and down arrow.""" + yield Static(self.placeholder, id="label") + yield Static("▼", classes="arrow down-arrow") + yield Static("▲", classes="arrow up-arrow") + + def _watch_has_value(self, has_value: bool) -> None: + """Toggle the class.""" + self.set_class(has_value, "-has-value") + + async def _on_click(self, event: events.Click) -> None: + """Inform ancestor we want to toggle.""" + self.post_message(self.Toggle()) + + +SelectType = TypeVar("SelectType") +"""The type used for data in the Select.""" +SelectOption: TypeAlias = "tuple[str, SelectType]" +"""The type used for options in the Select.""" + + +class Select(Generic[SelectType], Vertical, can_focus=True): + """Widget to select from a list of possible options. + + A Select displays the current selection. + When activated with ++enter++ the widget displays an overlay with a list of all possible options. + + """ + + BINDINGS = [("enter,down,space,up", "show_overlay")] + """ + | Key(s) | Description | + | :- | :- | + | enter,down,space,up | Activate the overlay | + """ + + DEFAULT_CSS = """ + Select { + height: auto; + } + + Select:focus > SelectCurrent { + border: tall $accent; + } + + Select { + height: auto; + } + + Select > SelectOverlay { + width: 1fr; + display: none; + height: auto; + max-height: 10; + overlay: screen; + constrain: y; + } + + Select .up-arrow { + display:none; + } + + Select.-expanded .down-arrow { + display:none; + } + + Select.-expanded .up-arrow { + display: block; + } + + Select.-expanded > SelectOverlay { + display: block; + } + + Select.-expanded > SelectCurrent { + border: tall $accent; + } + """ + + expanded: var[bool] = var(False, init=False) + """True to show the overlay, otherwise False.""" + prompt: var[str] = var[str]("Select") + """The prompt to show when no value is selected.""" + value: var[SelectType | None] = var[Optional[SelectType]](None) + """The value of the select.""" + + class Changed(Message, bubble=True): + """Posted when the select value was changed. + + This message can be handled using a `on_select_changed` method. + + """ + + def __init__(self, control: Select, value: SelectType | None) -> None: + """ + Initialize the Changed message. + + """ + super().__init__() + self.control = control + """The select control.""" + self.value = value + """The value of the Select when it changed.""" + + def __init__( + self, + options: Iterable[tuple[str, SelectType]], + *, + prompt: str = "Select", + allow_blank: bool = True, + value: SelectType | None = None, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + disabled: bool = False, + ): + """Initialize the Select control + + Args: + options: Options to select from. + prompt: Text to show in the control when no option is select. + allow_blank: Allow the selection of a blank option. + value: Initial value (should be one of the values in `options`). + name: The name of the select control. + id: The ID of the control the DOM. + classes: The CSS classes of the control. + disabled: Whether the control is disabled or not. + """ + super().__init__(name=name, id=id, classes=classes, disabled=disabled) + self._allow_blank = allow_blank + self.prompt = prompt + self._initial_options = list(options) + self._value: SelectType | None = value + + def set_options(self, options: Iterable[tuple[RenderableType, SelectType]]) -> None: + """Set the options for the Select. + + Args: + options: An iterable of tuples containing (STRING, VALUE). + """ + self._options: list[tuple[RenderableType, SelectType | None]] = list(options) + + if self._allow_blank: + self._options.insert(0, ("", None)) + + self._select_options: list[Option] = [ + ( + Option(Text(self.prompt, style="dim")) + if value is None + else Option(prompt) + ) + for prompt, value in self._options + ] + + option_list = self.query_one(SelectOverlay) + option_list.clear_options() + for option in self._select_options: + option_list.add_option(option) + + def _watch_value(self, value: SelectType | None) -> None: + """Update the current value when it changes.""" + self._value = value + if value is None: + self.query_one(SelectCurrent).update(None) + else: + for index, (prompt, _value) in enumerate(self._options): + if _value == value: + select_overlay = self.query_one(SelectOverlay) + select_overlay.highlighted = index + self.query_one(SelectCurrent).update(prompt) + break + else: + self.query_one(SelectCurrent).update(None) + + def compose(self) -> ComposeResult: + """Compose Select with overlay and current value.""" + yield SelectCurrent(self.prompt) + yield SelectOverlay() + + def _on_mount(self, _event: events.Mount) -> None: + """Set initial values.""" + self.set_options(self._initial_options) + self.value = self._value + + def _watch_expanded(self, expanded: bool) -> None: + """Display or hide overlay.""" + overlay = self.query_one(SelectOverlay) + self.set_class(expanded, "-expanded") + if expanded: + overlay.focus() + if self.value is None: + overlay.select(None) + self.query_one(SelectCurrent).has_value = False + else: + value = self.value + for index, (_prompt, prompt_value) in enumerate(self._options): + if value == prompt_value: + overlay.select(index) + break + self.query_one(SelectCurrent).has_value = True + + @on(SelectCurrent.Toggle) + def _select_current_toggle(self, event: SelectCurrent.Toggle) -> None: + """Show the overlay when toggled.""" + event.stop() + self.expanded = not self.expanded + + @on(SelectOverlay.Dismiss) + def _select_overlay_dismiss(self, event: SelectOverlay.Dismiss) -> None: + """Dismiss the overlay.""" + event.stop() + self.expanded = False + if not event.lost_focus: + # If the overlay didn't lose focus, we want to re-focus the select. + self.focus() + + @on(SelectOverlay.UpdateSelection) + def _update_selection(self, event: SelectOverlay.UpdateSelection) -> None: + """Update the current selection.""" + event.stop() + value = self._options[event.option_index][1] + self.value = value + + async def update_focus() -> None: + """Update focus and reset overlay.""" + self.focus() + self.expanded = False + + self.call_after_refresh(update_focus) # Prevents a little flicker + self.post_message(self.Changed(self, value)) + + def action_show_overlay(self) -> None: + """Show the overlay.""" + select_current = self.query_one(SelectCurrent) + select_current.has_value = True + self.expanded = True diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 5b06d31c07..cbee7ba35d 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -21,138 +21,137 @@ font-weight: 700; } - .terminal-1593336641-matrix { + .terminal-369237853-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1593336641-title { + .terminal-369237853-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1593336641-r1 { fill: #c5c8c6 } - .terminal-1593336641-r2 { fill: #7ae998 } - .terminal-1593336641-r3 { fill: #0a180e;font-weight: bold } - .terminal-1593336641-r4 { fill: #008139 } - .terminal-1593336641-r5 { fill: #e3dbce } - .terminal-1593336641-r6 { fill: #e1e1e1 } - .terminal-1593336641-r7 { fill: #e76580 } - .terminal-1593336641-r8 { fill: #f5e5e9;font-weight: bold } - .terminal-1593336641-r9 { fill: #780028 } + .terminal-369237853-r1 { fill: #c5c8c6 } + .terminal-369237853-r2 { fill: #7ae998 } + .terminal-369237853-r3 { fill: #0a180e;font-weight: bold } + .terminal-369237853-r4 { fill: #008139 } + .terminal-369237853-r5 { fill: #e1e1e1 } + .terminal-369237853-r6 { fill: #e76580 } + .terminal-369237853-r7 { fill: #f5e5e9;font-weight: bold } + .terminal-369237853-r8 { fill: #780028 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - AlignContainersApp + AlignContainersApp - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - center - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - - - - - - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - middle - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - - - - - - - + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + center + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + middle + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + + + + @@ -347,202 +346,202 @@ font-weight: 700; } - .terminal-3056812568-matrix { + .terminal-2978213952-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3056812568-title { + .terminal-2978213952-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3056812568-r1 { fill: #c5c8c6 } - .terminal-3056812568-r2 { fill: #e3e3e3 } - .terminal-3056812568-r3 { fill: #004578 } - .terminal-3056812568-r4 { fill: #e1e1e1 } - .terminal-3056812568-r5 { fill: #632ca6 } - .terminal-3056812568-r6 { fill: #dde6ed;font-weight: bold } - .terminal-3056812568-r7 { fill: #14191f } - .terminal-3056812568-r8 { fill: #23568b } + .terminal-2978213952-r1 { fill: #c5c8c6 } + .terminal-2978213952-r2 { fill: #e3e3e3 } + .terminal-2978213952-r3 { fill: #004578 } + .terminal-2978213952-r4 { fill: #e1e1e1 } + .terminal-2978213952-r5 { fill: #632ca6 } + .terminal-2978213952-r6 { fill: #dde6ed;font-weight: bold } + .terminal-2978213952-r7 { fill: #14191f } + .terminal-2978213952-r8 { fill: #23568b } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MyApp + MyApp - - - - MyApp - ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── - oktest - ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ -  0 ────────────────────────────────────── 1 ────────────────────────────────────── 2 ───── - -  Foo       Bar         Baz               Foo       Bar         Baz               Foo      -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY▁▁ ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY▁▁ ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH -  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH - ───────────────────────────────────────────────────────────────────────────────────────────── - - ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + + + + MyApp + ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + oktest + ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ +  0 ────────────────────────────────────── 1 ────────────────────────────────────── 2 ───── + +  Foo       Bar         Baz               Foo       Bar         Baz               Foo      +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY▁▁ ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY▁▁ ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH +  ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH  0123456789  IJKLMNOPQRSTUVWXY ABCDEFGH + ───────────────────────────────────────────────────────────────────────────────────────────── + + ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── @@ -572,136 +571,136 @@ font-weight: 700; } - .terminal-1625062503-matrix { + .terminal-3956291897-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1625062503-title { + .terminal-3956291897-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1625062503-r1 { fill: #c5c8c6 } - .terminal-1625062503-r2 { fill: #e3e3e3 } - .terminal-1625062503-r3 { fill: #1e1e1e } - .terminal-1625062503-r4 { fill: #0178d4 } - .terminal-1625062503-r5 { fill: #e1e1e1 } - .terminal-1625062503-r6 { fill: #e2e2e2 } - .terminal-1625062503-r7 { fill: #ddedf9 } + .terminal-3956291897-r1 { fill: #c5c8c6 } + .terminal-3956291897-r2 { fill: #e3e3e3 } + .terminal-3956291897-r3 { fill: #1e1e1e } + .terminal-3956291897-r4 { fill: #0178d4 } + .terminal-3956291897-r5 { fill: #e1e1e1 } + .terminal-3956291897-r6 { fill: #e2e2e2 } + .terminal-3956291897-r7 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - InputWidthAutoApp + InputWidthAutoApp - - - - InputWidthAutoApp - ▔▔▔▔▔▔▔▔▔▔ - Hello - ▁▁▁▁▁▁▁▁▁▁ - - - - - - - - - - - - - - - - - - - + + + + InputWidthAutoApp + ▔▔▔▔▔▔▔▔▔▔ + Hello + ▁▁▁▁▁▁▁▁▁▁ + + + + + + + + + + + + + + + + + + + @@ -732,137 +731,136 @@ font-weight: 700; } - .terminal-2470781732-matrix { + .terminal-2059832628-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2470781732-title { + .terminal-2059832628-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2470781732-r1 { fill: #1e1e1e } - .terminal-2470781732-r2 { fill: #c5c8c6 } - .terminal-2470781732-r3 { fill: #e1e1e1 } - .terminal-2470781732-r4 { fill: #183118 } - .terminal-2470781732-r5 { fill: #124512 } - .terminal-2470781732-r6 { fill: #0c580c } - .terminal-2470781732-r7 { fill: #066c06 } - .terminal-2470781732-r8 { fill: #008000 } + .terminal-2059832628-r1 { fill: #1e1e1e } + .terminal-2059832628-r2 { fill: #c5c8c6 } + .terminal-2059832628-r3 { fill: #183118 } + .terminal-2059832628-r4 { fill: #124512 } + .terminal-2059832628-r5 { fill: #0c580c } + .terminal-2059832628-r6 { fill: #066c06 } + .terminal-2059832628-r7 { fill: #008000 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - BorderAlphaApp + BorderAlphaApp - - - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - - - - + + + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + + + + @@ -1057,162 +1055,161 @@ font-weight: 700; } - .terminal-619468389-matrix { + .terminal-3643133712-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-619468389-title { + .terminal-3643133712-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-619468389-r1 { fill: #e1e1e1 } - .terminal-619468389-r2 { fill: #c5c8c6 } - .terminal-619468389-r3 { fill: #e1e1e1;font-weight: bold } - .terminal-619468389-r4 { fill: #454a50 } - .terminal-619468389-r5 { fill: #35383c } - .terminal-619468389-r6 { fill: #24292f;font-weight: bold } - .terminal-619468389-r7 { fill: #7c7d7e;font-weight: bold } - .terminal-619468389-r8 { fill: #000000 } - .terminal-619468389-r9 { fill: #0c0c0c } - .terminal-619468389-r10 { fill: #507bb3 } - .terminal-619468389-r11 { fill: #3c5577 } - .terminal-619468389-r12 { fill: #dde6ed;font-weight: bold } - .terminal-619468389-r13 { fill: #75828b;font-weight: bold } - .terminal-619468389-r14 { fill: #001541 } - .terminal-619468389-r15 { fill: #0c1833 } - .terminal-619468389-r16 { fill: #7ae998 } - .terminal-619468389-r17 { fill: #559767 } - .terminal-619468389-r18 { fill: #0a180e;font-weight: bold } - .terminal-619468389-r19 { fill: #192e1f;font-weight: bold } - .terminal-619468389-r20 { fill: #008139 } - .terminal-619468389-r21 { fill: #0c592e } - .terminal-619468389-r22 { fill: #ffcf56 } - .terminal-619468389-r23 { fill: #a5883f } - .terminal-619468389-r24 { fill: #211505;font-weight: bold } - .terminal-619468389-r25 { fill: #3a2a13;font-weight: bold } - .terminal-619468389-r26 { fill: #b86b00 } - .terminal-619468389-r27 { fill: #7a4c0c } - .terminal-619468389-r28 { fill: #e76580 } - .terminal-619468389-r29 { fill: #964858 } - .terminal-619468389-r30 { fill: #f5e5e9;font-weight: bold } - .terminal-619468389-r31 { fill: #978186;font-weight: bold } - .terminal-619468389-r32 { fill: #780028 } - .terminal-619468389-r33 { fill: #540c24 } + .terminal-3643133712-r1 { fill: #c5c8c6 } + .terminal-3643133712-r2 { fill: #e1e1e1;font-weight: bold } + .terminal-3643133712-r3 { fill: #454a50 } + .terminal-3643133712-r4 { fill: #35383c } + .terminal-3643133712-r5 { fill: #24292f;font-weight: bold } + .terminal-3643133712-r6 { fill: #7c7d7e;font-weight: bold } + .terminal-3643133712-r7 { fill: #000000 } + .terminal-3643133712-r8 { fill: #0c0c0c } + .terminal-3643133712-r9 { fill: #507bb3 } + .terminal-3643133712-r10 { fill: #3c5577 } + .terminal-3643133712-r11 { fill: #dde6ed;font-weight: bold } + .terminal-3643133712-r12 { fill: #75828b;font-weight: bold } + .terminal-3643133712-r13 { fill: #001541 } + .terminal-3643133712-r14 { fill: #0c1833 } + .terminal-3643133712-r15 { fill: #7ae998 } + .terminal-3643133712-r16 { fill: #559767 } + .terminal-3643133712-r17 { fill: #0a180e;font-weight: bold } + .terminal-3643133712-r18 { fill: #192e1f;font-weight: bold } + .terminal-3643133712-r19 { fill: #008139 } + .terminal-3643133712-r20 { fill: #0c592e } + .terminal-3643133712-r21 { fill: #ffcf56 } + .terminal-3643133712-r22 { fill: #a5883f } + .terminal-3643133712-r23 { fill: #211505;font-weight: bold } + .terminal-3643133712-r24 { fill: #3a2a13;font-weight: bold } + .terminal-3643133712-r25 { fill: #b86b00 } + .terminal-3643133712-r26 { fill: #7a4c0c } + .terminal-3643133712-r27 { fill: #e76580 } + .terminal-3643133712-r28 { fill: #964858 } + .terminal-3643133712-r29 { fill: #f5e5e9;font-weight: bold } + .terminal-3643133712-r30 { fill: #978186;font-weight: bold } + .terminal-3643133712-r31 { fill: #780028 } + .terminal-3643133712-r32 { fill: #540c24 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - ButtonsApp + ButtonsApp - - - - - Standard ButtonsDisabled Buttons - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - DefaultDefault - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Primary!Primary! - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Success!Success! - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Warning!Warning! - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Error!Error! - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - + + + + + Standard ButtonsDisabled Buttons + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + DefaultDefault + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Primary!Primary! + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Success!Success! + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Warning!Warning! + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Error!Error! + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + @@ -2485,135 +2482,134 @@ font-weight: 700; } - .terminal-1331556511-matrix { + .terminal-2323733830-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1331556511-title { + .terminal-2323733830-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1331556511-r1 { fill: #808080 } - .terminal-1331556511-r2 { fill: #e1e1e1 } - .terminal-1331556511-r3 { fill: #c5c8c6 } - .terminal-1331556511-r4 { fill: #ddedf9 } - .terminal-1331556511-r5 { fill: #e2e2e2 } + .terminal-2323733830-r1 { fill: #808080 } + .terminal-2323733830-r2 { fill: #e1e1e1 } + .terminal-2323733830-r3 { fill: #c5c8c6 } + .terminal-2323733830-r4 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - AlignAllApp + AlignAllApp - - - - ──────────────────────────────────────────────────────────────────────── - left topcenter topright top - - - - - ──────────────────────────────────────────────────────────────────────── - - ──────────────────────────────────────────────────────────────────────── - - - left middlecenter middleright middle - - - ──────────────────────────────────────────────────────────────────────── - - ──────────────────────────────────────────────────────────────────────── - - - - - - left bottomcenter bottomright bottom - ──────────────────────────────────────────────────────────────────────── + + + + ──────────────────────────────────────────────────────────────────────── + left topcenter topright top + + + + + ──────────────────────────────────────────────────────────────────────── + + ──────────────────────────────────────────────────────────────────────── + + + left middlecenter middleright middle + + + ──────────────────────────────────────────────────────────────────────── + + ──────────────────────────────────────────────────────────────────────── + + + + + + left bottomcenter bottomright bottom + ──────────────────────────────────────────────────────────────────────── @@ -3277,141 +3273,141 @@ font-weight: 700; } - .terminal-1997861159-matrix { + .terminal-1536397390-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1997861159-title { + .terminal-1536397390-title { font-size: 18px; font-weight: bold; font-family: arial; } - .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 } + .terminal-1536397390-r1 { fill: #c5c8c6 } + .terminal-1536397390-r2 { fill: #fea62b } + .terminal-1536397390-r3 { fill: #fea62b;font-weight: bold } + .terminal-1536397390-r4 { fill: #fea62b;font-weight: bold;font-style: italic; } + .terminal-1536397390-r5 { fill: #cc555a;font-weight: bold } + .terminal-1536397390-r6 { fill: #e1e1e1 } + .terminal-1536397390-r7 { fill: #1e1e1e } + .terminal-1536397390-r8 { fill: #1e1e1e;text-decoration: underline; } + .terminal-1536397390-r9 { fill: #fea62b;text-decoration: underline; } + .terminal-1536397390-r10 { fill: #4b4e55;text-decoration: underline; } + .terminal-1536397390-r11 { fill: #4ebf71 } + .terminal-1536397390-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… + @@ -5018,132 +5014,132 @@ font-weight: 700; } - .terminal-1840966081-matrix { + .terminal-1564714526-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1840966081-title { + .terminal-1564714526-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1840966081-r1 { fill: #e1e1e1 } - .terminal-1840966081-r2 { fill: #c5c8c6 } - .terminal-1840966081-r3 { fill: #ffffff } + .terminal-1564714526-r1 { fill: #e1e1e1 } + .terminal-1564714526-r2 { fill: #c5c8c6 } + .terminal-1564714526-r3 { fill: #ffffff } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - DockAllApp + DockAllApp - - - - - - - ────────────────────────────────────────────────────────── - top - - - - - - - leftright - - - - - - - - bottom - ────────────────────────────────────────────────────────── - - + + + + + + + ────────────────────────────────────────────────────────── + top + + + + + + + leftright + + + + + + + + bottom + ────────────────────────────────────────────────────────── + + @@ -6431,134 +6427,132 @@ font-weight: 700; } - .terminal-2838975926-matrix { + .terminal-2726481143-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2838975926-title { + .terminal-2726481143-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2838975926-r1 { fill: #efddef } - .terminal-2838975926-r2 { fill: #c5c8c6 } - .terminal-2838975926-r3 { fill: #000000 } - .terminal-2838975926-r4 { fill: #ddefef } - .terminal-2838975926-r5 { fill: #e1e1e1 } + .terminal-2726481143-r1 { fill: #c5c8c6 } + .terminal-2726481143-r2 { fill: #000000 } + .terminal-2726481143-r3 { fill: #e1e1e1 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - LayoutApp + LayoutApp - - - - - Layout - - Is - - Vertical - - - LayoutIsHorizontal - - - - - - - - - - - - - - + + + + + Layout + + Is + + Vertical + + + LayoutIsHorizontal + + + + + + + + + + + + + + @@ -7845,141 +7839,140 @@ font-weight: 700; } - .terminal-2245771963-matrix { + .terminal-4172255139-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2245771963-title { + .terminal-4172255139-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2245771963-r1 { fill: #ffffff } - .terminal-2245771963-r2 { fill: #c5c8c6 } - .terminal-2245771963-r3 { fill: #e0e0e0 } - .terminal-2245771963-r4 { fill: #ece5e5 } - .terminal-2245771963-r5 { fill: #eee8e3 } - .terminal-2245771963-r6 { fill: #e7e0e6 } - .terminal-2245771963-r7 { fill: #eae2e4 } - .terminal-2245771963-r8 { fill: #e3ede7 } - .terminal-2245771963-r9 { fill: #e8ede4 } - .terminal-2245771963-r10 { fill: #e1eceb } - .terminal-2245771963-r11 { fill: #eeeddf } + .terminal-4172255139-r1 { fill: #ffffff } + .terminal-4172255139-r2 { fill: #c5c8c6 } + .terminal-4172255139-r3 { fill: #ece5e5 } + .terminal-4172255139-r4 { fill: #eee8e3 } + .terminal-4172255139-r5 { fill: #e7e0e6 } + .terminal-4172255139-r6 { fill: #eae2e4 } + .terminal-4172255139-r7 { fill: #e3ede7 } + .terminal-4172255139-r8 { fill: #e8ede4 } + .terminal-4172255139-r9 { fill: #e1eceb } + .terminal-4172255139-r10 { fill: #eeeddf } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MarginAllApp + MarginAllApp - - - - ────────────────────────────────────────────────────────────────── - - - - marginmargin: 1  - no marginmargin: 1: 1 51 2 6 - - - - - ────────────────────────────────────────────────────────────────── - - ────────────────────────────────────────────────────────────────── - - - margin-bottom: 4 - - margin-right: margin-left: 3 - 3 - margin-top: 4 - - - - ────────────────────────────────────────────────────────────────── + + + + ────────────────────────────────────────────────────────────────── + + + + marginmargin: 1  + no marginmargin: 1: 1 51 2 6 + + + + + ────────────────────────────────────────────────────────────────── + + ────────────────────────────────────────────────────────────────── + + + margin-bottom: 4 + + margin-right: margin-left: 3 + 3 + margin-top: 4 + + + + ────────────────────────────────────────────────────────────────── @@ -8167,134 +8160,134 @@ font-weight: 700; } - .terminal-1398959741-matrix { + .terminal-987506037-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1398959741-title { + .terminal-987506037-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1398959741-r1 { fill: #c5c8c6 } - .terminal-1398959741-r2 { fill: #e8e0e7 } - .terminal-1398959741-r3 { fill: #eae3e5 } - .terminal-1398959741-r4 { fill: #ede6e6 } - .terminal-1398959741-r5 { fill: #efe9e4 } + .terminal-987506037-r1 { fill: #c5c8c6 } + .terminal-987506037-r2 { fill: #e8e0e7 } + .terminal-987506037-r3 { fill: #eae3e5 } + .terminal-987506037-r4 { fill: #ede6e6 } + .terminal-987506037-r5 { fill: #efe9e4 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MaxWidthApp + MaxWidthApp - - - - - - max-width:  - 50h - - - - - max-width: 999 - - - - - - max-width: 50% - - - - - - max-width: 30 - - + + + + + + max-width:  + 50h + + + + + max-width: 999 + + + + + + max-width: 50% + + + + + + max-width: 30 + + @@ -8644,134 +8637,134 @@ font-weight: 700; } - .terminal-292160688-matrix { + .terminal-3520697079-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-292160688-title { + .terminal-3520697079-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-292160688-r1 { fill: #000000 } - .terminal-292160688-r2 { fill: #0000ff } - .terminal-292160688-r3 { fill: #c5c8c6 } - .terminal-292160688-r4 { fill: #ff0000 } - .terminal-292160688-r5 { fill: #008000 } + .terminal-3520697079-r1 { fill: #000000 } + .terminal-3520697079-r2 { fill: #0000ff } + .terminal-3520697079-r3 { fill: #c5c8c6 } + .terminal-3520697079-r4 { fill: #ff0000 } + .terminal-3520697079-r5 { fill: #008000 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - OffsetApp + OffsetApp - - - - - Chani (offset 0  - ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀-3) - - - - Paul (offset 8 2)▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ - - - - ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ - - - Duncan (offset 4  - 10) - - - - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ - - - + + + + + Chani (offset 0  + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀-3) + + + + Paul (offset 8 2)▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ + + + + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ + + + Duncan (offset 4  + 10) + + + + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ + + + @@ -9436,136 +9429,136 @@ font-weight: 700; } - .terminal-2990670852-matrix { + .terminal-3720200886-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2990670852-title { + .terminal-3720200886-title { font-size: 18px; font-weight: bold; font-family: arial; } - .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 } + .terminal-3720200886-r1 { fill: #c5c8c6 } + .terminal-3720200886-r2 { fill: #000000 } + .terminal-3720200886-r3 { fill: #008000 } + .terminal-3720200886-r4 { fill: #e5f0e5 } + .terminal-3720200886-r5 { fill: #036a03 } + .terminal-3720200886-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. @@ -9750,138 +9743,138 @@ font-weight: 700; } - .terminal-1642992271-matrix { + .terminal-2103878337-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1642992271-title { + .terminal-2103878337-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1642992271-r1 { fill: #c5c8c6 } - .terminal-1642992271-r2 { fill: #e7e0e6 } - .terminal-1642992271-r3 { fill: #eae2e4 } - .terminal-1642992271-r4 { fill: #ece5e5 } - .terminal-1642992271-r5 { fill: #eee8e3 } - .terminal-1642992271-r6 { fill: #e8ede4 } - .terminal-1642992271-r7 { fill: #e3ede7 } - .terminal-1642992271-r8 { fill: #e1eceb } - .terminal-1642992271-r9 { fill: #eeeddf } + .terminal-2103878337-r1 { fill: #e7e0e6 } + .terminal-2103878337-r2 { fill: #c5c8c6 } + .terminal-2103878337-r3 { fill: #eae2e4 } + .terminal-2103878337-r4 { fill: #ece5e5 } + .terminal-2103878337-r5 { fill: #eee8e3 } + .terminal-2103878337-r6 { fill: #e8ede4 } + .terminal-2103878337-r7 { fill: #e3ede7 } + .terminal-2103878337-r8 { fill: #e1eceb } + .terminal-2103878337-r9 { fill: #eeeddf } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - PaddingAllApp + PaddingAllApp - - - - no padding - padding: 1padding:padding: 1 1 - 1 52 6 - - - - - - - - - - padding-right: 3padding-bottom: 4padding-left: 3 - - - - padding-top: 4 - - - - - - + + + + no padding + padding: 1padding:padding: 1 1 + 1 52 6 + + + + + + + + + + padding-right: 3padding-bottom: 4padding-left: 3 + + + + padding-top: 4 + + + + + + @@ -12291,141 +12284,141 @@ font-weight: 700; } - .terminal-1938916138-matrix { + .terminal-1052270191-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1938916138-title { + .terminal-1052270191-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1938916138-r1 { fill: #c5c8c6 } - .terminal-1938916138-r2 { fill: #e8e0e7 } - .terminal-1938916138-r3 { fill: #eae3e5 } - .terminal-1938916138-r4 { fill: #ede6e6 } - .terminal-1938916138-r5 { fill: #efe9e4 } - .terminal-1938916138-r6 { fill: #efeedf } - .terminal-1938916138-r7 { fill: #e9eee5 } - .terminal-1938916138-r8 { fill: #e4eee8 } - .terminal-1938916138-r9 { fill: #e2edeb } - .terminal-1938916138-r10 { fill: #dfebed } - .terminal-1938916138-r11 { fill: #ddedf9 } + .terminal-1052270191-r1 { fill: #c5c8c6 } + .terminal-1052270191-r2 { fill: #e8e0e7 } + .terminal-1052270191-r3 { fill: #eae3e5 } + .terminal-1052270191-r4 { fill: #ede6e6 } + .terminal-1052270191-r5 { fill: #efe9e4 } + .terminal-1052270191-r6 { fill: #efeedf } + .terminal-1052270191-r7 { fill: #e9eee5 } + .terminal-1052270191-r8 { fill: #e4eee8 } + .terminal-1052270191-r9 { fill: #e2edeb } + .terminal-1052270191-r10 { fill: #dfebed } + .terminal-1052270191-r11 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - HeightComparisonApp + HeightComparisonApp - - - - - - - - - - - - - - - #cells#percent#w#h#vw#vh#auto#fr1#fr3 - - - - - - - - - - - - ····•····•····•····•····•····•····•····•····•····•····•····•····•····•····•····• + + + + + + + + + + + + + + + #cells#percent#w#h#vw#vh#auto#fr1#fr3 + + + + + + + + + + + + ····•····•····•····•····•····•····•····•····•····•····•····•····•····•····•····• @@ -13569,168 +13562,168 @@ font-weight: 700; } - .terminal-4033540874-matrix { + .terminal-614458704-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-4033540874-title { + .terminal-614458704-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-4033540874-r1 { fill: #c5c8c6 } - .terminal-4033540874-r2 { fill: #e3e3e3 } - .terminal-4033540874-r3 { fill: #e1e1e1 } - .terminal-4033540874-r4 { fill: #e2e2e2 } - .terminal-4033540874-r5 { fill: #14191f } - .terminal-4033540874-r6 { fill: #004578 } - .terminal-4033540874-r7 { fill: #262626 } - .terminal-4033540874-r8 { fill: #e2e2e2;font-weight: bold;text-decoration: underline; } - .terminal-4033540874-r9 { fill: #e2e2e2;font-weight: bold } - .terminal-4033540874-r10 { fill: #7ae998 } - .terminal-4033540874-r11 { fill: #4ebf71;font-weight: bold } - .terminal-4033540874-r12 { fill: #008139 } - .terminal-4033540874-r13 { fill: #dde8f3;font-weight: bold } - .terminal-4033540874-r14 { fill: #ddedf9 } + .terminal-614458704-r1 { fill: #c5c8c6 } + .terminal-614458704-r2 { fill: #e3e3e3 } + .terminal-614458704-r3 { fill: #e1e1e1 } + .terminal-614458704-r4 { fill: #e2e2e2 } + .terminal-614458704-r5 { fill: #14191f } + .terminal-614458704-r6 { fill: #004578 } + .terminal-614458704-r7 { fill: #262626 } + .terminal-614458704-r8 { fill: #e2e2e2;font-weight: bold;text-decoration: underline; } + .terminal-614458704-r9 { fill: #e2e2e2;font-weight: bold } + .terminal-614458704-r10 { fill: #7ae998 } + .terminal-614458704-r11 { fill: #4ebf71;font-weight: bold } + .terminal-614458704-r12 { fill: #008139 } + .terminal-614458704-r13 { fill: #dde8f3;font-weight: bold } + .terminal-614458704-r14 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - Textual Demo + Textual Demo - - - - Textual Demo - - - TOP - - ▆▆ - - Widgets - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - - Rich contentTextual Demo - - Welcome! Textual is a framework for creating sophisticated - applications with the terminal. - CSS - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Start - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - - - - - - - -  CTRL+C  Quit  CTRL+B  Sidebar  CTRL+T  Toggle Dark mode  CTRL+S  Screenshot  F1  Notes  + + + + Textual Demo + + + TOP + + ▆▆ + + Widgets + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + Rich contentTextual Demo + + Welcome! Textual is a framework for creating sophisticated + applications with the terminal. + CSS + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Start + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + + + + + + + +  CTRL+C  Quit  CTRL+B  Sidebar  CTRL+T  Toggle Dark mode  CTRL+S  Screenshot  F1  Notes  @@ -14101,141 +14094,141 @@ font-weight: 700; } - .terminal-2702154472-matrix { + .terminal-2216843056-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2702154472-title { + .terminal-2216843056-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2702154472-r1 { fill: #c5c8c6 } - .terminal-2702154472-r2 { fill: #1e1e1e } - .terminal-2702154472-r3 { fill: #1f1f1f } - .terminal-2702154472-r4 { fill: #ff0000 } - .terminal-2702154472-r5 { fill: #dde8f3;font-weight: bold } - .terminal-2702154472-r6 { fill: #ddedf9 } - .terminal-2702154472-r7 { fill: #c7cdd2 } + .terminal-2216843056-r1 { fill: #c5c8c6 } + .terminal-2216843056-r2 { fill: #1e1e1e } + .terminal-2216843056-r3 { fill: #1f1f1f } + .terminal-2216843056-r4 { fill: #ff0000 } + .terminal-2216843056-r5 { fill: #dde8f3;font-weight: bold } + .terminal-2216843056-r6 { fill: #ddedf9 } + .terminal-2216843056-r7 { fill: #c7cdd2 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - TestApp + TestApp - - - - TestApp - ───────── - this - is - a - sample - sentence - and - here - are - some - wordsthis - is - a - sample - sentence - and - here - are - some - words -  CTRL+Q  Quit  - - - ▇▇ + + + + TestApp + ───────── + this + is + a + sample + sentence + and + here + are + some + wordsthis + is + a + sample + sentence + and + here + are + some + words +  CTRL+Q  Quit  + + + ▇▇ @@ -14435,135 +14428,135 @@ font-weight: 700; } - .terminal-1801121102-matrix { + .terminal-2886576672-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1801121102-title { + .terminal-2886576672-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1801121102-r1 { fill: #c5c8c6 } - .terminal-1801121102-r2 { fill: #e3e3e3 } - .terminal-1801121102-r3 { fill: #ffdddd } - .terminal-1801121102-r4 { fill: #e1e1e1 } - .terminal-1801121102-r5 { fill: #14191f } - .terminal-1801121102-r6 { fill: #ddedf9 } + .terminal-2886576672-r1 { fill: #c5c8c6 } + .terminal-2886576672-r2 { fill: #e3e3e3 } + .terminal-2886576672-r3 { fill: #ffdddd } + .terminal-2886576672-r4 { fill: #e1e1e1 } + .terminal-2886576672-r5 { fill: #14191f } + .terminal-2886576672-r6 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - StyleBugApp + StyleBugApp - - - - StyleBugApp - test widget 0 - test widget 1 - test widget 2 - test widget 3 - test widget 4 - test widget 5 - test widget 6 - test widget 7 - test widget 8 - test widget 9 - test widget 10 - test widget 11 - test widget 12▇▇ - test widget 13 - test widget 14 - test widget 15 - test widget 16 - test widget 17 - test widget 18 - test widget 19 - test widget 20 - test widget 21 + + + + StyleBugApp + test widget 0 + test widget 1 + test widget 2 + test widget 3 + test widget 4 + test widget 5 + test widget 6 + test widget 7 + test widget 8 + test widget 9 + test widget 10 + test widget 11 + test widget 12▇▇ + test widget 13 + test widget 14 + test widget 15 + test widget 16 + test widget 17 + test widget 18 + test widget 19 + test widget 20 + test widget 21 @@ -14751,138 +14744,137 @@ font-weight: 700; } - .terminal-1665781252-matrix { + .terminal-1298369243-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1665781252-title { + .terminal-1298369243-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1665781252-r1 { fill: #008000 } - .terminal-1665781252-r2 { fill: #c5c8c6 } - .terminal-1665781252-r3 { fill: #e0e4e0 } - .terminal-1665781252-r4 { fill: #e0e6e0 } + .terminal-1298369243-r1 { fill: #008000 } + .terminal-1298369243-r2 { fill: #c5c8c6 } + .terminal-1298369243-r3 { fill: #e0e6e0 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - TestApp + TestApp - - - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - - Hello - - - - - - - World - - - - - - - !! - - - - - - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + + Hello + + + + + + + World + + + + + + + !! + + + + + + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -14912,136 +14904,135 @@ font-weight: 700; } - .terminal-1035580841-matrix { + .terminal-2371169958-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1035580841-title { + .terminal-2371169958-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1035580841-r1 { fill: #c5c8c6 } - .terminal-1035580841-r2 { fill: #e3e3e3 } - .terminal-1035580841-r3 { fill: #ddddff } - .terminal-1035580841-r4 { fill: #e3e4e5 } - .terminal-1035580841-r5 { fill: #e2e3e3 } - .terminal-1035580841-r6 { fill: #14191f } - .terminal-1035580841-r7 { fill: #ddedf9 } + .terminal-2371169958-r1 { fill: #c5c8c6 } + .terminal-2371169958-r2 { fill: #e3e3e3 } + .terminal-2371169958-r3 { fill: #e3e4e5 } + .terminal-2371169958-r4 { fill: #e2e3e3 } + .terminal-2371169958-r5 { fill: #14191f } + .terminal-2371169958-r6 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - ScreenSplitApp + ScreenSplitApp - - - - ScreenSplitApp - This is content This is content number 0 - number 0This is content number 1 - This is content ▄▄This is content number 2 - number 1This is content number 3 - This is content This is content number 4▁▁ - number 2This is content number 5 - This is content This is content number 6 - number 3This is content number 7 - This is content This is content number 8 - number 4This is content number 9 - This is content This is content number 10 - number 5This is content number 11 - This is content This is content number 12 - number 6This is content number 13 - This is content This is content number 14 - number 7This is content number 15 - This is content This is content number 16 - number 8This is content number 17 - This is content This is content number 18 - number 9This is content number 19 - This is content This is content number 20 - number 10This is content number 21 + + + + ScreenSplitApp + This is content This is content number 0 + number 0This is content number 1 + This is content ▄▄This is content number 2 + number 1This is content number 3 + This is content This is content number 4▁▁ + number 2This is content number 5 + This is content This is content number 6 + number 3This is content number 7 + This is content This is content number 8 + number 4This is content number 9 + This is content This is content number 10 + number 5This is content number 11 + This is content This is content number 12 + number 6This is content number 13 + This is content This is content number 14 + number 7This is content number 15 + This is content This is content number 16 + number 8This is content number 17 + This is content This is content number 18 + number 9This is content number 19 + This is content This is content number 20 + number 10This is content number 21 @@ -15696,132 +15687,132 @@ font-weight: 700; } - .terminal-4077214022-matrix { + .terminal-2648118808-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-4077214022-title { + .terminal-2648118808-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-4077214022-r1 { fill: #c5c8c6 } - .terminal-4077214022-r2 { fill: #e3e3e3 } - .terminal-4077214022-r3 { fill: #e1e1e1 } + .terminal-2648118808-r1 { fill: #c5c8c6 } + .terminal-2648118808-r2 { fill: #e3e3e3 } + .terminal-2648118808-r3 { fill: #e1e1e1 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - HeaderApp + HeaderApp - - - - HeaderApp - - - - - - - - - - - - - - - - - - - - - - + + + + HeaderApp + + + + + + + + + + + + + + + + + + + + + + @@ -16482,146 +16473,146 @@ font-weight: 700; } - .terminal-2572323619-matrix { + .terminal-641812469-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2572323619-title { + .terminal-641812469-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2572323619-r1 { fill: #c5c8c6 } - .terminal-2572323619-r2 { fill: #e3e3e3 } - .terminal-2572323619-r3 { fill: #e1e1e1 } - .terminal-2572323619-r4 { fill: #e1e1e1;text-decoration: underline; } - .terminal-2572323619-r5 { fill: #e1e1e1;font-weight: bold } - .terminal-2572323619-r6 { fill: #e1e1e1;font-style: italic; } - .terminal-2572323619-r7 { fill: #98729f;font-weight: bold } - .terminal-2572323619-r8 { fill: #d0b344 } - .terminal-2572323619-r9 { fill: #98a84b } - .terminal-2572323619-r10 { fill: #00823d;font-style: italic; } - .terminal-2572323619-r11 { fill: #ffcf56 } - .terminal-2572323619-r12 { fill: #e76580 } - .terminal-2572323619-r13 { fill: #211505;font-weight: bold } - .terminal-2572323619-r14 { fill: #f5e5e9;font-weight: bold } - .terminal-2572323619-r15 { fill: #b86b00 } - .terminal-2572323619-r16 { fill: #780028 } + .terminal-641812469-r1 { fill: #c5c8c6 } + .terminal-641812469-r2 { fill: #e3e3e3 } + .terminal-641812469-r3 { fill: #e1e1e1 } + .terminal-641812469-r4 { fill: #e1e1e1;text-decoration: underline; } + .terminal-641812469-r5 { fill: #e1e1e1;font-weight: bold } + .terminal-641812469-r6 { fill: #e1e1e1;font-style: italic; } + .terminal-641812469-r7 { fill: #98729f;font-weight: bold } + .terminal-641812469-r8 { fill: #d0b344 } + .terminal-641812469-r9 { fill: #98a84b } + .terminal-641812469-r10 { fill: #00823d;font-style: italic; } + .terminal-641812469-r11 { fill: #ffcf56 } + .terminal-641812469-r12 { fill: #e76580 } + .terminal-641812469-r13 { fill: #211505;font-weight: bold } + .terminal-641812469-r14 { fill: #f5e5e9;font-weight: bold } + .terminal-641812469-r15 { fill: #b86b00 } + .terminal-641812469-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 + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ @@ -16809,136 +16800,136 @@ font-weight: 700; } - .terminal-1675990519-matrix { + .terminal-513592180-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1675990519-title { + .terminal-513592180-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1675990519-r1 { fill: #c5c8c6 } - .terminal-1675990519-r2 { fill: #e3e3e3 } - .terminal-1675990519-r3 { fill: #e1e1e1 } - .terminal-1675990519-r4 { fill: #ff0000 } - .terminal-1675990519-r5 { fill: #dde8f3;font-weight: bold } - .terminal-1675990519-r6 { fill: #ddedf9 } + .terminal-513592180-r1 { fill: #c5c8c6 } + .terminal-513592180-r2 { fill: #e3e3e3 } + .terminal-513592180-r3 { fill: #ff0000 } + .terminal-513592180-r4 { fill: #e1e1e1 } + .terminal-513592180-r5 { fill: #dde8f3;font-weight: bold } + .terminal-513592180-r6 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - DialogIssueApp + DialogIssueApp - - - - DialogIssueApp - - - - - - ─────────────────────────────────────── - - - - - - This should not cause a scrollbar to ap - - - - - - ─────────────────────────────────────── - - - - -  D  Toggle the dialog  + + + + DialogIssueApp + + + + + + ─────────────────────────────────────── + + + + + + This should not cause a scrollbar to ap + + + + + + ─────────────────────────────────────── + + + + +  D  Toggle the dialog  @@ -17936,135 +17927,135 @@ font-weight: 700; } - .terminal-543315859-matrix { + .terminal-2423395429-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-543315859-title { + .terminal-2423395429-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-543315859-r1 { fill: #c5c8c6 } - .terminal-543315859-r2 { fill: #e3e3e3 } - .terminal-543315859-r3 { fill: #e1e1e1 } - .terminal-543315859-r4 { fill: #dde8f3;font-weight: bold } - .terminal-543315859-r5 { fill: #ddedf9 } + .terminal-2423395429-r1 { fill: #c5c8c6 } + .terminal-2423395429-r2 { fill: #e3e3e3 } + .terminal-2423395429-r3 { fill: #e1e1e1 } + .terminal-2423395429-r4 { fill: #dde8f3;font-weight: bold } + .terminal-2423395429-r5 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - ModalApp + ModalApp - - - - ModalApp - Hello - - - - - - - - - - - - - - - - - - - - - -  ⏎  Open Dialog  + + + + ModalApp + Hello + + + + + + + + + + + + + + + + + + + + + +  ⏎  Open Dialog  @@ -18731,136 +18722,136 @@ font-weight: 700; } - .terminal-1812315577-matrix { + .terminal-1829927563-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1812315577-title { + .terminal-1829927563-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1812315577-r1 { fill: #c5c8c6 } - .terminal-1812315577-r2 { fill: #e3e3e3 } - .terminal-1812315577-r3 { fill: #e1e1e1 } - .terminal-1812315577-r4 { fill: #004578 } - .terminal-1812315577-r5 { fill: #e0e8ee;font-weight: bold } - .terminal-1812315577-r6 { fill: #e2e3e3 } - .terminal-1812315577-r7 { fill: #ddedf9 } + .terminal-1829927563-r1 { fill: #c5c8c6 } + .terminal-1829927563-r2 { fill: #e3e3e3 } + .terminal-1829927563-r3 { fill: #e1e1e1 } + .terminal-1829927563-r4 { fill: #004578 } + .terminal-1829927563-r5 { fill: #e0e8ee;font-weight: bold } + .terminal-1829927563-r6 { fill: #e2e3e3 } + .terminal-1829927563-r7 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - OptionListApp + OptionListApp - - - - OptionListApp - - - - ────────────────────────────────────────────────────── - Aerilon - Aquaria - Canceron - Caprica - Gemenon - Leonis - Libran - Picon - Sagittaron - Scorpia - Tauron - Virgon - - - ────────────────────────────────────────────────────── - - - + + + + OptionListApp + + + + ────────────────────────────────────────────────────── + Aerilon + Aquaria + Canceron + Caprica + Gemenon + Leonis + Libran + Picon + Sagittaron + Scorpia + Tauron + Virgon + + + ────────────────────────────────────────────────────── + + + @@ -18891,139 +18882,139 @@ font-weight: 700; } - .terminal-1041266590-matrix { + .terminal-2055091312-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1041266590-title { + .terminal-2055091312-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1041266590-r1 { fill: #c5c8c6 } - .terminal-1041266590-r2 { fill: #e3e3e3 } - .terminal-1041266590-r3 { fill: #e1e1e1 } - .terminal-1041266590-r4 { fill: #004578 } - .terminal-1041266590-r5 { fill: #e0e8ee;font-weight: bold } - .terminal-1041266590-r6 { fill: #e2e3e3 } - .terminal-1041266590-r7 { fill: #42464b } - .terminal-1041266590-r8 { fill: #777a7e } - .terminal-1041266590-r9 { fill: #14191f } - .terminal-1041266590-r10 { fill: #ddedf9 } + .terminal-2055091312-r1 { fill: #c5c8c6 } + .terminal-2055091312-r2 { fill: #e3e3e3 } + .terminal-2055091312-r3 { fill: #e1e1e1 } + .terminal-2055091312-r4 { fill: #004578 } + .terminal-2055091312-r5 { fill: #e0e8ee;font-weight: bold } + .terminal-2055091312-r6 { fill: #e2e3e3 } + .terminal-2055091312-r7 { fill: #42464b } + .terminal-2055091312-r8 { fill: #777a7e } + .terminal-2055091312-r9 { fill: #14191f } + .terminal-2055091312-r10 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - OptionListApp + OptionListApp - - - - OptionListApp - - - - ────────────────────────────────────────────────────── - Aerilon - Aquaria - ──────────────────────────────────────────────────── - Canceron - Caprica - ──────────────────────────────────────────────────── - Gemenon - ──────────────────────────────────────────────────── - Leonis - Libran - ────────────────────────────────────────────────────▅▅ - Picon - ──────────────────────────────────────────────────── - Sagittaron - ────────────────────────────────────────────────────── - - - + + + + OptionListApp + + + + ────────────────────────────────────────────────────── + Aerilon + Aquaria + ──────────────────────────────────────────────────── + Canceron + Caprica + ──────────────────────────────────────────────────── + Gemenon + ──────────────────────────────────────────────────── + Leonis + Libran + ────────────────────────────────────────────────────▅▅ + Picon + ──────────────────────────────────────────────────── + Sagittaron + ────────────────────────────────────────────────────── + + + @@ -19054,140 +19045,140 @@ font-weight: 700; } - .terminal-1620527509-matrix { + .terminal-1395459687-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1620527509-title { + .terminal-1395459687-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1620527509-r1 { fill: #c5c8c6 } - .terminal-1620527509-r2 { fill: #e3e3e3 } - .terminal-1620527509-r3 { fill: #e1e1e1 } - .terminal-1620527509-r4 { fill: #004578 } - .terminal-1620527509-r5 { fill: #e0e8ee;font-weight: bold;font-style: italic; } - .terminal-1620527509-r6 { fill: #e2e3e3 } - .terminal-1620527509-r7 { fill: #e0e8ee;font-weight: bold } - .terminal-1620527509-r8 { fill: #14191f } - .terminal-1620527509-r9 { fill: #e2e3e3;font-style: italic; } - .terminal-1620527509-r10 { fill: #e2e3e3;font-weight: bold } - .terminal-1620527509-r11 { fill: #ddedf9 } + .terminal-1395459687-r1 { fill: #c5c8c6 } + .terminal-1395459687-r2 { fill: #e3e3e3 } + .terminal-1395459687-r3 { fill: #e1e1e1 } + .terminal-1395459687-r4 { fill: #004578 } + .terminal-1395459687-r5 { fill: #e0e8ee;font-weight: bold;font-style: italic; } + .terminal-1395459687-r6 { fill: #e2e3e3 } + .terminal-1395459687-r7 { fill: #e0e8ee;font-weight: bold } + .terminal-1395459687-r8 { fill: #14191f } + .terminal-1395459687-r9 { fill: #e2e3e3;font-style: italic; } + .terminal-1395459687-r10 { fill: #e2e3e3;font-weight: bold } + .terminal-1395459687-r11 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - OptionListApp + OptionListApp - - - - OptionListApp - - - - ────────────────────────────────────────────────────── -                   Data for Aerilon                   - ┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ - Patron God   Population    Capital City   ▂▂ - ┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ - Demeter      1.2 Billion   Gaoth           - └───────────────┴────────────────┴─────────────────┘ -                   Data for Aquaria                   - ┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ - Patron God    Population   Capital City    - ┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ - Hermes        75,000       None            - └────────────────┴───────────────┴─────────────────┘ -                  Data for Canceron                   - ┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ - ────────────────────────────────────────────────────── - - - + + + + OptionListApp + + + + ────────────────────────────────────────────────────── +                   Data for Aerilon                   + ┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ + Patron God   Population    Capital City   ▂▂ + ┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ + Demeter      1.2 Billion   Gaoth           + └───────────────┴────────────────┴─────────────────┘ +                   Data for Aquaria                   + ┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ + Patron God    Population   Capital City    + ┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ + Hermes        75,000       None            + └────────────────┴───────────────┴─────────────────┘ +                  Data for Canceron                   + ┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ + ────────────────────────────────────────────────────── + + + @@ -19376,136 +19367,136 @@ font-weight: 700; } - .terminal-1392305496-matrix { + .terminal-3980370474-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1392305496-title { + .terminal-3980370474-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1392305496-r1 { fill: #ffff00 } - .terminal-1392305496-r2 { fill: #e3e3e3 } - .terminal-1392305496-r3 { fill: #c5c8c6 } - .terminal-1392305496-r4 { fill: #e1e1e1 } - .terminal-1392305496-r5 { fill: #dde8f3;font-weight: bold } - .terminal-1392305496-r6 { fill: #ddedf9 } + .terminal-3980370474-r1 { fill: #ffff00 } + .terminal-3980370474-r2 { fill: #e3e3e3 } + .terminal-3980370474-r3 { fill: #c5c8c6 } + .terminal-3980370474-r4 { fill: #e1e1e1 } + .terminal-3980370474-r5 { fill: #dde8f3;font-weight: bold } + .terminal-3980370474-r6 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - Layers + Layers - - - - ──────────────────────────────────Layers - It's full of stars! My God! It's full of sta - - This should float over the top - - - ────────────────────────────────── - - - - - - - - - - - - - - - - -  T  Toggle Screen  + + + + ──────────────────────────────────Layers + It's full of stars! My God! It's full of sta + + This should float over the top + + + ────────────────────────────────── + + + + + + + + + + + + + + + + +  T  Toggle Screen  @@ -19535,136 +19526,136 @@ font-weight: 700; } - .terminal-3727479996-matrix { + .terminal-1053593998-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3727479996-title { + .terminal-1053593998-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3727479996-r1 { fill: #ffff00 } - .terminal-3727479996-r2 { fill: #e3e3e3 } - .terminal-3727479996-r3 { fill: #c5c8c6 } - .terminal-3727479996-r4 { fill: #ddeedd } - .terminal-3727479996-r5 { fill: #dde8f3;font-weight: bold } - .terminal-3727479996-r6 { fill: #ddedf9 } + .terminal-1053593998-r1 { fill: #ffff00 } + .terminal-1053593998-r2 { fill: #e3e3e3 } + .terminal-1053593998-r3 { fill: #c5c8c6 } + .terminal-1053593998-r4 { fill: #ddeedd } + .terminal-1053593998-r5 { fill: #dde8f3;font-weight: bold } + .terminal-1053593998-r6 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - Layers + Layers - - - - ──────────────────────────────────Layers - It's full of stars! My God! It's full of sta - - This should float over the top - - - ────────────────────────────────── - - - - - - - - - - - - - - - - -  T  Toggle Screen  + + + + ──────────────────────────────────Layers + It's full of stars! My God! It's full of sta + + This should float over the top + + + ────────────────────────────────── + + + + + + + + + + + + + + + + +  T  Toggle Screen  @@ -19694,142 +19685,142 @@ font-weight: 700; } - .terminal-1570661136-matrix { + .terminal-700023403-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1570661136-title { + .terminal-700023403-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1570661136-r1 { fill: #c5c8c6 } - .terminal-1570661136-r2 { fill: #eae3e5 } - .terminal-1570661136-r3 { fill: #e8e0e7 } - .terminal-1570661136-r4 { fill: #efe9e4 } - .terminal-1570661136-r5 { fill: #ede6e6 } - .terminal-1570661136-r6 { fill: #efeedf } - .terminal-1570661136-r7 { fill: #e9eee5 } - .terminal-1570661136-r8 { fill: #e2edeb } - .terminal-1570661136-r9 { fill: #e4eee8;font-weight: bold } - .terminal-1570661136-r10 { fill: #dfebed;font-weight: bold } - .terminal-1570661136-r11 { fill: #dfe9ed } - .terminal-1570661136-r12 { fill: #e3e6eb;font-weight: bold } - .terminal-1570661136-r13 { fill: #e6e3e9 } + .terminal-700023403-r1 { fill: #c5c8c6 } + .terminal-700023403-r2 { fill: #eae3e5 } + .terminal-700023403-r3 { fill: #e8e0e7 } + .terminal-700023403-r4 { fill: #efe9e4 } + .terminal-700023403-r5 { fill: #ede6e6 } + .terminal-700023403-r6 { fill: #efeedf } + .terminal-700023403-r7 { fill: #e9eee5 } + .terminal-700023403-r8 { fill: #e2edeb } + .terminal-700023403-r9 { fill: #e4eee8;font-weight: bold } + .terminal-700023403-r10 { fill: #dfebed;font-weight: bold } + .terminal-700023403-r11 { fill: #dfe9ed } + .terminal-700023403-r12 { fill: #e3e6eb;font-weight: bold } + .terminal-700023403-r13 { fill: #e6e3e9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - PlaceholderApp + PlaceholderApp - - - - - Placeholder p2 here! - This is a custom label for p1. - #p4 - #p3#p5Placeholde - r - - Lorem ipsum dolor sit  - 26 x 6amet, consectetur 27 x 6 - adipiscing elit. Etiam  - feugiat ac elit sit amet  - - - Lorem ipsum dolor sit amet,  - consectetur adipiscing elit. Etiam 40 x 6 - feugiat ac elit sit amet accumsan.  - Suspendisse bibendum nec libero quis  - gravida. Phasellus id eleifend ligula. - Nullam imperdiet sem tellus, sed  - vehicula nisl faucibus sit amet. Lorem ipsum dolor sit amet,  - Praesent iaculis tempor ultricies. Sedconsectetur adipiscing elit. Etiam  - lacinia, tellus id rutrum lacinia, feugiat ac elit sit amet accumsan.  - sapien sapien congue mauris, sit amet Suspendisse bibendum nec libero quis  + + + + + Placeholder p2 here! + This is a custom label for p1. + #p4 + #p3#p5Placeholde + r + + Lorem ipsum dolor sit  + 26 x 6amet, consectetur 27 x 6 + adipiscing elit. Etiam  + feugiat ac elit sit amet  + + + Lorem ipsum dolor sit amet,  + consectetur adipiscing elit. Etiam 40 x 6 + feugiat ac elit sit amet accumsan.  + Suspendisse bibendum nec libero quis  + gravida. Phasellus id eleifend ligula. + Nullam imperdiet sem tellus, sed  + vehicula nisl faucibus sit amet. Lorem ipsum dolor sit amet,  + Praesent iaculis tempor ultricies. Sedconsectetur adipiscing elit. Etiam  + lacinia, tellus id rutrum lacinia, feugiat ac elit sit amet accumsan.  + sapien sapien congue mauris, sit amet Suspendisse bibendum nec libero quis  @@ -20015,135 +20006,135 @@ font-weight: 700; } - .terminal-230009450-matrix { + .terminal-1426024135-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-230009450-title { + .terminal-1426024135-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-230009450-r1 { fill: #c5c8c6 } - .terminal-230009450-r2 { fill: #e1e1e1 } - .terminal-230009450-r3 { fill: #4ebf71 } - .terminal-230009450-r4 { fill: #dde8f3;font-weight: bold } - .terminal-230009450-r5 { fill: #ddedf9 } + .terminal-1426024135-r1 { fill: #c5c8c6 } + .terminal-1426024135-r2 { fill: #4ebf71 } + .terminal-1426024135-r3 { fill: #e1e1e1 } + .terminal-1426024135-r4 { fill: #dde8f3;font-weight: bold } + .terminal-1426024135-r5 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - IndeterminateProgressBar + IndeterminateProgressBar - - - - - - - - - - - - - - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━100%--:--:-- - - - - - - - - - - - -  S  Start  + + + + + + + + + + + + + + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━100%--:--:-- + + + + + + + + + + + +  S  Start  @@ -20173,137 +20164,136 @@ font-weight: 700; } - .terminal-3162092160-matrix { + .terminal-1998155485-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3162092160-title { + .terminal-1998155485-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3162092160-r1 { fill: #c5c8c6 } - .terminal-3162092160-r2 { fill: #e1e1e1 } - .terminal-3162092160-r3 { fill: #b93c5b } - .terminal-3162092160-r4 { fill: #1e1e1e } - .terminal-3162092160-r5 { fill: #e1e1e1;text-decoration: underline; } - .terminal-3162092160-r6 { fill: #dde8f3;font-weight: bold } - .terminal-3162092160-r7 { fill: #ddedf9 } + .terminal-1998155485-r1 { fill: #c5c8c6 } + .terminal-1998155485-r2 { fill: #b93c5b } + .terminal-1998155485-r3 { fill: #1e1e1e } + .terminal-1998155485-r4 { fill: #e1e1e1;text-decoration: underline; } + .terminal-1998155485-r5 { fill: #dde8f3;font-weight: bold } + .terminal-1998155485-r6 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - StyledProgressBar + StyledProgressBar - - - - - - - - - - - - - - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━100%--:--:-- - - - - - - - - - - - -  S  Start  + + + + + + + + + + + + + + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━100%--:--:-- + + + + + + + + + + + +  S  Start  @@ -20333,136 +20323,136 @@ font-weight: 700; } - .terminal-1630089489-matrix { + .terminal-836496735-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1630089489-title { + .terminal-836496735-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1630089489-r1 { fill: #c5c8c6 } - .terminal-1630089489-r2 { fill: #e1e1e1 } - .terminal-1630089489-r3 { fill: #fea62b } - .terminal-1630089489-r4 { fill: #323232 } - .terminal-1630089489-r5 { fill: #dde8f3;font-weight: bold } - .terminal-1630089489-r6 { fill: #ddedf9 } + .terminal-836496735-r1 { fill: #c5c8c6 } + .terminal-836496735-r2 { fill: #fea62b } + .terminal-836496735-r3 { fill: #323232 } + .terminal-836496735-r4 { fill: #e1e1e1 } + .terminal-836496735-r5 { fill: #dde8f3;font-weight: bold } + .terminal-836496735-r6 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - IndeterminateProgressBar + IndeterminateProgressBar - - - - - - - - - - - - - - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━39%00:00:07 - - - - - - - - - - - -  S  Start  + + + + + + + + + + + + + + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━39%00:00:07 + + + + + + + + + + + +  S  Start  @@ -20492,138 +20482,137 @@ font-weight: 700; } - .terminal-1532901142-matrix { + .terminal-1783624548-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1532901142-title { + .terminal-1783624548-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1532901142-r1 { fill: #c5c8c6 } - .terminal-1532901142-r2 { fill: #e1e1e1 } - .terminal-1532901142-r3 { fill: #004578 } - .terminal-1532901142-r4 { fill: #152939 } - .terminal-1532901142-r5 { fill: #1e1e1e } - .terminal-1532901142-r6 { fill: #e1e1e1;text-decoration: underline; } - .terminal-1532901142-r7 { fill: #dde8f3;font-weight: bold } - .terminal-1532901142-r8 { fill: #ddedf9 } + .terminal-1783624548-r1 { fill: #c5c8c6 } + .terminal-1783624548-r2 { fill: #004578 } + .terminal-1783624548-r3 { fill: #152939 } + .terminal-1783624548-r4 { fill: #1e1e1e } + .terminal-1783624548-r5 { fill: #e1e1e1;text-decoration: underline; } + .terminal-1783624548-r6 { fill: #dde8f3;font-weight: bold } + .terminal-1783624548-r7 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - StyledProgressBar + StyledProgressBar - - - - - - - - - - - - - - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━39%00:00:07 - - - - - - - - - - - -  S  Start  + + + + + + + + + + + + + + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━39%00:00:07 + + + + + + + + + + + +  S  Start  @@ -20653,136 +20642,136 @@ font-weight: 700; } - .terminal-3440292978-matrix { + .terminal-2036756687-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3440292978-title { + .terminal-2036756687-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3440292978-r1 { fill: #c5c8c6 } - .terminal-3440292978-r2 { fill: #e1e1e1 } - .terminal-3440292978-r3 { fill: #323232 } - .terminal-3440292978-r4 { fill: #b93c5b } - .terminal-3440292978-r5 { fill: #dde8f3;font-weight: bold } - .terminal-3440292978-r6 { fill: #ddedf9 } + .terminal-2036756687-r1 { fill: #c5c8c6 } + .terminal-2036756687-r2 { fill: #323232 } + .terminal-2036756687-r3 { fill: #b93c5b } + .terminal-2036756687-r4 { fill: #e1e1e1 } + .terminal-2036756687-r5 { fill: #dde8f3;font-weight: bold } + .terminal-2036756687-r6 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - IndeterminateProgressBar + IndeterminateProgressBar - - - - - - - - - - - - - - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━--%--:--:-- - - - - - - - - - - - -  S  Start  + + + + + + + + + + + + + + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━--%--:--:-- + + + + + + + + + + + +  S  Start  @@ -20812,138 +20801,137 @@ font-weight: 700; } - .terminal-4046569674-matrix { + .terminal-4086988071-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-4046569674-title { + .terminal-4086988071-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-4046569674-r1 { fill: #c5c8c6 } - .terminal-4046569674-r2 { fill: #e1e1e1 } - .terminal-4046569674-r3 { fill: #fea62b } - .terminal-4046569674-r4 { fill: #004578 } - .terminal-4046569674-r5 { fill: #1e1e1e } - .terminal-4046569674-r6 { fill: #e1e1e1;text-decoration: underline; } - .terminal-4046569674-r7 { fill: #dde8f3;font-weight: bold } - .terminal-4046569674-r8 { fill: #ddedf9 } + .terminal-4086988071-r1 { fill: #c5c8c6 } + .terminal-4086988071-r2 { fill: #fea62b } + .terminal-4086988071-r3 { fill: #004578 } + .terminal-4086988071-r4 { fill: #1e1e1e } + .terminal-4086988071-r5 { fill: #e1e1e1;text-decoration: underline; } + .terminal-4086988071-r6 { fill: #dde8f3;font-weight: bold } + .terminal-4086988071-r7 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - StyledProgressBar + StyledProgressBar - - - - - - - - - - - - - - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━--%--:--:-- - - - - - - - - - - - -  S  Start  + + + + + + + + + + + + + + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━--%--:--:-- + + + + + + + + + + + +  S  Start  @@ -21456,137 +21444,137 @@ font-weight: 700; } - .terminal-1869274227-matrix { + .terminal-2779683141-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1869274227-title { + .terminal-2779683141-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1869274227-r1 { fill: #c5c8c6 } - .terminal-1869274227-r2 { fill: #e3e3e3 } - .terminal-1869274227-r3 { fill: #008000 } - .terminal-1869274227-r4 { fill: #ffff00 } - .terminal-1869274227-r5 { fill: #e1e1e1 } - .terminal-1869274227-r6 { fill: #dde8f3;font-weight: bold } - .terminal-1869274227-r7 { fill: #ddedf9 } + .terminal-2779683141-r1 { fill: #c5c8c6 } + .terminal-2779683141-r2 { fill: #e3e3e3 } + .terminal-2779683141-r3 { fill: #008000 } + .terminal-2779683141-r4 { fill: #ffff00 } + .terminal-2779683141-r5 { fill: #e1e1e1 } + .terminal-2779683141-r6 { fill: #dde8f3;font-weight: bold } + .terminal-2779683141-r7 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - VerticalRemoveApp + VerticalRemoveApp - - - - VerticalRemoveApp - ────────────────────────────────────────────────────────────────────────────── - ──────────────────── - This is a test label - ──────────────────── - ────────────────────────────────────────────────────────────────────────────── - - - - - - - - - - - - - - - - - -  A  Add  D  Delete  + + + + VerticalRemoveApp + ────────────────────────────────────────────────────────────────────────────── + ──────────────────── + This is a test label + ──────────────────── + ────────────────────────────────────────────────────────────────────────────── + + + + + + + + + + + + + + + + + +  A  Add  D  Delete  @@ -21616,135 +21604,135 @@ font-weight: 700; } - .terminal-1316892474-matrix { + .terminal-3992644605-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1316892474-title { + .terminal-3992644605-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1316892474-r1 { fill: #c5c8c6 } - .terminal-1316892474-r2 { fill: #e3e3e3 } - .terminal-1316892474-r3 { fill: #e1e1e1 } - .terminal-1316892474-r4 { fill: #dde8f3;font-weight: bold } - .terminal-1316892474-r5 { fill: #ddedf9 } + .terminal-3992644605-r1 { fill: #c5c8c6 } + .terminal-3992644605-r2 { fill: #e3e3e3 } + .terminal-3992644605-r3 { fill: #e1e1e1 } + .terminal-3992644605-r4 { fill: #dde8f3;font-weight: bold } + .terminal-3992644605-r5 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - ModalApp + ModalApp - - - - ModalApp - B - - - - - - - - - - - - - - - - - - - - - -  A  Push screen A  + + + + ModalApp + B + + + + + + + + + + + + + + + + + + + + + +  A  Push screen A  @@ -22087,134 +22075,618 @@ font-weight: 700; } - .terminal-1647606097-matrix { + .terminal-2749576739-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1647606097-title { + .terminal-2749576739-title { font-size: 18px; font-weight: bold; font-family: arial; } - .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 } + .terminal-2749576739-r1 { fill: #c5c8c6 } + .terminal-2749576739-r2 { fill: #e3e3e3 } + .terminal-2749576739-r3 { fill: #ff0000 } + .terminal-2749576739-r4 { fill: #dde2e8 } + .terminal-2749576739-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 + ────────────────────────────────────────────────────────────────────────────── + + + + + + ''' +# --- +# name: test_select + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SelectApp + + + + + + + + + + SelectApp + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Select + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + + + + + + + + + + + + + + + + + + + ''' +# --- +# name: test_select_expanded + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SelectApp + + + + + + + + + + SelectApp + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Select + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Select + 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. + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + + + + + + + + + + ''' +# --- +# name: test_select_expanded_changed + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SelectApp + + + + + + + + + + I must not fear. + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + I must not fear. + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + + + + + + + + + + + + + @@ -22245,136 +22717,136 @@ font-weight: 700; } - .terminal-2914557706-matrix { + .terminal-932889121-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2914557706-title { + .terminal-932889121-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2914557706-r1 { fill: #e1e1e1 } - .terminal-2914557706-r2 { fill: #c5c8c6 } - .terminal-2914557706-r3 { fill: #e1e1e1;font-weight: bold } - .terminal-2914557706-r4 { fill: #1e1e1e } - .terminal-2914557706-r5 { fill: #0178d4 } - .terminal-2914557706-r6 { fill: #e2e3e3 } - .terminal-2914557706-r7 { fill: #e3e8e8 } + .terminal-932889121-r1 { fill: #e1e1e1 } + .terminal-932889121-r2 { fill: #c5c8c6 } + .terminal-932889121-r3 { fill: #e1e1e1;font-weight: bold } + .terminal-932889121-r4 { fill: #1e1e1e } + .terminal-932889121-r5 { fill: #0178d4 } + .terminal-932889121-r6 { fill: #e2e3e3 } + .terminal-932889121-r7 { fill: #e3e8e8 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - SwitchApp + SwitchApp - - - - - - - - Example switches - - - ▔▔▔▔▔▔▔▔ - off:      - ▁▁▁▁▁▁▁▁ - ▔▔▔▔▔▔▔▔ - on:       - ▁▁▁▁▁▁▁▁ - ▔▔▔▔▔▔▔▔ - focused:  - ▁▁▁▁▁▁▁▁ - ▔▔▔▔▔▔▔▔ - custom:   - ▁▁▁▁▁▁▁▁ - - - - + + + + + + + + Example switches + + + ▔▔▔▔▔▔▔▔ + off:      + ▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔ + on:       + ▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔ + focused:  + ▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔ + custom:   + ▁▁▁▁▁▁▁▁ + + + + diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index fde61ae64c..ea5e321538 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -230,6 +230,23 @@ def test_progress_bar_completed_styled(snap_compare): assert snap_compare(WIDGET_EXAMPLES_DIR / "progress_bar_styled_.py", press=["u"]) +def test_select(snap_compare): + assert snap_compare(WIDGET_EXAMPLES_DIR / "select_widget.py") + + +def test_select_expanded(snap_compare): + assert snap_compare( + WIDGET_EXAMPLES_DIR / "select_widget.py", press=["tab", "enter"] + ) + + +def test_select_expanded_changed(snap_compare): + assert snap_compare( + WIDGET_EXAMPLES_DIR / "select_widget.py", + press=["tab", "enter", "down", "enter"], + ) + + # --- CSS properties --- # We have a canonical example for each CSS property that is shown in their docs. # If any of these change, something has likely broken, so snapshot each of them. diff --git a/tests/test_binding.py b/tests/test_binding.py index e73ecfb3d1..5c545d7a65 100644 --- a/tests/test_binding.py +++ b/tests/test_binding.py @@ -52,7 +52,7 @@ def test_bindings_merge_overlap(): def test_bad_binding_tuple(): with pytest.raises(BindingError): - _ = _Bindings((("a", "action"),)) + _ = _Bindings((("a",),)) with pytest.raises(BindingError): _ = _Bindings((("a", "action", "description", "too much"),)) diff --git a/tests/test_geometry.py b/tests/test_geometry.py index c8c663df54..b36845c390 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -447,3 +447,15 @@ def test_split_horizontal_negative(): Region(10, 5, 22, 14), Region(10, 19, 22, 1), ) + + +def test_translate_inside(): + # Needs to be moved up + assert Region(10, 20, 10, 20).translate_inside(Region(0, 0, 30, 25)) == Region( + 10, 5, 10, 20 + ) + + # Already inside + assert Region(10, 10, 20, 5).translate_inside(Region(0, 0, 100, 100)) == Region( + 10, 10, 20, 5 + ) diff --git a/tests/test_spatial_map.py b/tests/test_spatial_map.py index 413ca4cad6..6b053633c7 100644 --- a/tests/test_spatial_map.py +++ b/tests/test_spatial_map.py @@ -44,9 +44,9 @@ def test_get_values_in_region() -> None: spatial_map.insert( [ - (Region(10, 5, 5, 5), False, "foo"), - (Region(5, 20, 5, 5), False, "bar"), - (Region(0, 0, 40, 1), True, "title"), + (Region(10, 5, 5, 5), False, False, "foo"), + (Region(5, 20, 5, 5), False, False, "bar"), + (Region(0, 0, 40, 1), True, False, "title"), ] )