diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
index 1772620e98..b0fb210989 100644
--- a/.github/workflows/pythonpackage.yml
+++ b/.github/workflows/pythonpackage.yml
@@ -21,6 +21,17 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
+
+ # Python 3.9 is on macos-13 but not macos-latest (macos-14-arm64)
+ # https://github.com/actions/setup-python/issues/696#issuecomment-1637587760
+ exclude:
+ - { python-version: "3.8", os: "macos-latest" }
+ - { python-version: "3.9", os: "macos-latest" }
+ - { python-version: "3.11", os: "macos-latest" }
+ include:
+ - { python-version: "3.8", os: "macos-13" }
+ - { python-version: "3.9", os: "macos-13" }
+ - { python-version: "3.11", os: "macos-13" }
defaults:
run:
shell: bash
@@ -32,18 +43,18 @@ jobs:
uses: actions/setup-python@v4.7.1
with:
python-version: ${{ matrix.python-version }}
- cache: 'poetry'
+ cache: "poetry"
- name: Install dependencies
run: poetry install --no-interaction --extras syntax
if: ${{ matrix.python-version != '3.12' }}
- - name: Install dependencies for 3.12 # https://github.com/Textualize/textual/issues/3491#issuecomment-1854156476
+ - name: Install dependencies for 3.12 # https://github.com/Textualize/textual/issues/3491#issuecomment-1854156476
run: poetry install --no-interaction
if: ${{ matrix.python-version == '3.12' }}
- name: Test with pytest
run: |
poetry run pytest tests -v --cov=./src/textual --cov-report=xml:./coverage.xml --cov-report term-missing
if: ${{ matrix.python-version != '3.12' }}
- - name: Test with pytest for 3.12 # https://github.com/Textualize/textual/issues/3491#issuecomment-1854156476
+ - name: Test with pytest for 3.12 # https://github.com/Textualize/textual/issues/3491#issuecomment-1854156476
run: |
poetry run pytest tests -v --cov=./src/textual --cov-report=xml:./coverage.xml --cov-report term-missing -m 'not syntax'
if: ${{ matrix.python-version == '3.12' }}
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index f53b9c445b..5724b5fb19 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -17,7 +17,7 @@ repos:
- id: end-of-file-fixer # ensures that a file is either empty, or ends with one newline
- id: mixed-line-ending # replaces or checks mixed line ending
- repo: https://github.com/pycqa/isort
- rev: '5.12.0'
+ rev: '5.13.2'
hooks:
- id: isort
name: isort (python)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 190fb35e17..bde2d05678 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,10 +7,338 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Unreleased
+### Added
+
+- `TextArea.line_number_start` reactive attribute https://github.com/Textualize/textual/pull/4471
+- Added `DOMNode.mutate_reactive` https://github.com/Textualize/textual/pull/4731
+- Added "quality" parameter to `textual.color.Gradient` https://github.com/Textualize/textual/pull/4739
+- Added `textual.color.Gradient.get_rich_color` https://github.com/Textualize/textual/pull/4739
+- `Widget.remove_children` now accepts an iterable if widgets in addition to a selector https://github.com/Textualize/textual/issues/4735
+- Raise `ValueError` with improved error message when number of cells inserted using `DataTable.add_row` doesn't match the number of columns in the table https://github.com/Textualize/textual/pull/4742
+- Add `Tree.move_cursor` to programmatically move the cursor without selecting the node https://github.com/Textualize/textual/pull/4753
+- Added `Footer` component style handling of padding for the key/description https://github.com/Textualize/textual/pull/4651
+
+### Fixed
+
+- Fixed issue with `Tabs` where disabled tabs could still be activated by clicking the underline https://github.com/Textualize/textual/issues/4701
+- Fixed scroll_visible with margin https://github.com/Textualize/textual/pull/4719
+- Fixed programmatically disabling button stuck in hover state https://github.com/Textualize/textual/pull/4724
+- Fixed `DataTable` poor performance on startup and focus change when rows contain multi-line content https://github.com/Textualize/textual/pull/4748
+- Fixed `Tree` and `DirectoryTree` horizontal scrolling off-by-2 https://github.com/Textualize/textual/pull/4744
+- Fixed text-opacity in component styles https://github.com/Textualize/textual/pull/4747
+- Ensure `Tree.select_node` sends `NodeSelected` message https://github.com/Textualize/textual/pull/4753
+
+### Changed
+
+- "Discover" hits in the command palette are no longer sorted alphabetically https://github.com/Textualize/textual/pull/4720
+- `TreeNodeSelected` messages are now posted before `TreeNodeExpanded` messages
+when an expandable node is selected https://github.com/Textualize/textual/pull/4753
+- `Markdown.LinkClicked.href` is now automatically unquoted https://github.com/Textualize/textual/pull/4749
+
+
+## [0.72.0] - 2024-07-09
+
+### Changed
+
+- More predictable DOM removals. https://github.com/Textualize/textual/pull/4708
+
+### Fixed
+
+- Fixed clicking separator in OptionList moving cursor https://github.com/Textualize/textual/issues/4710
+- Fixed scrolling issue in OptionList https://github.com/Textualize/textual/pull/4709
+
+## [0.71.0] - 2024-06-29
+
+### Changed
+
+- Snapshot tests will normalize SVG output so that changes with no visual impact don't break snapshots, but this release will break most of them.
+- Breaking change: `App.push_screen` now returns an Awaitable rather than a screen. https://github.com/Textualize/textual/pull/4672
+- Breaking change: `Screen.dismiss` now returns an Awaitable rather than a bool. https://github.com/Textualize/textual/pull/4672
+
+### Fixed
+
+- Fixed grid + keyline when the grid has auto dimensions https://github.com/Textualize/textual/pull/4680
+- Fixed mouse code leakage https://github.com/Textualize/textual/pull/4681
+- Fixed link inside markdown table not posting a `Markdown.LinkClicked` message https://github.com/Textualize/textual/issues/4683
+- Fixed issue with mouse movements on non-active screen https://github.com/Textualize/textual/pull/4688
+
+## [0.70.0] - 2024-06-19
+
+### Fixed
+
+- Fixed erroneous mouse 'ButtonDown' reporting for mouse movement when any-event mode is enabled in xterm. https://github.com/Textualize/textual/pull/3647
+
+## [0.69.0] - 2024-06-16
+
+### Added
+
+- Added `App.simulate_key` https://github.com/Textualize/textual/pull/4657
+
+### Fixed
+
+- Fixed issue with pop_screen launched from an action https://github.com/Textualize/textual/pull/4657
+
+### Changed
+
+- `App.check_bindings` is now private
+- `App.action_check_bindings` is now `App.action_simulate_key`
+
+## [0.68.0] - 2024-06-14
+
+### Added
+
+- Added `ContentSwitcher.add_content`
+
+### Fixed
+
+- Improved handling of non-tty input https://github.com/Textualize/textual/pull/4647
+
+## [0.67.1] - 2024-06-12
+
+### Changed
+
+- Reverts Vim keys in DataTable, provides alternatives https://github.com/Textualize/textual/pull/4638
+
+## [0.67.0] - 2024-06-11
+
+### Added
+
+- Added support for Kitty's key protocol https://github.com/Textualize/textual/pull/4631
+- `ctrl+pageup`/`ctrl+pagedown` will scroll page left/right in DataTable https://github.com/Textualize/textual/pull/4633
+- `g`/`G` will scroll to the top/bottom of the DataTable https://github.com/Textualize/textual/pull/4633
+- Added simple `hjkl` key bindings to move the cursor in DataTable https://github.com/Textualize/textual/pull/4633
+
+### Changed
+
+- `home` and `end` now works horizontally instead of vertically in DataTable https://github.com/Textualize/textual/pull/4633
+- `Tree` and `DirectoryTree` nodes now have a bigger click target, spanning the full line https://github.com/Textualize/textual/pull/4636
+
+### Fixed
+
+- Fixed pageup/pagedown behavior in DataTable https://github.com/Textualize/textual/pull/4633
+- Added `App.CLOSE_TIMEOUT` https://github.com/Textualize/textual/pull/4635
+- Fixed deadlock on shutdown https://github.com/Textualize/textual/pull/4635
+
+## [0.66.0] - 2024-06-08
+
+### Changed
+
+- `get_content_height` will now return 0 if the renderable is Falsey https://github.com/Textualize/textual/pull/4617
+- Buttons may not be pressed within their "active_effect_duration" to prevent inadvertent activations https://github.com/Textualize/textual/pull/4621
+- `Screen.dismiss` is now a noop if the screen isn't active. Previously it would raise a `ScreenStackError`, now it returns `False`. https://github.com/Textualize/textual/pull/4621
+- Increased window for escape processing to 100ms https://github.com/Textualize/textual/pull/4625
+- Tooltips are now hidden when any key is pressed https://github.com/Textualize/textual/pull/4625
+
+### Added
+
+- Added `Screen.is_active`
+- Added `icon` reactive to Header widget https://github.com/Textualize/textual/pull/4627
+- Added `time_format` reactive to Header widget https://github.com/Textualize/textual/pull/4627
+- Added `tooltip` parameter to input widgets https://github.com/Textualize/textual/pull/4625
+
+## [0.65.2] - 2024-06-06
+
+### Fixed
+
+- Fixed issue with notifications and screen switches https://github.com/Textualize/textual/pull/4615
+
+### Added
+
+- Added textual.rlock.RLock https://github.com/Textualize/textual/pull/4615
+
+## [0.65.1] - 2024-06-05
+
+### Fixed
+
+- Fixed hot reloading with hatch rule https://github.com/Textualize/textual/pull/4606
+- Fixed hatch style parsing https://github.com/Textualize/textual/pull/4606
+
+## [0.65.0] - 2024-06-05
+
+### Added
+
+- Added Command Palette Opened, Closed, and OptionHighlighted events https://github.com/Textualize/textual/pull/4600
+- Added hatch style https://github.com/Textualize/textual/pull/4603
+
+### Fixed
+
+- Fixed DataTable cursor flicker on scroll https://github.com/Textualize/textual/pull/4598
+
+### Changes
+
+- TabbedContent will automatically make tabs active when a widget in a pane is focused https://github.com/Textualize/textual/issues/4593
+
+## [0.64.0] - 2024-06-03
+
+### Fixed
+
+- Fix traceback on exit https://github.com/Textualize/textual/pull/4575
+- Fixed `Markdown.goto_anchor` no longer scrolling the heading into view https://github.com/Textualize/textual/pull/4583
+- Fixed Footer flicker on initial focus https://github.com/Textualize/textual/issues/4573
+
+## [0.63.6] - 2024-05-29
+
+### Fixed
+
+- Fixed issue with bindings not refreshing https://github.com/Textualize/textual/pull/4571
+
+## [0.63.5] - 2024-05-28
+
+### Fixed
+
+- Fixed data table disappearing from tabs https://github.com/Textualize/textual/pull/4567
+
+### Added
+
+- Added `Styles.is_auto_width` and `Style.is_auto_height`
+
+## [0.63.4] - 2024-05-26
+
+### Added
+
+- Added `immediate` switch to `Signal.publish`
+
+### Fixed
+
+- Fixed freeze in recompose from bindings https://github.com/Textualize/textual/pull/4558
+
+## [0.63.3] - 2024-05-24
+
+### Fixed
+
+- Fixed `Footer` grid size https://github.com/Textualize/textual/pull/4545
+- Fixed bindings not updated on auto focus https://github.com/Textualize/textual/pull/4551
+
+### Changed
+
+- Attempting to mount on a non-mounted widget now raises a MountError https://github.com/Textualize/textual/pull/4547
+
+## [0.63.2] - 2024-05-23
+
+### Fixed
+
+- Fixed issue with namespaces in links https://github.com/Textualize/textual/pull/4546
+
+## [0.63.1] - 2024-05-22
+
+### Fixed
+
+- Fixed display of multiple bindings https://github.com/Textualize/textual/pull/4543
+
+## [0.63.0] - 2024-05-22
+
+### Fixed
+
+- Fixed actions in links https://github.com/Textualize/textual/pull/4540
+
+### Changed
+
+- Breaking change: New Footer (likely a drop in replacement, unless you have customized styles) https://github.com/Textualize/textual/pull/4537
+- Stylistic changes to Markdown (simpler headers, less margin, etc) https://github.com/Textualize/textual/pull/4541
+
+## [0.62.0] - 2024-05-20
+
+### Added
+
+- Added `start` and `end` properties to Markdown Navigator
+- Added `Widget.anchor`, `Widget.clear_anchor`, and `Widget.is_anchored` https://github.com/Textualize/textual/pull/4530
+
+## [0.61.1] - 2024-05-19
+
+### Fixed
+
+- Fixed auto grid columns ignoring gutter https://github.com/Textualize/textual/issues/4522
+
+## [0.61.0] - 2024-05-18
+
+### Added
+
+- Added `App.get_default_screen` https://github.com/Textualize/textual/pull/4520
+- Added dynamic binding via `DOMNode.check_action` https://github.com/Textualize/textual/pull/4516
+- Added `"focused"` action namespace so you can bind a key to an action on the focused widget https://github.com/Textualize/textual/pull/4516
+- Added "focused" to allowed action namespaces https://github.com/Textualize/textual/pull/4516
+
+### Changed
+
+- Breaking change: Actions (as used in bindings) will no longer check the app if they are unhandled. This was undocumented anyway, and not that useful. https://github.com/Textualize/textual/pull/4516
+- Breaking change: Renamed `App.namespace_bindings` to `active_bindings`
+
+
+## [0.60.1] - 2024-05-15
+
+### Fixed
+
+- Dependency issue
+
+## [0.60.0] - 2024-05-14
+
+### Fixed
+
+- Fixed auto width not working for option lists https://github.com/Textualize/textual/pull/4507
+
+### Added
+
+- Added `DOMNode.query_children` https://github.com/Textualize/textual/pull/4508
+
+## [0.59.0] - 2024-05-11
+
+### Fixed
+
+- Fixed `SelectionList` issues after removing an option https://github.com/Textualize/textual/pull/4464
+- Fixed `ListView` bugs with the initial index https://github.com/Textualize/textual/pull/4452
+- Fixed `Select` not closing https://github.com/Textualize/textual/pull/4499
+- Fixed setting `loading=False` removing all child loading indicators https://github.com/Textualize/textual/pull/4499
+
+### Changed
+
+- When displaying a message using `App.exit()`, the console no longer highlights things such as numbers.
+
+### Added
+
+- Added `message_signal` to MessagePump, to listen to events sent to another widget. https://github.com/Textualize/textual/pull/4487
+- Added `Widget.suppress_click` https://github.com/Textualize/textual/pull/4499
+
+## [0.58.1] - 2024-05-01
+
+### Fixed
+
+- Fixed issue with Markdown mounting content lazily https://github.com/Textualize/textual/pull/4466
+- Fixed intermittent issue with scrolling to focus https://github.com/Textualize/textual/commit/567caf8acb196260adf6a0a6250e3ff5093056d0
+- Fixed issue with scrolling to center https://github.com/Textualize/textual/pull/4469
+
+
+## [0.58.0] - 2024-04-25
+
+### Fixed
+
+- Fixed `TextArea` to end mouse selection only if currently selecting https://github.com/Textualize/textual/pull/4436
+- Fixed issue with scroll_to_widget https://github.com/Textualize/textual/pull/4446
+- Fixed issue with margins https://github.com/Textualize/textual/pull/4441
+
+### Changed
+
+- Added argument to signal callbacks https://github.com/Textualize/textual/pull/4438
+
+## [0.57.1] - 2024-04-20
+
+### Fixed
+
+- Fixed an off-by-one error in the line number of the `Document.end` property https://github.com/Textualize/textual/issues/4426
+- Fixed setting scrollbar colors not updating the scrollbar https://github.com/Textualize/textual/pull/4433
+- Fixed flushing in inline mode https://github.com/Textualize/textual/pull/4435
+
+### Added
+
+- Added `Offset.clamp` and `Size.clamp_offset` https://github.com/Textualize/textual/pull/4435
+
+
+## [0.57.0] - 2024-04-19
+
### Fixed
- Fixed `Integer` validator missing failure description when not a number https://github.com/Textualize/textual/issues/4413
- Fixed a crash in `DataTable` if you clicked a link in the border https://github.com/Textualize/textual/issues/4410
+- Fixed issue with cursor position https://github.com/Textualize/textual/pull/4429
### Added
@@ -49,7 +377,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added
- Added `Size.with_width` and `Size.with_height` https://github.com/Textualize/textual/pull/4393
-
+
### Fixed
- Fixed issue with inline mode and multiple screens https://github.com/Textualize/textual/pull/4393
@@ -64,7 +392,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
-- Fixed mouse escape sequences being generated with `mouse=False`
+- Fixed mouse escape sequences being generated with `mouse=False`
## [0.55.0] - 2024-04-1
@@ -314,6 +642,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Fixed display of keys when used in conjunction with other keys https://github.com/Textualize/textual/pull/3050
- Fixed double detection of Escape on Windows https://github.com/Textualize/textual/issues/4038
+
## [0.47.1] - 2024-01-05
### Fixed
@@ -1883,6 +2212,35 @@ https://textual.textualize.io/blog/2022/11/08/version-040/#version-040
- New handler system for messages that doesn't require inheritance
- Improved traceback handling
+[0.72.0]: https://github.com/Textualize/textual/compare/v0.71.0...v0.72.0
+[0.71.0]: https://github.com/Textualize/textual/compare/v0.70.0...v0.71.0
+[0.70.0]: https://github.com/Textualize/textual/compare/v0.69.0...v0.70.0
+[0.69.0]: https://github.com/Textualize/textual/compare/v0.68.0...v0.69.0
+[0.68.0]: https://github.com/Textualize/textual/compare/v0.67.1...v0.68.0
+[0.67.1]: https://github.com/Textualize/textual/compare/v0.67.0...v0.67.1
+[0.67.0]: https://github.com/Textualize/textual/compare/v0.66.0...v0.67.0
+[0.66.0]: https://github.com/Textualize/textual/compare/v0.65.2...v0.66.0
+[0.65.2]: https://github.com/Textualize/textual/compare/v0.65.1...v0.65.2
+[0.65.1]: https://github.com/Textualize/textual/compare/v0.65.0...v0.65.1
+[0.65.0]: https://github.com/Textualize/textual/compare/v0.64.0...v0.65.0
+[0.64.0]: https://github.com/Textualize/textual/compare/v0.63.6...v0.64.0
+[0.63.6]: https://github.com/Textualize/textual/compare/v0.63.5...v0.63.6
+[0.63.5]: https://github.com/Textualize/textual/compare/v0.63.4...v0.63.5
+[0.63.4]: https://github.com/Textualize/textual/compare/v0.63.3...v0.63.4
+[0.63.3]: https://github.com/Textualize/textual/compare/v0.63.2...v0.63.3
+[0.63.2]: https://github.com/Textualize/textual/compare/v0.63.1...v0.63.2
+[0.63.1]: https://github.com/Textualize/textual/compare/v0.63.0...v0.63.1
+[0.63.0]: https://github.com/Textualize/textual/compare/v0.62.0...v0.63.0
+[0.62.0]: https://github.com/Textualize/textual/compare/v0.61.1...v0.62.0
+[0.61.1]: https://github.com/Textualize/textual/compare/v0.61.0...v0.61.1
+[0.61.0]: https://github.com/Textualize/textual/compare/v0.60.1...v0.61.0
+[0.60.1]: https://github.com/Textualize/textual/compare/v0.60.0...v0.60.1
+[0.60.0]: https://github.com/Textualize/textual/compare/v0.59.0...v0.60.0
+[0.59.0]: https://github.com/Textualize/textual/compare/v0.58.1...v0.59.0
+[0.58.1]: https://github.com/Textualize/textual/compare/v0.58.0...v0.58.1
+[0.58.0]: https://github.com/Textualize/textual/compare/v0.57.1...v0.58.0
+[0.57.1]: https://github.com/Textualize/textual/compare/v0.57.0...v0.57.1
+[0.57.0]: https://github.com/Textualize/textual/compare/v0.56.3...v0.57.0
[0.56.3]: https://github.com/Textualize/textual/compare/v0.56.2...v0.56.3
[0.56.2]: https://github.com/Textualize/textual/compare/v0.56.1...v0.56.2
[0.56.1]: https://github.com/Textualize/textual/compare/v0.56.0...v0.56.1
diff --git a/docs/FAQ.md b/docs/FAQ.md
index 4b280e9dda..a449c56f72 100644
--- a/docs/FAQ.md
+++ b/docs/FAQ.md
@@ -68,7 +68,7 @@ For more information on ANSI colors in Textual, see [Why no Ansi Themes?](#why-d
!!! tip
See [*How To Center Things*](https://textual.textualize.io/how-to/center-things/) in the
- Textual documentation for a more comprensive answer to this question.
+ Textual documentation for a more comprehensive answer to this question.
To center a widget within a container use
[`align`](https://textual.textualize.io/styles/align/). But remember that
@@ -270,7 +270,7 @@ work in different environments you can try them out with `textual keys`.
## Why doesn't Textual look good on macOS?
-You may find that the default macOS Terminal.app doesn't render Textual apps (and likely other TUIs) very well, particuarily when it comes to box characters.
+You may find that the default macOS Terminal.app doesn't render Textual apps (and likely other TUIs) very well, particularly when it comes to box characters.
For instance, you may find it displays misaligned blocks and lines like this:
diff --git a/docs/blog/images/calcinline.png b/docs/blog/images/calcinline.png
new file mode 100644
index 0000000000..1cf54bb507
Binary files /dev/null and b/docs/blog/images/calcinline.png differ
diff --git a/docs/blog/images/inline1.excalidraw.svg b/docs/blog/images/inline1.excalidraw.svg
new file mode 100644
index 0000000000..4f04fb237b
--- /dev/null
+++ b/docs/blog/images/inline1.excalidraw.svg
@@ -0,0 +1,21 @@
+
+
+ eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO2caW7bSFx1MDAxNoD/51x1MDAxNIIzP7qBuFL7XHUwMDEyYDCwZStyXHUwMDFj2+1VTmZcdTAwMWFcckqiLMZcdTAwMTQpkZRspVx1MDAxMWAwZ+hcdTAwMWNjljPlJPOKdkRKMlx1MDAxNcWb1EmUwLGruDxcdTAwMTa/t5fz+5NSaSVcdTAwMTl23ZVcdTAwMTelXHUwMDE197Lh+F4zci5WntnxgVx1MDAxYsVeXHUwMDE4wFx1MDAxNE1/jsN+1EiPbCdJN37x/HnHic7dpOs7XHJcdTAwMTdccry47/hx0m96IWqEnede4nbiv9mvu07H/Ws37DSTXGJlN1l1m15cdTAwMTJGV/dyfbfjXHUwMDA2SVxmV/87/FxcKv2efs1JXHUwMDE3uY3EXHTOfDc9IZ3KXHUwMDA0JJjxyeHdMEilpVJcdCOVXHUwMDE5zXvxXHUwMDA23C5xmzDZXHUwMDAykd1sxlx1MDAwZa3UX56u8lajfr6zj/VvXHUwMDAz1yHRRT+7a8vz/cNk6KdSNaIwjlfbTtJoZ0fESVx1MDAxNJ67Na+ZtD8vXm58dG5cdTAwMWPCQmRnRWH/rFx1MDAxZLixXVx1MDAwMzJcdTAwMWFccrtOw0uG6TPi0ejVQrwoZSOX9k6UIcxcZlWGXHUwMDFhZrRQajRtLyAl0lpcdTAwMWGuXHUwMDE55lRzbiZcdTAwMDQrhz68XHUwMDBlXHUwMDEw7ClOP5lodadxflx1MDAwNvJcdTAwMDXN0TFJ5Fx1MDAwNHHXieClZcddXFw/slx1MDAwMEGYMEJJw5RmLJOj7Xpn7Vx1MDAwNFx1MDAwZWFcdTAwMTIjTbDhXFwyJoimMpPGTV9cclx1MDAxMVxcK1xml8jOtjJ0t5opJb9mLyRcdTAwMDK+tuwpQd/38+tcdTAwMTk0r9dzbKJuJzZzwGWX6nebzlx1MDAxNVx1MDAxOERcdTAwMTEmKeZSgJSjed9cdTAwMGLOJy/nh43zjKV09MOzWzCsjChCmGCl4aVcbknmhtjpVVx1MDAwNqe7XHUwMDA3bqvSOKqel/1cdTAwMWHFXHUwMDFi61x1MDAwNVx1MDAxME+AuDh8OdJcdTAwMDLAoFx1MDAxNFOjXGah4/hqhGFcdTAwMWSoxFx1MDAwMpBRWj9cdTAwMWO+xCBDODVcXGqBQZBpfKlGQlx0hTU2RmEpmZ7GXHUwMDE3c0OtXHUwMDFhfjP4ur7vdeNcdTAwMWLhhftcdTAwMTTBK7li5Gvs78nxK+dQXGYvejtcdTAwMDde69xcdTAwMDSV+JeXRfZ3Jrrk8dBlXHUwMDFjXHSARVx1MDAwMppEXHUwMDEzg8ctr5JAXHUwMDBio5JcdTAwMDHZXHUwMDE4kOJ3Qfdpy1x1MDAxMVTQaWxcdENcdTAwMWN0XHUwMDA3nFx1MDAxZOhcdTAwMGblWvNpblx0XHUwMDA1OSU3YHIxZYxyNcktXHUwMDAwXHUwMDBilvtbsrozsFU5XHUwMDFiM4Gt4kRcdTAwMWH4Oze2g+5cdTAwMWXpuoPN9ZbgZ69cdTAwMTmpXVbLtSXHVlx1MDAxMsSNXHUwMDExlFBcclx1MDAxNlx1MDAxNYtxalx1MDAwNThxooQ2XHUwMDE4XFwx0fKO1NYxXHUwMDE2XHUwMDBmRS1cdTAwMDSANtigmHxcdTAwMTfYSlWELVx1MDAwM3NLhNBsbm533uDt/dPN3v4gpju7XXZcdTAwMTJcdTAwMGZcdTAwMWG/LTm32iCwgYJDLKshvuTj3EJcdTAwMTiBOVx1MDAwNJZcdTAwMWNsMeNE3IlbQutcdTAwMTA1P1x1MDAxOLdKQKzOXHQ131xmt4l7mdxcdTAwMThcIlx1MDAxNEZcYlxcW88oclB/idmzzsn62/CiXHUwMDE19Fx1MDAwZvc3TpOeXGLeXHUwMDBivdzRLVx1MDAxN1x1MDAxNCmIXHIwY0bAs04kZ1xuI8CDXHUwMDE5XGJcdTAwMWQ0VzznfiehJa79c/voXHUwMDE2zDpEKIaBJJgqxm/glopJTjlIXHUwMDA1XGbrXHUwMDA1Yio1hORUf1x1MDAwNaaZVGGQXHUwMDFjeu/Talx1MDAwMFx1MDAxZVx1MDAxYq04XHUwMDFkz1x1MDAxZo691JTflOOo41x1MDAwNY6/Mjaz5ntnluZcdTAwMTXfbY1jnnhccsdcdTAwMWZNJ2E3m23AnVx1MDAxYy9wo+mVXHQj78ze5ajwrvCcbnVkUVDu3dSd2LWzdlxc30oniTCTo6OckzCqXHUwMDE1pGPzR0CXeu2UvD5cdTAwMWGcvCtv1JPGsLKvPLLcWkmZLYpcdTAwMTBuuMBgwlxyXHUwMDFlzzmpxogxQVx1MDAxNbf+RIviksldtZJcdTAwMTKMXGZT2cvNlDGXLVxcKSPF0qQ2YoGp5ZU2mjyl96iNWfTyWVx1MDAxYv9S6lx1MDAwZZN2XHUwMDE4lLzAXCKPusPH1ctZ95/U0Fx1MDAxYlx1MDAxNdTcUkHJ5OhIQVx1MDAwNbYhXHUwMDA1o/O7TYdcZumx13ldebllZCu52KItvrPkXG5KJVx1MDAwMlx1MDAxZsVcdTAwMDSXRFKFc4FtXG5cbjZIQZpcdTAwMDYpNzdE6clcdTAwMTg0U1DaMi7ndylcblx0JOlccvpJclx1MDAxMF8rKCNcdTAwMTCFa/pcdTAwMWSp56eP//70xz9cdTAwMTf59+N//lx1MDAxMXz6418lr9NccqOklLS9uDTzXHUwMDAzXHUwMDA3X53RXG6jktXrXHUwMDEyvP4z9yeCf37xhTPsp1x1MDAxYnlB8lPw81x1MDAxY/f4+L9Fr81/XHUwMDFm11j+oOHPQsNcXK6LiJm+a3ZnjnA5OTzyYJA9U1tcdTAwMWKe24GpzfiClVtcdTAwMGX1anHjpFLeXHUwMDFjen7052jNXHTEId9cdTAwMTKUgF9cdTAwMThrV1x1MDAwZdP0ykBgr4WEIFx1MDAxM1x1MDAxNlx1MDAwNcs71SxcdTAwMWWhN6chMIHogyy8uUHyKD9Yby5f0J/qzVx0SNgxJ/NT3H7/+vByUD1+e+iL9XpMnMBcdTAwMWK2lz1cZlOIci6UwMZcdTAwMDae48VcdTAwMGJu0yRcdTAwMDW8QDjKJeeYPVx1MDAxY7730puDlFx1MDAwZkIkyG+/XHUwMDE5fGdVi1x1MDAxNS6C1+a98FLN/H3lkzZda1x1MDAxZZQjUj1uXHUwMDFlXHUwMDFjNdpq66R6seTVYsjxXHJYPKZcdTAwMDVcdTAwMDXDy+SE7dVcdTAwMWFcdTAwMDG6WsExmuW7zsvXnKPEdpRccv4+wNW0cFOPzfkgIzTztzmq+28q4dbh+erQK7d5r856tfLakoMrXHUwMDE50jaxXHUwMDA150JcdTAwMTlcdTAwMTZ40uoqXHUwMDA040yJtHt3p/1cdTAwMTBcdTAwMGbcnlx1MDAwM72TWkqyyIrVI3Kriquq1LZ7zNdY3Jc7nlx1MDAxYuNcdTAwMWVf9VuDau3dpnpVq95qJ88jgmtcYjhpKYhSVLHc1pDP2Fx1MDAxMiWxpFx1MDAxMGOSO8Zcblx1MDAwZtye00pcdI5cdTAwMDVZeFf53rAtLDTqwiCBMNugs3ZobmYvutH+5j7ZXHUwMDEwu1x1MDAxYvuy2Ze7ZH/TXe5cYpdcdTAwMGKBIOfBXG645Fx1MDAxOPLSySiBXCJbZSRcdTAwMTAvSVx1MDAwNS+keFx1MDAwZs9CXHUwMDFhdFx1MDAwNljHkK0tvEH3UDXH77NBR1Vx/Z/BmlOt8fyJ53FP7jjbtdO1zdW9gyZdq78/7peXWy0h3kVUUS5cdTAwMTUziimWlVwi0j2hhlwiSPFsRFxm5Fx1MDAxOVpcXP5/zP6cLVx0gFx1MDAwMqtcdTAwMWb9ueyYb7Q/XHUwMDA3IXqhfnJqgcFfUVx1MDAxOPLXvM6GqO329ereK1V/XelttypLrp9UI1xi8Vx1MDAxOIUn5ZBCT2zFolx1MDAxNFx1MDAxMckoXHUwMDEznCqueXGs96jtOaGxXmhQl2on+5otV3fTzlx1MDAxZlxymVx1MDAxOWcsVUPmR3vuXHUwMDA3XHKP2Z6jhWmfpmCoXHUwMDE4IfOX2EhvfbuqlcvLb99eXjr97kbz6HLJ3Vx1MDAxN+OIQV5cdTAwMDc+Styw/1x1MDAwYuJNJLFcdTAwMDT/JpWcVVx1MDAxYXYxI4zMdF9PW62GaZhcdTAwMWJKXHUwMDE1XHUwMDE0XHQsXHUwMDA1xpRzwvGNXowypLUxIIfdlkem8z/wK5xA1irvIVx1MDAwMfyMUFx1MDAwNlx1MDAxMbtcdTAwMWX5UOzsRudkZ4/AONi+PN9cdTAwMWVe+q3tpNojvai25Veyus5cdTAwMTitTlx1MDAxNIVcdTAwMTcro5lcdTAwMGbX381ypZLl+6VcdTAwMGbZXHUwMDA2pKSwIM1cdTAwMTTmkI3kXHUwMDA0+ZK2bPT97aNcck81XHUwMDBl+/XtNVapbe3t9e5VW5phYm9/j+pcdTAwMDKJXHUwMDE4YkZAslx1MDAwNeGTytec7flcdTAwMWFzZCRcdTAwMTd2+5PUbEa0t3h1XHUwMDExttZjf1liQeryaFx1MDAwNb8rhbpcdTAwMDFmZoo3Zlx1MDAxMG53vOr5XHUwMDBifrM1fFx1MDAwMaZffVx1MDAxMWWGXHUwMDExNlxubD9k6oTh8X2Fmk7sx79LYaFcdTAwMTBlrjRcdTAwMDKGpaKQjlxiIFlOo6xcdTAwMDRcYmJ/aVBcdTAwMThObJljXG5larRS1NxH6W9cdTAwMWFlelx1MDAxN5RT26xulebEiVx1MDAxMyXrXtD0grPJU9ygmc3kRL7+T1x1MDAwNLbmXGJG0pSp0bfyr2LEIVFnRHNJMDNgMXjusDOna99cdTAwMTSCjI1rMFx1MDAxOFQoa1CmlsV34qRcdTAwMWN2Op61ub+EXHUwMDEwb06KnT7SmlXHtutMvVx1MDAwNnio/Nyk3nbtXHUwMDE1x1x1MDAxZG32XSljO/1h9P2vz248epVyg6RUXHUwMDAwN4RccpCW6/zpq1xuKWNgkCtpofzi1YoxvrrcJMHZXHUwMDA1n+T/vZ1LxoVcdTAwMDVcdTAwMTj7aNpQOn9cdTAwMDB7vMferVV3XHUwMDFijvP2/M3uprOxeeRcdTAwMWQtd1x1MDAwMKvA1UlcYl+lxvC4dLw8XG4mXHUwMDFlaSo45pxSlf8ls6VzyFx1MDAwNixcdTAwMTiwI+6hZHrP/lhqXHUwMDEwLVx1MDAxNyfM64+fXFwrzIrT7Vx1MDAxZSZwyZFo8Ghe87qGk11mZeC5XHUwMDE36zeuu/3YmDiV32Lops/54cmH/1x1MDAwM7lI91xmIn0=
+
+
+
+
+ terminal $ python inline.py ╭──────────────────────────────────────────╮ │ import this │ │ for n in range(10): │ │ print(n) │ ╰──────────────────────────────────────────╯ terminal $ python inline.py ╭──────────────────────────────────────────╮ │ import this │ │ for n in range(10): │ │ print(n) │ ╰──────────────────────────────────────────╯
diff --git a/docs/blog/images/inline2.excalidraw.svg b/docs/blog/images/inline2.excalidraw.svg
new file mode 100644
index 0000000000..3f0f6f1ab2
--- /dev/null
+++ b/docs/blog/images/inline2.excalidraw.svg
@@ -0,0 +1,21 @@
+
+
+ eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO2a627bRlx1MDAxNoD/5ylcdTAwMDRlf7RAzMzMmWuAxcJ27Nhx41xc3NSut0VAkyOJNUWyJGVbKVxmXHUwMDE0fYbmMbq7z5Qn2TO0I0qUZKuOb73Qhi3O9fDMd+acM9RPXHUwMDBmWq12Ocxs+0mrbU9cdTAwMDI/jsLcP24/cuVHNi+iNMEqVt1cdTAwMTfpIFx1MDAwZqqWvbLMiiePXHUwMDFm9/380JZZ7Fx1MDAwN9Y7ioqBXHUwMDFmXHUwMDE35SCMUi9I+4+j0vaLf7m/237f/jNL+2GZe/UkSzaMyjQ/m8vGtm+TssDR/433rdZP1d8x6XJcdTAwMWKUftKNbdWhqqpcdTAwMDWkjJlm8XaaVNJSobk2Wkkxalx1MDAxMVx1MDAxNU9xwtKGWN1BoW1d44rar9+Q7GiLraytL29mS/L5yTc/RLv1vJ0ojnfKYVxcyVx1MDAxNeRpUSz1/DLo1S2KMk9cdTAwMGbtblx1MDAxNJa9T+pcdTAwMWIrXHUwMDFm9S1SVEXdK09cdTAwMDfdXmJcdTAwMGKnXHUwMDA1OipNMz+IyqErI2RUeqaKJ6265Fx1MDAwNO+48oBcdTAwMTFGqVx1MDAwMEo0UC5H1WdcdTAwMDNIT3AlJZdMXHRcdTAwMDGsKdlqXHUwMDFh44qgZFx1MDAwZkl11bJcdTAwMWT4wWFcdTAwMTdcdTAwMDVMwlGbMveTXCLzc1xct7rd8fkzXHUwMDBiXHUwMDA2XHUwMDFlXGIjlDSgNIBcdTAwMWG16Nmo2yuxXHRI4mlKXGbnXHUwMDEyQFDNamFcdTAwMGJbrY0mRoBSXHUwMDEyRlx1MDAxNU6EbDOsOPm+XpBcdTAwMWNcdNt0PZJBXHUwMDFjj+szXHTP9TlRceAq1saQq4dcdTAwMWFkoX9cdTAwMDZcdTAwMDZVXHUwMDE0JJOaaSlquOIoOWxcdTAwMGVcdTAwMTenwWHNUlV6+uhcblx1MDAxNFx1MDAxYkPmQqy0MIpcdTAwMThtXHUwMDE2hvjHr7rrq2+Wj9f2uqTc3szWNlx1MDAwM39jXHUwMDBlxFxyXHUwMDEw71xmX+NR4Fx1MDAwMjjnhCjJTYNe5UmpXHUwMDA1Z6CMJOPV104vNZ6hnFx1MDAxOY7zXHUwMDExZeg0vUx7Qlx0RZBRXFxcdTAwMTgpQTfpXHUwMDE1XHUwMDFjgKEhij9ccr02jqOsmMmuXHUwMDE2MI9dzXEtJfDF0e2+j168XHUwMDEw2TqYcqhXzP7zb/rv315cdTAwMDVdemvoXG7taW64UVpqQyVcdTAwMTI8yS6VXHUwMDFlXHUwMDAzMMo4KnBTa0r2u9h92PFcdTAwMDVcdTAwMTNsmltcblx1MDAxZSeMXHQj0Vx1MDAwYjCuNZ9cdTAwMDaXMk+gXHUwMDA3MLjlXHUwMDEyXHUwMDE0iXHVXHUwMDA01/UlnCui/lxu5Jqxx2ySXHUwMDBiwJkyv4Pc7bWXXHUwMDFi5bF+2SGr79ZcdTAwMGZiXHUwMDE28/f7nftNrlx1MDAxNp7SXHUwMDA0d13BiFZKq1x1MDAwNrjCI1x1MDAxNCFcdTAwMTGMaSXImJlfXHLcXHUwMDAzQsRNgUu5llxcMKn+XHUwMDEyWy7qalx1MDAxZbhC4V4jcVFcdTAwMTdcdTAwMDZ36+nhXHUwMDBifTLs7m1nvb2NV1svI1x1MDAwMUf3XHUwMDFiXFyK0GhcZlx1MDAwM1x1MDAxNG5Uwlx1MDAwMLrpXHUwMDA2udzjUtAq1EVi9GdcdTAwMDW7XHUwMDBmKTvQWt5cdTAwMTi5Qlx1MDAxYuCamj9cdTAwMGa5pT0pZ2HL5NxIgUpCccVAwcLc/rD79sSHr0Gvvn6z+3SfLNFnavN+R7lMSc9grFx1MDAwZkJcdTAwMDKT+LyNUIFg7Ek0brycUMVByrncUut+rlx1MDAxZeYqgVx0mDGYK1x1MDAwMmFcbvhcZnSZmEJVXHUwMDAyZcDUWFxi81x1MDAwN0C1lipNyp3ova3inInSdb9cdTAwMWbFw4llrVx1MDAxOK5YzvtR4sftiZrlOOo6otux7UyiXkaBXHUwMDFmj6rLNKtrXHUwMDAznMmPXHUwMDEym09rJs2jrpvl67mz4nPajdGu4o0tzoFfWFfryvWV7Fx1MDAxMoRolo7sXHUwMDEyWUVcdTAwMWUx3VnYLjl59tan20d+9JzRaOdN9mpJsfttl5J66Cw4hlx1MDAwZlxcYOJcdTAwMDeT7lx1MDAwNFxcSmhcdTAwMDTarZIuir85q2SUeFx1MDAwNsZimNpcdTAwMThrXHUwMDAz/WSMXG4loZzDXHUwMDFk+1xyReg4pNdojLUr+GSM/2hlw7KXJq0occR72fB2zfKi+ZtcdTAwMDY60z7N1eyTz81ThDuA0Fx1MDAxNOo1uMw8i63lYfx8XHUwMDA11nskXGJ3XHUwMDBls414S2T32zxcdTAwMDXxJCa9mF9cdTAwMGJcdTAwMDBkrlx1MDAxZaXChFx1MDAxYlx1MDAwZkP/KrdmnEvdkKs2T9YxlvPPOVx1MDAxYlx1MDAxMk6QWVFeLfDIPCWG4VxmQ9C7Nk82juiNmufHXHUwMDBmv3389ee7/P3wn++Sj7/+0or6WZqXrbJcdTAwMTdcdTAwMTWtXHUwMDBiL2x81qOT5i1n1y1cdTAwMDSga7+g5Msnl/RwV5ZHSflF8uVcdTAwMDJzfPjfXevmv7e7Wf5Nw1x1MDAxZoWGhVxcXHUwMDE3XHUwMDE1XHUwMDE3+q5cdTAwMGJfb1x1MDAwMJ3rwDDrk1x1MDAxMjd1tXiA2Vx1MDAwYsPO9ru9XHUwMDFmXHUwMDA3sYSlwVx0W9/v7r+731x1MDAxZYxhXGJcdIxi2qdcdTAwMThRXHUwMDA006hcdFx1MDAxN8ZcdTAwMTnxmCGcXGKXi4HiXHLBalx1MDAxN2axL9BLTto6gVx0zIzzXG7mXHRcIlx1MDAwNSHoJSknMz1cdTAwMTlcdTAwMDNPa1xmdkFSYJrOyFx1MDAwMYlcdTAwMDDsj20+3699gqjGXGLOS07nu7tRn7r3iIxju9594dM1sqo3l5dcdTAwMDa7+1n323ykiFx0Xv08T4/bo5rT80/343Ugp3PfaVx1MDAwM/IhheKLXHUwMDA3fGHk2+Dbbnm4WiztvVx1MDAxYa7oZ6+D19dqLmFauumvM+IznsRcXEtzQrTShky+zMbVQGvSXFxcdTAwMTJpwL1qvsfmXCJcdTAwMTU+giHmXHUwMDFhjkyuZC63XHUwMDA29JlBzYDZSD5cdTAwMGZmyo3WinJYfPO/2MTvYPNXl1x1MDAxZS5cYkxP8Fx1MDAxMSWbtfWDwuyFUFx1MDAwNVx1MDAwMIg7tvqcw4X5LFx1MDAwYuqhsinRWktFZ1x1MDAxZVVjloXJXHUwMDAyo0BcYroppae+k0E1+ih5Uyyz22V5TM1+Xq5ESVx1MDAxOCXdZlx1MDAxN5uEdc2YyOffWdpcXCBcdTAwMWOpsqZg4OQnuFx1MDAwNFxmd1x01LJcdTAwMTRcdTAwMTRcdTAwMTVdXHUwMDFmqjlV+pnTgoerQ1D51X5CzbSFx35Rrqb9fuT23FcpRpxNqasnWnbm2LP+1CrgM43XNe02cyNOOtr6U6tGu7pcdTAwMTl9/v7RzNZKeSCldF9cdTAwMDKQXHUwMDA0o5/xzlxcee49s+S0qmLissHmM+yuKXrr4Vx1MDAxZYz/d46+mqDtZ9lOiVx1MDAxNI2WXHUwMDE2aY7C86S3Vln7KLLHKzNtzV0uhKhcdTAwMTbHbT22Qvv0wen/XHUwMDAxc82RiyJ9
+
+
+
+
+ terminal $ python inline.py ╭──────────────────────────────────────────╮ │ import this │ │ for n in range(10): │ │ print(n) │ ╰──────────────────────────────────────────╯
diff --git a/docs/blog/posts/inline-mode.md b/docs/blog/posts/inline-mode.md
new file mode 100644
index 0000000000..0127932144
--- /dev/null
+++ b/docs/blog/posts/inline-mode.md
@@ -0,0 +1,148 @@
+---
+draft: false
+date: 2024-04-20
+categories:
+ - DevLog
+authors:
+ - willmcgugan
+---
+
+# Behind the Curtain of Inline Terminal Applications
+
+Textual recently added the ability to run *inline* terminal apps.
+You can see this in action if you run the [calculator example](https://github.com/Textualize/textual/blob/main/examples/calculator.py):
+
+![Inline Calculator](../images/calcinline.png)
+
+The application appears directly under the prompt, rather than occupying the full height of the screen—which is more typical of TUI applications.
+You can interact with this calculator using keys *or* the mouse.
+When you press ++ctrl+c++ the calculator disappears and returns you to the prompt.
+
+Here's another app that creates an inline code editor:
+
+=== "Video"
+
+
+ VIDEO
+
+
+
+=== "inline.py"
+ ```python
+ from textual.app import App, ComposeResult
+ from textual.widgets import TextArea
+
+
+ class InlineApp(App):
+ CSS = """
+ TextArea {
+ height: auto;
+ max-height: 50vh;
+ }
+ """
+
+ def compose(self) -> ComposeResult:
+ yield TextArea(language="python")
+
+
+ if __name__ == "__main__":
+ InlineApp().run(inline=True)
+
+ ```
+
+This post will cover some of what goes on under the hood to make such inline apps work.
+
+It's not going to go in to too much detail.
+I'm assuming most readers will be more interested in a birds-eye view rather than all the gory details.
+
+
+
+## Programming the terminal
+
+Firstly, let's recap how you program the terminal.
+Broadly speaking, the terminal is a device for displaying text.
+You write (or print) text to the terminal which typically appears at the end of a continually growing text buffer.
+In addition to text you can also send [escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code), which are short sequences of characters that instruct the terminal to do things such as change the text color, scroll, or other more exotic things.
+
+We only need a few of these escape codes to implement inline apps.
+
+!!! note
+
+ I will gloss over the exact characters used for these escape codes.
+ It's enough to know that they exist for now.
+ If you implement any of this yourself, refer to the [wikipedia article](https://en.wikipedia.org/wiki/ANSI_escape_code).
+
+## Rendering frames
+
+The first step is to display the app, which is simply text (possibly with escape sequences to change color and style).
+The lines are terminated with a newline character (`"\n"`), *except* for the very last line (otherwise we get a blank line a the end which we don't need).
+Rather than a final newline, we write an escape code that moves the *cursor* back to it's prior position.
+
+The cursor is where text will be written.
+It's the same cursor you see as you type.
+Normally it will be at the end of the text in the terminal, but it can be moved around terminal with escape codes.
+It can be made invisible (as in Textual apps), but the terminal will keep track of the cursor, even if it can not be seen.
+
+Textual moves the cursor back to its original starting position so that subsequent frames will overwrite the previous frame.
+
+Here's a diagram that shows how the cursor is positioned:
+
+!!! note
+
+ I've drawn the cursor in red, although it isn't typically visible.
+
+
+
+--8<-- "docs/blog/images/inline1.excalidraw.svg"
+
+
+
+There is an additional consideration that comes in to play when the output has less lines than the previous frame.
+If we were to write a shorter frame, it wouldn't fully overwrite the previous frame.
+We would be left with a few lines of a previous frame that wouldn't update.
+
+The solution to this problem is to write an escape code that clears lines from the cursor downwards before we write a smaller frame.
+You can see this in action in the above video.
+The inline app can grow or shrink in size, and still be anchored to the bottom of the terminal.
+
+## Cursor input
+
+The cursor tells the terminal where any text will be written by the app, but it also assumes this will be where the user enters text.
+If you enter CJK (Chinese Japanese Korean) text in to the terminal, you will typically see a floating control that points where new text will be written.
+If you are on a Mac, the emoji entry dialog (++ctrl+cmd+space++) will also point at the current cursor position. To make this work in a sane way, we need to move the terminal's cursor to where any new text will appear.
+
+The following diagram shows the cursor moving to the point where new text is displayed.
+
+
+--8<-- "docs/blog/images/inline2.excalidraw.svg"
+
+
+This only really impacts text entry (such as the [Input](https://textual.textualize.io/widget_gallery/#input) and [TextArea](https://textual.textualize.io/widget_gallery/#textarea) widgets).
+
+## Mouse control
+
+Inline apps in Textual support mouse input, which works the same as fullscreen apps.
+
+To use the mouse in the terminal you send an escape code which tells the terminal to write encoded mouse coordinates to standard input.
+The mouse coordinates can then be parsed in much the same was as reading keys.
+
+In inline mode this works in a similar way, with an added complication that the mouse origin is at the top left of the terminal.
+In other words if you move the mouse to the top left of the terminal you get coordinate (0, 0), but the app expects (0, 0) to be where it was displayed.
+
+In order for the app to know where the mouse is relative to it's origin, we need to *ask* the terminal where the cursor is.
+We do this with an escape code, which tells the terminal to write the current cursor coordinate to standard input.
+We can then subtract that coordinate from the physical mouse coordinates, so we can send the app mouse events relative to its on-screen origin.
+
+
+## tl;dr
+
+[Escapes codes](https://en.wikipedia.org/wiki/ANSI_escape_code).
+
+## Found this interesting?
+
+If you are interested in Textual, join our [Discord server](https://discord.gg/Enf6Z3qhVr).
+
+Or follow me for more terminal shenanigans.
+
+- [@willmcgugan](https://twitter.com/willmcgugan)
+- [mastodon.social/@willmcgugan](https://mastodon.social/@willmcgugan)
diff --git a/docs/css_types/hatch.md b/docs/css_types/hatch.md
new file mode 100644
index 0000000000..b489bd3d3c
--- /dev/null
+++ b/docs/css_types/hatch.md
@@ -0,0 +1,31 @@
+# <hatch>
+
+The `` CSS type represents a character used in the [hatch](../styles/hatch.md) rule.
+
+## Syntax
+
+| Value | Description |
+| ------------ | ------------------------------ |
+| `cross` | A diagonal crossed line. |
+| `horizontal` | A horizontal line. |
+| `left` | A left leaning diagonal line. |
+| `right` | A right leaning diagonal line. |
+| `vertical` | A vertical line. |
+
+
+## Examples
+
+### CSS
+
+
+```css
+.some-class {
+ hatch: cross green;
+}
+```
+
+### Python
+
+```py
+widget.styles.hatch = ("cross", "red")
+```
diff --git a/docs/css_types/index.md b/docs/css_types/index.md
index 7f48be2def..1cb426581e 100644
--- a/docs/css_types/index.md
+++ b/docs/css_types/index.md
@@ -8,5 +8,5 @@ The CSS types will be denoted by a keyword enclosed by angle brackets `<` and `>
For example, the style [`align-horizontal`](../styles/align.md) references the CSS type [``](./horizontal.md):
--8<-- "docs/snippets/syntax_block_start.md"
-align-horizontal: <horizontal> ;
+align-horizontal: <horizontal> ;
--8<-- "docs/snippets/syntax_block_end.md"
diff --git a/docs/events/screen_resume.md b/docs/events/screen_resume.md
index cd5f17266d..177a23f4c7 100644
--- a/docs/events/screen_resume.md
+++ b/docs/events/screen_resume.md
@@ -5,3 +5,7 @@ title: ScreenResume
::: textual.events.ScreenResume
options:
heading_level: 1
+
+## See also
+
+- [ScreenSuspend](screen_suspend.md)
diff --git a/docs/events/screen_suspend.md b/docs/events/screen_suspend.md
index f4c21a3355..d9f99ba43f 100644
--- a/docs/events/screen_suspend.md
+++ b/docs/events/screen_suspend.md
@@ -5,3 +5,7 @@ title: ScreenSuspend
::: textual.events.ScreenSuspend
options:
heading_level: 1
+
+## See also
+
+- [ScreenResume](screen_resume.md)
diff --git a/docs/examples/guide/actions/actions03.py b/docs/examples/guide/actions/actions03.py
index ff68a58e97..a74527408e 100644
--- a/docs/examples/guide/actions/actions03.py
+++ b/docs/examples/guide/actions/actions03.py
@@ -3,9 +3,9 @@
TEXT = """
[b]Set your background[/b]
-[@click=set_background('red')]Red[/]
-[@click=set_background('green')]Green[/]
-[@click=set_background('blue')]Blue[/]
+[@click=app.set_background('red')]Red[/]
+[@click=app.set_background('green')]Green[/]
+[@click=app.set_background('blue')]Blue[/]
"""
diff --git a/docs/examples/guide/actions/actions04.py b/docs/examples/guide/actions/actions04.py
index c233400e74..0aa794763f 100644
--- a/docs/examples/guide/actions/actions04.py
+++ b/docs/examples/guide/actions/actions04.py
@@ -3,9 +3,9 @@
TEXT = """
[b]Set your background[/b]
-[@click=set_background('red')]Red[/]
-[@click=set_background('green')]Green[/]
-[@click=set_background('blue')]Blue[/]
+[@click=app.set_background('red')]Red[/]
+[@click=app.set_background('green')]Green[/]
+[@click=app.set_background('blue')]Blue[/]
"""
diff --git a/docs/examples/guide/actions/actions05.py b/docs/examples/guide/actions/actions05.py
index 341dc72153..d6e8e6d478 100644
--- a/docs/examples/guide/actions/actions05.py
+++ b/docs/examples/guide/actions/actions05.py
@@ -3,9 +3,9 @@
TEXT = """
[b]Set your background[/b]
-[@click=set_background('cyan')]Cyan[/]
-[@click=set_background('magenta')]Magenta[/]
-[@click=set_background('yellow')]Yellow[/]
+[@click=app.set_background('cyan')]Cyan[/]
+[@click=app.set_background('magenta')]Magenta[/]
+[@click=app.set_background('yellow')]Yellow[/]
"""
diff --git a/docs/examples/guide/actions/actions06.py b/docs/examples/guide/actions/actions06.py
new file mode 100644
index 0000000000..5114637ebc
--- /dev/null
+++ b/docs/examples/guide/actions/actions06.py
@@ -0,0 +1,48 @@
+from textual.app import App, ComposeResult
+from textual.containers import HorizontalScroll
+from textual.reactive import reactive
+from textual.widgets import Footer, Placeholder
+
+PAGES_COUNT = 5
+
+
+class PagesApp(App):
+ BINDINGS = [
+ ("n", "next", "Next"),
+ ("p", "previous", "Previous"),
+ ]
+
+ CSS_PATH = "actions06.tcss"
+
+ page_no = reactive(0)
+
+ def compose(self) -> ComposeResult:
+ with HorizontalScroll(id="page-container"):
+ for page_no in range(PAGES_COUNT):
+ yield Placeholder(f"Page {page_no}", id=f"page-{page_no}")
+ yield Footer()
+
+ def action_next(self) -> None:
+ self.page_no += 1
+ self.refresh_bindings() # (1)!
+ self.query_one(f"#page-{self.page_no}").scroll_visible()
+
+ def action_previous(self) -> None:
+ self.page_no -= 1
+ self.refresh_bindings() # (2)!
+ self.query_one(f"#page-{self.page_no}").scroll_visible()
+
+ def check_action(
+ self, action: str, parameters: tuple[object, ...]
+ ) -> bool | None: # (3)!
+ """Check if an action may run."""
+ if action == "next" and self.page_no == PAGES_COUNT - 1:
+ return False
+ if action == "previous" and self.page_no == 0:
+ return False
+ return True
+
+
+if __name__ == "__main__":
+ app = PagesApp()
+ app.run()
diff --git a/docs/examples/guide/actions/actions06.tcss b/docs/examples/guide/actions/actions06.tcss
new file mode 100644
index 0000000000..250ee3d753
--- /dev/null
+++ b/docs/examples/guide/actions/actions06.tcss
@@ -0,0 +1,4 @@
+#page-container {
+ # This hides the scrollbar
+ scrollbar-size: 0 0;
+}
diff --git a/docs/examples/guide/actions/actions07.py b/docs/examples/guide/actions/actions07.py
new file mode 100644
index 0000000000..0344dd4609
--- /dev/null
+++ b/docs/examples/guide/actions/actions07.py
@@ -0,0 +1,44 @@
+from textual.app import App, ComposeResult
+from textual.containers import HorizontalScroll
+from textual.reactive import reactive
+from textual.widgets import Footer, Placeholder
+
+PAGES_COUNT = 5
+
+
+class PagesApp(App):
+ BINDINGS = [
+ ("n", "next", "Next"),
+ ("p", "previous", "Previous"),
+ ]
+
+ CSS_PATH = "actions06.tcss"
+
+ page_no = reactive(0, bindings=True) # (1)!
+
+ def compose(self) -> ComposeResult:
+ with HorizontalScroll(id="page-container"):
+ for page_no in range(PAGES_COUNT):
+ yield Placeholder(f"Page {page_no}", id=f"page-{page_no}")
+ yield Footer()
+
+ def action_next(self) -> None:
+ self.page_no += 1
+ self.query_one(f"#page-{self.page_no}").scroll_visible()
+
+ def action_previous(self) -> None:
+ self.page_no -= 1
+ self.query_one(f"#page-{self.page_no}").scroll_visible()
+
+ def check_action(self, action: str, parameters: tuple[object, ...]) -> bool | None:
+ """Check if an action may run."""
+ if action == "next" and self.page_no == PAGES_COUNT - 1:
+ return None # (2)!
+ if action == "previous" and self.page_no == 0:
+ return None # (3)!
+ return True
+
+
+if __name__ == "__main__":
+ app = PagesApp()
+ app.run()
diff --git a/docs/examples/guide/reactivity/set_reactive03.py b/docs/examples/guide/reactivity/set_reactive03.py
new file mode 100644
index 0000000000..80d3a45bd9
--- /dev/null
+++ b/docs/examples/guide/reactivity/set_reactive03.py
@@ -0,0 +1,21 @@
+from textual.app import App, ComposeResult
+from textual.reactive import reactive
+from textual.widgets import Input, Label
+
+
+class MultiGreet(App):
+ names: reactive[list[str]] = reactive(list, recompose=True) # (1)!
+
+ def compose(self) -> ComposeResult:
+ yield Input(placeholder="Give me a name")
+ for name in self.names:
+ yield Label(f"Hello, {name}")
+
+ def on_input_submitted(self, event: Input.Changed) -> None:
+ self.names.append(event.value)
+ self.mutate_reactive(MultiGreet.names) # (2)!
+
+
+if __name__ == "__main__":
+ app = MultiGreet()
+ app.run()
diff --git a/docs/examples/styles/hatch.py b/docs/examples/styles/hatch.py
new file mode 100644
index 0000000000..bbdce366ff
--- /dev/null
+++ b/docs/examples/styles/hatch.py
@@ -0,0 +1,22 @@
+from textual.app import App, ComposeResult
+from textual.containers import Horizontal, Vertical
+from textual.widgets import Static
+
+HATCHES = ("cross", "horizontal", "custom", "left", "right")
+
+
+class HatchApp(App):
+ CSS_PATH = "hatch.tcss"
+
+ def compose(self) -> ComposeResult:
+ with Horizontal():
+ for hatch in HATCHES:
+ static = Static(classes=f"hatch {hatch}")
+ static.border_title = hatch
+ with Vertical():
+ yield static
+
+
+if __name__ == "__main__":
+ app = HatchApp()
+ app.run()
diff --git a/docs/examples/styles/hatch.tcss b/docs/examples/styles/hatch.tcss
new file mode 100644
index 0000000000..b2bcbce119
--- /dev/null
+++ b/docs/examples/styles/hatch.tcss
@@ -0,0 +1,20 @@
+.hatch {
+ height: 1fr;
+ border: solid $secondary;
+
+ &.cross {
+ hatch: cross $success;
+ }
+ &.horizontal {
+ hatch: horizontal $success 80%;
+ }
+ &.custom {
+ hatch: "T" $success 60%;
+ }
+ &.left {
+ hatch: left $success 40%;
+ }
+ &.right {
+ hatch: right $success 20%;
+ }
+}
diff --git a/docs/examples/widgets/classic_footer.py b/docs/examples/widgets/classic_footer.py
new file mode 100644
index 0000000000..104b4fc93e
--- /dev/null
+++ b/docs/examples/widgets/classic_footer.py
@@ -0,0 +1,25 @@
+from textual.app import App, ComposeResult
+from textual.binding import Binding
+from textual.widgets import ClassicFooter
+
+
+class FooterApp(App):
+ BINDINGS = [
+ Binding(key="q", action="quit", description="Quit the app"),
+ Binding(
+ key="question_mark",
+ action="help",
+ description="Show help screen",
+ key_display="?",
+ ),
+ Binding(key="delete", action="delete", description="Delete the thing"),
+ Binding(key="j", action="down", description="Scroll down", show=False),
+ ]
+
+ def compose(self) -> ComposeResult:
+ yield ClassicFooter()
+
+
+if __name__ == "__main__":
+ app = FooterApp()
+ app.run()
diff --git a/docs/examples/widgets/data_table_cursors.py b/docs/examples/widgets/data_table_cursors.py
index 8c33f2be51..8139b426f6 100644
--- a/docs/examples/widgets/data_table_cursors.py
+++ b/docs/examples/widgets/data_table_cursors.py
@@ -16,7 +16,7 @@
(10, "Darren Burns", "Scotland", 51.84),
]
-cursors = cycle(["column", "row", "cell"])
+cursors = cycle(["column", "row", "cell", "none"])
class TableApp(App):
diff --git a/docs/getting_started.md b/docs/getting_started.md
index 46addc0bc2..60fd5023fa 100644
--- a/docs/getting_started.md
+++ b/docs/getting_started.md
@@ -38,7 +38,7 @@ pip install textual-dev
### From conda-forge
-Textual is also available on [conda-forge](https://conda-forge.org/). The preferred package manager for conda-forge is currently [micromamba](https://mamba.readthedocs.io/en/latest/installation.html#micromamba):
+Textual is also available on [conda-forge](https://conda-forge.org/). The preferred package manager for conda-forge is currently [micromamba](https://mamba.readthedocs.io/en/latest/installation/micromamba-installation.html):
```
micromamba install -c conda-forge textual
@@ -104,8 +104,9 @@ cd textual/examples/
python code_browser.py ../
```
+### Widget examples
-
+In addition to the example apps, you can also find the code listings used to generate the screenshots in these docs in the `docs/examples` directory.
## Need help?
diff --git a/docs/guide/actions.md b/docs/guide/actions.md
index 4e7c8f8c19..312ca53c8e 100644
--- a/docs/guide/actions.md
+++ b/docs/guide/actions.md
@@ -118,10 +118,88 @@ Textual supports the following action namespaces:
- `app` invokes actions on the App.
- `screen` invokes actions on the screen.
+- `focused` invokes actions on the currently focused widget (if there is one).
In the previous example if you wanted a link to set the background on the app rather than the widget, we could set a link to `app.set_background('red')`.
+## Dynamic actions
+
+!!! tip "Added in version 0.61.0"
+
+There may be situations where an action is temporarily unavailable due to some internal state within your app.
+For instance, consider an app with a fixed number of pages and actions to go to the next and previous page.
+It doesn't make sense to go to the previous page if we are on the first, or the next page when we are on the last page.
+
+We could easily add this logic to the action methods, but the [footer][textual.widgets.Footer] would still display the keys even if they would have no effect.
+The user may wonder why the app is showing keys that don't appear to work.
+
+We can solve this issue by implementing the [`check_action`][textual.dom.DOMNode.check_action] on our app, screen, or widget.
+This method is called with the name of the action and any parameters, prior to running actions or refreshing the footer.
+It should return one of the following values:
+
+- `True` to show the key and run the action as normal.
+- `False` to hide the key and prevent the action running.
+- `None` to disable the key (show dimmed), and prevent the action running.
+
+Let's write an app to put this into practice:
+
+=== "actions06.py"
+
+ ```python title="actions06.py" hl_lines="27 32 35-43"
+ --8<-- "docs/examples/guide/actions/actions06.py"
+ ```
+
+ 1. Prompts the footer to refresh, if bindings change.
+ 2. Prompts the footer to refresh, if bindings change.
+ 3. Guards the actions from running and also what keys are displayed in the footer.
+
+=== "actions06.tcss"
+
+ ```css title="actions06.tcss"
+ --8<-- "docs/examples/guide/actions/actions06.tcss"
+ ```
+
+=== "Output"
+
+ ```{.textual path="docs/examples/guide/actions/actions06.py"}
+ ```
+
+This app has key bindings for ++n++ and ++p++ to navigate the pages.
+Notice how the keys are hidden from the footer when they would have no effect.
+
+The actions above call [`refresh_bindings`][textual.dom.DOMNode.refresh_bindings] to prompt Textual to refresh the footer.
+An alternative to doing this manually is to set `bindings=True` on a [reactive](./reactivity.md), which will refresh the bindings if the reactive changes.
+
+Let's make this change.
+We will also demonstrate what the footer will show if we return `None` from `check_action` (rather than `False`):
+
+
+=== "actions07.py"
+
+ ```python title="actions06.py" hl_lines="17 36 38"
+ --8<-- "docs/examples/guide/actions/actions07.py"
+ ```
+
+ 1. The `bindings=True` causes the footer to refresh when `page_no` changes.
+ 2. Returning `None` disables the key in the footer rather than hides it
+ 3. Returning `None` disables the key in the footer rather than hides it.
+
+=== "actions06.tcss"
+
+ ```css title="actions06.tcss"
+ --8<-- "docs/examples/guide/actions/actions06.tcss"
+ ```
+
+=== "Output"
+
+ ```{.textual path="docs/examples/guide/actions/actions07.py"}
+ ```
+
+Note how the logic is the same but we don't need to explicitly call [`refresh_bindings`][textual.dom.DOMNode.refresh_bindings].
+The change to `check_action` also causes the disabled footer keys to be grayed out, indicating they are temporarily unavailable.
+
+
## Builtin actions
Textual supports the following builtin actions which are defined on the app.
@@ -129,16 +207,16 @@ Textual supports the following builtin actions which are defined on the app.
- [action_add_class][textual.app.App.action_add_class]
- [action_back][textual.app.App.action_back]
- [action_bell][textual.app.App.action_bell]
-- [action_check_bindings][textual.app.App.action_check_bindings]
-- [action_focus][textual.app.App.action_focus]
- [action_focus_next][textual.app.App.action_focus_next]
- [action_focus_previous][textual.app.App.action_focus_previous]
+- [action_focus][textual.app.App.action_focus]
- [action_pop_screen][textual.app.App.action_pop_screen]
- [action_push_screen][textual.app.App.action_push_screen]
- [action_quit][textual.app.App.action_quit]
- [action_remove_class][textual.app.App.action_remove_class]
- [action_screenshot][textual.app.App.action_screenshot]
-- [action_switch_screen][textual.app.App.action_switch_screen]
+- [action_simulate_key][textual.app.App.action_simulate_key]
- [action_suspend_process][textual.app.App.action_suspend_process]
+- [action_switch_screen][textual.app.App.action_switch_screen]
- [action_toggle_class][textual.app.App.action_toggle_class]
- [action_toggle_dark][textual.app.App.action_toggle_dark]
diff --git a/docs/guide/app.md b/docs/guide/app.md
index 2431f389b9..54df97175d 100644
--- a/docs/guide/app.md
+++ b/docs/guide/app.md
@@ -47,6 +47,10 @@ Inline apps are useful for tools that integrate closely with the typical workflo
To run an app in inline mode set the `inline` parameter to `True` when you call [App.run()][textual.app.App.run]. See [Style Inline Apps](../how-to/style-inline-apps.md) for how to apply additional styles to inline apps.
+!!! note
+
+ Inline mode is not currently supported on Windows.
+
## Events
Textual has an event system you can use to respond to key presses, mouse actions, and internal state changes. Event handlers are methods prefixed with `on_` followed by the name of the event.
diff --git a/docs/guide/command_palette.md b/docs/guide/command_palette.md
index 5fa1bebc08..484ccbe49c 100644
--- a/docs/guide/command_palette.md
+++ b/docs/guide/command_palette.md
@@ -113,7 +113,6 @@ this is to aid in command discoverability.
- `discover` accepts no parameters (instead of the search value)
- `discover` yields instances of [`DiscoveryHit`][textual.command.DiscoveryHit] (instead of instances of [`Hit`][textual.command.Hit])
-- discovery hits are sorted in ascending alphabetical order because there is no matching and no match score is generated
Instances of [`DiscoveryHit`][textual.command.DiscoveryHit] contain information about how the hit should be displayed, an optional help string, and a callback which will be run if the user selects that command.
diff --git a/docs/guide/events.md b/docs/guide/events.md
index 0f06d6f7ce..9197949a33 100644
--- a/docs/guide/events.md
+++ b/docs/guide/events.md
@@ -268,7 +268,7 @@ The `on` decorator also accepts selectors as keyword arguments that may be used
The snippet below shows how to match the message [`TabbedContent.TabActivated`][textual.widgets.TabbedContent.TabActivated] only when the tab with id `home` was activated:
```py
-@on(TabbedContent.TabActivated, tab="#home")
+@on(TabbedContent.TabActivated, pane="#home")
def home_tab(self) -> None:
self.log("Switched back to home tab.")
...
diff --git a/docs/guide/queries.md b/docs/guide/queries.md
index 5401257874..d33659f382 100644
--- a/docs/guide/queries.md
+++ b/docs/guide/queries.md
@@ -10,17 +10,20 @@ Selectors are a very useful idea and can do more than apply styles. We can also
## Query one
-The [query_one][textual.dom.DOMNode.query_one] method gets a single widget in an app or other widget. If you call it with a selector it will return the first matching widget.
+The [query_one][textual.dom.DOMNode.query_one] method is used to retrieve a single widget that matches a selector or a type.
-Let's say we have a widget with an ID of `send` and we want to get a reference to it in our app. We could do this with the following:
+Let's say we have a widget with an ID of `send` and we want to get a reference to it in our app.
+We could do this with the following line of code:
```python
send_button = self.query_one("#send")
```
-If there is no widget with an ID of `send`, Textual will raise a [NoMatches][textual.css.query.NoMatches] exception. Otherwise it will return the matched widget.
+This will retrieve a widget with an ID of `send`, if there is exactly one.
+If there are no matching widgets, Textual will raise a [NoMatches][textual.css.query.NoMatches] exception.
+If there is more than one match, Textual will raise a [TooManyMatches][textual.css.query.TooManyMatches] exception.
-You can also add a second parameter for the expected type.
+You can also add a second parameter for the expected type, which will ensure that you get the type you are expecting.
```python
send_button = self.query_one("#send", Button)
@@ -32,9 +35,16 @@ If the matched widget is *not* a button (i.e. if `isinstance(widget, Button)` eq
The second parameter allows type-checkers like MyPy to know the exact return type. Without it, MyPy would only know the result of `query_one` is a Widget (the base class).
+You can also specify a widget type in place of a selector, which will return a widget of that type.
+For instance, the following would return a `Button` instance (assuming there is a single Button).
+
+```python
+my_button = self.query_one(Button)
+```
+
## Making queries
-Apps and widgets have a [query][textual.dom.DOMNode.query] method which finds (or queries) widgets. This method returns a [DOMQuery][textual.css.query.DOMQuery] object which is a list-like container of widgets.
+Apps and widgets also have a [query][textual.dom.DOMNode.query] method which finds (or queries) widgets. This method returns a [DOMQuery][textual.css.query.DOMQuery] object which is a list-like container of widgets.
If you call `query` with no arguments, you will get back a `DOMQuery` containing all widgets. This method is *recursive*, meaning it will also return child widgets (as many levels as required).
diff --git a/docs/guide/reactivity.md b/docs/guide/reactivity.md
index 0d191ad832..ff7a1cebdf 100644
--- a/docs/guide/reactivity.md
+++ b/docs/guide/reactivity.md
@@ -383,6 +383,31 @@ The following app contains a fix for this issue:
The line `self.set_reactive(Greeter.greeting, greeting)` sets the `greeting` attribute but doesn't immediately invoke the watcher.
+## Mutable reactives
+
+Textual can detect when you set a reactive to a new value, but it can't detect when you _mutate_ a value.
+In practice, this means that Textual can detect changes to basic types (int, float, str, etc.), but not if you update a collection, such as a list or dict.
+
+You can still use collections and other mutable objects in reactives, but you will need to call [`mutate_reactive`][textual.dom.DOMNode.mutate_reactive] after making changes for the reactive superpowers to work.
+
+Here's an example, that uses a reactive list:
+
+=== "set_reactive03.py"
+
+ ```python hl_lines="16"
+ --8<-- "docs/examples/guide/reactivity/set_reactive03.py"
+ ```
+
+ 1. Creates a reactive list of strings.
+ 2. Explicitly mutate the reactive list.
+
+=== "Output"
+
+ ```{.textual path="docs/examples/guide/reactivity/set_reactive03.py" press="W,i,l,l,enter"}
+ ```
+
+Note the call to `mutate_reactive`. Without it, the display would not update when a new name is appended to the list.
+
## Data binding
Reactive attributes from one widget may be *bound* (connected) to another widget, so that changes to a single reactive will automatically update another widget (potentially more than one).
diff --git a/docs/guide/screens.md b/docs/guide/screens.md
index 4f57412fd0..0b72bf589f 100644
--- a/docs/guide/screens.md
+++ b/docs/guide/screens.md
@@ -128,6 +128,7 @@ Like [pop_screen](#pop-screen), if the screen being replaced is not installed it
You can also switch screens with the `"app.switch_screen"` action which accepts the name of the screen to switch to.
+
## Screen opacity
If a screen has a background color with an *alpha* component, then the background color will be blended with the screen beneath it.
@@ -355,3 +356,12 @@ One for a dashboard, one for settings, and one for help.
We've bound keys to each of these screens, so the user can switch between the screens.
Pressing ++d++, ++s++, or ++h++ switches between these modes.
+
+
+## Screen events
+
+Textual will send a [ScreenSuspend](../events/screen_suspend.md) event to screens that have become inactive due to another screen being pushed, or switching via a mode.
+
+When a screen becomes active, Textual will send a [ScreenResume](../events/screen_resume.md) event to the newly active screen.
+
+These events can be useful if you want to disable processing for a screen that is no longer visible, for example.
diff --git a/docs/guide/workers.md b/docs/guide/workers.md
index 71a06c093e..ecc3f541b0 100644
--- a/docs/guide/workers.md
+++ b/docs/guide/workers.md
@@ -10,6 +10,9 @@ There are many interesting uses for Textual which require reading data from an i
When an app requests data from the network it is important that it doesn't prevent the user interface from updating.
In other words, the requests should be concurrent (happen at the same time) as the UI updates.
+This is also true for anything that could take a significant time (more than a few milliseconds) to complete.
+For instance, reading from a [subprocess](https://docs.python.org/3/library/asyncio-subprocess.html#asyncio-subprocess) or doing compute heavy work.
+
Managing this concurrency is a tricky topic, in any language or framework.
Even for experienced developers, there are gotchas which could make your app lock up or behave oddly.
Textual's Worker API makes concurrency far less error prone and easier to reason about.
diff --git a/docs/robots.txt b/docs/robots.txt
new file mode 100644
index 0000000000..d6d23f0eed
--- /dev/null
+++ b/docs/robots.txt
@@ -0,0 +1 @@
+Sitemap: https://textual.textualize.io/sitemap.xml
diff --git a/docs/styles/grid/index.md b/docs/styles/grid/index.md
index 10c68267c0..f7ccd54045 100644
--- a/docs/styles/grid/index.md
+++ b/docs/styles/grid/index.md
@@ -16,17 +16,17 @@ For an in-depth look at the grid layout, visit the grid [guide](../../guide/layo
## Syntax
--8<-- "docs/snippets/syntax_block_start.md"
-column-span : <integer> ;
+column-span : <integer> ;
-grid-columns : <scalar> +;
+grid-columns : <scalar> +;
-grid-gutter : <scalar> [<scalar> ];
+grid-gutter : <scalar> [<scalar> ];
-grid-rows : <scalar> +;
+grid-rows : <scalar> +;
-grid-size : <integer> [<integer> ];
+grid-size : <integer> [<integer> ];
-row-span : <integer> ;
+row-span : <integer> ;
--8<-- "docs/snippets/syntax_block_end.md"
Visit each style's reference page to learn more about how the values are used.
diff --git a/docs/styles/hatch.md b/docs/styles/hatch.md
new file mode 100644
index 0000000000..41cac23edb
--- /dev/null
+++ b/docs/styles/hatch.md
@@ -0,0 +1,58 @@
+# Hatch
+
+The `hatch` style fills a widget's background with a repeating character for a pleasing textured effect.
+
+## Syntax
+
+--8<-- "docs/snippets/syntax_block_start.md"
+hatch: (<hatch> | CHARACTER) <color> [<percentage> ]
+--8<-- "docs/snippets/syntax_block_end.md"
+
+The hatch type can be specified with a constant, or a string. For example, `cross` for cross hatch, or `"T"` for a custom character.
+
+The color can be any Textual color value.
+
+An optional percentage can be used to set the opacity.
+
+## Examples
+
+
+An app to show a few hatch effects.
+
+=== "Output"
+
+ ```{.textual path="docs/examples/styles/hatch.py"}
+ ```
+
+=== "hatch.py"
+
+ ```py
+ --8<-- "docs/examples/styles/hatch.py"
+ ```
+
+=== "hatch.tcss"
+
+ ```css
+ --8<-- "docs/examples/styles/hatch.tcss"
+ ```
+
+
+## CSS
+
+```css
+/* Red cross hatch */
+hatch: cross red;
+/* Right diagonals, 50% transparent green. */
+hatch: right green 50%;
+/* T custom character in 80% blue. **/
+hatch: "T" blue 80%;
+```
+
+
+## Python
+
+```py
+widget.styles.hatch = ("cross", "red")
+widget.styles.hatch = ("right", "rgba(0,255,0,128)")
+widget.styles.hatch = ("T", "blue")
+```
diff --git a/docs/widget_gallery.md b/docs/widget_gallery.md
index ca82b5d4e3..1d5ef708cb 100644
--- a/docs/widget_gallery.md
+++ b/docs/widget_gallery.md
@@ -34,6 +34,21 @@ A classic checkbox control.
```{.textual path="docs/examples/widgets/checkbox.py"}
```
+## ClassicFooter
+
+The original Footer widget.
+
+!!! warning
+
+ This has been replaced by [`Footer`](#footer), and will be removed in Textual v1.0
+
+[ClassicFooter reference](./widgets/classic_footer.md){ .md-button .md-button--primary }
+
+```{.textual path="docs/examples/widgets/classic_footer.py" columns="70" lines="12"}
+```
+
+
+
## Collapsible
Content that may be toggled on and off by clicking a title.
@@ -90,6 +105,7 @@ A footer to display and interact with key bindings.
```
+
## Header
A header to display the app's title and subtitle.
diff --git a/docs/widgets/classic_footer.md b/docs/widgets/classic_footer.md
new file mode 100644
index 0000000000..0917ded739
--- /dev/null
+++ b/docs/widgets/classic_footer.md
@@ -0,0 +1,64 @@
+# ClassicFooter
+
+!!! warning "Deprecated widget"
+
+ This is an older version of the Textual Footer, and will be replaced with [Footer](./footer.md) in Textual v1.0
+
+
+A simple footer widget which is docked to the bottom of its parent container. Displays
+available keybindings for the currently focused widget.
+
+- [ ] Focusable
+- [ ] Container
+
+## Example
+
+The example below shows an app with a single keybinding that contains only a `ClassicFooter`
+widget. Notice how the `ClassicFooter` automatically displays the keybinding.
+
+=== "Output"
+
+ ```{.textual path="docs/examples/widgets/classic_footer.py"}
+ ```
+
+=== "footer.py"
+
+ ```python
+ --8<-- "docs/examples/widgets/classic_footer.py"
+ ```
+
+## Reactive Attributes
+
+| Name | Type | Default | Description |
+| --------------- | ----- | ------- | --------------------------------------------------------------------------------------------------------- |
+| `highlight_key` | `str` | `None` | Stores the currently highlighted key. This is typically the key the cursor is hovered over in the footer. |
+
+## Messages
+
+This widget posts no messages.
+
+## Bindings
+
+This widget has no bindings.
+
+## Component Classes
+
+The footer widget provides the following component classes:
+
+::: textual.widgets.ClassicFooter.COMPONENT_CLASSES
+ options:
+ show_root_heading: false
+ show_root_toc_entry: false
+
+## Additional Notes
+
+* You can prevent keybindings from appearing in the footer by setting the `show` argument of the `Binding` to `False`.
+* You can customize the text that appears for the key itself in the footer using the `key_display` argument of `Binding`.
+
+
+---
+
+
+::: textual.widgets.ClassicFooter
+ options:
+ heading_level: 2
diff --git a/docs/widgets/data_table.md b/docs/widgets/data_table.md
index ab1981c0f1..321f9ac8e4 100644
--- a/docs/widgets/data_table.md
+++ b/docs/widgets/data_table.md
@@ -1,6 +1,8 @@
# DataTable
-A table widget optimized for displaying a lot of data.
+A widget to display text in a table. This includes the ability to update data, use a cursor to navigate data, respond to mouse clicks, delete rows or columns, and individually render each cell as a Rich Text renderable. DataTable provides an efficiently displayed and updated table capable for most applications.
+
+Applications may have custom rules for formatting, numbers, repopulating tables after searching or filtering, and responding to selections. The widget emits events to interface with custom logic.
- [x] Focusable
- [ ] Container
@@ -55,13 +57,27 @@ The method [add_column][textual.widgets.DataTable.add_column] also accepts a `ke
Keys are important because cells in a data table can change location due to factors like row deletion and sorting.
Thus, using keys instead of coordinates allows us to refer to data without worrying about its current location in the table.
-If you want to change the table based solely on coordinates, you can use the [coordinate_to_cell_key][textual.widgets.DataTable.coordinate_to_cell_key] method to convert a coordinate to a _cell key_, which is a `(row_key, column_key)` pair.
+If you want to change the table based solely on coordinates, you may need to convert that coordinate to a cell key first using the [coordinate_to_cell_key][textual.widgets.DataTable.coordinate_to_cell_key] method.
### Cursors
+A cursor allows navigating within a table with the keyboard or mouse. There are four cursor types: `"cell"` (the default), `"row"`, `"column"`, and `"none"`.
+
+ Change the cursor type by assigning to
+the [`cursor_type`][textual.widgets.DataTable.cursor_type] reactive attribute.
The coordinate of the cursor is exposed via the [`cursor_coordinate`][textual.widgets.DataTable.cursor_coordinate] reactive attribute.
-Three types of cursors are supported: `cell`, `row`, and `column`.
-Change the cursor type by assigning to the [`cursor_type`][textual.widgets.DataTable.cursor_type] reactive attribute.
+
+Using the keyboard, arrow keys, ++page-up++, ++page-down++, ++home++ and ++end++ move the cursor highlight, emitting a [`CellHighlighted`][textual.widgets.DataTable.CellHighlighted]
+message, then enter selects the cell, emitting a [`CellSelected`][textual.widgets.DataTable.CellSelected] message. If the
+`cursor_type` is row, then [`RowHighlighted`][textual.widgets.DataTable.RowHighlighted] and [`RowSelected`][textual.widgets.DataTable.RowSelected]
+are emitted, similarly for [`ColumnHighlighted`][textual.widgets.DataTable.ColumnHighlighted] and [`ColumnSelected`][textual.widgets.DataTable.ColumnSelected].
+
+When moving the mouse over the table, a [`MouseMove`][textual.events.MouseMove] event is emitted, the cell hovered over is styled,
+and the [`hover_coordinate`][textual.widgets.DataTable.hover_coordinate] reactive attribute is updated. Clicking the mouse
+then emits the [`CellHighlighted`][textual.widgets.DataTable.CellHighlighted] and [`CellSelected`][textual.widgets.DataTable.CellSelected]
+events.
+
+A new table starts with no cell highlighted, i.e., row and column are zero. You can force the first item to highlight with `move_cursor(row=1, column=1)`. All row and column indexes start at one.
=== "Column Cursor"
@@ -78,18 +94,21 @@ Change the cursor type by assigning to the [`cursor_type`][textual.widgets.DataT
```{.textual path="docs/examples/widgets/data_table_cursors.py" press="c,c"}
```
+=== "No Cursor"
+
+ ```{.textual path="docs/examples/widgets/data_table_cursors.py" press="c,c,c"}
+ ```
+
=== "data_table_cursors.py"
```python
--8<-- "docs/examples/widgets/data_table_cursors.py"
```
-You can change the position of the cursor using the arrow keys, ++page-up++, ++page-down++, ++home++ and ++end++,
-or by assigning to the `cursor_coordinate` reactive attribute.
### Updating data
-Cells can be updated in the `DataTable` by using the [update_cell][textual.widgets.DataTable.update_cell] and [update_cell_at][textual.widgets.DataTable.update_cell_at] methods.
+Cells can be updated using the [update_cell][textual.widgets.DataTable.update_cell] and [update_cell_at][textual.widgets.DataTable.update_cell_at] methods.
### Removing data
@@ -143,12 +162,16 @@ visible as you scroll through the data table.
### Sorting
-The `DataTable` can be sorted using the [sort][textual.widgets.DataTable.sort] method. In order to sort your data by a column, you can provide the `key` you supplied to the `add_column` method or a `ColumnKey`. You can then pass one more column keys to the `sort` method to sort by one or more columns.
+The DataTable rows can be sorted using the [`sort`][textual.widgets.DataTable.sort] method.
-Additionally, you can sort your `DataTable` with a custom function (or other callable) via the `key` argument. Similar to the `key` parameter of the built-in [sorted()](https://docs.python.org/3/library/functions.html#sorted) function, your function (or other callable) should take a single argument (row) and return a key to use for sorting purposes.
+There are three methods of using [`sort`][textual.widgets.DataTable.sort]:
-Providing both `columns` and `key` will limit the row information sent to your `key` function (or other callable) to only the columns specified.
+* By Column. Pass columns in as parameters to sort by the natural order of one or more columns. Specify a column using either a [`ColumnKey`][textual.widgets.data_table.ColumnKey] instance or the `key` you supplied to [`add_column`][textual.widgets.DataTable.add_column]. For example, `sort("country", "region")` would sort by country, and, when the country values are equal, by region.
+* By Key function. Pass a function as the `key` parameter to sort, similar to the [key function parameter](https://docs.python.org/3/howto/sorting.html#key-functions) of Python's [`sorted`](https://docs.python.org/3/library/functions.html#sorted) built-in. The function will be called once per row with a tuple of all row values.
+* By both Column and Key function. You can specify which columns to include as parameters to your key function. For example, `sort("hours", "rate", key=lambda h, r: h*r)` passes two values to the key function for each row.
+The `reverse` argument reverses the order of your sort. Note that correct sorting may require your key function to undo your formatting.
+
=== "Output"
```{.textual path="docs/examples/widgets/data_table_sort.py"}
@@ -160,14 +183,14 @@ Providing both `columns` and `key` will limit the row information sent to your `
--8<-- "docs/examples/widgets/data_table_sort.py"
```
-### Labelled rows
+### Labeled rows
A "label" can be attached to a row using the [add_row][textual.widgets.DataTable.add_row] method.
This will add an extra column to the left of the table which the cursor cannot interact with.
This column is similar to the leftmost column in a spreadsheet containing the row numbers.
The example below shows how to attach simple numbered labels to rows.
-=== "Labelled rows"
+=== "Labeled rows"
```{.textual path="docs/examples/widgets/data_table_labels.py"}
```
@@ -186,7 +209,7 @@ The example below shows how to attach simple numbered labels to rows.
| `show_row_labels` | `bool` | `True` | Show the row labels (if applicable) |
| `fixed_rows` | `int` | `0` | Number of fixed rows (rows which do not scroll) |
| `fixed_columns` | `int` | `0` | Number of fixed columns (columns which do not scroll) |
-| `zebra_stripes` | `bool` | `False` | Display alternating colors on rows |
+| `zebra_stripes` | `bool` | `False` | Style with alternating colors on rows |
| `header_height` | `int` | `1` | Height of header row |
| `show_cursor` | `bool` | `True` | Show the cursor |
| `cursor_type` | `str` | `"cell"` | One of `"cell"`, `"row"`, `"column"`, or `"none"` |
diff --git a/docs/widgets/footer.md b/docs/widgets/footer.md
index fcb25cf836..dfd11e109b 100644
--- a/docs/widgets/footer.md
+++ b/docs/widgets/footer.md
@@ -1,11 +1,17 @@
# Footer
+!!! tip "Added in version 0.63.0"
+
+ This is a second iteration of the Footer.
+ The version prior to 0.63.0 is available as [ClassicFooter](./classic_footer.md) to help with backwards compatibility, but will be removed in v1.0.
+
A simple footer widget which is docked to the bottom of its parent container. Displays
available keybindings for the currently focused widget.
- [ ] Focusable
- [ ] Container
+
## Example
The example below shows an app with a single keybinding that contains only a `Footer`
@@ -24,9 +30,11 @@ widget. Notice how the `Footer` automatically displays the keybinding.
## Reactive Attributes
-| Name | Type | Default | Description |
-| --------------- | ----- | ------- | --------------------------------------------------------------------------------------------------------- |
-| `highlight_key` | `str` | `None` | Stores the currently highlighted key. This is typically the key the cursor is hovered over in the footer. |
+| Name | Type | Default | Description |
+| ----------------- | ------ | ------- | ----------------------------------------------------------------------- |
+| `upper_case_keys` | `bool` | `False` | Display the keys in upper case. |
+| `ctrl_to_caret` | `bool` | `True` | Replace "ctrl+" with "^" to denote a key that requires holding ++CTRL++ |
+| `compact` | `bool` | `False` | Display a more compact footer. |
## Messages
@@ -38,12 +46,8 @@ This widget has no bindings.
## Component Classes
-The footer widget provides the following component classes:
+This widget has no component classes.
-::: textual.widgets.Footer.COMPONENT_CLASSES
- options:
- show_root_heading: false
- show_root_toc_entry: false
## Additional Notes
diff --git a/docs/widgets/tabbed_content.md b/docs/widgets/tabbed_content.md
index e6edb899cc..de317b957b 100644
--- a/docs/widgets/tabbed_content.md
+++ b/docs/widgets/tabbed_content.md
@@ -97,7 +97,7 @@ The following example contains a `TabbedContent` with three tabs.
## Styling
The `TabbedContent` widget is composed of two main sub-widgets: a
-[`Tabs`](tabs.md) and a [`ContentSwitcher`]((content_switcher.md)); you can
+[`Tabs`](tabs.md) and a [`ContentSwitcher`](content_switcher.md); you can
style them accordingly.
The tabs within the `Tabs` widget will have prefixed IDs; each ID being the
diff --git a/docs/widgets/text_area.md b/docs/widgets/text_area.md
index 9e24442d30..0824f4c697 100644
--- a/docs/widgets/text_area.md
+++ b/docs/widgets/text_area.md
@@ -353,6 +353,8 @@ the `show_line_numbers` attribute to `True` or `False`.
Setting this attribute will immediately repaint the `TextArea` to reflect the new value.
+You can also change the start line number (the topmost line number in the gutter) by setting the `line_number_start` reactive attribute.
+
### Extending `TextArea`
Sometimes, you may wish to subclass `TextArea` to add some extra functionality.
@@ -481,7 +483,7 @@ We can freely edit the text, and the syntax highlighting will update immediately
Recall that we map names (like `@heading`) from the tree-sitter highlight query to Rich style objects inside the `TextAreaTheme.syntax_styles` dictionary.
If you notice some highlights are missing after registering a language, the issue may be:
-1. The current `TextAreaTheme` doesn't contain a mapping for the name in the highlight query. Adding a new to `syntax_styles` should resolve the issue.
+1. The current `TextAreaTheme` doesn't contain a mapping for the name in the highlight query. Adding a new key-value pair to `syntax_styles` should resolve the issue.
2. The highlight query doesn't assign a name to the pattern you expect to be highlighted. In this case you'll need to update the highlight query to assign to the name.
!!! tip
@@ -506,6 +508,7 @@ A detailed view of these classes is out of scope, but do note that a lot of the
| `theme` | `str` | `"css"` | The theme to use. |
| `selection` | `Selection` | `Selection()` | The current selection. |
| `show_line_numbers` | `bool` | `False` | Show or hide line numbers. |
+| `line_number_start` | `int` | `1` | The start line number in the gutter. |
| `indent_width` | `int` | `4` | The number of spaces to indent and width of tabs. |
| `match_cursor_bracket` | `bool` | `True` | Enable/disable highlighting matching brackets under cursor. |
| `cursor_blink` | `bool` | `True` | Enable/disable blinking of the cursor when the widget has focus. |
diff --git a/docs/widgets/toast.md b/docs/widgets/toast.md
index a324150e7f..0a7e321ce3 100644
--- a/docs/widgets/toast.md
+++ b/docs/widgets/toast.md
@@ -14,13 +14,21 @@ A widget which displays a notification message.
You can customize the style of Toasts by targeting the `Toast` [CSS type](../guide/CSS.md#type-selector).
For example:
-
```scss
Toast {
padding: 3;
}
```
+If you wish to change the location of Toasts, it is possible by targeting the `ToastRack` CSS type.
+For example:
+
+```scss
+ToastRack {
+ align: right top;
+}
+```
+
The three severity levels also have corresponding
[classes](../guide/CSS.md#class-name-selector), allowing you to target the
different styles of notification. They are:
diff --git a/examples/code_browser.tcss b/examples/code_browser.tcss
index 21fc7cad00..a97a35fe9e 100644
--- a/examples/code_browser.tcss
+++ b/examples/code_browser.tcss
@@ -23,6 +23,7 @@ CodeBrowser.-show-tree #tree-view {
#code-view {
overflow: auto scroll;
min-width: 100%;
+ hatch: right $primary;
}
#code {
width: auto;
diff --git a/examples/dictionary.py b/examples/dictionary.py
index b8b31096dd..4965507482 100644
--- a/examples/dictionary.py
+++ b/examples/dictionary.py
@@ -13,7 +13,7 @@
class DictionaryApp(App):
- """Searches ab dictionary API as-you-type."""
+ """Searches a dictionary API as-you-type."""
CSS_PATH = "dictionary.tcss"
@@ -22,18 +22,13 @@ def compose(self) -> ComposeResult:
with VerticalScroll(id="results-container"):
yield Markdown(id="results")
- def on_mount(self) -> None:
- """Called when app starts."""
- # Give the input focus, so we can start typing straight away
- self.query_one(Input).focus()
-
async def on_input_changed(self, message: Input.Changed) -> None:
"""A coroutine to handle a text changed message."""
if message.value:
self.lookup_word(message.value)
else:
# Clear the results
- self.query_one("#results", Markdown).update("")
+ await self.query_one("#results", Markdown).update("")
@work(exclusive=True)
async def lookup_word(self, word: str) -> None:
@@ -46,6 +41,7 @@ async def lookup_word(self, word: str) -> None:
results = response.json()
except Exception:
self.query_one("#results", Markdown).update(response.text)
+ return
if word == self.query_one(Input).value:
markdown = self.make_word_markdown(results)
diff --git a/examples/five_by_five.py b/examples/five_by_five.py
index 6859cf86c6..a29cfcbafd 100644
--- a/examples/five_by_five.py
+++ b/examples/five_by_five.py
@@ -21,7 +21,7 @@
class Help(Screen):
"""The help screen for the application."""
- BINDINGS = [("escape,space,q,question_mark", "pop_screen", "Close")]
+ BINDINGS = [("escape,space,q,question_mark", "app.pop_screen", "Close")]
"""Bindings for the help screen."""
def compose(self) -> ComposeResult:
@@ -159,8 +159,8 @@ class Game(Screen):
BINDINGS = [
Binding("n", "new_game", "New Game"),
- Binding("question_mark", "push_screen('help')", "Help", key_display="?"),
- Binding("q", "quit", "Quit"),
+ Binding("question_mark", "app.push_screen('help')", "Help", key_display="?"),
+ Binding("q", "app.quit", "Quit"),
Binding("up,w,k", "navigate(-1,0)", "Move Up", False),
Binding("down,s,j", "navigate(1,0)", "Move Down", False),
Binding("left,a,h", "navigate(0,-1)", "Move Left", False),
diff --git a/examples/markdown.py b/examples/markdown.py
index a6d26cb190..2cf43e4399 100644
--- a/examples/markdown.py
+++ b/examples/markdown.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
from pathlib import Path
from sys import argv
@@ -7,6 +9,8 @@
class MarkdownApp(App):
+ """A simple Markdown viewer application."""
+
BINDINGS = [
("t", "toggle_table_of_contents", "TOC"),
("b", "back", "Back"),
@@ -25,23 +29,41 @@ def compose(self) -> ComposeResult:
yield MarkdownViewer()
async def on_mount(self) -> None:
- self.markdown_viewer.focus()
+ """Go to the first path when the app starts."""
try:
await self.markdown_viewer.go(self.path)
except FileNotFoundError:
self.exit(message=f"Unable to load {self.path!r}")
+ def on_markdown_viewer_navigator_updated(self) -> None:
+ """Refresh bindings for forward / back when the document changes."""
+ self.refresh_bindings()
+
def action_toggle_table_of_contents(self) -> None:
+ """Toggles display of the table of contents."""
self.markdown_viewer.show_table_of_contents = (
not self.markdown_viewer.show_table_of_contents
)
async def action_back(self) -> None:
+ """Navigate backwards."""
await self.markdown_viewer.back()
async def action_forward(self) -> None:
+ """Navigate forwards."""
await self.markdown_viewer.forward()
+ def check_action(self, action: str, _) -> bool | None:
+ """Check if certain actions can be performed."""
+ if action == "forward" and self.markdown_viewer.navigator.end:
+ # Disable footer link if we can't go forward
+ return None
+ if action == "back" and self.markdown_viewer.navigator.start:
+ # Disable footer link if we can't go backward
+ return None
+ # All other keys display as normal
+ return True
+
if __name__ == "__main__":
app = MarkdownApp()
diff --git a/mkdocs-common.yml b/mkdocs-common.yml
index 6834c0ff50..924179a95d 100644
--- a/mkdocs-common.yml
+++ b/mkdocs-common.yml
@@ -63,6 +63,7 @@ theme:
name: Switch to light mode
plugins:
+ git-revision-date-localized:
search:
autorefs:
mkdocstrings:
diff --git a/mkdocs-nav.yml b/mkdocs-nav.yml
index 4fa9912b41..169f3ef0d0 100644
--- a/mkdocs-nav.yml
+++ b/mkdocs-nav.yml
@@ -30,6 +30,7 @@ nav:
- "css_types/index.md"
- "css_types/border.md"
- "css_types/color.md"
+ - "css_types/hatch.md"
- "css_types/horizontal.md"
- "css_types/integer.md"
- "css_types/keyline.md"
@@ -96,6 +97,7 @@ nav:
- "styles/grid/grid_rows.md"
- "styles/grid/grid_size.md"
- "styles/grid/row_span.md"
+ - "styles/hatch.md"
- "styles/height.md"
- "styles/keyline.md"
- "styles/layer.md"
@@ -139,6 +141,7 @@ nav:
- Widgets:
- "widgets/button.md"
- "widgets/checkbox.md"
+ - "widgets/classic_footer.md"
- "widgets/collapsible.md"
- "widgets/content_switcher.md"
- "widgets/data_table.md"
diff --git a/poetry.lock b/poetry.lock
index 483c8f9b4e..639c7a1f37 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,88 +1,88 @@
-# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
[[package]]
name = "aiohttp"
-version = "3.9.4"
+version = "3.9.5"
description = "Async http client/server framework (asyncio)"
optional = false
python-versions = ">=3.8"
files = [
- {file = "aiohttp-3.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:76d32588ef7e4a3f3adff1956a0ba96faabbdee58f2407c122dd45aa6e34f372"},
- {file = "aiohttp-3.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:56181093c10dbc6ceb8a29dfeea1e815e1dfdc020169203d87fd8d37616f73f9"},
- {file = "aiohttp-3.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7a5b676d3c65e88b3aca41816bf72831898fcd73f0cbb2680e9d88e819d1e4d"},
- {file = "aiohttp-3.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1df528a85fb404899d4207a8d9934cfd6be626e30e5d3a5544a83dbae6d8a7e"},
- {file = "aiohttp-3.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f595db1bceabd71c82e92df212dd9525a8a2c6947d39e3c994c4f27d2fe15b11"},
- {file = "aiohttp-3.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c0b09d76e5a4caac3d27752027fbd43dc987b95f3748fad2b924a03fe8632ad"},
- {file = "aiohttp-3.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:689eb4356649ec9535b3686200b231876fb4cab4aca54e3bece71d37f50c1d13"},
- {file = "aiohttp-3.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3666cf4182efdb44d73602379a66f5fdfd5da0db5e4520f0ac0dcca644a3497"},
- {file = "aiohttp-3.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b65b0f8747b013570eea2f75726046fa54fa8e0c5db60f3b98dd5d161052004a"},
- {file = "aiohttp-3.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1885d2470955f70dfdd33a02e1749613c5a9c5ab855f6db38e0b9389453dce7"},
- {file = "aiohttp-3.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0593822dcdb9483d41f12041ff7c90d4d1033ec0e880bcfaf102919b715f47f1"},
- {file = "aiohttp-3.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:47f6eb74e1ecb5e19a78f4a4228aa24df7fbab3b62d4a625d3f41194a08bd54f"},
- {file = "aiohttp-3.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c8b04a3dbd54de6ccb7604242fe3ad67f2f3ca558f2d33fe19d4b08d90701a89"},
- {file = "aiohttp-3.9.4-cp310-cp310-win32.whl", hash = "sha256:8a78dfb198a328bfb38e4308ca8167028920fb747ddcf086ce706fbdd23b2926"},
- {file = "aiohttp-3.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:e78da6b55275987cbc89141a1d8e75f5070e577c482dd48bd9123a76a96f0bbb"},
- {file = "aiohttp-3.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c111b3c69060d2bafc446917534150fd049e7aedd6cbf21ba526a5a97b4402a5"},
- {file = "aiohttp-3.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:efbdd51872cf170093998c87ccdf3cb5993add3559341a8e5708bcb311934c94"},
- {file = "aiohttp-3.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7bfdb41dc6e85d8535b00d73947548a748e9534e8e4fddd2638109ff3fb081df"},
- {file = "aiohttp-3.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd9d334412961125e9f68d5b73c1d0ab9ea3f74a58a475e6b119f5293eee7ba"},
- {file = "aiohttp-3.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35d78076736f4a668d57ade00c65d30a8ce28719d8a42471b2a06ccd1a2e3063"},
- {file = "aiohttp-3.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:824dff4f9f4d0f59d0fa3577932ee9a20e09edec8a2f813e1d6b9f89ced8293f"},
- {file = "aiohttp-3.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52b8b4e06fc15519019e128abedaeb56412b106ab88b3c452188ca47a25c4093"},
- {file = "aiohttp-3.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eae569fb1e7559d4f3919965617bb39f9e753967fae55ce13454bec2d1c54f09"},
- {file = "aiohttp-3.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:69b97aa5792428f321f72aeb2f118e56893371f27e0b7d05750bcad06fc42ca1"},
- {file = "aiohttp-3.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4d79aad0ad4b980663316f26d9a492e8fab2af77c69c0f33780a56843ad2f89e"},
- {file = "aiohttp-3.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:d6577140cd7db19e430661e4b2653680194ea8c22c994bc65b7a19d8ec834403"},
- {file = "aiohttp-3.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:9860d455847cd98eb67897f5957b7cd69fbcb436dd3f06099230f16a66e66f79"},
- {file = "aiohttp-3.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:69ff36d3f8f5652994e08bd22f093e11cfd0444cea310f92e01b45a4e46b624e"},
- {file = "aiohttp-3.9.4-cp311-cp311-win32.whl", hash = "sha256:e27d3b5ed2c2013bce66ad67ee57cbf614288bda8cdf426c8d8fe548316f1b5f"},
- {file = "aiohttp-3.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d6a67e26daa686a6fbdb600a9af8619c80a332556245fa8e86c747d226ab1a1e"},
- {file = "aiohttp-3.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c5ff8ff44825736a4065d8544b43b43ee4c6dd1530f3a08e6c0578a813b0aa35"},
- {file = "aiohttp-3.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d12a244627eba4e9dc52cbf924edef905ddd6cafc6513849b4876076a6f38b0e"},
- {file = "aiohttp-3.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dcad56c8d8348e7e468899d2fb3b309b9bc59d94e6db08710555f7436156097f"},
- {file = "aiohttp-3.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7e69a7fd4b5ce419238388e55abd220336bd32212c673ceabc57ccf3d05b55"},
- {file = "aiohttp-3.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4870cb049f10d7680c239b55428916d84158798eb8f353e74fa2c98980dcc0b"},
- {file = "aiohttp-3.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2feaf1b7031ede1bc0880cec4b0776fd347259a723d625357bb4b82f62687b"},
- {file = "aiohttp-3.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:939393e8c3f0a5bcd33ef7ace67680c318dc2ae406f15e381c0054dd658397de"},
- {file = "aiohttp-3.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d2334e387b2adcc944680bebcf412743f2caf4eeebd550f67249c1c3696be04"},
- {file = "aiohttp-3.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e0198ea897680e480845ec0ffc5a14e8b694e25b3f104f63676d55bf76a82f1a"},
- {file = "aiohttp-3.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e40d2cd22914d67c84824045861a5bb0fb46586b15dfe4f046c7495bf08306b2"},
- {file = "aiohttp-3.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:aba80e77c227f4234aa34a5ff2b6ff30c5d6a827a91d22ff6b999de9175d71bd"},
- {file = "aiohttp-3.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:fb68dc73bc8ac322d2e392a59a9e396c4f35cb6fdbdd749e139d1d6c985f2527"},
- {file = "aiohttp-3.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f3460a92638dce7e47062cf088d6e7663adb135e936cb117be88d5e6c48c9d53"},
- {file = "aiohttp-3.9.4-cp312-cp312-win32.whl", hash = "sha256:32dc814ddbb254f6170bca198fe307920f6c1308a5492f049f7f63554b88ef36"},
- {file = "aiohttp-3.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:63f41a909d182d2b78fe3abef557fcc14da50c7852f70ae3be60e83ff64edba5"},
- {file = "aiohttp-3.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c3770365675f6be220032f6609a8fbad994d6dcf3ef7dbcf295c7ee70884c9af"},
- {file = "aiohttp-3.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:305edae1dea368ce09bcb858cf5a63a064f3bff4767dec6fa60a0cc0e805a1d3"},
- {file = "aiohttp-3.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6f121900131d116e4a93b55ab0d12ad72573f967b100e49086e496a9b24523ea"},
- {file = "aiohttp-3.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b71e614c1ae35c3d62a293b19eface83d5e4d194e3eb2fabb10059d33e6e8cbf"},
- {file = "aiohttp-3.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:419f009fa4cfde4d16a7fc070d64f36d70a8d35a90d71aa27670bba2be4fd039"},
- {file = "aiohttp-3.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b39476ee69cfe64061fd77a73bf692c40021f8547cda617a3466530ef63f947"},
- {file = "aiohttp-3.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b33f34c9c7decdb2ab99c74be6443942b730b56d9c5ee48fb7df2c86492f293c"},
- {file = "aiohttp-3.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c78700130ce2dcebb1a8103202ae795be2fa8c9351d0dd22338fe3dac74847d9"},
- {file = "aiohttp-3.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:268ba22d917655d1259af2d5659072b7dc11b4e1dc2cb9662fdd867d75afc6a4"},
- {file = "aiohttp-3.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:17e7c051f53a0d2ebf33013a9cbf020bb4e098c4bc5bce6f7b0c962108d97eab"},
- {file = "aiohttp-3.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7be99f4abb008cb38e144f85f515598f4c2c8932bf11b65add0ff59c9c876d99"},
- {file = "aiohttp-3.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:d58a54d6ff08d2547656356eea8572b224e6f9bbc0cf55fa9966bcaac4ddfb10"},
- {file = "aiohttp-3.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7673a76772bda15d0d10d1aa881b7911d0580c980dbd16e59d7ba1422b2d83cd"},
- {file = "aiohttp-3.9.4-cp38-cp38-win32.whl", hash = "sha256:e4370dda04dc8951012f30e1ce7956a0a226ac0714a7b6c389fb2f43f22a250e"},
- {file = "aiohttp-3.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:eb30c4510a691bb87081192a394fb661860e75ca3896c01c6d186febe7c88530"},
- {file = "aiohttp-3.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:84e90494db7df3be5e056f91412f9fa9e611fbe8ce4aaef70647297f5943b276"},
- {file = "aiohttp-3.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7d4845f8501ab28ebfdbeab980a50a273b415cf69e96e4e674d43d86a464df9d"},
- {file = "aiohttp-3.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:69046cd9a2a17245c4ce3c1f1a4ff8c70c7701ef222fce3d1d8435f09042bba1"},
- {file = "aiohttp-3.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b73a06bafc8dcc508420db43b4dd5850e41e69de99009d0351c4f3007960019"},
- {file = "aiohttp-3.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:418bb0038dfafeac923823c2e63226179976c76f981a2aaad0ad5d51f2229bca"},
- {file = "aiohttp-3.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71a8f241456b6c2668374d5d28398f8e8cdae4cce568aaea54e0f39359cd928d"},
- {file = "aiohttp-3.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:935c369bf8acc2dc26f6eeb5222768aa7c62917c3554f7215f2ead7386b33748"},
- {file = "aiohttp-3.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74e4e48c8752d14ecfb36d2ebb3d76d614320570e14de0a3aa7a726ff150a03c"},
- {file = "aiohttp-3.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:916b0417aeddf2c8c61291238ce25286f391a6acb6f28005dd9ce282bd6311b6"},
- {file = "aiohttp-3.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9b6787b6d0b3518b2ee4cbeadd24a507756ee703adbac1ab6dc7c4434b8c572a"},
- {file = "aiohttp-3.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:221204dbda5ef350e8db6287937621cf75e85778b296c9c52260b522231940ed"},
- {file = "aiohttp-3.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:10afd99b8251022ddf81eaed1d90f5a988e349ee7d779eb429fb07b670751e8c"},
- {file = "aiohttp-3.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2506d9f7a9b91033201be9ffe7d89c6a54150b0578803cce5cb84a943d075bc3"},
- {file = "aiohttp-3.9.4-cp39-cp39-win32.whl", hash = "sha256:e571fdd9efd65e86c6af2f332e0e95dad259bfe6beb5d15b3c3eca3a6eb5d87b"},
- {file = "aiohttp-3.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:7d29dd5319d20aa3b7749719ac9685fbd926f71ac8c77b2477272725f882072d"},
- {file = "aiohttp-3.9.4.tar.gz", hash = "sha256:6ff71ede6d9a5a58cfb7b6fffc83ab5d4a63138276c771ac91ceaaddf5459644"},
+ {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"},
+ {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"},
+ {file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"},
+ {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"},
+ {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"},
+ {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"},
+ {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"},
+ {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"},
+ {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"},
+ {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"},
+ {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"},
+ {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"},
+ {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"},
+ {file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"},
+ {file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"},
+ {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"},
+ {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"},
+ {file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"},
+ {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"},
+ {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"},
+ {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"},
+ {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"},
+ {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"},
+ {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"},
+ {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"},
+ {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"},
+ {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"},
+ {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"},
+ {file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"},
+ {file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"},
+ {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"},
+ {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"},
+ {file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"},
+ {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"},
+ {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"},
+ {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"},
+ {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"},
+ {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"},
+ {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"},
+ {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"},
+ {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"},
+ {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"},
+ {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"},
+ {file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"},
+ {file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"},
+ {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"},
+ {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"},
+ {file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"},
+ {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"},
+ {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"},
+ {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"},
+ {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"},
+ {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"},
+ {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"},
+ {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"},
+ {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"},
+ {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"},
+ {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"},
+ {file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"},
+ {file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"},
+ {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"},
+ {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"},
+ {file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"},
+ {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"},
+ {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"},
+ {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"},
+ {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"},
+ {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"},
+ {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"},
+ {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"},
+ {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"},
+ {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"},
+ {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"},
+ {file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"},
+ {file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"},
+ {file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"},
]
[package.dependencies]
@@ -164,13 +164,13 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p
[[package]]
name = "babel"
-version = "2.14.0"
+version = "2.15.0"
description = "Internationalization utilities"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"},
- {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"},
+ {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"},
+ {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"},
]
[package.dependencies]
@@ -181,33 +181,33 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
[[package]]
name = "black"
-version = "24.1.1"
+version = "24.4.2"
description = "The uncompromising code formatter."
optional = false
python-versions = ">=3.8"
files = [
- {file = "black-24.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2588021038bd5ada078de606f2a804cadd0a3cc6a79cb3e9bb3a8bf581325a4c"},
- {file = "black-24.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a95915c98d6e32ca43809d46d932e2abc5f1f7d582ffbe65a5b4d1588af7445"},
- {file = "black-24.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa6a0e965779c8f2afb286f9ef798df770ba2b6cee063c650b96adec22c056a"},
- {file = "black-24.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5242ecd9e990aeb995b6d03dc3b2d112d4a78f2083e5a8e86d566340ae80fec4"},
- {file = "black-24.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fc1ec9aa6f4d98d022101e015261c056ddebe3da6a8ccfc2c792cbe0349d48b7"},
- {file = "black-24.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0269dfdea12442022e88043d2910429bed717b2d04523867a85dacce535916b8"},
- {file = "black-24.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3d64db762eae4a5ce04b6e3dd745dcca0fb9560eb931a5be97472e38652a161"},
- {file = "black-24.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5d7b06ea8816cbd4becfe5f70accae953c53c0e53aa98730ceccb0395520ee5d"},
- {file = "black-24.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e2c8dfa14677f90d976f68e0c923947ae68fa3961d61ee30976c388adc0b02c8"},
- {file = "black-24.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a21725862d0e855ae05da1dd25e3825ed712eaaccef6b03017fe0853a01aa45e"},
- {file = "black-24.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07204d078e25327aad9ed2c64790d681238686bce254c910de640c7cc4fc3aa6"},
- {file = "black-24.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:a83fe522d9698d8f9a101b860b1ee154c1d25f8a82ceb807d319f085b2627c5b"},
- {file = "black-24.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:08b34e85170d368c37ca7bf81cf67ac863c9d1963b2c1780c39102187ec8dd62"},
- {file = "black-24.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7258c27115c1e3b5de9ac6c4f9957e3ee2c02c0b39222a24dc7aa03ba0e986f5"},
- {file = "black-24.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40657e1b78212d582a0edecafef133cf1dd02e6677f539b669db4746150d38f6"},
- {file = "black-24.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e298d588744efda02379521a19639ebcd314fba7a49be22136204d7ed1782717"},
- {file = "black-24.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:34afe9da5056aa123b8bfda1664bfe6fb4e9c6f311d8e4a6eb089da9a9173bf9"},
- {file = "black-24.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:854c06fb86fd854140f37fb24dbf10621f5dab9e3b0c29a690ba595e3d543024"},
- {file = "black-24.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3897ae5a21ca132efa219c029cce5e6bfc9c3d34ed7e892113d199c0b1b444a2"},
- {file = "black-24.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:ecba2a15dfb2d97105be74bbfe5128bc5e9fa8477d8c46766505c1dda5883aac"},
- {file = "black-24.1.1-py3-none-any.whl", hash = "sha256:5cdc2e2195212208fbcae579b931407c1fa9997584f0a415421748aeafff1168"},
- {file = "black-24.1.1.tar.gz", hash = "sha256:48b5760dcbfe5cf97fd4fba23946681f3a81514c6ab8a45b50da67ac8fbc6c7b"},
+ {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"},
+ {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"},
+ {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"},
+ {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"},
+ {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"},
+ {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"},
+ {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"},
+ {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"},
+ {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"},
+ {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"},
+ {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"},
+ {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"},
+ {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"},
+ {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"},
+ {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"},
+ {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"},
+ {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"},
+ {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"},
+ {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"},
+ {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"},
+ {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"},
+ {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"},
]
[package.dependencies]
@@ -383,63 +383,63 @@ files = [
[[package]]
name = "coverage"
-version = "7.4.4"
+version = "7.5.1"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"},
- {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"},
- {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"},
- {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"},
- {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"},
- {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"},
- {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"},
- {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"},
- {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"},
- {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"},
- {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"},
- {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"},
- {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"},
- {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"},
- {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"},
- {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"},
- {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"},
- {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"},
- {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"},
- {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"},
- {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"},
- {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"},
- {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"},
- {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"},
- {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"},
- {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"},
- {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"},
- {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"},
- {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"},
- {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"},
- {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"},
- {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"},
- {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"},
- {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"},
- {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"},
- {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"},
- {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"},
- {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"},
- {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"},
- {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"},
- {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"},
- {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"},
- {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"},
- {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"},
- {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"},
- {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"},
- {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"},
- {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"},
- {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"},
- {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"},
- {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"},
- {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"},
+ {file = "coverage-7.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0884920835a033b78d1c73b6d3bbcda8161a900f38a488829a83982925f6c2e"},
+ {file = "coverage-7.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:39afcd3d4339329c5f58de48a52f6e4e50f6578dd6099961cf22228feb25f38f"},
+ {file = "coverage-7.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b0ceee8147444347da6a66be737c9d78f3353b0681715b668b72e79203e4a"},
+ {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a9ca3f2fae0088c3c71d743d85404cec8df9be818a005ea065495bedc33da35"},
+ {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd215c0c7d7aab005221608a3c2b46f58c0285a819565887ee0b718c052aa4e"},
+ {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4bf0655ab60d754491004a5efd7f9cccefcc1081a74c9ef2da4735d6ee4a6223"},
+ {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61c4bf1ba021817de12b813338c9be9f0ad5b1e781b9b340a6d29fc13e7c1b5e"},
+ {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db66fc317a046556a96b453a58eced5024af4582a8dbdc0c23ca4dbc0d5b3146"},
+ {file = "coverage-7.5.1-cp310-cp310-win32.whl", hash = "sha256:b016ea6b959d3b9556cb401c55a37547135a587db0115635a443b2ce8f1c7228"},
+ {file = "coverage-7.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:df4e745a81c110e7446b1cc8131bf986157770fa405fe90e15e850aaf7619bc8"},
+ {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"},
+ {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"},
+ {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"},
+ {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"},
+ {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"},
+ {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"},
+ {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"},
+ {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"},
+ {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"},
+ {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"},
+ {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"},
+ {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"},
+ {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"},
+ {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"},
+ {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"},
+ {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"},
+ {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"},
+ {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"},
+ {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"},
+ {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"},
+ {file = "coverage-7.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2213def81a50519d7cc56ed643c9e93e0247f5bbe0d1247d15fa520814a7cd7"},
+ {file = "coverage-7.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5037f8fcc2a95b1f0e80585bd9d1ec31068a9bcb157d9750a172836e98bc7a90"},
+ {file = "coverage-7.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3721c2c9e4c4953a41a26c14f4cef64330392a6d2d675c8b1db3b645e31f0e"},
+ {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca498687ca46a62ae590253fba634a1fe9836bc56f626852fb2720f334c9e4e5"},
+ {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cdcbc320b14c3e5877ee79e649677cb7d89ef588852e9583e6b24c2e5072661"},
+ {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:57e0204b5b745594e5bc14b9b50006da722827f0b8c776949f1135677e88d0b8"},
+ {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fe7502616b67b234482c3ce276ff26f39ffe88adca2acf0261df4b8454668b4"},
+ {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9e78295f4144f9dacfed4f92935fbe1780021247c2fabf73a819b17f0ccfff8d"},
+ {file = "coverage-7.5.1-cp38-cp38-win32.whl", hash = "sha256:1434e088b41594baa71188a17533083eabf5609e8e72f16ce8c186001e6b8c41"},
+ {file = "coverage-7.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:0646599e9b139988b63704d704af8e8df7fa4cbc4a1f33df69d97f36cb0a38de"},
+ {file = "coverage-7.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4cc37def103a2725bc672f84bd939a6fe4522310503207aae4d56351644682f1"},
+ {file = "coverage-7.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc0b4d8bfeabd25ea75e94632f5b6e047eef8adaed0c2161ada1e922e7f7cece"},
+ {file = "coverage-7.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d0a0f5e06881ecedfe6f3dd2f56dcb057b6dbeb3327fd32d4b12854df36bf26"},
+ {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9735317685ba6ec7e3754798c8871c2f49aa5e687cc794a0b1d284b2389d1bd5"},
+ {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d21918e9ef11edf36764b93101e2ae8cc82aa5efdc7c5a4e9c6c35a48496d601"},
+ {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c3e757949f268364b96ca894b4c342b41dc6f8f8b66c37878aacef5930db61be"},
+ {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:79afb6197e2f7f60c4824dd4b2d4c2ec5801ceb6ba9ce5d2c3080e5660d51a4f"},
+ {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d0d98d95dd18fe29dc66808e1accf59f037d5716f86a501fc0256455219668"},
+ {file = "coverage-7.5.1-cp39-cp39-win32.whl", hash = "sha256:1cc0fe9b0b3a8364093c53b0b4c0c2dd4bb23acbec4c9240b5f284095ccf7981"},
+ {file = "coverage-7.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:dde0070c40ea8bb3641e811c1cfbf18e265d024deff6de52c5950677a8fb1e0f"},
+ {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"},
+ {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"},
]
[package.extras]
@@ -458,13 +458,13 @@ files = [
[[package]]
name = "exceptiongroup"
-version = "1.2.0"
+version = "1.2.1"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
files = [
- {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
- {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
+ {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"},
+ {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"},
]
[package.extras]
@@ -472,13 +472,13 @@ test = ["pytest (>=6)"]
[[package]]
name = "filelock"
-version = "3.13.4"
+version = "3.14.0"
description = "A platform independent file lock."
optional = false
python-versions = ">=3.8"
files = [
- {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"},
- {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"},
+ {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"},
+ {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"},
]
[package.extras]
@@ -692,13 +692,13 @@ socks = ["socksio (==1.*)"]
[[package]]
name = "identify"
-version = "2.5.35"
+version = "2.5.36"
description = "File identification library for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"},
- {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"},
+ {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"},
+ {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"},
]
[package.extras]
@@ -745,15 +745,29 @@ files = [
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
+[[package]]
+name = "isort"
+version = "5.13.2"
+description = "A Python utility / library to sort Python imports."
+optional = false
+python-versions = ">=3.8.0"
+files = [
+ {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
+ {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
+]
+
+[package.extras]
+colors = ["colorama (>=0.4.6)"]
+
[[package]]
name = "jinja2"
-version = "3.1.3"
+version = "3.1.4"
description = "A very fast and expressive template engine."
optional = false
python-versions = ">=3.7"
files = [
- {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"},
- {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"},
+ {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
+ {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
]
[package.dependencies]
@@ -897,13 +911,13 @@ files = [
[[package]]
name = "mdit-py-plugins"
-version = "0.4.0"
+version = "0.4.1"
description = "Collection of plugins for markdown-it-py"
optional = false
python-versions = ">=3.8"
files = [
- {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"},
- {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"},
+ {file = "mdit_py_plugins-0.4.1-py3-none-any.whl", hash = "sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a"},
+ {file = "mdit_py_plugins-0.4.1.tar.gz", hash = "sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c"},
]
[package.dependencies]
@@ -938,34 +952,34 @@ files = [
[[package]]
name = "mkdocs"
-version = "1.5.3"
+version = "1.6.0"
description = "Project documentation with Markdown."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "mkdocs-1.5.3-py3-none-any.whl", hash = "sha256:3b3a78e736b31158d64dbb2f8ba29bd46a379d0c6e324c2246c3bc3d2189cfc1"},
- {file = "mkdocs-1.5.3.tar.gz", hash = "sha256:eb7c99214dcb945313ba30426c2451b735992c73c2e10838f76d09e39ff4d0e2"},
+ {file = "mkdocs-1.6.0-py3-none-any.whl", hash = "sha256:1eb5cb7676b7d89323e62b56235010216319217d4af5ddc543a91beb8d125ea7"},
+ {file = "mkdocs-1.6.0.tar.gz", hash = "sha256:a73f735824ef83a4f3bcb7a231dcab23f5a838f88b7efc54a0eef5fbdbc3c512"},
]
[package.dependencies]
click = ">=7.0"
colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""}
ghp-import = ">=1.0"
-importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""}
+importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
jinja2 = ">=2.11.1"
-markdown = ">=3.2.1"
+markdown = ">=3.3.6"
markupsafe = ">=2.0.1"
mergedeep = ">=1.3.4"
+mkdocs-get-deps = ">=0.2.0"
packaging = ">=20.5"
pathspec = ">=0.11.1"
-platformdirs = ">=2.2.0"
pyyaml = ">=5.1"
pyyaml-env-tag = ">=0.1"
watchdog = ">=2.0"
[package.extras]
i18n = ["babel (>=2.9.0)"]
-min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pathspec (==0.11.1)", "platformdirs (==2.2.0)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"]
+min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"]
[[package]]
name = "mkdocs-autorefs"
@@ -996,15 +1010,49 @@ files = [
[package.dependencies]
mkdocs = "*"
+[[package]]
+name = "mkdocs-get-deps"
+version = "0.2.0"
+description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"},
+ {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"},
+]
+
+[package.dependencies]
+importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""}
+mergedeep = ">=1.3.4"
+platformdirs = ">=2.2.0"
+pyyaml = ">=5.1"
+
+[[package]]
+name = "mkdocs-git-revision-date-localized-plugin"
+version = "1.2.5"
+description = "Mkdocs plugin that enables displaying the localized date of the last git modification of a markdown file."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "mkdocs_git_revision_date_localized_plugin-1.2.5-py3-none-any.whl", hash = "sha256:d796a18b07cfcdb154c133e3ec099d2bb5f38389e4fd54d3eb516a8a736815b8"},
+ {file = "mkdocs_git_revision_date_localized_plugin-1.2.5.tar.gz", hash = "sha256:0c439816d9d0dba48e027d9d074b2b9f1d7cd179f74ba46b51e4da7bb3dc4b9b"},
+]
+
+[package.dependencies]
+babel = ">=2.7.0"
+GitPython = "*"
+mkdocs = ">=1.0"
+pytz = "*"
+
[[package]]
name = "mkdocs-material"
-version = "9.5.18"
+version = "9.5.23"
description = "Documentation that simply works"
optional = false
python-versions = ">=3.8"
files = [
- {file = "mkdocs_material-9.5.18-py3-none-any.whl", hash = "sha256:1e0e27fc9fe239f9064318acf548771a4629d5fd5dfd45444fd80a953fe21eb4"},
- {file = "mkdocs_material-9.5.18.tar.gz", hash = "sha256:a43f470947053fa2405c33995f282d24992c752a50114f23f30da9d8d0c57e62"},
+ {file = "mkdocs_material-9.5.23-py3-none-any.whl", hash = "sha256:ffd08a5beaef3cd135aceb58ded8b98bbbbf2b70e5b656f6a14a63c917d9b001"},
+ {file = "mkdocs_material-9.5.23.tar.gz", hash = "sha256:4627fc3f15de2cba2bde9debc2fd59b9888ef494beabfe67eb352e23d14bf288"},
]
[package.dependencies]
@@ -1012,7 +1060,7 @@ babel = ">=2.10,<3.0"
colorama = ">=0.4,<1.0"
jinja2 = ">=3.0,<4.0"
markdown = ">=3.2,<4.0"
-mkdocs = ">=1.5.3,<1.6.0"
+mkdocs = ">=1.6,<2.0"
mkdocs-material-extensions = ">=1.3,<2.0"
paginate = ">=0.5,<1.0"
pygments = ">=2.16,<3.0"
@@ -1038,25 +1086,25 @@ files = [
[[package]]
name = "mkdocs-rss-plugin"
-version = "1.12.1"
+version = "1.12.2"
description = "MkDocs plugin which generates a static RSS feed using git log and page.meta."
optional = false
-python-versions = ">=3.8, <4"
+python-versions = "<4,>=3.8"
files = [
- {file = "mkdocs-rss-plugin-1.12.1.tar.gz", hash = "sha256:5df9bddfdc1465623def1b14c2656c5e8f62fa7c8bd1c0c667e01fc86105d415"},
- {file = "mkdocs_rss_plugin-1.12.1-py2.py3-none-any.whl", hash = "sha256:acfb8eec95f1db389b36770baf99af3b87e38484cc37993194a35aa173eb3fe8"},
+ {file = "mkdocs_rss_plugin-1.12.2-py2.py3-none-any.whl", hash = "sha256:2e1d5cb871494f2634b9c3fe523a4246ba5c00c5f6627242101675d3c63b2bdd"},
+ {file = "mkdocs_rss_plugin-1.12.2.tar.gz", hash = "sha256:313f127967ebcf14ad6f74f1f6f89f054deeb3a1de510770778ce150b75f2247"},
]
[package.dependencies]
GitPython = ">=3.1,<3.2"
mkdocs = ">=1.4,<2"
pytz = {version = "==2022.*", markers = "python_version < \"3.9\""}
-tzdata = {version = "==2023.*", markers = "python_version >= \"3.9\" and sys_platform == \"win32\""}
+tzdata = {version = "==2024.*", markers = "python_version >= \"3.9\" and sys_platform == \"win32\""}
[package.extras]
dev = ["black", "flake8 (>=6,<8)", "flake8-bugbear (>=23.12)", "flake8-builtins (>=2.1)", "flake8-eradicate (>=1)", "flake8-isort (>=6)", "pre-commit (>=3,<4)"]
-doc = ["mkdocs-git-committers-plugin-2 (>=1.2,<2.3)", "mkdocs-git-revision-date-localized-plugin (>=1,<1.3)", "mkdocs-material[imaging] (>=9.5.1,<10)", "mkdocs-minify-plugin (==0.8.*)", "mkdocstrings[python] (>=0.18,<1)", "termynal (>=0.11.1,<0.12)"]
-test = ["feedparser (>=6.0.11,<6.1)", "jsonfeed-util (>=1.1.2,<2)", "mkdocs-material[imaging] (>=9)", "pytest-cov (>=4,<4.2)", "validator-collection (>=1.5,<1.6)"]
+doc = ["mkdocs-git-committers-plugin-2 (>=1.2,<2.4)", "mkdocs-git-revision-date-localized-plugin (>=1,<1.3)", "mkdocs-material[imaging] (>=9.5.1,<10)", "mkdocs-minify-plugin (==0.8.*)", "mkdocstrings[python] (>=0.18,<1)", "termynal (>=0.11.1,<0.13)"]
+test = ["feedparser (>=6.0.11,<6.1)", "jsonfeed-util (>=1.1.2,<2)", "mkdocs-material[imaging] (>=9)", "pytest-cov (>=4,<5.1)", "validator-collection (>=1.5,<1.6)"]
[[package]]
name = "mkdocstrings"
@@ -1264,38 +1312,38 @@ files = [
[[package]]
name = "mypy"
-version = "1.9.0"
+version = "1.10.0"
description = "Optional static typing for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"},
- {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"},
- {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"},
- {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"},
- {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"},
- {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"},
- {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"},
- {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"},
- {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"},
- {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"},
- {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"},
- {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"},
- {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"},
- {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"},
- {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"},
- {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"},
- {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"},
- {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"},
- {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"},
- {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"},
- {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"},
- {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"},
- {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"},
- {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"},
- {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"},
- {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"},
- {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"},
+ {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"},
+ {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"},
+ {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"},
+ {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"},
+ {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"},
+ {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"},
+ {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"},
+ {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"},
+ {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"},
+ {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"},
+ {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"},
+ {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"},
+ {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"},
+ {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"},
+ {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"},
+ {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"},
+ {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"},
+ {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"},
+ {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"},
+ {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"},
+ {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"},
+ {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"},
+ {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"},
+ {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"},
+ {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"},
+ {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"},
+ {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"},
]
[package.dependencies]
@@ -1368,28 +1416,29 @@ files = [
[[package]]
name = "platformdirs"
-version = "4.2.0"
-description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+version = "4.2.2"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
optional = false
python-versions = ">=3.8"
files = [
- {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"},
- {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"},
+ {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
+ {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
]
[package.extras]
docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
+type = ["mypy (>=1.8)"]
[[package]]
name = "pluggy"
-version = "1.4.0"
+version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
- {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
+ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
+ {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[package.extras]
@@ -1416,32 +1465,31 @@ virtualenv = ">=20.10.0"
[[package]]
name = "pygments"
-version = "2.17.2"
+version = "2.18.0"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"},
- {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
+ {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
+ {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
]
[package.extras]
-plugins = ["importlib-metadata"]
windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pymdown-extensions"
-version = "10.7.1"
+version = "10.8.1"
description = "Extension pack for Python Markdown."
optional = false
python-versions = ">=3.8"
files = [
- {file = "pymdown_extensions-10.7.1-py3-none-any.whl", hash = "sha256:f5cc7000d7ff0d1ce9395d216017fa4df3dde800afb1fb72d1c7d3fd35e710f4"},
- {file = "pymdown_extensions-10.7.1.tar.gz", hash = "sha256:c70e146bdd83c744ffc766b4671999796aba18842b268510a329f7f64700d584"},
+ {file = "pymdown_extensions-10.8.1-py3-none-any.whl", hash = "sha256:f938326115884f48c6059c67377c46cf631c733ef3629b6eed1349989d1b30cb"},
+ {file = "pymdown_extensions-10.8.1.tar.gz", hash = "sha256:3ab1db5c9e21728dabf75192d71471f8e50f216627e9a1fa9535ecb0231b9940"},
]
[package.dependencies]
-markdown = ">=3.5"
+markdown = ">=3.6"
pyyaml = "*"
[package.extras]
@@ -1561,6 +1609,7 @@ files = [
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
+ {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
@@ -1568,8 +1617,15 @@ files = [
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
+ {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
+ {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
+ {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
+ {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
+ {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
+ {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
+ {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
@@ -1586,6 +1642,7 @@ files = [
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
+ {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
@@ -1593,6 +1650,7 @@ files = [
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
+ {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
@@ -1614,104 +1672,90 @@ pyyaml = "*"
[[package]]
name = "regex"
-version = "2023.12.25"
+version = "2024.5.15"
description = "Alternative regular expression module, to replace re."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"},
- {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"},
- {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"},
- {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"},
- {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"},
- {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"},
- {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"},
- {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"},
- {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"},
- {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"},
- {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"},
- {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"},
- {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"},
- {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"},
- {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"},
- {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"},
- {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"},
- {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"},
- {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"},
- {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"},
- {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"},
- {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"},
- {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"},
- {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"},
- {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"},
- {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"},
- {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"},
- {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"},
- {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"},
- {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"},
- {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"},
- {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"},
- {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"},
- {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"},
- {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"},
- {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"},
- {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"},
- {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"},
- {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"},
- {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"},
- {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"},
- {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"},
- {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"},
- {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"},
- {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"},
- {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"},
- {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"},
- {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"},
- {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"},
- {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"},
- {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"},
- {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"},
- {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"},
- {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"},
- {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"},
- {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"},
- {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"},
- {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"},
- {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"},
- {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"},
- {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"},
- {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"},
- {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"},
- {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"},
- {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"},
- {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"},
- {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"},
- {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"},
- {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"},
- {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"},
- {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"},
- {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"},
- {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"},
- {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"},
- {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"},
- {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"},
- {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"},
- {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"},
- {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"},
- {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"},
- {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"},
- {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"},
- {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"},
- {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"},
- {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"},
- {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"},
- {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"},
- {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"},
- {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"},
- {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"},
- {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"},
- {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"},
- {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"},
+ {file = "regex-2024.5.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a81e3cfbae20378d75185171587cbf756015ccb14840702944f014e0d93ea09f"},
+ {file = "regex-2024.5.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b59138b219ffa8979013be7bc85bb60c6f7b7575df3d56dc1e403a438c7a3f6"},
+ {file = "regex-2024.5.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0bd000c6e266927cb7a1bc39d55be95c4b4f65c5be53e659537537e019232b1"},
+ {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eaa7ddaf517aa095fa8da0b5015c44d03da83f5bd49c87961e3c997daed0de7"},
+ {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba68168daedb2c0bab7fd7e00ced5ba90aebf91024dea3c88ad5063c2a562cca"},
+ {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e8d717bca3a6e2064fc3a08df5cbe366369f4b052dcd21b7416e6d71620dca1"},
+ {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1337b7dbef9b2f71121cdbf1e97e40de33ff114801263b275aafd75303bd62b5"},
+ {file = "regex-2024.5.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9ebd0a36102fcad2f03696e8af4ae682793a5d30b46c647eaf280d6cfb32796"},
+ {file = "regex-2024.5.15-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9efa1a32ad3a3ea112224897cdaeb6aa00381627f567179c0314f7b65d354c62"},
+ {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1595f2d10dff3d805e054ebdc41c124753631b6a471b976963c7b28543cf13b0"},
+ {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b802512f3e1f480f41ab5f2cfc0e2f761f08a1f41092d6718868082fc0d27143"},
+ {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a0981022dccabca811e8171f913de05720590c915b033b7e601f35ce4ea7019f"},
+ {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:19068a6a79cf99a19ccefa44610491e9ca02c2be3305c7760d3831d38a467a6f"},
+ {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1b5269484f6126eee5e687785e83c6b60aad7663dafe842b34691157e5083e53"},
+ {file = "regex-2024.5.15-cp310-cp310-win32.whl", hash = "sha256:ada150c5adfa8fbcbf321c30c751dc67d2f12f15bd183ffe4ec7cde351d945b3"},
+ {file = "regex-2024.5.15-cp310-cp310-win_amd64.whl", hash = "sha256:ac394ff680fc46b97487941f5e6ae49a9f30ea41c6c6804832063f14b2a5a145"},
+ {file = "regex-2024.5.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f5b1dff3ad008dccf18e652283f5e5339d70bf8ba7c98bf848ac33db10f7bc7a"},
+ {file = "regex-2024.5.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c6a2b494a76983df8e3d3feea9b9ffdd558b247e60b92f877f93a1ff43d26656"},
+ {file = "regex-2024.5.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a32b96f15c8ab2e7d27655969a23895eb799de3665fa94349f3b2fbfd547236f"},
+ {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10002e86e6068d9e1c91eae8295ef690f02f913c57db120b58fdd35a6bb1af35"},
+ {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec54d5afa89c19c6dd8541a133be51ee1017a38b412b1321ccb8d6ddbeb4cf7d"},
+ {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10e4ce0dca9ae7a66e6089bb29355d4432caed736acae36fef0fdd7879f0b0cb"},
+ {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e507ff1e74373c4d3038195fdd2af30d297b4f0950eeda6f515ae3d84a1770f"},
+ {file = "regex-2024.5.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1f059a4d795e646e1c37665b9d06062c62d0e8cc3c511fe01315973a6542e40"},
+ {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0721931ad5fe0dda45d07f9820b90b2148ccdd8e45bb9e9b42a146cb4f695649"},
+ {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:833616ddc75ad595dee848ad984d067f2f31be645d603e4d158bba656bbf516c"},
+ {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:287eb7f54fc81546346207c533ad3c2c51a8d61075127d7f6d79aaf96cdee890"},
+ {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:19dfb1c504781a136a80ecd1fff9f16dddf5bb43cec6871778c8a907a085bb3d"},
+ {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:119af6e56dce35e8dfb5222573b50c89e5508d94d55713c75126b753f834de68"},
+ {file = "regex-2024.5.15-cp311-cp311-win32.whl", hash = "sha256:1c1c174d6ec38d6c8a7504087358ce9213d4332f6293a94fbf5249992ba54efa"},
+ {file = "regex-2024.5.15-cp311-cp311-win_amd64.whl", hash = "sha256:9e717956dcfd656f5055cc70996ee2cc82ac5149517fc8e1b60261b907740201"},
+ {file = "regex-2024.5.15-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:632b01153e5248c134007209b5c6348a544ce96c46005d8456de1d552455b014"},
+ {file = "regex-2024.5.15-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e64198f6b856d48192bf921421fdd8ad8eb35e179086e99e99f711957ffedd6e"},
+ {file = "regex-2024.5.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68811ab14087b2f6e0fc0c2bae9ad689ea3584cad6917fc57be6a48bbd012c49"},
+ {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ec0c2fea1e886a19c3bee0cd19d862b3aa75dcdfb42ebe8ed30708df64687a"},
+ {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0c0c0003c10f54a591d220997dd27d953cd9ccc1a7294b40a4be5312be8797b"},
+ {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2431b9e263af1953c55abbd3e2efca67ca80a3de8a0437cb58e2421f8184717a"},
+ {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a605586358893b483976cffc1723fb0f83e526e8f14c6e6614e75919d9862cf"},
+ {file = "regex-2024.5.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391d7f7f1e409d192dba8bcd42d3e4cf9e598f3979cdaed6ab11288da88cb9f2"},
+ {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9ff11639a8d98969c863d4617595eb5425fd12f7c5ef6621a4b74b71ed8726d5"},
+ {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4eee78a04e6c67e8391edd4dad3279828dd66ac4b79570ec998e2155d2e59fd5"},
+ {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8fe45aa3f4aa57faabbc9cb46a93363edd6197cbc43523daea044e9ff2fea83e"},
+ {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d0a3d8d6acf0c78a1fff0e210d224b821081330b8524e3e2bc5a68ef6ab5803d"},
+ {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c486b4106066d502495b3025a0a7251bf37ea9540433940a23419461ab9f2a80"},
+ {file = "regex-2024.5.15-cp312-cp312-win32.whl", hash = "sha256:c49e15eac7c149f3670b3e27f1f28a2c1ddeccd3a2812cba953e01be2ab9b5fe"},
+ {file = "regex-2024.5.15-cp312-cp312-win_amd64.whl", hash = "sha256:673b5a6da4557b975c6c90198588181029c60793835ce02f497ea817ff647cb2"},
+ {file = "regex-2024.5.15-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:87e2a9c29e672fc65523fb47a90d429b70ef72b901b4e4b1bd42387caf0d6835"},
+ {file = "regex-2024.5.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3bea0ba8b73b71b37ac833a7f3fd53825924165da6a924aec78c13032f20850"},
+ {file = "regex-2024.5.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bfc4f82cabe54f1e7f206fd3d30fda143f84a63fe7d64a81558d6e5f2e5aaba9"},
+ {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5bb9425fe881d578aeca0b2b4b3d314ec88738706f66f219c194d67179337cb"},
+ {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64c65783e96e563103d641760664125e91bd85d8e49566ee560ded4da0d3e704"},
+ {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf2430df4148b08fb4324b848672514b1385ae3807651f3567871f130a728cc3"},
+ {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5397de3219a8b08ae9540c48f602996aa6b0b65d5a61683e233af8605c42b0f2"},
+ {file = "regex-2024.5.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:455705d34b4154a80ead722f4f185b04c4237e8e8e33f265cd0798d0e44825fa"},
+ {file = "regex-2024.5.15-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2b6f1b3bb6f640c1a92be3bbfbcb18657b125b99ecf141fb3310b5282c7d4ed"},
+ {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3ad070b823ca5890cab606c940522d05d3d22395d432f4aaaf9d5b1653e47ced"},
+ {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5b5467acbfc153847d5adb21e21e29847bcb5870e65c94c9206d20eb4e99a384"},
+ {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e6662686aeb633ad65be2a42b4cb00178b3fbf7b91878f9446075c404ada552f"},
+ {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:2b4c884767504c0e2401babe8b5b7aea9148680d2e157fa28f01529d1f7fcf67"},
+ {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3cd7874d57f13bf70078f1ff02b8b0aa48d5b9ed25fc48547516c6aba36f5741"},
+ {file = "regex-2024.5.15-cp38-cp38-win32.whl", hash = "sha256:e4682f5ba31f475d58884045c1a97a860a007d44938c4c0895f41d64481edbc9"},
+ {file = "regex-2024.5.15-cp38-cp38-win_amd64.whl", hash = "sha256:d99ceffa25ac45d150e30bd9ed14ec6039f2aad0ffa6bb87a5936f5782fc1569"},
+ {file = "regex-2024.5.15-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13cdaf31bed30a1e1c2453ef6015aa0983e1366fad2667657dbcac7b02f67133"},
+ {file = "regex-2024.5.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cac27dcaa821ca271855a32188aa61d12decb6fe45ffe3e722401fe61e323cd1"},
+ {file = "regex-2024.5.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7dbe2467273b875ea2de38ded4eba86cbcbc9a1a6d0aa11dcf7bd2e67859c435"},
+ {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f18a9a3513a99c4bef0e3efd4c4a5b11228b48aa80743be822b71e132ae4f5"},
+ {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d347a741ea871c2e278fde6c48f85136c96b8659b632fb57a7d1ce1872547600"},
+ {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1878b8301ed011704aea4c806a3cadbd76f84dece1ec09cc9e4dc934cfa5d4da"},
+ {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4babf07ad476aaf7830d77000874d7611704a7fcf68c9c2ad151f5d94ae4bfc4"},
+ {file = "regex-2024.5.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35cb514e137cb3488bce23352af3e12fb0dbedd1ee6e60da053c69fb1b29cc6c"},
+ {file = "regex-2024.5.15-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cdd09d47c0b2efee9378679f8510ee6955d329424c659ab3c5e3a6edea696294"},
+ {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:72d7a99cd6b8f958e85fc6ca5b37c4303294954eac1376535b03c2a43eb72629"},
+ {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a094801d379ab20c2135529948cb84d417a2169b9bdceda2a36f5f10977ebc16"},
+ {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c0c18345010870e58238790a6779a1219b4d97bd2e77e1140e8ee5d14df071aa"},
+ {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:16093f563098448ff6b1fa68170e4acbef94e6b6a4e25e10eae8598bb1694b5d"},
+ {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e38a7d4e8f633a33b4c7350fbd8bad3b70bf81439ac67ac38916c4a86b465456"},
+ {file = "regex-2024.5.15-cp39-cp39-win32.whl", hash = "sha256:71a455a3c584a88f654b64feccc1e25876066c4f5ef26cd6dd711308aa538694"},
+ {file = "regex-2024.5.15-cp39-cp39-win_amd64.whl", hash = "sha256:cab12877a9bdafde5500206d1020a584355a97884dfd388af3699e9137bf7388"},
+ {file = "regex-2024.5.15.tar.gz", hash = "sha256:d3ee02d9e5f482cc8309134a91eeaacbdd2261ba111b0fef3748eeb4913e6a2c"},
]
[[package]]
@@ -2083,13 +2127,13 @@ files = [
[[package]]
name = "tzdata"
-version = "2023.4"
+version = "2024.1"
description = "Provider of IANA time zone data"
optional = false
python-versions = ">=2"
files = [
- {file = "tzdata-2023.4-py2.py3-none-any.whl", hash = "sha256:aa3ace4329eeacda5b7beb7ea08ece826c28d761cda36e747cfbf97996d39bf3"},
- {file = "tzdata-2023.4.tar.gz", hash = "sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9"},
+ {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"},
+ {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"},
]
[[package]]
@@ -2125,13 +2169,13 @@ zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "virtualenv"
-version = "20.25.1"
+version = "20.26.2"
description = "Virtual Python Environment builder"
optional = false
python-versions = ">=3.7"
files = [
- {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"},
- {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"},
+ {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"},
+ {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"},
]
[package.dependencies]
@@ -2140,7 +2184,7 @@ filelock = ">=3.12.2,<4"
platformdirs = ">=3.9.1,<5"
[package.extras]
-docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
+docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
[[package]]
@@ -2308,4 +2352,4 @@ syntax = ["tree-sitter", "tree-sitter-languages"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
-content-hash = "4d935ffc4d465a2e51881f971fe3b9b62b9a1a8462ae511a512b5d7fae06a733"
+content-hash = "7cf7f99ade9d00e6b4b190af1563eaeccb55e4e8e424f67ace7da2e3e9c62308"
diff --git a/pyproject.toml b/pyproject.toml
index 239a53e19f..167aa57c55 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "textual"
-version = "0.56.4"
+version = "0.72.0"
homepage = "https://github.com/Textualize/textual"
repository = "https://github.com/Textualize/textual"
documentation = "https://textual.textualize.io/"
@@ -52,25 +52,27 @@ tree-sitter-languages = { version = "1.10.2", optional = true }
syntax = ["tree-sitter", "tree_sitter_languages"]
[tool.poetry.group.dev.dependencies]
-pytest = "^7.1.3"
-black = "24.1.1"
-mypy = "^1.0.0"
-pytest-cov = "^2.12.1"
+black = "24.4.2"
+griffe = "0.32.3"
+httpx = "^0.23.1"
mkdocs = "^1.3.0"
+mkdocs-exclude = "^1.0.2"
+mkdocs-git-revision-date-localized-plugin = "^1.2.5"
+mkdocs-material = "^9.0.11"
+mkdocs-rss-plugin = "^1.5.0"
mkdocstrings = { extras = ["python"], version = "^0.20.0" }
mkdocstrings-python = "0.10.1"
-mkdocs-material = "^9.0.11"
-mkdocs-exclude = "^1.0.2"
+mypy = "^1.0.0"
pre-commit = "^2.13.0"
-mkdocs-rss-plugin = "^1.5.0"
-httpx = "^0.23.1"
-types-setuptools = "^67.2.0.1"
-textual-dev = "^1.2.0"
+pytest = "^7.1.3"
pytest-asyncio = "*"
+pytest-cov = "^2.12.1"
pytest-textual-snapshot = ">=0.4.0"
+textual-dev = "^1.2.0"
+types-setuptools = "^67.2.0.1"
types-tree-sitter = "^0.20.1.4"
types-tree-sitter-languages = "^1.7.0.1"
-griffe = "0.32.3"
+isort = "^5.13.2"
[tool.pytest.ini_options]
asyncio_mode = "auto"
diff --git a/src/textual/_animator.py b/src/textual/_animator.py
index 21234fee8b..8a669b2c8a 100644
--- a/src/textual/_animator.py
+++ b/src/textual/_animator.py
@@ -21,7 +21,7 @@
"""Animation keys are the id of the object and the attribute being animated."""
EasingFunction = Callable[[float], float]
-"""Signature for a function that parametrises animation speed.
+"""Signature for a function that parametrizes animation speed.
An easing function must map the interval [0, 1] into the interval [0, 1].
"""
diff --git a/src/textual/_callback.py b/src/textual/_callback.py
index 756a9352af..d2fd2403c2 100644
--- a/src/textual/_callback.py
+++ b/src/textual/_callback.py
@@ -48,7 +48,7 @@ async def _invoke(callback: Callable, *params: object) -> Any:
return result
-async def invoke(callback: Callable[[], Any], *params: object) -> Any:
+async def invoke(callback: Callable[..., Any], *params: object) -> Any:
"""Invoke a callback with an arbitrary number of parameters.
Args:
diff --git a/src/textual/_compositor.py b/src/textual/_compositor.py
index e3dd08a50f..f1ce3caa1d 100644
--- a/src/textual/_compositor.py
+++ b/src/textual/_compositor.py
@@ -1000,12 +1000,17 @@ def _get_renders(
)
def render_update(
- self, full: bool = False, screen_stack: list[Screen] | None = None
+ self,
+ full: bool = False,
+ screen_stack: list[Screen] | None = None,
+ simplify: bool = False,
) -> RenderableType | None:
"""Render an update renderable.
Args:
+ full: Perform a full update if `True`, otherwise a partial update.
screen_stack: Screen stack list. Defaults to None.
+ simplify: Simplify segments.
Returns:
A renderable for the update, or `None` if no update was required.
@@ -1014,7 +1019,7 @@ def render_update(
visible_screen_stack.set([] if screen_stack is None else screen_stack)
screen_region = self.size.region
if full or screen_region in self._dirty_regions:
- return self.render_full_update()
+ return self.render_full_update(simplify=simplify)
else:
return self.render_partial_update()
@@ -1038,9 +1043,12 @@ def render_inline(
strips = self.render_strips(size)
return InlineUpdate(strips, clear=clear)
- def render_full_update(self) -> LayoutUpdate:
+ def render_full_update(self, simplify: bool = False) -> LayoutUpdate:
"""Render a full update.
+ Args:
+ simplify: Simplify the segments (combine contiguous segments).
+
Returns:
A LayoutUpdate renderable.
"""
@@ -1048,7 +1056,11 @@ def render_full_update(self) -> LayoutUpdate:
self._dirty_regions.clear()
crop = screen_region
chops = self._render_chops(crop, lambda y: True)
- render_strips = [Strip.join(chop.values()) for chop in chops]
+ if simplify:
+ render_strips = [Strip.join(chop.values()).simplify() for chop in chops]
+ else:
+ render_strips = [Strip.join(chop.values()) for chop in chops]
+
return LayoutUpdate(render_strips, screen_region)
def render_partial_update(self) -> ChopsUpdate | None:
diff --git a/src/textual/_event_broker.py b/src/textual/_event_broker.py
index 1b63a6cf5e..fe6727e105 100644
--- a/src/textual/_event_broker.py
+++ b/src/textual/_event_broker.py
@@ -4,15 +4,29 @@
class NoHandler(Exception):
- pass
+ """Raised when handler isn't found in the meta."""
class HandlerArguments(NamedTuple):
+ """Information for event handler."""
+
modifiers: set[str]
action: Any
def extract_handler_actions(event_name: str, meta: dict[str, Any]) -> HandlerArguments:
+ """Extract action from meta dict.
+
+ Args:
+ event_name: Event to check from.
+ meta: Meta information (stored in Rich Style)
+
+ Raises:
+ NoHandler: If no handler is found.
+
+ Returns:
+ Action information.
+ """
event_path = event_name.split(".")
for key, value in meta.items():
if key.startswith("@"):
@@ -21,7 +35,3 @@ def extract_handler_actions(event_name: str, meta: dict[str, Any]) -> HandlerArg
modifiers = name_args[len(event_path) :]
return HandlerArguments(set(modifiers), value)
raise NoHandler(f"No handler for {event_name!r}")
-
-
-if __name__ == "__main__":
- print(extract_handler_actions("mouse.down", {"@mouse.down.hot": "app.bell()"}))
diff --git a/src/textual/_immutable_sequence_view.py b/src/textual/_immutable_sequence_view.py
index 2f01ddd2d5..823c73e6a4 100644
--- a/src/textual/_immutable_sequence_view.py
+++ b/src/textual/_immutable_sequence_view.py
@@ -3,7 +3,7 @@
from __future__ import annotations
from sys import maxsize
-from typing import Generic, Iterator, Sequence, TypeVar, overload
+from typing import TYPE_CHECKING, Generic, Iterator, Sequence, TypeVar, overload
T = TypeVar("T")
@@ -19,11 +19,13 @@ def __init__(self, wrap: Sequence[T]) -> None:
"""
self._wrap = wrap
- @overload
- def __getitem__(self, index: int) -> T: ...
+ if TYPE_CHECKING:
- @overload
- def __getitem__(self, index: slice) -> ImmutableSequenceView[T]: ...
+ @overload
+ def __getitem__(self, index: int) -> T: ...
+
+ @overload
+ def __getitem__(self, index: slice) -> ImmutableSequenceView[T]: ...
def __getitem__(self, index: int | slice) -> T | ImmutableSequenceView[T]:
return (
diff --git a/src/textual/_keyboard_protocol.py b/src/textual/_keyboard_protocol.py
new file mode 100644
index 0000000000..d5f364bd0d
--- /dev/null
+++ b/src/textual/_keyboard_protocol.py
@@ -0,0 +1,121 @@
+# https://sw.kovidgoyal.net/kitty/keyboard-protocol/#functional-key-definitions
+FUNCTIONAL_KEYS = {
+ "27u": "escape",
+ "13u": "enter",
+ "9u": "tab",
+ "127u": "backspace",
+ "2~": "insert",
+ "3~": "delete",
+ "1D": "left",
+ "1C": "right",
+ "1A": "up",
+ "1B": "down",
+ "5~": "pageup",
+ "6~": "pagedown",
+ "1H": "home",
+ "7~": "home",
+ "1F": "end",
+ "8~": "end",
+ "57358u": "caps_lock",
+ "57359u": "scroll_lock",
+ "57360u": "num_lock",
+ "57361u": "print_screen",
+ "57362u": "pause",
+ "57363u": "menu",
+ "1P": "f1",
+ "11~": "f1",
+ "1Q": "f2",
+ "12~": "f2",
+ "13~": "f3",
+ "1R": "f3",
+ "1S": "f4",
+ "14~": "f4",
+ "15~": "f5",
+ "17~": "f6",
+ "18~": "f7",
+ "19~": "f8",
+ "20~": "f9",
+ "21~": "f10",
+ "23~": "f11",
+ "24~": "f12",
+ "57376u": "f13",
+ "57377u": "f14",
+ "57378u": "f15",
+ "57379u": "f16",
+ "57380u": "f17",
+ "57381u": "f18",
+ "57382u": "f19",
+ "57383u": "f20",
+ "57384u": "f21",
+ "57385u": "f22",
+ "57386u": "f23",
+ "57387u": "f24",
+ "57388u": "f25",
+ "57389u": "f26",
+ "57390u": "f27",
+ "57391u": "f28",
+ "57392u": "f29",
+ "57393u": "f30",
+ "57394u": "f31",
+ "57395u": "f32",
+ "57396u": "f33",
+ "57397u": "f34",
+ "57398u": "f35",
+ "57399u": "kp_0",
+ "57400u": "kp_1",
+ "57401u": "kp_2",
+ "57402u": "kp_3",
+ "57403u": "kp_4",
+ "57404u": "kp_5",
+ "57405u": "kp_6",
+ "57406u": "kp_7",
+ "57407u": "kp_8",
+ "57408u": "kp_9",
+ "57409u": "kp_decimal",
+ "57410u": "kp_divide",
+ "57411u": "kp_multiply",
+ "57412u": "kp_subtract",
+ "57413u": "kp_add",
+ "57414u": "kp_enter",
+ "57415u": "kp_equal",
+ "57416u": "kp_separator",
+ "57417u": "kp_left",
+ "57418u": "kp_right",
+ "57419u": "kp_up",
+ "57420u": "kp_down",
+ "57421u": "kp_page_up",
+ "57422u": "kp_page_down",
+ "57423u": "kp_home",
+ "57424u": "kp_end",
+ "57425u": "kp_insert",
+ "57426u": "kp_delete",
+ "1E": "kp_begin",
+ "57427~": "kp_begin",
+ "57428u": "media_play",
+ "57429u": "media_pause",
+ "57430u": "media_play_pause",
+ "57431u": "media_reverse",
+ "57432u": "media_stop",
+ "57433u": "media_fast_forward",
+ "57434u": "media_rewind",
+ "57435u": "media_track_next",
+ "57436u": "media_track_previous",
+ "57437u": "media_record",
+ "57438u": "lower_volume",
+ "57439u": "raise_volume",
+ "57440u": "mute_volume",
+ "57441u": "left_shift",
+ "57442u": "left_control",
+ "57443u": "left_alt",
+ "57444u": "left_super",
+ "57445u": "left_hyper",
+ "57446u": "left_meta",
+ "57447u": "right_shift",
+ "57448u": "right_control",
+ "57449u": "right_alt",
+ "57450u": "right_super",
+ "57451u": "right_hyper",
+ "57452u": "right_meta",
+ "57453u": "iso_level3_shift",
+ "57454u": "iso_level5_shift",
+}
diff --git a/src/textual/_node_list.py b/src/textual/_node_list.py
index ac9b425f32..198558777d 100644
--- a/src/textual/_node_list.py
+++ b/src/textual/_node_list.py
@@ -157,11 +157,13 @@ def __iter__(self) -> Iterator[Widget]:
def __reversed__(self) -> Iterator[Widget]:
return reversed(self._nodes)
- @overload
- def __getitem__(self, index: int) -> Widget: ...
+ if TYPE_CHECKING:
- @overload
- def __getitem__(self, index: slice) -> list[Widget]: ...
+ @overload
+ def __getitem__(self, index: int) -> Widget: ...
+
+ @overload
+ def __getitem__(self, index: slice) -> list[Widget]: ...
def __getitem__(self, index: int | slice) -> Widget | list[Widget]:
return self._nodes[index]
diff --git a/src/textual/_on.py b/src/textual/_on.py
index fd4c3d12a0..10e0d80c42 100644
--- a/src/textual/_on.py
+++ b/src/textual/_on.py
@@ -43,7 +43,7 @@ def quit_button(self) -> None:
Example:
```python
# Handle the activation of the tab "#home" within the `TabbedContent` "#tabs".
- @on(TabbedContent.TabActivated, "#tabs", tab="#home")
+ @on(TabbedContent.TabActivated, "#tabs", pane="#home")
def switch_to_home(self) -> None:
self.log("Switching back to the home tab.")
...
diff --git a/src/textual/_partition.py b/src/textual/_partition.py
index be93fbe1be..09920bf579 100644
--- a/src/textual/_partition.py
+++ b/src/textual/_partition.py
@@ -21,7 +21,7 @@ def partition(
"""
result: tuple[list[T], list[T]] = ([], [])
- appends = (result[0].append, result[1].append)
+ appends = (result[1].append, result[0].append)
for value in iterable:
- appends[1 if predicate(value) else 0](value)
+ appends[not predicate(value)](value)
return result
diff --git a/src/textual/_segment_tools.py b/src/textual/_segment_tools.py
index 219c6dcadf..5db0f4d70e 100644
--- a/src/textual/_segment_tools.py
+++ b/src/textual/_segment_tools.py
@@ -4,6 +4,7 @@
from __future__ import annotations
+import re
from typing import Iterable
from rich.segment import Segment
@@ -258,3 +259,36 @@ def blank_lines(count: int) -> list[list[Segment]]:
if bottom_blank_lines:
yield from blank_lines(bottom_blank_lines)
+
+
+_re_spaces = re.compile(r"(\s+|\S+)")
+
+
+def apply_hatch(
+ segments: Iterable[Segment],
+ character: str,
+ hatch_style: Style,
+ _split=_re_spaces.split,
+) -> Iterable[Segment]:
+ """Replace run of spaces with another character + style.
+
+ Args:
+ segments: Segments to process.
+ character: Character to replace spaces.
+ hatch_style: Style of replacement characters.
+
+ Yields:
+ Segments.
+ """
+ _Segment = Segment
+ for segment in segments:
+ if " " not in segment.text:
+ yield segment
+ else:
+ text, style, _ = segment
+ for token in _split(text):
+ if token:
+ if token.isspace():
+ yield _Segment(character * len(token), hatch_style)
+ else:
+ yield _Segment(token, style)
diff --git a/src/textual/_styles_cache.py b/src/textual/_styles_cache.py
index 674b2a52bf..7029b6b4f4 100644
--- a/src/textual/_styles_cache.py
+++ b/src/textual/_styles_cache.py
@@ -14,7 +14,7 @@
from ._border import get_box, render_border_label, render_row
from ._context import active_app
from ._opacity import _apply_opacity
-from ._segment_tools import line_pad, line_trim
+from ._segment_tools import apply_hatch, line_pad, line_trim
from .color import Color
from .constants import DEBUG
from .filter import LineFilter
@@ -311,6 +311,17 @@ def render_line(
inner = from_color(bgcolor=(base_background + background).rich_color)
outer = from_color(bgcolor=base_background.rich_color)
+ def line_post(segments: Iterable[Segment]) -> Iterable[Segment]:
+ """Apply effects to segments inside the border."""
+ if styles.has_rule("hatch"):
+ character, color = styles.hatch
+ if character != " " and color.a > 0:
+ hatch_style = Style.from_color(
+ (background + color).rich_color, background.rich_color
+ )
+ return apply_hatch(segments, character, hatch_style)
+ return segments
+
def post(segments: Iterable[Segment]) -> Iterable[Segment]:
"""Post process segments to apply opacity and tint.
@@ -320,6 +331,7 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]:
Returns:
New list of segments
"""
+
try:
app = active_app.get()
ansi_theme = app.ansi_theme
@@ -421,6 +433,7 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]:
line = [make_blank(width - 1, background_style), right]
else:
line = [make_blank(width, background_style)]
+ line = line_post(line)
else:
# Content with border and padding (C)
content_y = y - gutter.top
@@ -433,7 +446,7 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]:
line = Segment.apply_style(line, inner)
if styles.text_opacity != 1.0:
line = TextOpacity.process_segments(line, styles.text_opacity)
- line = line_pad(line, pad_left, pad_right, inner)
+ line = line_post(line_pad(line, pad_left, pad_right, inner))
if border_left or border_right:
# Add left / right border
diff --git a/src/textual/_time.py b/src/textual/_time.py
index fea8a569ed..13446dfb84 100644
--- a/src/textual/_time.py
+++ b/src/textual/_time.py
@@ -1,10 +1,9 @@
import asyncio
-import platform
+import sys
from asyncio import sleep as asyncio_sleep
from time import monotonic, perf_counter
-PLATFORM = platform.system()
-WINDOWS = PLATFORM == "Windows"
+WINDOWS = sys.platform == "win32"
if WINDOWS:
diff --git a/src/textual/_work_decorator.py b/src/textual/_work_decorator.py
index 71bd67ac1e..9863d08662 100644
--- a/src/textual/_work_decorator.py
+++ b/src/textual/_work_decorator.py
@@ -33,42 +33,42 @@ class WorkerDeclarationError(Exception):
"""An error in the declaration of a worker method."""
-@overload
-def work(
- method: Callable[FactoryParamSpec, Coroutine[None, None, ReturnType]],
- *,
- name: str = "",
- group: str = "default",
- exit_on_error: bool = True,
- exclusive: bool = False,
- description: str | None = None,
- thread: bool = False,
-) -> Callable[FactoryParamSpec, "Worker[ReturnType]"]: ...
-
-
-@overload
-def work(
- method: Callable[FactoryParamSpec, ReturnType],
- *,
- name: str = "",
- group: str = "default",
- exit_on_error: bool = True,
- exclusive: bool = False,
- description: str | None = None,
- thread: bool = False,
-) -> Callable[FactoryParamSpec, "Worker[ReturnType]"]: ...
-
+if TYPE_CHECKING:
-@overload
-def work(
- *,
- name: str = "",
- group: str = "default",
- exit_on_error: bool = True,
- exclusive: bool = False,
- description: str | None = None,
- thread: bool = False,
-) -> Decorator[..., ReturnType]: ...
+ @overload
+ def work(
+ method: Callable[FactoryParamSpec, Coroutine[None, None, ReturnType]],
+ *,
+ name: str = "",
+ group: str = "default",
+ exit_on_error: bool = True,
+ exclusive: bool = False,
+ description: str | None = None,
+ thread: bool = False,
+ ) -> Callable[FactoryParamSpec, "Worker[ReturnType]"]: ...
+
+ @overload
+ def work(
+ method: Callable[FactoryParamSpec, ReturnType],
+ *,
+ name: str = "",
+ group: str = "default",
+ exit_on_error: bool = True,
+ exclusive: bool = False,
+ description: str | None = None,
+ thread: bool = False,
+ ) -> Callable[FactoryParamSpec, "Worker[ReturnType]"]: ...
+
+ @overload
+ def work(
+ *,
+ name: str = "",
+ group: str = "default",
+ exit_on_error: bool = True,
+ exclusive: bool = False,
+ description: str | None = None,
+ thread: bool = False,
+ ) -> Decorator[..., ReturnType]: ...
def work(
@@ -103,7 +103,7 @@ def decorator(
method: (
Callable[DecoratorParamSpec, ReturnType]
| Callable[DecoratorParamSpec, Coroutine[None, None, ReturnType]]
- )
+ ),
) -> Callable[DecoratorParamSpec, Worker[ReturnType]]:
"""The decorator."""
diff --git a/src/textual/_worker_manager.py b/src/textual/_worker_manager.py
index ab69947718..6c90b9641a 100644
--- a/src/textual/_worker_manager.py
+++ b/src/textual/_worker_manager.py
@@ -175,5 +175,7 @@ async def wait_for_complete(self, workers: Iterable[Worker] | None = None) -> No
Args:
workers: An iterable of workers or None to wait for all workers in the manager.
"""
-
- await asyncio.gather(*[worker.wait() for worker in (workers or self)])
+ try:
+ await asyncio.gather(*[worker.wait() for worker in (workers or self)])
+ except asyncio.CancelledError:
+ pass
diff --git a/src/textual/_xterm_parser.py b/src/textual/_xterm_parser.py
index fc84391f39..5b810273fa 100644
--- a/src/textual/_xterm_parser.py
+++ b/src/textual/_xterm_parser.py
@@ -1,13 +1,13 @@
from __future__ import annotations
import re
-import unicodedata
from typing import Any, Callable, Generator, Iterable
from typing_extensions import Final
from . import events, messages
from ._ansi_sequences import ANSI_SEQUENCES_KEYS, IGNORE_SEQUENCE
+from ._keyboard_protocol import FUNCTIONAL_KEYS
from ._parser import Awaitable, Parser, TokenCallback
from .keys import KEY_NAME_REPLACEMENTS, Keys, _character_to_key
@@ -32,6 +32,8 @@
FOCUSOUT: Final[str] = "\x1b[O"
"""Sequence received when focus is lost from the terminal."""
+_re_extended_key: Final = re.compile(r"\x1b\[(?:(\d+)(?:;(\d+))?)?([u~ABCDEFHPQRS])")
+
class XTermParser(Parser[events.Event]):
_re_sgr_mouse = re.compile(r"\x1b\[<(\d+);(\d+);(\d+)([Mm])")
@@ -41,10 +43,12 @@ def __init__(self, more_data: Callable[[], bool], debug: bool = False) -> None:
self.last_x = 0
self.last_y = 0
- self._debug_log_file = open("keys.log", "wt") if debug else None
+ self._debug_log_file = open("keys.log", "at") if debug else None
super().__init__()
+ self.debug_log("---")
+
def debug_log(self, *args: Any) -> None: # pragma: no cover
if self._debug_log_file is not None:
self._debug_log_file.write(" ".join(args) + "\n")
@@ -73,13 +77,14 @@ def parse_mouse_code(self, code: str) -> events.Event | None:
)
button = 0
else:
- if buttons & 32:
+ button = (buttons + 1) & 3
+ # XTerm events for mouse movement can look like mouse button down events. But if there is no key pressed,
+ # it's a mouse move event.
+ if buttons & 32 or button == 0:
event_class = events.MouseMove
else:
event_class = events.MouseDown if state == "M" else events.MouseUp
- button = (buttons + 1) & 3
-
event = event_class(
x,
y,
@@ -102,7 +107,7 @@ def parse_mouse_code(self, code: str) -> events.Event | None:
the reissued sequence being emitted as key events.
"""
- def parse(self, on_token: TokenCallback) -> Generator[Awaitable, str, None]:
+ def parse(self, _on_token: TokenCallback) -> Generator[Awaitable, str, None]:
ESC = "\x1b"
read1 = self.read1
sequence_to_key_events = self._sequence_to_key_events
@@ -111,6 +116,11 @@ def parse(self, on_token: TokenCallback) -> Generator[Awaitable, str, None]:
bracketed_paste = False
use_prior_escape = False
+ def on_token(token: events.Event) -> None:
+ """Hook to log events."""
+ self.debug_log(str(token))
+ _on_token(token)
+
def on_key_token(event: events.Key) -> None:
"""Token callback wrapper for handling keys.
@@ -230,6 +240,21 @@ def reissue_sequence_as_keys(reissue_sequence: str) -> None:
break
if not bracketed_paste:
+ # Check cursor position report
+ if (
+ cursor_position_match := _re_cursor_position.match(sequence)
+ ) is not None:
+ row, column = cursor_position_match.groups()
+ # Cursor position report conflicts with f3 key
+ # If it is a keypress, "row" will be 1, so ignore
+ if int(row) != 1:
+ on_token(
+ events.CursorPosition(
+ x=int(column) - 1, y=int(row) - 1
+ )
+ )
+ break
+
# Was it a pressed key event that we received?
key_events = list(sequence_to_key_events(sequence))
for key_event in key_events:
@@ -258,24 +283,16 @@ def reissue_sequence_as_keys(reissue_sequence: str) -> None:
on_token(messages.TerminalSupportsSynchronizedOutput())
break
- # Or a cursor position query?
- if (
- cursor_position_match := _re_cursor_position.match(sequence)
- ) is not None:
- row, column = cursor_position_match.groups()
- on_token(
- events.CursorPosition(x=int(column) - 1, y=int(row) - 1)
- )
- break
-
else:
if not bracketed_paste:
for event in sequence_to_key_events(character):
on_key_token(event)
- def _sequence_to_key_events(
- self, sequence: str, _unicode_name=unicodedata.name
- ) -> Iterable[events.Key]:
+ if self._debug_log_file is not None:
+ self._debug_log_file.close()
+ self._debug_log_file = None
+
+ def _sequence_to_key_events(self, sequence: str) -> Iterable[events.Key]:
"""Map a sequence of code points on to a sequence of keys.
Args:
@@ -284,6 +301,37 @@ def _sequence_to_key_events(
Returns:
Keys
"""
+
+ if (match := _re_extended_key.match(sequence)) is not None:
+ number, modifiers, end = match.groups()
+ number = number or 1
+ if not (key := FUNCTIONAL_KEYS.get(f"{number}{end}", "")):
+ try:
+ key = _character_to_key(chr(int(number)))
+ except Exception:
+ key = chr(int(number))
+ key_tokens: list[str] = []
+ if modifiers:
+ modifier_bits = int(modifiers) - 1
+ MODIFIERS = (
+ "shift",
+ "alt",
+ "ctrl",
+ "hyper",
+ "meta",
+ "caps_lock",
+ "num_lock",
+ )
+ for bit, modifier in zip(range(8), MODIFIERS):
+ if modifier_bits & (1 << bit):
+ key_tokens.append(modifier)
+ key_tokens.sort()
+ key_tokens.append(key)
+ yield events.Key(
+ f'{"+".join(key_tokens)}', sequence if len(sequence) == 1 else None
+ )
+ return
+
keys = ANSI_SEQUENCES_KEYS.get(sequence)
# If we're being asked to ignore the key...
if keys is IGNORE_SEQUENCE:
@@ -292,6 +340,7 @@ def _sequence_to_key_events(
# to is the ignore key) and the sequence that was ignored as
# the character.
yield events.Key(Keys.Ignore, sequence)
+ return
if isinstance(keys, tuple):
# If the sequence mapped to a tuple, then it's values from the
# `Keys` enum. Raise key events from what we find in the tuple.
@@ -313,5 +362,5 @@ def _sequence_to_key_events(
name = sequence
name = KEY_NAME_REPLACEMENTS.get(name, name)
yield events.Key(name, sequence)
- except:
+ except Exception:
yield events.Key(sequence, sequence)
diff --git a/src/textual/actions.py b/src/textual/actions.py
index 9365bb268b..a7ff7bbde2 100644
--- a/src/textual/actions.py
+++ b/src/textual/actions.py
@@ -2,11 +2,12 @@
import ast
import re
+from functools import lru_cache
from typing import Any
from typing_extensions import TypeAlias
-ActionParseResult: TypeAlias = "tuple[str, tuple[Any, ...]]"
+ActionParseResult: TypeAlias = "tuple[str, str, tuple[object, ...]]"
"""An action is its name and the arbitrary tuple of its arguments."""
@@ -21,6 +22,7 @@ class ActionError(Exception):
re_action_args = re.compile(r"([\w\.]+)\((.*)\)")
+@lru_cache(maxsize=1024)
def parse(action: str) -> ActionParseResult:
"""Parses an action string.
@@ -52,4 +54,6 @@ def parse(action: str) -> ActionParseResult:
action_name = action
action_args = ()
- return action_name, action_args
+ namespace, _, action_name = action_name.rpartition(".")
+
+ return namespace, action_name, action_args
diff --git a/src/textual/app.py b/src/textual/app.py
index 7ed7275d0a..160e75d55a 100644
--- a/src/textual/app.py
+++ b/src/textual/app.py
@@ -12,7 +12,6 @@
import inspect
import io
import os
-import platform
import signal
import sys
import threading
@@ -27,7 +26,6 @@
)
from datetime import datetime
from functools import partial
-from pathlib import PurePath
from time import perf_counter
from typing import (
TYPE_CHECKING,
@@ -40,11 +38,9 @@
Generic,
Iterable,
Iterator,
- List,
Sequence,
Type,
TypeVar,
- Union,
overload,
)
from weakref import WeakKeyDictionary, WeakSet
@@ -82,6 +78,7 @@
from ._wait import wait_for_idle
from ._worker_manager import WorkerManager
from .actions import ActionParseResult, SkipAction
+from .await_complete import AwaitComplete
from .await_remove import AwaitRemove
from .binding import Binding, BindingType, _Bindings
from .command import CommandPalette, Provider
@@ -102,17 +99,19 @@
_get_key_display,
_get_unicode_name_from_key,
)
-from .messages import CallbackType
+from .messages import CallbackType, Prune
from .notifications import Notification, Notifications, Notify, SeverityLevel
from .reactive import Reactive
from .renderables.blank import Blank
from .screen import (
+ ActiveBinding,
Screen,
ScreenResultCallbackType,
ScreenResultType,
- _SystemModalScreen,
+ SystemModalScreen,
)
from .signal import Signal
+from .timer import Timer
from .widget import AwaitMount, Widget
from .widgets._toast import ToastRack
from .worker import NoActiveWorker, get_current_worker
@@ -131,8 +130,7 @@
from .pilot import Pilot
from .widget import MountError # type: ignore # noqa: F401
-PLATFORM = platform.system()
-WINDOWS = PLATFORM == "Windows"
+WINDOWS = sys.platform == "win32"
# asyncio will warn against resources not being cleared
if constants.DEBUG:
@@ -225,14 +223,6 @@ class SuspendNotSupported(Exception):
ReturnType = TypeVar("ReturnType")
-
-CSSPathType = Union[
- str,
- PurePath,
- List[Union[str, PurePath]],
-]
-"""Valid ways of specifying paths to CSS files."""
-
CallThreadReturnType = TypeVar("CallThreadReturnType")
@@ -364,6 +354,9 @@ class MyApp(App[None]):
ENABLE_COMMAND_PALETTE: ClassVar[bool] = True
"""Should the [command palette][textual.command.CommandPalette] be enabled for the application?"""
+ NOTIFICATION_TIMEOUT: ClassVar[float] = 5
+ """Default number of seconds to show notifications before removing them."""
+
COMMANDS: ClassVar[set[type[Provider] | Callable[[], type[Provider]]]] = {
get_system_commands
}
@@ -377,6 +370,9 @@ class MyApp(App[None]):
Binding("ctrl+backslash", "command_palette", show=False, priority=True),
]
+ CLOSE_TIMEOUT: float | None = 5.0
+ """Timeout waiting for widget's to close, or `None` for no timeout."""
+
title: Reactive[str] = Reactive("", compute=False)
sub_title: Reactive[str] = Reactive("", compute=False)
@@ -453,7 +449,7 @@ def __init__(
soft_wrap=False,
)
self._workers = WorkerManager(self)
- self.error_console = Console(markup=False, stderr=True)
+ self.error_console = Console(markup=False, highlight=False, stderr=True)
self.driver_class = driver_class or self.get_driver_class()
self._screen_stacks: dict[str, list[Screen[Any]]] = {"_default": []}
"""A stack of screens per mode."""
@@ -466,7 +462,7 @@ def __init__(
self._driver: Driver | None = None
self._exit_renderables: list[RenderableType] = []
- self._action_targets = {"app", "screen"}
+ self._action_targets = {"app", "screen", "focused"}
self._animator = Animator(self)
self._animate = self._animator.bind(self)
self.mouse_position = Offset(0, 0)
@@ -585,7 +581,6 @@ def __init__(
else None
)
self._screenshot: str | None = None
- self._dom_lock = asyncio.Lock()
self._dom_ready = False
self._batch_count = 0
self._notifications = Notifications()
@@ -603,7 +598,7 @@ def __init__(
self._original_stderr = sys.__stderr__
"""The original stderr stream (before redirection etc)."""
- self.app_suspend_signal = Signal(self, "app-suspend")
+ self.app_suspend_signal: Signal[App] = Signal(self, "app-suspend")
"""The signal that is published when the app is suspended.
When [`App.suspend`][textual.app.App.suspend] is called this signal
@@ -611,7 +606,7 @@ def __init__(
[subscribe][textual.signal.Signal.subscribe] to this signal to
perform work before the suspension takes place.
"""
- self.app_resume_signal = Signal(self, "app-resume")
+ self.app_resume_signal: Signal[App] = Signal(self, "app-resume")
"""The signal that is published when the app is resumed after a suspend.
When the app is resumed after a
@@ -645,7 +640,7 @@ def validate_title(self, title: Any) -> str:
return str(title)
def validate_sub_title(self, sub_title: Any) -> str:
- """Make sure the sub-title is set to a string."""
+ """Make sure the subtitle is set to a string."""
return str(sub_title)
@property
@@ -671,7 +666,7 @@ def return_code(self) -> int | None:
Non-zero codes indicate errors.
A value of 1 means the app exited with a fatal error.
- If the app wasn't exited yet, this will be `None`.
+ If the app hasn't exited yet, this will be `None`.
Example:
The return code can be used to exit the process via `sys.exit`.
@@ -698,7 +693,7 @@ def children(self) -> Sequence["Widget"]:
next(
screen
for screen in reversed(self._screen_stack)
- if not isinstance(screen, _SystemModalScreen)
+ if not isinstance(screen, SystemModalScreen)
),
)
except StopIteration:
@@ -776,6 +771,16 @@ async def stop_animation(self, attribute: str, complete: bool = True) -> None:
"""
await self._animator.stop_animation(self, attribute, complete)
+ @property
+ def is_dom_root(self) -> bool:
+ """Is this a root node (i.e. the App)?"""
+ return True
+
+ @property
+ def is_attached(self) -> bool:
+ """Is this node linked to the app through the DOM?"""
+ return True
+
@property
def debug(self) -> bool:
"""Is debug mode enabled?"""
@@ -855,7 +860,7 @@ def focused(self) -> Widget | None:
return focused
@property
- def namespace_bindings(self) -> dict[str, tuple[DOMNode, Binding]]:
+ def active_bindings(self) -> dict[str, ActiveBinding]:
"""Get currently active bindings.
If no widget is focused, then app-level bindings are returned.
@@ -864,20 +869,22 @@ def namespace_bindings(self) -> dict[str, tuple[DOMNode, Binding]]:
This property may be used to inspect current bindings.
Returns:
- A map of keys to a tuple containing the DOMNode and Binding that key corresponds to.
+ Active binding information
"""
+ return self.screen.active_bindings
- bindings_map: dict[str, tuple[DOMNode, Binding]] = {}
- for namespace, bindings in self._binding_chain:
- for key, binding in bindings.keys.items():
- if existing_key_and_binding := bindings_map.get(key):
- _, existing_binding = existing_key_and_binding
- if binding.priority and not existing_binding.priority:
- bindings_map[key] = (namespace, binding)
- else:
- bindings_map[key] = (namespace, binding)
+ def get_default_screen(self) -> Screen:
+ """Get the default screen.
- return bindings_map
+ This is called when the App is first composed. The returned screen instance
+ will be the first screen on the stack.
+
+ Implement this method if you would like to use a custom Screen as the default screen.
+
+ Returns:
+ A screen instance.
+ """
+ return Screen(id="_default")
def _set_active(self) -> None:
"""Set this app to be the currently active app."""
@@ -1071,7 +1078,7 @@ def _log(
) -> None:
"""Write to logs or devtools.
- Positional args will logged. Keyword args will be prefixed with the key.
+ Positional args will be logged. Keyword args will be prefixed with the key.
Example:
```python
@@ -1234,7 +1241,7 @@ def export_screenshot(self, *, title: str | None = None) -> str:
safe_box=False,
)
screen_render = self.screen._compositor.render_update(
- full=True, screen_stack=self.app._background_screens
+ full=True, screen_stack=self.app._background_screens, simplify=True
)
console.print(screen_render)
return console.export_svg(title=title or self.title)
@@ -1449,15 +1456,24 @@ def on_app_ready() -> None:
app_ready_event.set()
async def run_app(app: App) -> None:
- if message_hook is not None:
- message_hook_context_var.set(message_hook)
- app._loop = asyncio.get_running_loop()
- app._thread_id = threading.get_ident()
- await app._process_messages(
- ready_callback=on_app_ready,
- headless=headless,
- terminal_size=size,
- )
+ """Run the apps message loop.
+
+ Args:
+ app: App to run.
+ """
+
+ try:
+ if message_hook is not None:
+ message_hook_context_var.set(message_hook)
+ app._loop = asyncio.get_running_loop()
+ app._thread_id = threading.get_ident()
+ await app._process_messages(
+ ready_callback=on_app_ready,
+ headless=headless,
+ terminal_size=size,
+ )
+ finally:
+ app_ready_event.set()
# Launch the app in the "background"
active_message_pump.set(app)
@@ -1501,7 +1517,7 @@ async def run_async(
mouse: Enable mouse support.
size: Force terminal size to `(WIDTH, HEIGHT)`,
or None to auto-detect.
- auto_pilot: An auto pilot coroutine.
+ auto_pilot: An autopilot coroutine.
Returns:
App return value.
@@ -1559,7 +1575,10 @@ async def run_auto_pilot(
if auto_pilot_task is not None:
await auto_pilot_task
finally:
- await app._shutdown()
+ try:
+ await asyncio.shield(app._shutdown())
+ except asyncio.CancelledError:
+ pass
return app.return_value
@@ -1661,20 +1680,29 @@ async def _on_css_change(self) -> None:
self.stylesheet.update(screen)
def render(self) -> RenderResult:
+ """Render method, inherited from widget, to render the screen's background.
+
+ May be overridden to customize background visuals.
+
+ """
return Blank(self.styles.background)
ExpectType = TypeVar("ExpectType", bound=Widget)
- @overload
- def get_child_by_id(self, id: str) -> Widget: ...
+ if TYPE_CHECKING:
+
+ @overload
+ def get_child_by_id(self, id: str) -> Widget: ...
- @overload
- def get_child_by_id(self, id: str, expect_type: type[ExpectType]) -> ExpectType: ...
+ @overload
+ def get_child_by_id(
+ self, id: str, expect_type: type[ExpectType]
+ ) -> ExpectType: ...
def get_child_by_id(
self, id: str, expect_type: type[ExpectType] | None = None
) -> ExpectType | Widget:
- """Get the first child (immediate descendent) of this DOMNode with the given ID.
+ """Get the first child (immediate descendant) of this DOMNode with the given ID.
Args:
id: The ID of the node to search for.
@@ -1694,13 +1722,15 @@ def get_child_by_id(
else self.screen.get_child_by_id(id, expect_type)
)
- @overload
- def get_widget_by_id(self, id: str) -> Widget: ...
+ if TYPE_CHECKING:
- @overload
- def get_widget_by_id(
- self, id: str, expect_type: type[ExpectType]
- ) -> ExpectType: ...
+ @overload
+ def get_widget_by_id(self, id: str) -> Widget: ...
+
+ @overload
+ def get_widget_by_id(
+ self, id: str, expect_type: type[ExpectType]
+ ) -> ExpectType: ...
def get_widget_by_id(
self, id: str, expect_type: type[ExpectType] | None = None
@@ -1814,7 +1844,7 @@ def mount_all(
return self.mount(*widgets, before=before, after=after)
def _init_mode(self, mode: str) -> AwaitMount:
- """Do internal initialisation of a new screen stack mode.
+ """Do internal initialization of a new screen stack mode.
Args:
mode: Name of the mode.
@@ -1889,7 +1919,7 @@ def add_mode(
self.MODES[mode] = base_screen
- def remove_mode(self, mode: str) -> None:
+ def remove_mode(self, mode: str) -> AwaitComplete:
"""Removes a mode from the app.
Screens that are running in the stack of that mode are scheduled for pruning.
@@ -1909,12 +1939,17 @@ def remove_mode(self, mode: str) -> None:
del self.MODES[mode]
if mode not in self._screen_stacks:
- return
+ return AwaitComplete.nothing()
stack = self._screen_stacks[mode]
del self._screen_stacks[mode]
- for screen in reversed(stack):
- self._replace_screen(screen)
+
+ async def remove_screens() -> None:
+ """Remove screens."""
+ for screen in reversed(stack):
+ await self._replace_screen(screen)
+
+ return AwaitComplete(remove_screens()).call_next(self)
def is_screen_installed(self, screen: Screen | str) -> bool:
"""Check if a given screen has been installed.
@@ -2009,7 +2044,7 @@ def _load_screen_css(self, screen: Screen):
self.stylesheet.reparse()
self.stylesheet.update(self)
- def _replace_screen(self, screen: Screen) -> Screen:
+ async def _replace_screen(self, screen: Screen) -> Screen:
"""Handle the replaced screen.
Args:
@@ -2025,25 +2060,27 @@ def _replace_screen(self, screen: Screen) -> Screen:
if not self.is_screen_installed(screen) and all(
screen not in stack for stack in self._screen_stacks.values()
):
- screen.remove()
+ await screen.remove()
self.log.system(f"{screen} REMOVED")
return screen
- @overload
- def push_screen(
- self,
- screen: Screen[ScreenResultType] | str,
- callback: ScreenResultCallbackType[ScreenResultType] | None = None,
- wait_for_dismiss: Literal[False] = False,
- ) -> AwaitMount: ...
+ if TYPE_CHECKING:
- @overload
- def push_screen(
- self,
- screen: Screen[ScreenResultType] | str,
- callback: ScreenResultCallbackType[ScreenResultType] | None = None,
- wait_for_dismiss: Literal[True] = True,
- ) -> asyncio.Future[ScreenResultType]: ...
+ @overload
+ def push_screen(
+ self,
+ screen: Screen[ScreenResultType] | str,
+ callback: ScreenResultCallbackType[ScreenResultType] | None = None,
+ wait_for_dismiss: Literal[False] = False,
+ ) -> AwaitMount: ...
+
+ @overload
+ def push_screen(
+ self,
+ screen: Screen[ScreenResultType] | str,
+ callback: ScreenResultCallbackType[ScreenResultType] | None = None,
+ wait_for_dismiss: Literal[True] = True,
+ ) -> asyncio.Future[ScreenResultType]: ...
def push_screen(
self,
@@ -2105,13 +2142,15 @@ def push_screen(
else:
return await_mount
- @overload
- async def push_screen_wait(
- self, screen: Screen[ScreenResultType]
- ) -> ScreenResultType: ...
+ if TYPE_CHECKING:
+
+ @overload
+ async def push_screen_wait(
+ self, screen: Screen[ScreenResultType]
+ ) -> ScreenResultType: ...
- @overload
- async def push_screen_wait(self, screen: str) -> Any: ...
+ @overload
+ async def push_screen_wait(self, screen: str) -> Any: ...
async def push_screen_wait(
self, screen: Screen[ScreenResultType] | str
@@ -2126,9 +2165,10 @@ async def push_screen_wait(
Returns:
The screen's result.
"""
+ await self._flush_next_callbacks()
return await self.push_screen(screen, wait_for_dismiss=True)
- def switch_screen(self, screen: Screen | str) -> AwaitMount:
+ def switch_screen(self, screen: Screen | str) -> AwaitComplete:
"""Switch to another [screen](/guide/screens) by replacing the top of the screen stack with a new screen.
Args:
@@ -2142,16 +2182,24 @@ def switch_screen(self, screen: Screen | str) -> AwaitMount:
next_screen, await_mount = self._get_screen(screen)
if screen is self.screen or next_screen is self.screen:
self.log.system(f"Screen {screen} is already current.")
- return AwaitMount(self.screen, [])
+ return AwaitComplete.nothing()
- previous_screen = self._replace_screen(self._screen_stack.pop())
- previous_screen._pop_result_callback()
+ top_screen = self._screen_stack.pop()
+
+ top_screen._pop_result_callback()
self._load_screen_css(next_screen)
self._screen_stack.append(next_screen)
self.screen.post_message(events.ScreenResume())
self.screen._push_result_callback(self.screen, None)
self.log.system(f"{self.screen} is current (SWITCHED)")
- return await_mount
+
+ async def do_switch() -> None:
+ """Task to perform switch."""
+
+ await await_mount()
+ await self._replace_screen(top_screen)
+
+ return AwaitComplete(do_switch()).call_next(self)
def install_screen(self, screen: Screen, name: str) -> None:
"""Install a screen.
@@ -2182,14 +2230,14 @@ def install_screen(self, screen: Screen, name: str) -> None:
def uninstall_screen(self, screen: Screen | str) -> str | None:
"""Uninstall a screen.
- If the screen was not previously installed then this method is a null-op.
+ If the screen was not previously installed, then this method is a null-op.
Uninstalling a screen allows Textual to delete it when it is popped or switched.
Note that uninstalling a screen is only required if you have previously installed it
with [install_screen][textual.app.App.install_screen].
Textual will also uninstall screens automatically on exit.
Args:
- screen: The screen to uninstall or the name of a installed screen.
+ screen: The screen to uninstall or the name of an installed screen.
Returns:
The name of the screen that was uninstalled, or None if no screen was uninstalled.
@@ -2213,22 +2261,29 @@ def uninstall_screen(self, screen: Screen | str) -> str | None:
return name
return None
- def pop_screen(self) -> Screen[object]:
+ def pop_screen(self) -> AwaitComplete:
"""Pop the current [screen](/guide/screens) from the stack, and switch to the previous screen.
Returns:
The screen that was replaced.
"""
+
screen_stack = self._screen_stack
if len(screen_stack) <= 1:
raise ScreenStackError(
"Can't pop screen; there must be at least one screen on the stack"
)
- previous_screen = self._replace_screen(screen_stack.pop())
+
+ previous_screen = screen_stack.pop()
previous_screen._pop_result_callback()
self.screen.post_message(events.ScreenResume())
self.log.system(f"{self.screen} is active")
- return previous_screen
+
+ async def do_pop() -> None:
+ """Task to pop the screen."""
+ await self._replace_screen(previous_screen)
+
+ return AwaitComplete(do_pop()).call_next(self)
def set_focus(self, widget: Widget | None, scroll_visible: bool = True) -> None:
"""Focus (or unfocus) a widget. A focused widget will receive key events first.
@@ -2261,6 +2316,29 @@ def _set_mouse_over(self, widget: Widget | None) -> None:
finally:
self.mouse_over = widget
+ def _update_mouse_over(self, screen: Screen) -> None:
+ """Updates the mouse over after the next refresh.
+
+ This method is called whenever a widget is added or removed, which may change
+ the widget under the mouse.
+
+ """
+
+ if self.mouse_over is None or not screen.is_active:
+ return
+
+ async def check_mouse() -> None:
+ """Check if the mouse over widget has changed."""
+ try:
+ widget, _ = screen.get_widget_at(*self.mouse_position)
+ except NoWidget:
+ pass
+ else:
+ if widget is not self.mouse_over:
+ self._set_mouse_over(widget)
+
+ self.call_after_refresh(check_mouse)
+
def capture_mouse(self, widget: Widget | None) -> None:
"""Send all mouse events to the given widget or disable mouse capture.
@@ -2309,7 +2387,7 @@ def _handle_exception(self, error: Exception) -> None:
self._return_code = 1
# If we're running via pilot and this is the first exception encountered,
# take note of it so that we can re-raise for test frameworks later.
- if self.is_headless and self._exception is None:
+ if self._exception is None:
self._exception = error
self._exception_event.set()
@@ -2494,8 +2572,7 @@ async def invoke_ready_callback() -> None:
try:
await self.animator.stop()
finally:
- for timer in list(self._timers):
- timer.stop()
+ await Timer._stop_all(self._timers)
self._running = True
try:
@@ -2527,6 +2604,7 @@ async def invoke_ready_callback() -> None:
console = Console()
console.print(self.screen._compositor)
console.print()
+
driver.stop_application_mode()
except Exception as error:
self._handle_exception(error)
@@ -2579,9 +2657,14 @@ async def recompose(self) -> None:
Recomposing will remove children and call `self.compose` again to remount.
"""
- async with self.screen.batch():
- await self.screen.query("*").exclude(".-textual-system").remove()
- await self.screen.mount_all(compose(self))
+ if self._exit:
+ return
+ try:
+ async with self.screen.batch():
+ await self.screen.query("*").exclude(".-textual-system").remove()
+ await self.screen.mount_all(compose(self))
+ except ScreenStackError:
+ pass
def _register_child(
self, parent: DOMNode, child: Widget, before: int | None, after: int | None
@@ -2615,7 +2698,7 @@ def _register_child(
# position (for now) of meaning "okay really what I mean is
# do an append, like if I'd asked to add with no before or
# after". So... we insert before the next item in the node
- # list, iff after isn't -1.
+ # list, if after isn't -1.
parent._nodes._insert(after + 1, child)
else:
# At this point we appear to not be adding before or after,
@@ -2667,6 +2750,9 @@ def _register(
apply_stylesheet = self.stylesheet.apply
for widget in widget_list:
+ widget._closing = False
+ widget._closed = False
+ widget._pruning = False
if not isinstance(widget, Widget):
raise AppError(f"Can't register {widget!r}; expected a Widget instance")
if widget not in self._registry:
@@ -2698,7 +2784,7 @@ async def _disconnect_devtools(self):
await self.devtools.disconnect()
def _start_widget(self, parent: Widget, widget: Widget) -> None:
- """Start a widget (run it's task) so that it can receive messages.
+ """Start a widget (run its task) so that it can receive messages.
Args:
parent: The parent of the Widget.
@@ -2727,13 +2813,13 @@ async def _close_all(self) -> None:
for stack in self._screen_stacks.values():
for stack_screen in reversed(stack):
if stack_screen._running:
- await self._prune_node(stack_screen)
+ await self._prune(stack_screen)
stack.clear()
# Close pre-defined screens.
for screen in self.SCREENS.values():
if isinstance(screen, Screen) and screen._running:
- await self._prune_node(screen)
+ await self._prune(screen)
# Close any remaining nodes
# Should be empty by now
@@ -2747,6 +2833,7 @@ async def _shutdown(self) -> None:
self._running = False
if driver is not None:
driver.disable_input()
+
await self._close_all()
await self._close_messages()
@@ -2770,7 +2857,7 @@ async def _shutdown(self) -> None:
async def _on_exit_app(self) -> None:
self._begin_batch() # Prevent repaint / layout while shutting down
- await self._message_queue.put(None)
+ self._message_queue.put_nowait(None)
def refresh(
self,
@@ -2840,16 +2927,23 @@ def _display(self, screen: Screen, renderable: RenderableType | None) -> None:
try:
try:
if isinstance(renderable, CompositorUpdate):
- cursor_x, cursor_y = self._previous_cursor_position
- terminal_sequence = Control.move(
- -cursor_x, -cursor_y
- ).segment.text
- cursor_x, cursor_y = self.cursor_position
- terminal_sequence += renderable.render_segments(console)
- terminal_sequence += Control.move(
- cursor_x, cursor_y
- ).segment.text
- self._previous_cursor_position = self.cursor_position
+ cursor_position = self.screen.outer_size.clamp_offset(
+ self.cursor_position
+ )
+ if self._driver.is_inline:
+ terminal_sequence = Control.move(
+ *(-self._previous_cursor_position)
+ ).segment.text
+ terminal_sequence += renderable.render_segments(console)
+ terminal_sequence += Control.move(
+ *cursor_position
+ ).segment.text
+ else:
+ terminal_sequence = renderable.render_segments(console)
+ terminal_sequence += Control.move_to(
+ *cursor_position
+ ).segment.text
+ self._previous_cursor_position = cursor_position
else:
segments = console.render(renderable)
terminal_sequence = console._render_buffer(segments)
@@ -2926,20 +3020,20 @@ def _binding_chain(self) -> list[tuple[DOMNode, _Bindings]]:
return namespace_bindings
- @property
- def _modal_binding_chain(self) -> list[tuple[DOMNode, _Bindings]]:
- """The binding chain, ignoring everything before the last modal."""
- binding_chain = self._binding_chain
- for index, (node, _bindings) in enumerate(binding_chain, 1):
- if node.is_modal:
- return binding_chain[:index]
- return binding_chain
-
- async def check_bindings(self, key: str, priority: bool = False) -> bool:
+ def simulate_key(self, key: str) -> None:
+ """Simulate a key press.
+
+ This will perform the same action as if the user had pressed the key.
+
+ Args:
+ key: Key to simulate. May also be the name of a key, e.g. "space".
+ """
+ self.call_later(self._check_bindings, key)
+
+ async def _check_bindings(self, key: str, priority: bool = False) -> bool:
"""Handle a key press.
- This method is used internally by the bindings system, but may be called directly
- if you wish to *simulate* a key being pressed.
+ This method is used internally by the bindings system.
Args:
key: A key.
@@ -2949,7 +3043,9 @@ async def check_bindings(self, key: str, priority: bool = False) -> bool:
True if the key was handled by a binding, otherwise False
"""
for namespace, bindings in (
- reversed(self._binding_chain) if priority else self._modal_binding_chain
+ reversed(self.screen._binding_chain)
+ if priority
+ else self.screen._modal_binding_chain
):
binding = bindings.keys.get(key)
if binding is not None and binding.priority == priority:
@@ -2961,7 +3057,7 @@ async def on_event(self, event: events.Event) -> None:
# Handle input events that haven't been forwarded
# If the event has been forwarded it may have bubbled up back to the App
if isinstance(event, events.Compose):
- screen: Screen[Any] = Screen(id=f"_default")
+ screen: Screen[Any] = self.get_default_screen()
self._register(self, screen)
self._screen_stack.append(screen)
screen.post_message(events.ScreenResume())
@@ -3000,7 +3096,12 @@ async def on_event(self, event: events.Event) -> None:
pass
elif isinstance(event, events.Key):
- if not await self.check_bindings(event.key, priority=True):
+ if self.focused:
+ try:
+ self.screen._clear_tooltip()
+ except NoScreen:
+ pass
+ if not await self._check_bindings(event.key, priority=True):
forward_target = self.focused or self.screen
forward_target._forward_event(event)
else:
@@ -3014,10 +3115,58 @@ async def on_event(self, event: events.Event) -> None:
else:
await super().on_event(event)
+ def _parse_action(
+ self, action: str | ActionParseResult, default_namespace: DOMNode
+ ) -> tuple[DOMNode, str, tuple[object, ...]]:
+ """Parse an action.
+
+ Args:
+ action: An action string.
+
+ Raises:
+ ActionError: If there are any errors parsing the action string.
+
+ Returns:
+ A tuple of (node or None, action name, tuple of parameters).
+ """
+ if isinstance(action, tuple):
+ destination, action_name, params = action
+ else:
+ destination, action_name, params = actions.parse(action)
+
+ action_target: DOMNode | None = None
+ if destination:
+ if destination not in self._action_targets:
+ raise ActionError(f"Action namespace {destination} is not known")
+ action_target = getattr(self, destination, None)
+ if action_target is None:
+ raise ActionError("Action target {destination!r} not available")
+ return (
+ (default_namespace if action_target is None else action_target),
+ action_name,
+ params,
+ )
+
+ def _check_action_state(
+ self, action: str, default_namespace: DOMNode
+ ) -> bool | None:
+ """Check if an action is enabled.
+
+ Args:
+ action: An action string.
+
+ Returns:
+ State of an action.
+ """
+ action_target, action_name, parameters = self._parse_action(
+ action, default_namespace
+ )
+ return action_target.check_action(action_name, parameters)
+
async def run_action(
self,
action: str | ActionParseResult,
- default_namespace: object | None = None,
+ default_namespace: DOMNode | None = None,
) -> bool:
"""Perform an [action](/guide/actions).
@@ -3031,28 +3180,17 @@ async def run_action(
Returns:
True if the event has been handled.
"""
- if isinstance(action, str):
- target, params = actions.parse(action)
- else:
- target, params = action
- implicit_destination = True
- if "." in target:
- destination, action_name = target.split(".", 1)
- if destination not in self._action_targets:
- raise ActionError(f"Action namespace {destination} is not known")
- action_target = getattr(self, destination)
- implicit_destination = True
- else:
- action_target = default_namespace if default_namespace is not None else self
- action_name = target
+ action_target, action_name, params = self._parse_action(
+ action, self if default_namespace is None else default_namespace
+ )
- handled = await self._dispatch_action(action_target, action_name, params)
- if not handled and implicit_destination and not isinstance(action_target, App):
- handled = await self.app._dispatch_action(self.app, action_name, params)
- return handled
+ if action_target.check_action(action_name, params):
+ return await self._dispatch_action(action_target, action_name, params)
+ else:
+ return False
async def _dispatch_action(
- self, namespace: object, action_name: str, params: Any
+ self, namespace: DOMNode, action_name: str, params: Any
) -> bool:
"""Dispatch an action to an action method.
@@ -3089,10 +3227,11 @@ async def _dispatch_action(
except SkipAction:
# The action method raised this to explicitly not handle the action
log.system(f" {action_name!r} skipped.")
+
return False
async def _broker_event(
- self, event_name: str, event: events.Event, default_namespace: object | None
+ self, event_name: str, event: events.Event, default_namespace: DOMNode
) -> bool:
"""Allow the app an opportunity to dispatch events to action system.
@@ -3114,10 +3253,16 @@ async def _broker_event(
return False
else:
event.stop()
- if isinstance(action, str) or (isinstance(action, tuple) and len(action) == 2):
- await self.run_action(action, default_namespace=default_namespace) # type: ignore[arg-type]
- elif callable(action):
- await action()
+
+ if isinstance(action, str):
+ await self.run_action(action, default_namespace)
+ elif isinstance(action, tuple) and len(action) == 2:
+ action_name, action_params = action
+ namespace, parsed_action, _ = actions.parse(action_name)
+ await self.run_action(
+ (namespace, parsed_action, action_params),
+ default_namespace,
+ )
else:
if isinstance(action, tuple) and self.debug:
# It's a tuple and made it this far, which means it'll be a
@@ -3136,7 +3281,7 @@ async def _on_layout(self, message: messages.Layout) -> None:
message.stop()
async def _on_key(self, event: events.Key) -> None:
- if not (await self.check_bindings(event.key)):
+ if not (await self._check_bindings(event.key)):
await self.dispatch_key(event)
async def _on_shutdown_request(self, event: events.ShutdownRequest) -> None:
@@ -3153,168 +3298,61 @@ async def _on_app_focus(self, event: events.AppFocus) -> None:
"""App has focus."""
# Required by textual-web to manage focus in a web page.
self.app_focus = True
+ self.screen.refresh_bindings()
async def _on_app_blur(self, event: events.AppBlur) -> None:
"""App has lost focus."""
# Required by textual-web to manage focus in a web page.
self.app_focus = False
+ self.screen.refresh_bindings()
- def _detach_from_dom(self, widgets: list[Widget]) -> list[Widget]:
- """Detach a list of widgets from the DOM.
+ def _prune(self, *nodes: Widget, parent: DOMNode | None = None) -> AwaitRemove:
+ """Prune nodes from DOM.
Args:
- widgets: The list of widgets to detach from the DOM.
-
- Returns:
- The list of widgets that should be pruned.
-
- Note:
- A side-effect of calling this function is that each parent of
- each affected widget will be made to forget about the affected
- child.
- """
-
- # We've been given a list of widgets to remove, but removing those
- # will also result in other (descendent) widgets being removed. So
- # to start with let's get a list of everything that's not going to
- # be in the DOM by the time we've finished. Note that, at this
- # point, it's entirely possible that there will be duplicates.
- everything_to_remove: list[Widget] = []
- for widget in widgets:
- everything_to_remove.extend(
- widget.walk_children(
- Widget, with_self=True, method="depth", reverse=True
- )
- )
-
- # Next up, let's quickly create a deduped collection of things to
- # remove and ensure that, if one of them is the focused widget,
- # focus gets moved to somewhere else.
- dedupe_to_remove = set(everything_to_remove)
- if self.screen.focused in dedupe_to_remove:
- self.screen._reset_focus(
- self.screen.focused,
- [to_remove for to_remove in dedupe_to_remove if to_remove.can_focus],
- )
-
- # Next, we go through the set of widgets we've been asked to remove
- # and try and find the minimal collection of widgets that will
- # result in everything else that should be removed, being removed.
- # In other words: find the smallest set of ancestors in the DOM that
- # will remove the widgets requested for removal, and also ensure
- # that all knock-on effects happen too.
- request_remove = set(widgets)
- pruned_remove = [
- widget for widget in widgets if request_remove.isdisjoint(widget.ancestors)
- ]
-
- # Now that we know that minimal set of widgets, we go through them
- # and get their parents to forget about them. This has the effect of
- # snipping each affected branch from the DOM.
- for widget in pruned_remove:
- if widget.parent is not None:
- widget.parent._nodes._remove(widget)
-
- for node in pruned_remove:
- node._detach()
-
- # Return the list of widgets that should end up being sent off in a
- # prune event.
- return pruned_remove
-
- def _walk_children(self, root: Widget) -> Iterable[list[Widget]]:
- """Walk children depth first, generating widgets and a list of their siblings.
+ parent: Parent node.
Returns:
- The child widgets of root.
+ Optional awaitable.
"""
- stack: list[Widget] = [root]
- pop = stack.pop
- push = stack.append
-
- while stack:
- widget = pop()
- children = [*widget._nodes, *widget._get_virtual_dom()]
- if children:
- yield children
- for child in widget._nodes:
- push(child)
+ if not nodes:
+ return AwaitRemove([])
+ pruning_nodes: set[Widget] = {*nodes}
+ for node in nodes:
+ node.post_message(Prune())
+ pruning_nodes.update(node.walk_children(with_self=True))
- def _remove_nodes(
- self, widgets: list[Widget], parent: DOMNode | None
- ) -> AwaitRemove:
- """Remove nodes from DOM, and return an awaitable that awaits cleanup.
-
- Args:
- widgets: List of nodes to remove.
- parent: Parent node of widgets, or None for no parent.
+ try:
+ screen = nodes[0].screen
+ except (ScreenStackError, NoScreen):
+ pass
+ else:
+ if screen.focused and screen.focused in pruning_nodes:
+ screen._reset_focus(screen.focused, list(pruning_nodes))
- Returns:
- Awaitable that returns when the nodes have been fully removed.
- """
+ for node in pruning_nodes:
+ node._pruning = True
- async def prune_widgets_task(
- widgets: list[Widget], finished_event: asyncio.Event
- ) -> None:
- """Prune widgets as a background task.
+ def post_mount() -> None:
+ """Called after removing children."""
- Args:
- widgets: Widgets to prune.
- finished_event: Event to set when complete.
- """
- try:
- await self._prune_nodes(widgets)
- finally:
- finished_event.set()
- if parent is not None:
+ if parent is not None:
+ try:
+ screen = parent.screen
+ except (ScreenStackError, NoScreen):
+ pass
+ else:
+ if screen._running:
+ self._update_mouse_over(screen)
+ finally:
parent.refresh(layout=True)
- removed_widgets = self._detach_from_dom(widgets)
-
- finished_event = asyncio.Event()
- remove_task = create_task(
- prune_widgets_task(removed_widgets, finished_event), name="prune nodes"
+ await_complete = AwaitRemove(
+ [task for node in nodes if (task := node._task) is not None],
+ post_mount,
)
-
- await_remove = AwaitRemove(finished_event, remove_task)
- self.call_next(await_remove)
- return await_remove
-
- async def _prune_nodes(self, widgets: list[Widget]) -> None:
- """Remove nodes and children.
-
- Args:
- widgets: Widgets to remove.
- """
- async with self._dom_lock:
- for widget in widgets:
- await self._prune_node(widget)
-
- async def _prune_node(self, root: Widget) -> None:
- """Remove a node and its children. Children are removed before parents.
-
- Args:
- root: Node to remove.
- """
- # Pruning a node that has been removed is a no-op
- if root not in self._registry:
- return
-
- node_children = list(self._walk_children(root))
-
- for children in reversed(node_children):
- # Closing children can be done asynchronously.
- close_messages = [
- child._close_messages(wait=True) for child in children if child._running
- ]
- # TODO: What if a message pump refuses to exit?
- if close_messages:
- await asyncio.gather(*close_messages)
- for child in children:
- self._unregister(child)
-
- await root._close_messages(wait=True)
- self._unregister(root)
+ self.call_next(await_complete)
+ return await_complete
def _watch_app_focus(self, focus: bool) -> None:
"""Respond to changes in app focus."""
@@ -3329,7 +3367,10 @@ def _watch_app_focus(self, focus: bool) -> None:
and self.screen.focused is None
):
# ...settle focus back on that widget.
- self.screen.set_focus(self._last_focused_on_app_blur)
+ # Don't scroll the newly focused widget, as this can be quite jarring
+ self.screen.set_focus(
+ self._last_focused_on_app_blur, scroll_visible=False
+ )
except NoScreen:
pass
# Now that we have focus back on the app and we don't need the
@@ -3342,14 +3383,15 @@ def _watch_app_focus(self, focus: bool) -> None:
# Remove focus for now.
self.screen.set_focus(None)
- async def action_check_bindings(self, key: str) -> None:
- """An [action](/guide/actions) to handle a key press using the binding system.
+ async def action_simulate_key(self, key: str) -> None:
+ """An [action](/guide/actions) to simulate a key press.
+
+ This will invoke the same actions as if the user had pressed the key.
Args:
key: The key to process.
"""
- if not await self.check_bindings(key, priority=True):
- await self.check_bindings(key, priority=False)
+ self.simulate_key(key)
async def action_quit(self) -> None:
"""An [action](/guide/actions) to quit the app as soon as possible."""
@@ -3475,7 +3517,7 @@ def _refresh_notifications(self) -> None:
# or one will turn up. Things will work out later.
return
# Update the toast rack.
- toast_rack.show(self._notifications)
+ self.call_later(toast_rack.show, self._notifications)
def notify(
self,
@@ -3483,7 +3525,7 @@ def notify(
*,
title: str = "",
severity: SeverityLevel = "information",
- timeout: float = Notification.timeout,
+ timeout: float | None = None,
) -> None:
"""Create a notification.
@@ -3496,7 +3538,7 @@ def notify(
message: The message for the notification.
title: The title for the notification.
severity: The severity of the notification.
- timeout: The timeout (in seconds) for the notification.
+ timeout: The timeout (in seconds) for the notification, or `None` for default.
The `notify` method is used to create an application-wide
notification, shown in a [`Toast`][textual.widgets._toast.Toast],
@@ -3531,6 +3573,8 @@ def notify(
self.notify("It's against my programming to impersonate a deity.", title="")
```
"""
+ if timeout is None:
+ timeout = self.NOTIFICATION_TIMEOUT
notification = Notification(message, title, severity, timeout)
self.post_message(Notify(notification))
@@ -3562,12 +3606,12 @@ def action_command_palette(self) -> None:
def _suspend_signal(self) -> None:
"""Signal that the application is being suspended."""
- self.app_suspend_signal.publish()
+ self.app_suspend_signal.publish(self)
@on(Driver.SignalResume)
def _resume_signal(self) -> None:
"""Signal that the application is being resumed from a suspension."""
- self.app_resume_signal.publish()
+ self.app_resume_signal.publish(self)
@contextmanager
def suspend(self) -> Iterator[None]:
diff --git a/src/textual/await_complete.py b/src/textual/await_complete.py
index 2ff1068862..ebc3c84ea5 100644
--- a/src/textual/await_complete.py
+++ b/src/textual/await_complete.py
@@ -4,6 +4,9 @@
from typing import Any, Awaitable, Generator
import rich.repr
+from typing_extensions import Self
+
+from .message_pump import MessagePump
@rich.repr.auto(angular=True)
@@ -18,6 +21,15 @@ def __init__(self, *awaitables: Awaitable) -> None:
"""
self._future: Future[Any] = gather(*awaitables)
+ def call_next(self, node: MessagePump) -> Self:
+ """Await after the next message.
+
+ Args:
+ node: The node which created the object.
+ """
+ node.call_next(self)
+ return self
+
async def __call__(self) -> Any:
return await self
diff --git a/src/textual/await_remove.py b/src/textual/await_remove.py
index f02fe5b840..28698c1c94 100644
--- a/src/textual/await_remove.py
+++ b/src/textual/await_remove.py
@@ -2,33 +2,36 @@
An *optionally* awaitable object returned by methods that remove widgets.
"""
-from asyncio import Event, Task
-from typing import Generator
+from __future__ import annotations
+import asyncio
+from asyncio import Task, gather
+from typing import Generator
-class AwaitRemove:
- """An awaitable returned by a method that removes DOM nodes.
+from ._callback import invoke
+from ._types import CallbackType
- Returned by [Widget.remove][textual.widget.Widget.remove] and
- [DOMQuery.remove][textual.css.query.DOMQuery.remove].
- """
- def __init__(self, finished_flag: Event, task: Task) -> None:
- """Initialise the instance of ``AwaitRemove``.
+class AwaitRemove:
+ """An awaitable that waits for nodes to be removed."""
- Args:
- finished_flag: The asyncio event to wait on.
- task: The task which does the remove (required to keep a reference).
- """
- self.finished_flag = finished_flag
- self._task = task
+ def __init__(
+ self, tasks: list[Task], post_remove: CallbackType | None = None
+ ) -> None:
+ self._tasks = tasks
+ self._post_remove = post_remove
async def __call__(self) -> None:
await self
def __await__(self) -> Generator[None, None, None]:
+ current_task = asyncio.current_task()
+ tasks = [task for task in self._tasks if task is not current_task]
+
async def await_prune() -> None:
"""Wait for the prune operation to finish."""
- await self.finished_flag.wait()
+ await gather(*tasks)
+ if self._post_remove is not None:
+ await invoke(self._post_remove)
return await_prune().__await__()
diff --git a/src/textual/binding.py b/src/textual/binding.py
index 409fe9de56..3cc2828df3 100644
--- a/src/textual/binding.py
+++ b/src/textual/binding.py
@@ -8,7 +8,7 @@
from __future__ import annotations
from dataclasses import dataclass
-from typing import TYPE_CHECKING, Iterable
+from typing import TYPE_CHECKING, Iterable, NamedTuple
import rich.repr
@@ -17,6 +17,8 @@
if TYPE_CHECKING:
from typing_extensions import TypeAlias
+ from .dom import DOMNode
+
BindingType: TypeAlias = "Binding | tuple[str, str] | tuple[str, str, str]"
@@ -50,6 +52,17 @@ class Binding:
"""Enable priority binding for this key."""
+class ActiveBinding(NamedTuple):
+ """Information about an active binding (returned from [active_bindings][textual.screen.active_bindings])."""
+
+ node: DOMNode
+ """The node where the binding is defined."""
+ binding: Binding
+ """The binding information."""
+ enabled: bool
+ """Is the binding enabled? (enabled bindings are typically rendered dim)"""
+
+
@rich.repr.auto
class _Bindings:
"""Manage a set of bindings."""
diff --git a/src/textual/cache.py b/src/textual/cache.py
index 420d90004f..9186df7cda 100644
--- a/src/textual/cache.py
+++ b/src/textual/cache.py
@@ -8,7 +8,7 @@
from __future__ import annotations
-from typing import Dict, Generic, KeysView, TypeVar, overload
+from typing import TYPE_CHECKING, Dict, Generic, KeysView, TypeVar, overload
CacheKey = TypeVar("CacheKey")
CacheValue = TypeVar("CacheValue")
@@ -103,8 +103,7 @@ def set(self, key: CacheKey, value: CacheValue) -> None:
key: Key.
value: Value.
"""
- link = self._cache.get(key)
- if link is None:
+ if self._cache.get(key) is None:
head = self._head
if not head:
# First link references itself
@@ -128,13 +127,15 @@ def set(self, key: CacheKey, value: CacheValue) -> None:
__setitem__ = set
- @overload
- def get(self, key: CacheKey) -> CacheValue | None: ...
+ if TYPE_CHECKING:
- @overload
- def get(
- self, key: CacheKey, default: DefaultValue
- ) -> CacheValue | DefaultValue: ...
+ @overload
+ def get(self, key: CacheKey) -> CacheValue | None: ...
+
+ @overload
+ def get(
+ self, key: CacheKey, default: DefaultValue
+ ) -> CacheValue | DefaultValue: ...
def get(
self, key: CacheKey, default: DefaultValue | None = None
@@ -148,8 +149,8 @@ def get(
Returns:
Either the value or a default.
"""
- link = self._cache.get(key)
- if link is None:
+
+ if (link := self._cache.get(key)) is None:
self.misses += 1
return default
if link is not self._head:
@@ -166,7 +167,7 @@ def get(
def __getitem__(self, key: CacheKey) -> CacheValue:
link = self._cache.get(key)
- if link is None:
+ if (link := self._cache.get(key)) is None:
self.misses += 1
raise KeyError(key)
if link is not self._head:
@@ -268,13 +269,15 @@ def set(self, key: CacheKey, value: CacheValue) -> None:
__setitem__ = set
- @overload
- def get(self, key: CacheKey) -> CacheValue | None: ...
+ if TYPE_CHECKING:
- @overload
- def get(
- self, key: CacheKey, default: DefaultValue
- ) -> CacheValue | DefaultValue: ...
+ @overload
+ def get(self, key: CacheKey) -> CacheValue | None: ...
+
+ @overload
+ def get(
+ self, key: CacheKey, default: DefaultValue
+ ) -> CacheValue | DefaultValue: ...
def get(
self, key: CacheKey, default: DefaultValue | None = None
diff --git a/src/textual/color.py b/src/textual/color.py
index ab5996dda3..31ce703844 100644
--- a/src/textual/color.py
+++ b/src/textual/color.py
@@ -42,12 +42,11 @@
from rich.color_triplet import ColorTriplet
from typing_extensions import Final
-from textual.css.scalar import percentage_string_to_float
-from textual.css.tokenize import CLOSE_BRACE, COMMA, DECIMAL, OPEN_BRACE, PERCENT
-from textual.suggestions import get_suggestion
-
from ._color_constants import COLOR_NAME_TO_RGB
+from .css.scalar import percentage_string_to_float
+from .css.tokenize import CLOSE_BRACE, COMMA, DECIMAL, OPEN_BRACE, PERCENT
from .geometry import clamp
+from .suggestions import get_suggestion
_TRUECOLOR = ColorType.TRUECOLOR
@@ -224,6 +223,7 @@ def clamped(self) -> Color:
return color
@property
+ @lru_cache(1024)
def rich_color(self) -> RichColor:
"""This color encoded in Rich's Color class.
@@ -551,25 +551,74 @@ def get_contrast_text(self, alpha: float = 0.95) -> Color:
class Gradient:
"""Defines a color gradient."""
- def __init__(self, *stops: tuple[float, Color]) -> None:
+ def __init__(self, *stops: tuple[float, Color | str], quality: int = 200) -> None:
"""Create a color gradient that blends colors to form a spectrum.
- A gradient is defined by a sequence of "stops" consisting of a float and a color.
- The stop indicate the color at that point on a spectrum between 0 and 1.
+ A gradient is defined by a sequence of "stops" consisting of a tuple containing a float and a color.
+ The stop indicates the color at that point on a spectrum between 0 and 1.
+ Colors may be given as a [Color][textual.color.Color] instance, or a string that
+ can be parsed into a Color (with [Color.parse][textual.color.Color.parse]).
+
+ The quality of the argument defines the number of _steps_ in the gradient.
+ 200 was chosen so that there was no obvious banding in [LinearGradient][textual.renderables.gradient.LinearGradient].
+ Higher values are unlikely to yield any benefit, but lower values may result in quicker rendering.
Args:
- stops: A colors stop.
+ stops: Color stops.
+ quality: The number of steps in the gradient.
Raises:
ValueError: If any stops are missing (must be at least a stop for 0 and 1).
"""
- self._stops = sorted(stops)
+ parse = Color.parse
+ self._stops = sorted(
+ [
+ (
+ (position, parse(color))
+ if isinstance(color, str)
+ else (position, color)
+ )
+ for position, color in stops
+ ]
+ )
if len(stops) < 2:
raise ValueError("At least 2 stops required.")
if self._stops[0][0] != 0.0:
raise ValueError("First stop must be 0.")
if self._stops[-1][0] != 1.0:
raise ValueError("Last stop must be 1.")
+ self._quality = quality
+ self._colors: list[Color] | None = None
+ self._rich_colors: list[RichColor] | None = None
+
+ @property
+ def colors(self) -> list[Color]:
+ """A list of colors in the gradient."""
+ position = 0
+ quality = self._quality
+
+ if self._colors is None:
+ colors: list[Color] = []
+ add_color = colors.append
+ (stop1, color1), (stop2, color2) = self._stops[0:2]
+ for step_position in range(quality):
+ step = step_position / (quality - 1)
+ while step > stop2:
+ position += 1
+ (stop1, color1), (stop2, color2) = self._stops[
+ position : position + 2
+ ]
+ add_color(color1.blend(color2, (step - stop1) / (stop2 - stop1)))
+ self._colors = colors
+ assert len(self._colors) == self._quality
+ return self._colors
+
+ @property
+ def rich_colors(self) -> list[RichColor]:
+ """A list of colors in the gradient (for the Rich library)."""
+ if self._rich_colors is None:
+ self._rich_colors = [color.rich_color for color in self.colors]
+ return self._rich_colors
def get_color(self, position: float) -> Color:
"""Get a color from the gradient at a position between 0 and 1.
@@ -580,17 +629,26 @@ def get_color(self, position: float) -> Color:
position: A number between 0 and 1, where 0 is the first stop, and 1 is the last.
Returns:
- A color.
+ A Textual color.
"""
- # TODO: consider caching
- position = clamp(position, 0.0, 1.0)
- for (stop1, color1), (stop2, color2) in zip(self._stops, self._stops[1:]):
- if stop2 >= position >= stop1:
- return color1.blend(
- color2,
- (position - stop1) / (stop2 - stop1),
- )
- raise AssertionError("Can't get here if `_stops` is valid")
+ quality = self._quality - 1
+ color_index = int(clamp(position * quality, 0, quality))
+ return self.colors[color_index]
+
+ def get_rich_color(self, position: float) -> RichColor:
+ """Get a (Rich) color from the gradient at a position between 0 and 1.
+
+ Positions that are between stops will return a blended color.
+
+ Args:
+ position: A number between 0 and 1, where 0 is the first stop, and 1 is the last.
+
+ Returns:
+ A (Rich) color.
+ """
+ quality = self._quality - 1
+ color_index = int(clamp(position * quality, 0, quality))
+ return self.rich_colors[color_index]
# Color constants
@@ -598,6 +656,8 @@ def get_color(self, position: float) -> Color:
"""A constant for pure white."""
BLACK: Final = Color(0, 0, 0)
"""A constant for pure black."""
+TRANSPARENT: Final = Color.parse("transparent")
+"""A constant for transparent."""
def rgb_to_lab(rgb: Color) -> Lab:
diff --git a/src/textual/command.py b/src/textual/command.py
index 7cf5faf0ea..2cd523767a 100644
--- a/src/textual/command.py
+++ b/src/textual/command.py
@@ -34,8 +34,9 @@
from .containers import Horizontal, Vertical
from .events import Click, Mount
from .fuzzy import Matcher
+from .message import Message
from .reactive import var
-from .screen import Screen, _SystemModalScreen
+from .screen import Screen, SystemModalScreen
from .timer import Timer
from .types import CallbackType, IgnoreReturnCallbackType
from .widget import Widget
@@ -353,7 +354,9 @@ class CommandList(OptionList, can_focus=False):
border-right: none;
height: auto;
max-height: 70vh;
- background: $panel;
+ background: transparent;
+ padding: 0;
+ text-style: bold;
}
CommandList:focus {
@@ -369,11 +372,11 @@ class CommandList(OptionList, can_focus=False):
}
CommandList > .option-list--option-highlighted {
- background: $accent;
+ background: $primary;
}
CommandList > .option-list--option {
- padding-left: 1;
+ padding-left: 2;
}
"""
@@ -409,13 +412,13 @@ class CommandInput(Input):
CommandInput, CommandInput:focus {
border: blank;
width: 1fr;
- background: $panel;
+ background: transparent;
padding-left: 0;
}
"""
-class CommandPalette(_SystemModalScreen[CallbackType]):
+class CommandPalette(SystemModalScreen[CallbackType]):
"""The Textual command palette."""
COMPONENT_CLASSES: ClassVar[set[str]] = {
@@ -430,18 +433,19 @@ class CommandPalette(_SystemModalScreen[CallbackType]):
"""
DEFAULT_CSS = """
+
+
CommandPalette:inline {
/* If the command palette is invoked in inline mode, we may need additional lines. */
min-height: 20;
}
CommandPalette {
- background: $background 30%;
- align-horizontal: center;
+ background: $background 60%;
+ align-horizontal: center;
}
- CommandPalette > .command-palette--help-text {
- background: transparent;
- color: $text-muted;
+ CommandPalette > .command-palette--help-text {
+ text-style: dim not bold;
}
CommandPalette:dark > .command-palette--highlight {
@@ -454,17 +458,16 @@ class CommandPalette(_SystemModalScreen[CallbackType]):
}
CommandPalette > Vertical {
- margin-top: 3;
- width: 90%;
+ margin-top: 3;
height: 100%;
visibility: hidden;
+ background: $primary 20%;
}
CommandPalette #--input {
height: auto;
visibility: visible;
border: hkey $primary;
- background: $panel;
}
CommandPalette #--input.--list-visible {
@@ -489,7 +492,6 @@ class CommandPalette(_SystemModalScreen[CallbackType]):
CommandPalette LoadingIndicator {
height: auto;
visibility: hidden;
- background: $panel;
border-bottom: hkey $primary;
}
@@ -541,6 +543,24 @@ class CommandPalette(_SystemModalScreen[CallbackType]):
_PALETTE_ID: Final[str] = "--command-palette"
"""The internal ID for the command palette."""
+ @dataclass
+ class OptionHighlighted(Message):
+ """Posted to App when an option is highlighted in the command palette."""
+
+ highlighted_event: OptionList.OptionHighlighted
+ """The option highlighted event from the OptionList within the command palette."""
+
+ @dataclass
+ class Opened(Message):
+ """Posted to App when the command palette is opened."""
+
+ @dataclass
+ class Closed(Message):
+ """Posted to App when the command palette is closed."""
+
+ option_selected: bool
+ """True if an option was selected, False if the palette was closed without selecting an option."""
+
def __init__(self) -> None:
"""Initialise the command palette."""
super().__init__(id=self._PALETTE_ID)
@@ -605,14 +625,14 @@ def compose(self) -> ComposeResult:
with Vertical():
with Horizontal(id="--input"):
yield SearchIcon()
- yield CommandInput(placeholder="Command Palette Search...")
+ yield CommandInput(placeholder="Search for commands…")
if not self.run_on_select:
yield Button("\u25b6")
with Vertical(id="--results"):
yield CommandList()
yield LoadingIndicator()
- def _on_click(self, event: Click) -> None:
+ def _on_click(self, event: Click) -> None: # type: ignore[override]
"""Handle the click event.
Args:
@@ -623,10 +643,13 @@ def _on_click(self, event: Click) -> None:
"""
if self.get_widget_at(event.screen_x, event.screen_y)[0] is self:
self._cancel_gather_commands()
+ self.app.post_message(CommandPalette.Closed(option_selected=False))
self.dismiss()
def _on_mount(self, _: Mount) -> None:
"""Configure the command palette once the DOM is ready."""
+
+ self.app.post_message(CommandPalette.Opened())
self._calling_screen = self.app.screen_stack[-2]
match_style = self.get_component_rich_style(
@@ -642,7 +665,7 @@ def _on_mount(self, _: Mount) -> None:
provider._post_init()
self._gather_commands("")
- async def _on_unmount(self) -> None:
+ async def _on_unmount(self) -> None: # type: ignore[override]
"""Shutdown providers when command palette is closed."""
if self._providers:
await wait(
@@ -696,7 +719,7 @@ def _show_no_matches() -> None:
command_list = self.query_one(CommandList)
command_list.add_option(
Option(
- Align.center(Text("No matches found")),
+ Align.center(Text("No matches found", style="not bold")),
disabled=True,
id=self._NO_MATCHES,
)
@@ -831,41 +854,6 @@ async def _search_for(
for search in searches:
search.cancel()
- @staticmethod
- def _sans_background(style: Style) -> Style:
- """Returns the given style minus the background color.
-
- Args:
- style: The style to remove the color from.
-
- Returns:
- The given style, minus its background.
- """
- # Here we're pulling out all of the styles *minus* the background.
- # This should probably turn into a utility method on Style
- # eventually. The reason for this is we want the developer to be
- # able to style the help text with a component class, but we want
- # the background to always be the background at any given moment in
- # the context of an OptionList. At the moment this act of copying
- # sans bgcolor seems to be the only way to achieve this.
- return Style(
- blink2=style.blink2,
- blink=style.blink,
- bold=style.bold,
- color=style.color,
- conceal=style.conceal,
- dim=style.dim,
- encircle=style.encircle,
- frame=style.frame,
- italic=style.italic,
- link=style.link,
- overline=style.overline,
- reverse=style.reverse,
- strike=style.strike,
- underline2=style.underline2,
- underline=style.underline,
- )
-
def _refresh_command_list(
self, command_list: CommandList, commands: list[Command], clear_current: bool
) -> None:
@@ -877,7 +865,7 @@ def _refresh_command_list(
clear_current: Should the current content of the list be cleared first?
"""
# For the moment, this is a fairly naive approach to populating the
- # command list with a sorted list of commands. Every time we add a
+ # command list with a list of commands. Every time we add a
# new one we're nuking the list of options and populating them
# again. If this turns out to not be a great approach, we may try
# and get a lot smarter with this (ideally OptionList will grow a
@@ -888,7 +876,7 @@ def _refresh_command_list(
if command_list.highlighted is not None and not clear_current
else None
)
- command_list.clear_options().add_options(sorted(commands, reverse=True))
+ command_list.clear_options().add_options(commands)
if highlighted is not None and highlighted.id:
command_list.highlighted = command_list.get_option_index(highlighted.id)
self._list_visible = bool(command_list.option_count)
@@ -912,8 +900,8 @@ async def _gather_commands(self, search_value: str) -> None:
# We'll potentially use the help text style a lot so let's grab it
# the once for use in the loop further down.
- help_style = self._sans_background(
- self.get_component_rich_style("command-palette--help-text")
+ help_style = self.get_component_rich_style(
+ "command-palette--help-text", partial=True
)
# The list to hold on to the commands we've gathered from the
@@ -974,7 +962,9 @@ async def _gather_commands(self, search_value: str) -> None:
# list of commands that have been gathered so far.
prompt = hit.prompt
if hit.help:
- prompt = Group(prompt, Text(hit.help, style=help_style))
+ help_text = Text(hit.help)
+ help_text.stylize(help_style)
+ prompt = Group(prompt, help_text)
gathered_commands.append(Command(prompt, hit, id=str(command_id)))
# Before we go making any changes to the UI, we do a quick
@@ -1085,12 +1075,14 @@ def _select_or_command(
# ...we should return it to the parent screen and let it
# decide what to do with it (hopefully it'll run it).
self._cancel_gather_commands()
+ self.app.post_message(CommandPalette.Closed(option_selected=True))
self.dismiss(self._selected_command.command)
@on(OptionList.OptionHighlighted)
def _stop_event_leak(self, event: OptionList.OptionHighlighted) -> None:
"""Stop any unused events so they don't leak to the application."""
event.stop()
+ self.app.post_message(CommandPalette.OptionHighlighted(highlighted_event=event))
def _action_escape(self) -> None:
"""Handle a request to escape out of the command palette."""
@@ -1098,6 +1090,7 @@ def _action_escape(self) -> None:
self._list_visible = False
else:
self._cancel_gather_commands()
+ self.app.post_message(CommandPalette.Closed(option_selected=False))
self.dismiss()
def _action_command_list(self, action: str) -> None:
diff --git a/src/textual/containers.py b/src/textual/containers.py
index c8fe96194e..91b64b6a1a 100644
--- a/src/textual/containers.py
+++ b/src/textual/containers.py
@@ -47,6 +47,8 @@ class ScrollableContainer(Widget, can_focus=True, inherit_bindings=False):
Binding("end", "scroll_end", "Scroll End", show=False),
Binding("pageup", "page_up", "Page Up", show=False),
Binding("pagedown", "page_down", "Page Down", show=False),
+ Binding("ctrl+pageup", "page_left", "Page Left", show=False),
+ Binding("ctrl+pagedown", "page_right", "Page Right", show=False),
]
"""Keyboard bindings for scrollable containers.
@@ -60,6 +62,8 @@ class ScrollableContainer(Widget, can_focus=True, inherit_bindings=False):
| end | Scroll to the end position, if scrolling is available. |
| pageup | Scroll up one page, if vertical scrolling is available. |
| pagedown | Scroll down one page, if vertical scrolling is available. |
+ | ctrl+pageup | Scroll left one page, if horizontal scrolling is available. |
+ | ctrl+pagedown | Scroll right one page, if horizontal scrolling is available. |
"""
@@ -102,7 +106,7 @@ class Horizontal(Widget, inherit_bindings=False):
class HorizontalScroll(ScrollableContainer):
- """A container with horizontal layout and an automatic scrollbar on the Y axis."""
+ """A container with horizontal layout and an automatic scrollbar on the X axis."""
DEFAULT_CSS = """
HorizontalScroll {
diff --git a/src/textual/css/_help_text.py b/src/textual/css/_help_text.py
index fd0788359f..f2ca1564c8 100644
--- a/src/textual/css/_help_text.py
+++ b/src/textual/css/_help_text.py
@@ -308,7 +308,7 @@ def color_property_help_text(
error: Exception | None = None,
) -> HelpText:
"""Help text to show when the user supplies an invalid value for a color
- property. For example, an unparseable color string.
+ property. For example, an unparsable color string.
Args:
property_name: The name of the property.
diff --git a/src/textual/css/_style_properties.py b/src/textual/css/_style_properties.py
index efcc309930..361cdfae40 100644
--- a/src/textual/css/_style_properties.py
+++ b/src/textual/css/_style_properties.py
@@ -17,7 +17,8 @@
from typing_extensions import TypeAlias
from .._border import normalize_border_value
-from ..color import Color, ColorParseError
+from .._cells import cell_len
+from ..color import TRANSPARENT, Color, ColorParseError
from ..geometry import NULL_SPACING, Spacing, SpacingDimensions, clamp
from ._error_tools import friendly_list
from ._help_text import (
@@ -31,7 +32,7 @@
string_enum_help_text,
style_flags_property_help_text,
)
-from .constants import VALID_STYLE_FLAGS
+from .constants import HATCHES, VALID_STYLE_FLAGS
from .errors import StyleTypeError, StyleValueError
from .scalar import (
NULL_SCALAR,
@@ -48,6 +49,7 @@
if TYPE_CHECKING:
from ..canvas import CanvasLineType
from .._layout import Layout
+ from ..widget import Widget
from .styles import StylesBase
from .types import AlignHorizontal, AlignVertical, DockEdge, EdgeType
@@ -900,7 +902,7 @@ def __get__(
"""
return cast(Color, obj.get_rule(self.name, self._default_color))
- def __set__(self, obj: StylesBase, color: Color | str | None):
+ def __set__(self, obj: StylesBase, color: Color | str | None) -> None:
"""Set the Color.
Args:
@@ -946,6 +948,27 @@ def __set__(self, obj: StylesBase, color: Color | str | None):
raise StyleValueError(f"Invalid color value {color}")
+class ScrollbarColorProperty(ColorProperty):
+ """A descriptor to set scrollbar color(s)."""
+
+ def __set__(self, obj: StylesBase, color: Color | str | None) -> None:
+ super().__set__(obj, color)
+
+ if obj.node is None:
+ return
+
+ from ..widget import Widget
+
+ if isinstance(obj.node, Widget):
+ widget = obj.node
+
+ if widget.show_horizontal_scrollbar:
+ widget.horizontal_scrollbar.refresh()
+
+ if widget.show_vertical_scrollbar:
+ widget.vertical_scrollbar.refresh()
+
+
class StyleFlagsProperty:
"""Descriptor for getting and set style flag properties (e.g. ``bold italic underline``)."""
@@ -966,7 +989,7 @@ def __get__(
"""
return cast(Style, obj.get_rule(self.name, Style.null()))
- def __set__(self, obj: StylesBase, style_flags: Style | str | None):
+ def __set__(self, obj: StylesBase, style_flags: Style | str | None) -> None:
"""Set the style using a style flag string.
Args:
@@ -1117,3 +1140,30 @@ def __set__(
horizontal, vertical = value
setattr(obj, self.horizontal, horizontal)
setattr(obj, self.vertical, vertical)
+
+
+class HatchProperty:
+ """Property to expose hatch style."""
+
+ def __get__(self, obj: StylesBase, type: type[StylesBase]) -> tuple[str, Color]:
+ return cast("tuple[str, Color]", obj.get_rule("hatch", (" ", TRANSPARENT)))
+
+ def __set__(self, obj: StylesBase, value: tuple[str, Color | str] | None) -> None:
+ _rich_traceback_omit = True
+ if value is None:
+ obj.clear_rule("hatch")
+ return
+ character, color = value
+ if len(character) != 1:
+ try:
+ character = HATCHES[character]
+ except KeyError:
+ raise ValueError(
+ f"Expected a character or hatch value here; found {character!r}"
+ ) from None
+ if cell_len(character) != 1:
+ raise ValueError("Hatch character must have a cell length of 1")
+ if isinstance(color, str):
+ color = Color.parse(color)
+ hatch = (character, color)
+ obj.set_rule("hatch", hatch)
diff --git a/src/textual/css/_styles_builder.py b/src/textual/css/_styles_builder.py
index 8b66fec39b..622312a861 100644
--- a/src/textual/css/_styles_builder.py
+++ b/src/textual/css/_styles_builder.py
@@ -6,9 +6,10 @@
import rich.repr
from .._border import BorderValue, normalize_border_value
+from .._cells import cell_len
from .._duration import _duration_as_seconds
from .._easing import EASING
-from ..color import Color, ColorParseError
+from ..color import TRANSPARENT, Color, ColorParseError
from ..geometry import Spacing, SpacingDimensions, clamp
from ..suggestions import get_suggestion
from ._error_tools import friendly_list
@@ -36,6 +37,7 @@
text_align_help_text,
)
from .constants import (
+ HATCHES,
VALID_ALIGN_HORIZONTAL,
VALID_ALIGN_VERTICAL,
VALID_BORDER,
@@ -43,6 +45,7 @@
VALID_CONSTRAIN,
VALID_DISPLAY,
VALID_EDGE,
+ VALID_HATCH,
VALID_KEYLINE,
VALID_OVERFLOW,
VALID_OVERLAY,
@@ -1054,6 +1057,75 @@ def process_constrain(self, name: str, tokens: list[Token]) -> None:
else:
self.styles._rules[name] = value # type: ignore
+ def process_hatch(self, name: str, tokens: list[Token]) -> None:
+ if not tokens:
+ return
+ character: str | None = None
+ color = TRANSPARENT
+ opacity = 1.0
+
+ if len(tokens) not in (2, 3):
+ self.error(name, tokens[0], "2 or 3 values expected here")
+
+ character_token, color_token, *opacity_tokens = tokens
+
+ if character_token.name == "token":
+ if character_token.value not in VALID_HATCH:
+ self.error(
+ name,
+ tokens[0],
+ string_enum_help_text(name, VALID_HATCH, context="css"),
+ )
+ character = HATCHES[character_token.value]
+ elif character_token.name == "string":
+ character = character_token.value[1:-1]
+ if len(character) != 1:
+ self.error(
+ name,
+ character_token,
+ f"Hatch type requires a string of length 1; got {character_token.value}",
+ )
+ if cell_len(character) != 1:
+ self.error(
+ name,
+ character_token,
+ f"Hatch type requires a string with a *cell length* of 1; got {character_token.value}",
+ )
+
+ if color_token.name in ("color", "token"):
+ try:
+ color = Color.parse(color_token.value)
+ except Exception as error:
+ self.error(
+ name,
+ color_token,
+ color_property_help_text(name, context="css", error=error),
+ )
+ else:
+ self.error(
+ name, color_token, f"Expected a color; found {color_token.value!r}"
+ )
+
+ if opacity_tokens:
+ opacity_token = opacity_tokens[0]
+ if opacity_token.name == "scalar":
+ opacity_scalar = opacity = Scalar.parse(opacity_token.value)
+ if opacity_scalar.unit != Unit.PERCENT:
+ self.error(
+ name,
+ opacity_token,
+ "hatch alpha must be given as a percentage.",
+ )
+ opacity = clamp(opacity_scalar.value / 100.0, 0, 1.0)
+ else:
+ self.error(
+ name,
+ opacity_token,
+ f"expected a percentage here; found {opacity_token.value!r}",
+ )
+
+ self.styles._rules[name] = (character or " ", color.multiply_alpha(opacity))
+
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 27c662dde9..07e2523989 100644
--- a/src/textual/css/constants.py
+++ b/src/textual/css/constants.py
@@ -74,3 +74,11 @@
VALID_OVERLAY: Final = {"none", "screen"}
VALID_CONSTRAIN: Final = {"x", "y", "both", "inflect", "none"}
VALID_KEYLINE: Final = {"none", "thin", "heavy", "double"}
+VALID_HATCH: Final = {"left", "right", "cross", "vertical", "horizontal"}
+HATCHES: Final = {
+ "left": "╲",
+ "right": "╱",
+ "cross": "╳",
+ "horizontal": "─",
+ "vertical": "│",
+}
diff --git a/src/textual/css/query.py b/src/textual/css/query.py
index d3cf48b512..6dec06f542 100644
--- a/src/textual/css/query.py
+++ b/src/textual/css/query.py
@@ -55,12 +55,7 @@ class WrongType(QueryError):
@rich.repr.auto(angular=True)
class DOMQuery(Generic[QueryType]):
- __slots__ = [
- "_node",
- "_nodes",
- "_filters",
- "_excludes",
- ]
+ __slots__ = ["_node", "_nodes", "_filters", "_excludes", "_deep"]
def __init__(
self,
@@ -68,6 +63,7 @@ def __init__(
*,
filter: str | None = None,
exclude: str | None = None,
+ deep: bool = True,
parent: DOMQuery | None = None,
) -> None:
"""Initialize a query object.
@@ -80,6 +76,7 @@ def __init__(
node: A DOM node.
filter: Query to filter children in the node.
exclude: Query to exclude children in the node.
+ deep: Query should be deep, i.e. recursive.
parent: The parent query, if this is the result of filtering another query.
Raises:
@@ -94,6 +91,7 @@ def __init__(
self._excludes: list[tuple[SelectorSet, ...]] = (
parent._excludes.copy() if parent else []
)
+ self._deep = deep
if filter is not None:
try:
self._filters.append(parse_selectors(filter))
@@ -118,9 +116,12 @@ def nodes(self) -> list[QueryType]:
from ..widget import Widget
if self._nodes is None:
+ initial_nodes = list(
+ self._node.walk_children(Widget) if self._deep else self._node._nodes
+ )
nodes = [
node
- for node in self._node.walk_children(Widget)
+ for node in initial_nodes
if all(match(selector_set, node) for selector_set in self._filters)
]
nodes = [
@@ -144,11 +145,13 @@ def __iter__(self) -> Iterator[QueryType]:
def __reversed__(self) -> Iterator[QueryType]:
return reversed(self.nodes)
- @overload
- def __getitem__(self, index: int) -> QueryType: ...
+ if TYPE_CHECKING:
+
+ @overload
+ def __getitem__(self, index: int) -> QueryType: ...
- @overload
- def __getitem__(self, index: slice) -> list[QueryType]: ...
+ @overload
+ def __getitem__(self, index: slice) -> list[QueryType]: ...
def __getitem__(self, index: int | slice) -> QueryType | list[QueryType]:
return self.nodes[index]
@@ -156,14 +159,20 @@ def __getitem__(self, index: int | slice) -> QueryType | list[QueryType]:
def __rich_repr__(self) -> rich.repr.Result:
try:
if self._filters:
- yield "query", " AND ".join(
- ",".join(selector.css for selector in selectors)
- for selectors in self._filters
+ yield (
+ "query",
+ " AND ".join(
+ ",".join(selector.css for selector in selectors)
+ for selectors in self._filters
+ ),
)
if self._excludes:
- yield "exclude", " OR ".join(
- ",".join(selector.css for selector in selectors)
- for selectors in self._excludes
+ yield (
+ "exclude",
+ " OR ".join(
+ ",".join(selector.css for selector in selectors)
+ for selectors in self._excludes
+ ),
)
except AttributeError:
pass
@@ -178,7 +187,12 @@ def filter(self, selector: str) -> DOMQuery[QueryType]:
New DOM Query.
"""
- return DOMQuery(self.node, filter=selector, parent=self)
+ return DOMQuery(
+ self.node,
+ filter=selector,
+ deep=self._deep,
+ parent=self,
+ )
def exclude(self, selector: str) -> DOMQuery[QueryType]:
"""Exclude nodes that match a given selector.
@@ -189,13 +203,20 @@ def exclude(self, selector: str) -> DOMQuery[QueryType]:
Returns:
New DOM query.
"""
- return DOMQuery(self.node, exclude=selector, parent=self)
+ return DOMQuery(
+ self.node,
+ exclude=selector,
+ deep=self._deep,
+ parent=self,
+ )
- @overload
- def first(self) -> QueryType: ...
+ if TYPE_CHECKING:
- @overload
- def first(self, expect_type: type[ExpectType]) -> ExpectType: ...
+ @overload
+ def first(self) -> QueryType: ...
+
+ @overload
+ def first(self, expect_type: type[ExpectType]) -> ExpectType: ...
def first(
self, expect_type: type[ExpectType] | None = None
@@ -225,11 +246,13 @@ def first(
else:
raise NoMatches(f"No nodes match {self!r} on {self.node!r}")
- @overload
- def only_one(self) -> QueryType: ...
+ if TYPE_CHECKING:
+
+ @overload
+ def only_one(self) -> QueryType: ...
- @overload
- def only_one(self, expect_type: type[ExpectType]) -> ExpectType: ...
+ @overload
+ def only_one(self, expect_type: type[ExpectType]) -> ExpectType: ...
def only_one(
self, expect_type: type[ExpectType] | None = None
@@ -270,11 +293,13 @@ def only_one(
pass
return the_one
- @overload
- def last(self) -> QueryType: ...
+ if TYPE_CHECKING:
- @overload
- def last(self, expect_type: type[ExpectType]) -> ExpectType: ...
+ @overload
+ def last(self) -> QueryType: ...
+
+ @overload
+ def last(self, expect_type: type[ExpectType]) -> ExpectType: ...
def last(
self, expect_type: type[ExpectType] | None = None
@@ -301,11 +326,13 @@ def last(
)
return last
- @overload
- def results(self) -> Iterator[QueryType]: ...
+ if TYPE_CHECKING:
+
+ @overload
+ def results(self) -> Iterator[QueryType]: ...
- @overload
- def results(self, filter_type: type[ExpectType]) -> Iterator[ExpectType]: ...
+ @overload
+ def results(self, filter_type: type[ExpectType]) -> Iterator[ExpectType]: ...
def results(
self, filter_type: type[ExpectType] | None = None
@@ -383,8 +410,7 @@ def remove(self) -> AwaitRemove:
An awaitable object that waits for the widgets to be removed.
"""
app = active_app.get()
- await_remove = app._remove_nodes(list(self), self._node)
- return await_remove
+ return app._prune(*self.nodes, parent=self._node)
def set_styles(
self, css: str | None = None, **update_styles
diff --git a/src/textual/css/scalar.py b/src/textual/css/scalar.py
index 0ef332c571..849f566732 100644
--- a/src/textual/css/scalar.py
+++ b/src/textual/css/scalar.py
@@ -12,15 +12,15 @@
class ScalarError(Exception):
- pass
+ """Base class for exceptions raised by the Scalar class."""
class ScalarResolveError(ScalarError):
- pass
+ """Raised for errors resolving scalars (unlikely to occur in practice)."""
class ScalarParseError(ScalarError):
- pass
+ """Raised when a scalar couldn't be parsed from a string."""
@unique
diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py
index 6566d61847..a157e53801 100644
--- a/src/textual/css/styles.py
+++ b/src/textual/css/styles.py
@@ -22,6 +22,7 @@
ColorProperty,
DockProperty,
FractionalProperty,
+ HatchProperty,
IntegerProperty,
KeylineProperty,
LayoutProperty,
@@ -31,6 +32,7 @@
OverflowProperty,
ScalarListProperty,
ScalarProperty,
+ ScrollbarColorProperty,
SpacingProperty,
StringEnumProperty,
StyleFlagsProperty,
@@ -185,6 +187,8 @@ class RulesMap(TypedDict, total=False):
border_subtitle_background: Color
border_subtitle_style: Style
+ hatch: tuple[str, Color]
+
overlay: Overlay
constrain: Constrain
@@ -288,15 +292,15 @@ class StylesBase(ABC):
transitions = TransitionsProperty()
tint = ColorProperty("transparent")
- scrollbar_color = ColorProperty("ansi_bright_magenta")
- scrollbar_color_hover = ColorProperty("ansi_yellow")
- scrollbar_color_active = ColorProperty("ansi_bright_yellow")
+ scrollbar_color = ScrollbarColorProperty("ansi_bright_magenta")
+ scrollbar_color_hover = ScrollbarColorProperty("ansi_yellow")
+ scrollbar_color_active = ScrollbarColorProperty("ansi_bright_yellow")
- scrollbar_corner_color = ColorProperty("#666666")
+ scrollbar_corner_color = ScrollbarColorProperty("#666666")
- scrollbar_background = ColorProperty("#555555")
- scrollbar_background_hover = ColorProperty("#444444")
- scrollbar_background_active = ColorProperty("black")
+ scrollbar_background = ScrollbarColorProperty("#555555")
+ scrollbar_background_hover = ScrollbarColorProperty("#444444")
+ scrollbar_background_active = ScrollbarColorProperty("black")
scrollbar_gutter = StringEnumProperty(
VALID_SCROLLBAR_GUTTER, "auto", layout=True, refresh_children=True
@@ -354,6 +358,8 @@ class StylesBase(ABC):
border_subtitle_background = ColorProperty(Color(0, 0, 0, 0))
border_subtitle_style = StyleFlagsProperty()
+ hatch = HatchProperty()
+
overlay = StringEnumProperty(
VALID_OVERLAY, "none", layout=True, refresh_parent=True
)
@@ -440,6 +446,18 @@ def is_relative_height(self) -> bool:
height = self.height
return height is not None and height.unit in (Unit.FRACTION, Unit.PERCENT)
+ @property
+ def is_auto_width(self) -> bool:
+ """Does the node have automatic width?"""
+ width = self.width
+ return width is not None and width.unit == Unit.AUTO
+
+ @property
+ def is_auto_height(self) -> bool:
+ """Does the node have automatic height?"""
+ height = self.height
+ return height is not None and height.unit == Unit.AUTO
+
@abstractmethod
def has_rule(self, rule: str) -> bool:
"""Check if a rule is set on this Styles object.
@@ -642,9 +660,15 @@ def partial_rich_style(self) -> Style:
Rich Style object.
"""
style = Style(
- color=(self.color.rich_color if self.has_rule("color") else None),
+ color=(
+ self.color.rich_color
+ if self.has_rule("color") and self.color.a > 0
+ else None
+ ),
bgcolor=(
- self.background.rich_color if self.has_rule("background") else None
+ self.background.rich_color
+ if self.has_rule("background") and self.background.a > 0
+ else None
),
)
style += self.text_style
@@ -1063,6 +1087,9 @@ def append_declaration(name: str, value: str) -> None:
keyline_type, keyline_color = self.keyline
if keyline_type != "none":
append_declaration("keyline", f"{keyline_type}, {keyline_color.css}")
+ if "hatch" in rules:
+ hatch_character, hatch_color = self.hatch
+ append_declaration("hatch", f'"{hatch_character}" {hatch_color.css}')
lines.sort()
return lines
@@ -1176,6 +1203,7 @@ def animate(
)
def __rich_repr__(self) -> rich.repr.Result:
+ yield self.node
for rule_name in RULE_NAMES:
if self.has_rule(rule_name):
yield rule_name, getattr(self, rule_name)
diff --git a/src/textual/css/stylesheet.py b/src/textual/css/stylesheet.py
index 0728f7584d..19d2b6a6ce 100644
--- a/src/textual/css/stylesheet.py
+++ b/src/textual/css/stylesheet.py
@@ -138,7 +138,7 @@ class CssSource(NamedTuple):
@rich.repr.auto(angular=True)
class Stylesheet:
- """A Stylsheet generated from Textual CSS."""
+ """A Stylesheet generated from Textual CSS."""
def __init__(self, *, variables: dict[str, str] | None = None) -> None:
self._rules: list[RuleSet] = []
diff --git a/src/textual/document/_document.py b/src/textual/document/_document.py
index ca850285b5..386a6a7713 100644
--- a/src/textual/document/_document.py
+++ b/src/textual/document/_document.py
@@ -182,11 +182,13 @@ def start(self) -> Location:
def end(self) -> Location:
"""Returns the location of the end of the document."""
- @overload
- def __getitem__(self, line_index: int) -> str: ...
+ if TYPE_CHECKING:
- @overload
- def __getitem__(self, line_index: slice) -> list[str]: ...
+ @overload
+ def __getitem__(self, line_index: int) -> str: ...
+
+ @overload
+ def __getitem__(self, line_index: slice) -> list[str]: ...
@abstractmethod
def __getitem__(self, line_index: int | slice) -> str | list[str]:
@@ -351,7 +353,7 @@ def start(self) -> Location:
def end(self) -> Location:
"""Returns the location of the end of the document."""
last_line = self._lines[-1]
- return (self.line_count, len(last_line))
+ return (self.line_count - 1, len(last_line))
def get_index_from_location(self, location: Location) -> int:
"""Given a location, returns the index from the document's text.
diff --git a/src/textual/document/_edit.py b/src/textual/document/_edit.py
index d0bd5ad83d..dc9c04aeba 100644
--- a/src/textual/document/_edit.py
+++ b/src/textual/document/_edit.py
@@ -81,8 +81,16 @@ def do(self, text_area: TextArea, record_selection: bool = True) -> EditResult:
)
row_offset = new_edit_to_row - edit_bottom_row
- target_selection_start_row = selection_start_row + row_offset
- target_selection_end_row = selection_end_row + row_offset
+ target_selection_start_row = (
+ selection_start_row + row_offset
+ if edit_bottom_row <= selection_start_row
+ else selection_start_row
+ )
+ target_selection_end_row = (
+ selection_end_row + row_offset
+ if edit_bottom_row <= selection_end_row
+ else selection_end_row
+ )
if self.maintain_selection_offset:
self._updated_selection = Selection(
diff --git a/src/textual/dom.py b/src/textual/dom.py
index 1a6d969cf3..bc75cadd86 100644
--- a/src/textual/dom.py
+++ b/src/textual/dom.py
@@ -206,6 +206,8 @@ def __init__(
dict[str, tuple[MessagePump, Reactive | object]] | None
) = None
+ self._pruning = False
+
super().__init__()
def set_reactive(
@@ -219,7 +221,7 @@ def set_reactive(
```
Args:
- name: Name of reactive attribute.
+ reactive: A reactive property (use the class scope syntax, i.e. `MyClass.my_reactive`).
value: New value of reactive.
Raises:
@@ -235,6 +237,29 @@ def set_reactive(
)
setattr(self, f"_reactive_{reactive.name}", value)
+ def mutate_reactive(self, reactive: Reactive[ReactiveType]) -> None:
+ """Force an update to a mutable reactive.
+
+ Example:
+ ```python
+ self.reactive_name_list.append("Jessica")
+ self.mutate_reactive(MyClass.reactive_name_list)
+ ```
+
+ Textual will automatically detect when a reactive is set to a new value, but it is unable
+ to detect if a value is _mutated_ (such as updating a list, dict, or attribute of an object).
+ If you do wish to use a collection or other mutable object in a reactive, then you can call
+ this method after your reactive is updated. This will ensure that all the reactive _superpowers_
+ work.
+
+ Args:
+ reactive: A reactive property (use the class scope syntax, i.e. `MyClass.my_reactive`).
+ """
+
+ internal_name = f"_reactive_{reactive.name}"
+ value = getattr(self, internal_name)
+ reactive._set(self, value, always=True)
+
def data_bind(
self,
*reactives: Reactive[Any],
@@ -484,7 +509,7 @@ def __init_subclass__(
]
)
- def get_component_styles(self, name: str) -> RenderStyles:
+ def get_component_styles(self, *names: str) -> RenderStyles:
"""Get a "component" styles object (must be defined in COMPONENT_CLASSES classvar).
Args:
@@ -496,9 +521,16 @@ def get_component_styles(self, name: str) -> RenderStyles:
Returns:
A Styles object.
"""
- if name not in self._component_styles:
- raise KeyError(f"No {name!r} key in COMPONENT_CLASSES")
- styles = self._component_styles[name]
+ styles = RenderStyles(self, Styles(), Styles())
+ for name in names:
+ if name not in self._component_styles:
+ raise KeyError(f"No {name!r} key in COMPONENT_CLASSES")
+ component_styles = self._component_styles[name]
+ styles.node = component_styles.node
+ styles.base.merge(component_styles.base)
+ styles.inline.merge(component_styles.inline)
+ styles._updates += 1
+
return styles
def _post_mount(self):
@@ -746,8 +778,7 @@ def css_path_nodes(self) -> list[DOMNode]:
append = result.append
node: DOMNode = self
- while isinstance(node._parent, DOMNode):
- node = node._parent
+ while isinstance((node := node._parent), DOMNode):
append(node)
return result[::-1]
@@ -778,7 +809,9 @@ def display(self) -> bool:
my_widget.display = False # Hide my_widget
```
"""
- return self.styles.display != "none" and not (self._closing or self._closed)
+ return self.styles.display != "none" and not (
+ self._closing or self._closed or self._pruning
+ )
@display.setter
def display(self, new_val: bool | str) -> None:
@@ -1078,12 +1111,11 @@ def ancestors_with_self(self) -> list[DOMNode]:
Returns:
A list of nodes.
"""
- nodes: list[MessagePump | None] = []
+ nodes: list[MessagePump | None] = [self]
add_node = nodes.append
node: MessagePump | None = self
- while node is not None:
+ while (node := node._parent) is not None:
add_node(node)
- node = node._parent
return cast("list[DOMNode]", nodes)
@property
@@ -1093,7 +1125,12 @@ def ancestors(self) -> list[DOMNode]:
Returns:
A list of nodes.
"""
- return self.ancestors_with_self[1:]
+ nodes: list[MessagePump | None] = []
+ add_node = nodes.append
+ node: MessagePump | None = self
+ while (node := node._parent) is not None:
+ add_node(node)
+ return cast("list[DOMNode]", nodes)
@property
def displayed_children(self) -> list[Widget]:
@@ -1180,24 +1217,26 @@ def _add_children(self, *nodes: Widget) -> None:
WalkType = TypeVar("WalkType", bound="DOMNode")
- @overload
- def walk_children(
- self,
- filter_type: type[WalkType],
- *,
- with_self: bool = False,
- method: WalkMethod = "depth",
- reverse: bool = False,
- ) -> list[WalkType]: ...
+ if TYPE_CHECKING:
- @overload
- def walk_children(
- self,
- *,
- with_self: bool = False,
- method: WalkMethod = "depth",
- reverse: bool = False,
- ) -> list[DOMNode]: ...
+ @overload
+ def walk_children(
+ self,
+ filter_type: type[WalkType],
+ *,
+ with_self: bool = False,
+ method: WalkMethod = "depth",
+ reverse: bool = False,
+ ) -> list[WalkType]: ...
+
+ @overload
+ def walk_children(
+ self,
+ *,
+ with_self: bool = False,
+ method: WalkMethod = "depth",
+ reverse: bool = False,
+ ) -> list[DOMNode]: ...
def walk_children(
self,
@@ -1233,11 +1272,13 @@ def walk_children(
nodes.reverse()
return cast("list[DOMNode]", nodes)
- @overload
- def query(self, selector: str | None) -> DOMQuery[Widget]: ...
+ if TYPE_CHECKING:
+
+ @overload
+ def query(self, selector: str | None = None) -> DOMQuery[Widget]: ...
- @overload
- def query(self, selector: type[QueryType]) -> DOMQuery[QueryType]: ...
+ @overload
+ def query(self, selector: type[QueryType]) -> DOMQuery[QueryType]: ...
def query(
self, selector: str | type[QueryType] | None = None
@@ -1258,14 +1299,49 @@ def query(
else:
return DOMQuery[QueryType](self, filter=selector.__name__)
- @overload
- def query_one(self, selector: str) -> Widget: ...
+ if TYPE_CHECKING:
+
+ @overload
+ def query_children(self, selector: str | None = None) -> DOMQuery[Widget]: ...
- @overload
- def query_one(self, selector: type[QueryType]) -> QueryType: ...
+ @overload
+ def query_children(self, selector: type[QueryType]) -> DOMQuery[QueryType]: ...
- @overload
- def query_one(self, selector: str, expect_type: type[QueryType]) -> QueryType: ...
+ def query_children(
+ self, selector: str | type[QueryType] | None = None
+ ) -> DOMQuery[Widget] | DOMQuery[QueryType]:
+ """Query the DOM for the immediate children that match a selector or widget type.
+
+ Note that this will not return child widgets more than a single level deep.
+ If you want to a query to potentially match all children in the widget tree,
+ see [query][textual.dom.DOMNode.query].
+
+ Args:
+ selector: A CSS selector, widget type, or `None` for all nodes.
+
+ Returns:
+ A query object.
+ """
+ from .css.query import DOMQuery, QueryType
+ from .widget import Widget
+
+ if isinstance(selector, str) or selector is None:
+ return DOMQuery[Widget](self, deep=False, filter=selector)
+ else:
+ return DOMQuery[QueryType](self, deep=False, filter=selector.__name__)
+
+ if TYPE_CHECKING:
+
+ @overload
+ def query_one(self, selector: str) -> Widget: ...
+
+ @overload
+ def query_one(self, selector: type[QueryType]) -> QueryType: ...
+
+ @overload
+ def query_one(
+ self, selector: str, expect_type: type[QueryType]
+ ) -> QueryType: ...
def query_one(
self,
@@ -1453,6 +1529,31 @@ def refresh(
) -> Self:
return self
+ def check_action(self, action: str, parameters: tuple[object, ...]) -> bool | None:
+ """Check whether an action is enabled.
+
+ Implement this method to add logic for [dynamic actions](/guide/actions#dynamic-actions) / bindings.
+
+ Args:
+ action: The name of an action.
+ action_parameters: A tuple of any action parameters.
+
+ Returns:
+ `True` if the action is enabled+visible,
+ `False` if the action is disabled+hidden,
+ `None` if the action is disabled+visible (grayed out in footer)
+ """
+ return True
+
+ def refresh_bindings(self) -> None:
+ """Call to prompt widgets such as the [Footer][textual.widgets.Footer] to update
+ the display of key bindings.
+
+ See [actions](/guide/actions#dynamic-actions) for how to use this method.
+
+ """
+ self.screen.refresh_bindings()
+
async def action_toggle(self, attribute_name: str) -> None:
"""Toggle an attribute on the node.
diff --git a/src/textual/driver.py b/src/textual/driver.py
index f0ad89c98b..2165711a54 100644
--- a/src/textual/driver.py
+++ b/src/textual/driver.py
@@ -99,7 +99,6 @@ def process_event(self, event: events.Event) -> None:
and not event.button
and self._last_move_event is not None
):
-
# Deduplicate self._down_buttons while preserving order.
buttons = list(dict.fromkeys(self._down_buttons).keys())
self._down_buttons.clear()
diff --git a/src/textual/drivers/_input_reader.py b/src/textual/drivers/_input_reader.py
index 84c72d3633..30f652284b 100644
--- a/src/textual/drivers/_input_reader.py
+++ b/src/textual/drivers/_input_reader.py
@@ -1,8 +1,8 @@
-import platform
+import sys
__all__ = ["InputReader"]
-WINDOWS = platform.system() == "Windows"
+WINDOWS = sys.platform == "win32"
if WINDOWS:
from ._input_reader_windows import InputReader
diff --git a/src/textual/drivers/linux_driver.py b/src/textual/drivers/linux_driver.py
index 061b732bda..41d19911ed 100644
--- a/src/textual/drivers/linux_driver.py
+++ b/src/textual/drivers/linux_driver.py
@@ -14,6 +14,8 @@
import rich.repr
from .. import events
+from .._loop import loop_last
+from .._parser import ParseError
from .._xterm_parser import XTermParser
from ..driver import Driver
from ..geometry import Size
@@ -46,6 +48,7 @@ def __init__(
super().__init__(app, debug=debug, mouse=mouse, size=size)
self._file = sys.__stderr__
self.fileno = sys.__stdin__.fileno()
+ self.input_tty = sys.__stdin__.isatty()
self.attrs_before: list[Any] | None = None
self.exit_event = Event()
self._key_thread: Thread | None = None
@@ -115,6 +118,7 @@ def _enable_mouse_support(self) -> None:
"""Enable reporting of mouse events."""
if not self._mouse:
return
+
write = self.write
write("\x1b[?1000h") # SET_VT200_MOUSE
write("\x1b[?1003h") # SET_ANY_EVENT_MOUSE
@@ -192,7 +196,7 @@ def _stop_again(*_) -> None:
loop = asyncio.get_running_loop()
- def send_size_event():
+ def send_size_event() -> None:
terminal_size = self._get_terminal_size()
width, height = terminal_size
textual_size = Size(width, height)
@@ -234,10 +238,14 @@ def on_terminal_resize(signum, stack) -> None:
# defaults to ASCII EOT = Ctrl-D = 4.)
newattr[tty.CC][termios.VMIN] = 1
- termios.tcsetattr(self.fileno, termios.TCSANOW, newattr)
+ try:
+ termios.tcsetattr(self.fileno, termios.TCSANOW, newattr)
+ except termios.error:
+ pass
self.write("\x1b[?25l") # Hide cursor
- self.write("\033[?1004h") # Enable FocusIn/FocusOut.
+ self.write("\x1b[?1004h") # Enable FocusIn/FocusOut.
+ self.write("\x1b[>1u") # https://sw.kovidgoyal.net/kitty/keyboard-protocol/
self.flush()
self._key_thread = Thread(target=self._run_input_thread)
send_size_event()
@@ -245,6 +253,9 @@ def on_terminal_resize(signum, stack) -> None:
self._request_terminal_sync_mode_support()
self._enable_bracketed_paste()
+ # Appears to fix an issue enabling mouse support in iTerm 3.5.0
+ self._enable_mouse_support()
+
# If we need to ask the app to signal that we've come back from a
# SIGTSTP...
if self._must_signal_resume:
@@ -259,6 +270,8 @@ def _request_terminal_sync_mode_support(self) -> None:
# Terminals should ignore this sequence if not supported.
# Apple terminal doesn't, and writes a single 'p' in to the terminal,
# so we will make a special case for Apple terminal (which doesn't support sync anyway).
+ if not self.input_tty:
+ return
if os.environ.get("TERM_PROGRAM", "") != "Apple_Terminal":
self.write("\033[?2026$p")
self.flush()
@@ -304,8 +317,11 @@ def disable_input(self) -> None:
if self._key_thread is not None:
self._key_thread.join()
self.exit_event.clear()
- termios.tcflush(self.fileno, termios.TCIFLUSH)
- except Exception as error:
+ try:
+ termios.tcflush(self.fileno, termios.TCIFLUSH)
+ except termios.error:
+ pass
+ except Exception:
# TODO: log this
pass
@@ -320,10 +336,14 @@ def stop_application_mode(self) -> None:
except termios.error:
pass
- # Alt screen false, show cursor
- self.write("\x1b[?1049l" + "\x1b[?25h")
- self.write("\033[?1004l") # Disable FocusIn/FocusOut.
- self.flush()
+ # Alt screen false, show cursor
+ self.write("\x1b[?1049l")
+ self.write("\x1b[?25h")
+ self.write("\x1b[?1004l") # Disable FocusIn/FocusOut.
+ self.write(
+ "\x1b[ None:
"""Perform cleanup."""
@@ -356,8 +376,8 @@ def run_input_thread(self) -> None:
def more_data() -> bool:
"""Check if there is more data to parse."""
- for _key, events in selector.select(0.01):
- if events & EVENT_READ:
+ for _key, selector_events in selector.select(0.1):
+ if selector_events & EVENT_READ:
return True
return False
@@ -368,15 +388,36 @@ def more_data() -> bool:
decode = utf8_decoder
read = os.read
+ def process_selector_events(
+ selector_events: list[tuple[selectors.SelectorKey, int]],
+ final: bool = False,
+ ) -> None:
+ """Process events from selector.
+
+ Args:
+ selector_events: List of selector events.
+ final: True if this is the last call.
+
+ """
+ for last, (_selector_key, mask) in loop_last(selector_events):
+ if mask & EVENT_READ:
+ unicode_data = decode(read(fileno, 1024 * 4), final=final and last)
+ if not unicode_data:
+ # This can occur if the stdin is piped
+ break
+ for event in feed(unicode_data):
+ self.process_event(event)
+
try:
while not self.exit_event.is_set():
- selector_events = selector.select(0.1)
- for _selector_key, mask in selector_events:
- if mask & EVENT_READ:
- unicode_data = decode(
- read(fileno, 1024), final=self.exit_event.is_set()
- )
- for event in feed(unicode_data):
- self.process_event(event)
+ process_selector_events(selector.select(0.1))
+
+ process_selector_events(selector.select(0.1), final=True)
+
finally:
selector.close()
+ try:
+ for event in feed(""):
+ pass
+ except ParseError:
+ pass
diff --git a/src/textual/drivers/linux_inline_driver.py b/src/textual/drivers/linux_inline_driver.py
index 01fc44b0df..66bde69260 100644
--- a/src/textual/drivers/linux_inline_driver.py
+++ b/src/textual/drivers/linux_inline_driver.py
@@ -108,7 +108,7 @@ def _run_input_thread(self) -> None:
"""
try:
self.run_input_thread()
- except BaseException as error:
+ except BaseException:
import rich.traceback
self._app.call_later(
@@ -127,7 +127,7 @@ def run_input_thread(self) -> None:
def more_data() -> bool:
"""Check if there is more data to parse."""
- for _key, events in selector.select(0.01):
+ for _key, events in selector.select(0.1):
if events & EVENT_READ:
return True
return False
@@ -158,22 +158,32 @@ def more_data() -> bool:
def start_application_mode(self) -> None:
loop = asyncio.get_running_loop()
- def send_size_event() -> None:
+ def send_size_event(clear: bool = False) -> None:
+ """Send the resize event, optionally clearing the screen.
+
+ Args:
+ clear: Clear the screen.
+ """
terminal_size = self._get_terminal_size()
width, height = terminal_size
textual_size = Size(width, height)
event = events.Resize(textual_size, textual_size)
+
+ async def update_size() -> None:
+ """Update the screen size."""
+ if clear:
+ self.write("\x1b[2J")
+ await self._app._post_message(event)
+
asyncio.run_coroutine_threadsafe(
- self._app._post_message(event),
+ update_size(),
loop=loop,
)
- def on_terminal_resize(signum, stack) -> None:
- self.write("\x1b[2J")
- self.flush()
- send_size_event()
+ def on_terminal_resize(signum, stack) -> None:
+ send_size_event(clear=True)
- signal.signal(signal.SIGWINCH, on_terminal_resize)
+ signal.signal(signal.SIGWINCH, on_terminal_resize)
self.write("\x1b[?25l") # Hide cursor
self.write("\033[?1004h") # Enable FocusIn/FocusOut.
@@ -266,6 +276,10 @@ def disable_input(self) -> None:
# TODO: log this
pass
+ def flush(self):
+ """Flush any buffered data."""
+ self._file.flush()
+
def stop_application_mode(self) -> None:
"""Stop application mode, restore state."""
self._disable_bracketed_paste()
diff --git a/src/textual/drivers/web_driver.py b/src/textual/drivers/web_driver.py
index 1a3055eb06..f4536639c7 100644
--- a/src/textual/drivers/web_driver.py
+++ b/src/textual/drivers/web_driver.py
@@ -14,7 +14,6 @@
import asyncio
import json
import os
-import platform
import signal
import sys
from codecs import getincrementaldecoder
@@ -29,7 +28,7 @@
from ._byte_stream import ByteStream
from ._input_reader import InputReader
-WINDOWS = platform.system() == "Windows"
+WINDOWS = sys.platform == "win32"
class _ExitInput(Exception):
@@ -147,7 +146,7 @@ def do_exit() -> None:
self._enable_bracketed_paste()
self.flush()
self._key_thread.start()
- self._app.post_message(events.AppBlur())
+ self._app.call_later(self._app.post_message, events.AppBlur())
def disable_input(self) -> None:
"""Disable further input."""
diff --git a/src/textual/events.py b/src/textual/events.py
index ce5ecac4e3..17e3e3e046 100644
--- a/src/textual/events.py
+++ b/src/textual/events.py
@@ -560,7 +560,8 @@ class Enter(Event, bubble=False, verbose=True):
class Leave(Event, bubble=False, verbose=True):
- """Sent when the mouse is moved away from a widget.
+ """Sent when the mouse is moved away from a widget, or if a widget is
+ programmatically disabled while hovered.
- [ ] Bubbles
- [X] Verbose
diff --git a/src/textual/expand_tabs.py b/src/textual/expand_tabs.py
index 9026a6fabe..a019aa11c0 100644
--- a/src/textual/expand_tabs.py
+++ b/src/textual/expand_tabs.py
@@ -67,7 +67,7 @@ def expand_text_tabs_from_widths(line: Text, tab_widths: list[int]) -> Text:
This will return a new Text instance with tab characters expanded into a
number of spaces. Each time a tab is encountered, it's expanded into the
next integer encountered in the `tab_widths` list. Consequently, the length
- of `tab_widths` should match the number of tab chracters in `line`.
+ of `tab_widths` should match the number of tab characters in `line`.
Args:
line: The `Text` instance to expand tabs in.
diff --git a/src/textual/fuzzy.py b/src/textual/fuzzy.py
index 236f83e32c..b09f8e1604 100644
--- a/src/textual/fuzzy.py
+++ b/src/textual/fuzzy.py
@@ -114,52 +114,3 @@ def highlight(self, candidate: str) -> Text:
text.stylize(self._match_style, offset, offset + 1)
return text
-
-
-if __name__ == "__main__":
- from itertools import permutations
- from string import ascii_lowercase
- from time import monotonic
-
- from rich import print
- from rich.rule import Rule
-
- matcher = Matcher("foo.bar")
- print(Rule())
- print("Query is:", matcher.query)
- print("Rule is:", matcher.query_pattern)
- print(Rule())
- candidates = (
- "foo.bar",
- " foo.bar ",
- "Hello foo.bar world",
- "f o o . b a r",
- "f o o .bar",
- "foo. b a r",
- "Lots of text before the foo.bar",
- "foo.bar up front and then lots of text afterwards",
- "This has an o in it but does not have a match",
- "Let's find one obvious match. But blat around always roughly.",
- )
- results = sorted(
- [
- (matcher.match(candidate), matcher.highlight(candidate))
- for candidate in candidates
- ],
- key=lambda pair: pair[0],
- reverse=True,
- )
- for score, highlight in results:
- print(f"{score:.15f} '", highlight, "'", sep="")
- print(Rule())
-
- RUNS = 5
- candidates = [
- "".join(permutation) for permutation in permutations(ascii_lowercase[:10])
- ]
- matcher = Matcher(ascii_lowercase[:10])
- start = monotonic()
- for _ in range(RUNS):
- for candidate in candidates:
- _ = matcher.match(candidate)
- print(f"{RUNS * len(candidates)} matches in {monotonic() - start:.5f} seconds")
diff --git a/src/textual/geometry.py b/src/textual/geometry.py
index 2a7879a983..b754b335ea 100644
--- a/src/textual/geometry.py
+++ b/src/textual/geometry.py
@@ -151,6 +151,19 @@ def get_distance_to(self, other: Offset) -> float:
distance: float = ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) ** 0.5
return distance
+ def clamp(self, width: int, height: int) -> Offset:
+ """Clamp the offset to fit within a rectangle of width x height.
+
+ Args:
+ width: Width to clamp.
+ height: Height to clamp.
+
+ Returns:
+ A new offset.
+ """
+ x, y = self
+ return Offset(clamp(x, 0, width - 1), clamp(y, 0, height - 1))
+
class Size(NamedTuple):
"""The dimensions (width and height) of a rectangular region.
@@ -268,6 +281,17 @@ def __contains__(self, other: Any) -> bool:
width, height = self
return width > x >= 0 and height > y >= 0
+ def clamp_offset(self, offset: Offset) -> Offset:
+ """Clamp an offset to fit within the width x height.
+
+ Args:
+ offset: An offset.
+
+ Returns:
+ A new offset that will fit inside the dimensions defined in the Size.
+ """
+ return offset.clamp(self.width, self.height)
+
class Region(NamedTuple):
"""Defines a rectangular region.
@@ -982,7 +1006,7 @@ def inflect(
class Spacing(NamedTuple):
- """The spacing around a renderable, such as padding and border.
+ """Stores spacing around a widget, such as padding and border.
Spacing is defined by four integers for the space at the top, right, bottom, and left of a region.
diff --git a/src/textual/keys.py b/src/textual/keys.py
index 36da438d24..0006b9a645 100644
--- a/src/textual/keys.py
+++ b/src/textual/keys.py
@@ -271,7 +271,7 @@ def _get_unicode_name_from_key(key: str) -> str:
This function can be seen as a pseudo-inverse of the function `_character_to_key`.
"""
- return KEY_TO_UNICODE_NAME.get(key, key.upper())
+ return KEY_TO_UNICODE_NAME.get(key, key)
def _get_key_aliases(key: str) -> list[str]:
diff --git a/src/textual/layouts/grid.py b/src/textual/layouts/grid.py
index 077b905074..ad9d40ff66 100644
--- a/src/textual/layouts/grid.py
+++ b/src/textual/layouts/grid.py
@@ -6,7 +6,7 @@
from .._layout import ArrangeResult, Layout, WidgetPlacement
from .._resolve import resolve
from ..css.scalar import Scalar
-from ..geometry import Region, Size
+from ..geometry import Region, Size, Spacing
if TYPE_CHECKING:
from ..widget import Widget
@@ -32,9 +32,18 @@ def arrange(
viewport = parent.screen.size
keyline_style, keyline_color = styles.keyline
offset = (0, 0)
- if keyline_style != "none":
+ gutter_spacing: Spacing | None
+ if keyline_style == "none":
+ gutter_spacing = None
+ else:
size -= (2, 2)
offset = (1, 1)
+ gutter_spacing = Spacing(
+ gutter_vertical,
+ gutter_horizontal,
+ gutter_vertical,
+ gutter_horizontal,
+ )
def cell_coords(column_count: int) -> Iterable[tuple[int, int]]:
"""Iterate over table coordinates ad infinitum.
@@ -206,14 +215,15 @@ def apply_height_limits(widget: Widget, height: int) -> int:
if widget.styles.row_span != 1:
continue
column_width = columns[column][1]
+ gutter_width, gutter_height = widget.styles.gutter.totals
widget_height = apply_height_limits(
widget,
widget.get_content_height(
size,
viewport,
- column_width,
+ column_width - gutter_width,
)
- + widget.styles.gutter.height,
+ + gutter_height,
)
height = max(height, widget_height)
row_scalars[row] = Scalar.from_number(height)
@@ -226,6 +236,7 @@ def apply_height_limits(widget: Widget, height: int) -> int:
add_widget = widgets.append
max_column = len(columns) - 1
max_row = len(rows) - 1
+
for widget, (column, row, column_span, row_span) in cell_size_map.items():
x = columns[column][0]
if row > max_row:
@@ -245,7 +256,17 @@ def apply_height_limits(widget: Widget, height: int) -> int:
.clip_size(cell_size)
.shrink(margin)
)
- add_placement(WidgetPlacement(region + offset, margin, widget))
+ add_placement(
+ WidgetPlacement(
+ region + offset,
+ (
+ margin
+ if gutter_spacing is None
+ else margin.grow_maximum(gutter_spacing)
+ ),
+ widget,
+ )
+ )
add_widget(widget)
return placements
diff --git a/src/textual/layouts/horizontal.py b/src/textual/layouts/horizontal.py
index 02672ae879..e4581f5734 100644
--- a/src/textual/layouts/horizontal.py
+++ b/src/textual/layouts/horizontal.py
@@ -38,7 +38,7 @@ def arrange(
]
)
+ (box_margins[0].left + box_margins[-1].right),
- min(
+ max(
[
margin_top + margin_bottom
for margin_top, _, margin_bottom, _ in box_margins
diff --git a/src/textual/layouts/vertical.py b/src/textual/layouts/vertical.py
index 995b135439..a81518f1d2 100644
--- a/src/textual/layouts/vertical.py
+++ b/src/textual/layouts/vertical.py
@@ -29,7 +29,7 @@ def arrange(
]
if box_margins:
resolve_margin = Size(
- min(
+ max(
[
margin_right + margin_left
for _, margin_right, _, margin_left in box_margins
diff --git a/src/textual/logging.py b/src/textual/logging.py
index 106a4d318b..2389b9443b 100644
--- a/src/textual/logging.py
+++ b/src/textual/logging.py
@@ -20,7 +20,7 @@ def __init__(self, stderr: bool = True, stdout: bool = False) -> None:
Args:
stderr: Log to stderr when there is no active app.
- stdout: Log to stdout when there is not active app.
+ stdout: Log to stdout when there is no active app.
"""
super().__init__()
self._stderr = stderr
diff --git a/src/textual/message.py b/src/textual/message.py
index 84c840480d..383eb34d33 100644
--- a/src/textual/message.py
+++ b/src/textual/message.py
@@ -154,4 +154,5 @@ def _bubble_to(self, widget: MessagePump) -> None:
Args:
widget: Target of bubble.
"""
+ self._no_default_action = False
widget.post_message(self)
diff --git a/src/textual/message_pump.py b/src/textual/message_pump.py
index a7514f4625..108c51b4cf 100644
--- a/src/textual/message_pump.py
+++ b/src/textual/message_pump.py
@@ -40,6 +40,7 @@
from .events import Event
from .message import Message
from .reactive import Reactive, TooManyComputesError
+from .signal import Signal
from .timer import Timer, TimerCallback
if TYPE_CHECKING:
@@ -134,6 +135,12 @@ def __init__(self, parent: MessagePump | None = None) -> None:
self._next_callbacks: list[events.Callback] = []
self._thread_id: int = threading.get_ident()
self._prevented_messages_on_mount = self._prevent_message_types_stack[-1]
+ self.message_signal: Signal[Message] = Signal(self, "messages")
+ """Subscribe to this signal to be notified of all messages sent to this widget.
+
+ This is a fairly low-level mechanism, and shouldn't replace regular message handling.
+
+ """
@property
def _prevent_message_types_stack(self) -> list[set[type[Message]]]:
@@ -197,6 +204,11 @@ def message_queue_size(self) -> int:
"""The current size of the message queue."""
return self._message_queue.qsize()
+ @property
+ def is_dom_root(self):
+ """Is this a root node (i.e. the App)?"""
+ return False
+
@property
def app(self) -> "App[object]":
"""
@@ -221,12 +233,22 @@ def app(self) -> "App[object]":
active_app.set(node)
return node
+ @property
+ def is_attached(self) -> bool:
+ """Is this node linked to the app through the DOM?"""
+ if self.app._exit:
+ return False
+ node: MessagePump | None = self
+ while (node := node._parent) is not None:
+ if node.is_dom_root:
+ return True
+ return False
+
@property
def is_parent_active(self) -> bool:
"""Is the parent active?"""
- return bool(
- self._parent and not self._parent._closed and not self._parent._closing
- )
+ parent = self._parent
+ return bool(parent is not None and not parent._closed and not parent._closing)
@property
def is_running(self) -> bool:
@@ -242,19 +264,6 @@ def log(self) -> Logger:
"""
return self.app._logger
- @property
- def is_attached(self) -> bool:
- """Is the node attached to the app via the DOM?"""
- from .app import App
-
- node = self
-
- while not isinstance(node, App):
- if node._parent is None:
- return False
- node = node._parent
- return True
-
def _attach(self, parent: MessagePump) -> None:
"""Set the parent, and therefore attach this node to the tree.
@@ -276,6 +285,7 @@ def check_message_enabled(self, message: Message) -> bool:
Returns:
`True` if the message will be sent, or `False` if it is disabled.
"""
+
return type(message) not in self._disabled_messages
def disable_messages(self, *messages: type[Message]) -> None:
@@ -348,11 +358,12 @@ def set_timer(
Returns:
A timer object.
"""
+
timer = Timer(
self,
delay,
name=name or f"set_timer#{Timer._timer_count}",
- callback=callback,
+ callback=None if callback is None else partial(self.call_next, callback),
repeat=0,
pause=pause,
)
@@ -443,29 +454,21 @@ def call_next(self, callback: Callback, *args: Any, **kwargs: Any) -> None:
def _on_invoke_later(self, message: messages.InvokeLater) -> None:
# Forward InvokeLater message to the Screen
- self.app.screen._invoke_later(
- message.callback, message._sender or active_message_pump.get()
- )
-
- def _close_messages_no_wait(self) -> None:
- """Request the message queue to immediately exit."""
- self._message_queue.put_nowait(messages.CloseMessages())
-
- async def _on_close_messages(self, message: messages.CloseMessages) -> None:
- await self._close_messages()
+ if self.app._running:
+ self.app.screen._invoke_later(
+ message.callback, message._sender or active_message_pump.get()
+ )
async def _close_messages(self, wait: bool = True) -> None:
"""Close message queue, and optionally wait for queue to finish processing."""
if self._closed or self._closing:
return
self._closing = True
- stop_timers = list(self._timers)
- for timer in stop_timers:
- timer.stop()
- self._timers.clear()
- await self._message_queue.put(events.Unmount())
+ if self._timers:
+ await Timer._stop_all(self._timers)
+ self._timers.clear()
Reactive._reset_object(self)
- await self._message_queue.put(None)
+ self._message_queue.put_nowait(None)
if wait and self._task is not None and asyncio.current_task() != self._task:
try:
running_widget = active_message_pump.get()
@@ -473,7 +476,10 @@ async def _close_messages(self, wait: bool = True) -> None:
running_widget = None
if running_widget is None or running_widget is not self:
- await self._task
+ try:
+ await self._task
+ except CancelledError:
+ pass
def _start_messages(self) -> None:
"""Start messages task."""
@@ -499,8 +505,15 @@ async def _process_messages(self) -> None:
pass
finally:
self._running = False
- for timer in list(self._timers):
- timer.stop()
+ try:
+ if self._timers:
+ await Timer._stop_all(self._timers)
+ self._timers.clear()
+ finally:
+ await self._message_loop_exit()
+
+ async def _message_loop_exit(self) -> None:
+ """Called when the message loop has completed."""
async def _pre_process(self) -> bool:
"""Procedure to run before processing messages.
@@ -533,6 +546,13 @@ async def _pre_process(self) -> bool:
def _post_mount(self):
"""Called after the object has been mounted."""
+ def _close_messages_no_wait(self) -> None:
+ """Request the message queue to immediately exit."""
+ self._message_queue.put_nowait(messages.CloseMessages())
+
+ async def _on_close_messages(self, message: messages.CloseMessages) -> None:
+ await self._close_messages()
+
async def _process_messages_loop(self) -> None:
"""Process messages until the queue is closed."""
_rich_traceback_guard = True
@@ -571,6 +591,7 @@ async def _process_messages_loop(self) -> None:
self.app._handle_exception(error)
break
finally:
+ self.message_signal.publish(message)
self._message_queue.task_done()
current_time = time()
@@ -724,7 +745,7 @@ async def _on_message(self, message: Message) -> None:
if message._sender is not None and message._sender == self._parent:
# parent is sender, so we stop propagation after parent
message.stop()
- if self.is_parent_active and not self._parent._closing:
+ if self.is_parent_active and self.is_attached:
message._bubble_to(self._parent)
def check_idle(self) -> None:
@@ -777,6 +798,8 @@ def post_message(self, message: Message) -> bool:
return True
async def on_callback(self, event: events.Callback) -> None:
+ if self.app._closing:
+ return
await invoke(event.callback)
# TODO: Does dispatch_key belong on message pump?
@@ -828,6 +851,8 @@ def get_key_handler(pump: MessagePump, key: str) -> Callable | None:
return handled
async def on_timer(self, event: events.Timer) -> None:
+ if not self.app._running:
+ return
event.prevent_default()
event.stop()
if event.callback is not None:
diff --git a/src/textual/messages.py b/src/textual/messages.py
index 91a54b2811..efa1fb673a 100644
--- a/src/textual/messages.py
+++ b/src/textual/messages.py
@@ -17,6 +17,11 @@ class CloseMessages(Message, verbose=True):
"""Requests message pump to close."""
+@rich.repr.auto
+class Prune(Message, verbose=True, bubble=False):
+ """Ask the node to prune (remove from DOM)."""
+
+
@rich.repr.auto
class ExitApp(Message, verbose=True):
"""Exit the app."""
diff --git a/src/textual/pilot.py b/src/textual/pilot.py
index 8226eb97ee..739a66591e 100644
--- a/src/textual/pilot.py
+++ b/src/textual/pilot.py
@@ -366,7 +366,11 @@ async def _wait_for_screen(self, timeout: float = 30.0) -> bool:
Raises:
WaitForScreenTimeout: If the screen and its children didn't finish processing within the timeout.
"""
- children = [self.app, *self.app.screen.walk_children(with_self=True)]
+ try:
+ screen = self.app.screen
+ except Exception:
+ return False
+ children = [self.app, *screen.walk_children(with_self=True)]
count = 0
count_zero_event = asyncio.Event()
diff --git a/src/textual/reactive.py b/src/textual/reactive.py
index 41b240c1b2..2507b14ab5 100644
--- a/src/textual/reactive.py
+++ b/src/textual/reactive.py
@@ -73,6 +73,7 @@ def invoke_watcher(
value: The new value of the attribute.
"""
_rich_traceback_omit = True
+
param_count = count_parameters(watch_function)
reset_token = active_message_pump.set(watcher_object)
try:
@@ -105,6 +106,7 @@ class Reactive(Generic[ReactiveType]):
always_update: Call watchers even when the new value equals the old value.
compute: Run compute methods when attribute is changed.
recompose: Compose the widget again when the attribute changes.
+ bindings: Refresh bindings when the reactive changes.
"""
_reactives: ClassVar[dict[str, object]] = {}
@@ -119,6 +121,7 @@ def __init__(
always_update: bool = False,
compute: bool = True,
recompose: bool = False,
+ bindings: bool = False,
) -> None:
self._default = default
self._layout = layout
@@ -127,6 +130,7 @@ def __init__(
self._always_update = always_update
self._run_compute = compute
self._recompose = recompose
+ self._bindings = bindings
self._owner: Type[MessageTarget] | None = None
def __rich_repr__(self) -> rich.repr.Result:
@@ -217,15 +221,19 @@ def __set_name__(self, owner: Type[MessageTarget], name: str) -> None:
default = self._default
setattr(owner, f"_default_{name}", default)
- @overload
- def __get__(
- self: Reactive[ReactiveType], obj: ReactableType, obj_type: type[ReactableType]
- ) -> ReactiveType: ...
+ if TYPE_CHECKING:
- @overload
- def __get__(
- self: Reactive[ReactiveType], obj: None, obj_type: type[ReactableType]
- ) -> Reactive[ReactiveType]: ...
+ @overload
+ def __get__(
+ self: Reactive[ReactiveType],
+ obj: ReactableType,
+ obj_type: type[ReactableType],
+ ) -> ReactiveType: ...
+
+ @overload
+ def __get__(
+ self: Reactive[ReactiveType], obj: None, obj_type: type[ReactableType]
+ ) -> Reactive[ReactiveType]: ...
def __get__(
self: Reactive[ReactiveType],
@@ -254,7 +262,7 @@ def __get__(
else:
return getattr(obj, internal_name)
- def __set__(self, obj: Reactable, value: ReactiveType) -> None:
+ def _set(self, obj: Reactable, value: ReactiveType, always: bool = False) -> None:
_rich_traceback_omit = True
if not hasattr(obj, "_id"):
@@ -279,7 +287,7 @@ def __set__(self, obj: Reactable, value: ReactiveType) -> None:
if callable(public_validate_function):
value = public_validate_function(value)
# If the value has changed, or this is the first time setting the value
- if current_value != value or self._always_update:
+ if always or self._always_update or current_value != value:
# Store the internal value
setattr(obj, self.internal_name, value)
@@ -289,6 +297,9 @@ def __set__(self, obj: Reactable, value: ReactiveType) -> None:
if self._run_compute:
self._compute(obj)
+ if self._bindings:
+ obj.refresh_bindings()
+
# Refresh according to descriptor flags
if self._layout or self._repaint or self._recompose:
obj.refresh(
@@ -297,6 +308,11 @@ def __set__(self, obj: Reactable, value: ReactiveType) -> None:
recompose=self._recompose,
)
+ def __set__(self, obj: Reactable, value: ReactiveType) -> None:
+ _rich_traceback_omit = True
+
+ self._set(obj, value)
+
@classmethod
def _check_watchers(cls, obj: Reactable, name: str, old_value: Any) -> None:
"""Check watchers, and call watch methods / computes
@@ -367,6 +383,7 @@ class reactive(Reactive[ReactiveType]):
repaint: Perform a repaint on change.
init: Call watchers on initialize (post mount).
always_update: Call watchers even when the new value equals the old value.
+ bindings: Refresh bindings when the reactive changes.
"""
def __init__(
@@ -378,6 +395,7 @@ def __init__(
init: bool = True,
always_update: bool = False,
recompose: bool = False,
+ bindings: bool = False,
) -> None:
super().__init__(
default,
@@ -386,6 +404,7 @@ def __init__(
init=init,
always_update=always_update,
recompose=recompose,
+ bindings=bindings,
)
@@ -396,6 +415,7 @@ class var(Reactive[ReactiveType]):
default: A default value or callable that returns a default.
init: Call watchers on initialize (post mount).
always_update: Call watchers even when the new value equals the old value.
+ bindings: Refresh bindings when the reactive changes.
"""
def __init__(
@@ -403,6 +423,7 @@ def __init__(
default: ReactiveType | Callable[[], ReactiveType],
init: bool = True,
always_update: bool = False,
+ bindings: bool = False,
) -> None:
super().__init__(
default,
@@ -410,6 +431,7 @@ def __init__(
repaint=False,
init=init,
always_update=always_update,
+ bindings=bindings,
)
diff --git a/src/textual/renderables/gradient.py b/src/textual/renderables/gradient.py
index 7dd359f7a7..82534b8f7f 100644
--- a/src/textual/renderables/gradient.py
+++ b/src/textual/renderables/gradient.py
@@ -1,10 +1,8 @@
from __future__ import annotations
-from functools import lru_cache
from math import cos, pi, sin
from typing import Sequence
-from rich.color import Color as RichColor
from rich.console import Console, ConsoleOptions, RenderResult
from rich.segment import Segment
from rich.style import Style
@@ -59,6 +57,7 @@ def __init__(
(stop, Color.parse(color) if isinstance(color, str) else color)
for stop, color in stops
]
+ self._color_gradient = Gradient(*self._stops)
def __rich_console__(
self, console: Console, options: ConsoleOptions
@@ -75,38 +74,20 @@ def __rich_console__(
new_line = Segment.line()
- color_gradient = Gradient(*self._stops)
-
_Segment = Segment
- get_color = color_gradient.get_color
+ get_color = self._color_gradient.get_rich_color
from_color = Style.from_color
- @lru_cache(maxsize=1024)
- def get_rich_color(color_offset: int) -> RichColor:
- """Get a Rich color in the gradient.
-
- Args:
- color_index: A offset within the color gradient normalized between 0 and 255.
-
- Returns:
- A Rich color.
- """
- return get_color(color_offset / 255).rich_color
-
for line_y in range(height):
point_y = float(line_y) * 2 - center_y
point_x = 0 - center_x
- x1 = (center_x + (point_x * cos_angle - point_y * sin_angle)) / width * 255
+ x1 = (center_x + (point_x * cos_angle - point_y * sin_angle)) / width
x2 = (
- (center_x + (point_x * cos_angle - (point_y + 1.0) * sin_angle))
- / width
- * 255
- )
+ center_x + (point_x * cos_angle - (point_y + 1.0) * sin_angle)
+ ) / width
point_x = width - center_x
- end_x1 = (
- (center_x + (point_x * cos_angle - point_y * sin_angle)) / width * 255
- )
+ end_x1 = (center_x + (point_x * cos_angle - point_y * sin_angle)) / width
delta_x = (end_x1 - x1) / width
if abs(delta_x) < 0.0001:
@@ -114,8 +95,8 @@ def get_rich_color(color_offset: int) -> RichColor:
yield _Segment(
"▀" * width,
from_color(
- get_rich_color(int(x1)),
- get_rich_color(int(x2)),
+ get_color(x1),
+ get_color(x2),
),
)
@@ -124,8 +105,8 @@ def get_rich_color(color_offset: int) -> RichColor:
_Segment(
"▀",
from_color(
- get_rich_color(int(x1 + x * delta_x)),
- get_rich_color(int(x2 + x * delta_x)),
+ get_color(x1 + x * delta_x),
+ get_color(x2 + x * delta_x),
),
)
for x in range(width)
diff --git a/src/textual/rlock.py b/src/textual/rlock.py
new file mode 100644
index 0000000000..d7a6af2d5e
--- /dev/null
+++ b/src/textual/rlock.py
@@ -0,0 +1,61 @@
+from __future__ import annotations
+
+from asyncio import Lock, Task, current_task
+
+
+class RLock:
+ """A re-entrant asyncio lock."""
+
+ def __init__(self) -> None:
+ self._owner: Task | None = None
+ self._count = 0
+ self._lock = Lock()
+
+ async def acquire(self) -> None:
+ """Wait until the lock can be acquired."""
+ task = current_task()
+ assert task is not None
+ if self._owner is None or self._owner is not task:
+ await self._lock.acquire()
+ self._owner = task
+ self._count += 1
+
+ def release(self) -> None:
+ """Release a previously acquired lock."""
+ task = current_task()
+ assert task is not None
+ self._count -= 1
+ if self._count < 0:
+ # Should not occur if every acquire as a release
+ raise RuntimeError("RLock.release called too many times")
+ if self._owner is task:
+ if not self._count:
+ self._owner = None
+ self._lock.release()
+
+ @property
+ def is_locked(self):
+ """Return True if lock is acquired."""
+ return self._lock.locked()
+
+ async def __aenter__(self) -> None:
+ """Asynchronous context manager to acquire and release lock."""
+ await self.acquire()
+
+ async def __aexit__(self, _type, _value, _traceback) -> None:
+ """Exit the context manager."""
+ self.release()
+
+
+if __name__ == "__main__":
+ from asyncio import Lock
+
+ async def locks():
+ lock = RLock()
+ async with lock:
+ async with lock:
+ print("Hello")
+
+ import asyncio
+
+ asyncio.run(locks())
diff --git a/src/textual/screen.py b/src/textual/screen.py
index 861a298976..cf233fbc8a 100644
--- a/src/textual/screen.py
+++ b/src/textual/screen.py
@@ -33,7 +33,8 @@
from ._context import active_message_pump, visible_screen_stack
from ._path import CSSPathType, _css_path_type_as_list, _make_path_object_relative
from ._types import CallbackType
-from .binding import Binding
+from .await_complete import AwaitComplete
+from .binding import ActiveBinding, Binding, _Bindings
from .css.match import match
from .css.parse import parse_selectors
from .css.query import NoMatches, QueryType
@@ -69,6 +70,7 @@
"""Type of a screen result callback function."""
+@rich.repr.auto
class ResultCallback(Generic[ScreenResultType]):
"""Holds the details of a callback."""
@@ -136,7 +138,7 @@ class Screen(Generic[ScreenResultType], Widget):
Screen {
layout: vertical;
overflow-y: auto;
- background: $surface;
+ background: $surface;
&:inline {
height: auto;
@@ -164,7 +166,8 @@ class Screen(Generic[ScreenResultType], Widget):
"""
focused: Reactive[Widget | None] = Reactive(None)
- """The focused [widget][textual.widget.Widget] or `None` for no focus."""
+ """The focused [widget][textual.widget.Widget] or `None` for no focus.
+ To set focus, do not update this value directly. Use [set_focus][textual.screen.Screen.set_focus] instead."""
stack_updates: Reactive[int] = Reactive(0, repaint=False)
"""An integer that updates when the screen is resumed."""
sub_title: Reactive[str | None] = Reactive(None, compute=False)
@@ -179,8 +182,8 @@ class Screen(Generic[ScreenResultType], Widget):
"""
BINDINGS = [
- Binding("tab", "focus_next", "Focus Next", show=False),
- Binding("shift+tab", "focus_previous", "Focus Previous", show=False),
+ Binding("tab", "app.focus_next", "Focus Next", show=False),
+ Binding("shift+tab", "app.focus_previous", "Focus Previous", show=False),
]
def __init__(
@@ -221,9 +224,16 @@ def __init__(
self.title = self.TITLE
self.sub_title = self.SUB_TITLE
- self.screen_layout_refresh_signal = Signal(self, "layout-refresh")
+ self.screen_layout_refresh_signal: Signal[Screen] = Signal(
+ self, "layout-refresh"
+ )
"""The signal that is published when the screen's layout is refreshed."""
+ self._bindings_updated = False
+ """Indicates that a binding update was requested."""
+ self.bindings_updated_signal: Signal[Screen] = Signal(self, "bindings_updated")
+ """A signal published when the bindings have been updated"""
+
@property
def is_modal(self) -> bool:
"""Is the screen modal?"""
@@ -262,7 +272,93 @@ def layers(self) -> tuple[str, ...]:
extras.append("_tooltips")
return (*super().layers, *extras)
+ def _watch_focused(self):
+ self.refresh_bindings()
+
+ def _watch_stack_updates(self):
+ self.refresh_bindings()
+
+ def refresh_bindings(self) -> None:
+ """Call to request a refresh of bindings."""
+ self.log.debug("Bindings updated")
+ self._bindings_updated = True
+ self.check_idle()
+
+ @property
+ def _binding_chain(self) -> list[tuple[DOMNode, _Bindings]]:
+ """Binding chain from this screen."""
+ focused = self.focused
+ if focused is not None and focused.loading:
+ focused = None
+ namespace_bindings: list[tuple[DOMNode, _Bindings]]
+
+ if focused is None:
+ namespace_bindings = [
+ (self, self._bindings),
+ (self.app, self.app._bindings),
+ ]
+ else:
+ namespace_bindings = [
+ (node, node._bindings) for node in focused.ancestors_with_self
+ ]
+
+ return namespace_bindings
+
+ @property
+ def _modal_binding_chain(self) -> list[tuple[DOMNode, _Bindings]]:
+ """The binding chain, ignoring everything before the last modal."""
+ binding_chain = self._binding_chain
+ for index, (node, _bindings) in enumerate(binding_chain, 1):
+ if node.is_modal:
+ return binding_chain[:index]
+ return binding_chain
+
+ @property
+ def active_bindings(self) -> dict[str, ActiveBinding]:
+ """Get currently active bindings for this screen.
+
+ If no widget is focused, then app-level bindings are returned.
+ If a widget is focused, then any bindings present in the screen and app are merged and returned.
+
+ This property may be used to inspect current bindings.
+
+ Returns:
+ A map of keys to a tuple containing (namespace, binding, enabled boolean).
+ """
+
+ bindings_map: dict[str, ActiveBinding] = {}
+ for namespace, bindings in self._modal_binding_chain:
+ for key, binding in bindings.keys.items():
+ action_state = self.app._check_action_state(binding.action, namespace)
+ if action_state is False:
+ continue
+ if existing_key_and_binding := bindings_map.get(key):
+ _, existing_binding, _ = existing_key_and_binding
+ if binding.priority and not existing_binding.priority:
+ bindings_map[key] = ActiveBinding(
+ namespace, binding, bool(action_state)
+ )
+ else:
+ bindings_map[key] = ActiveBinding(
+ namespace, binding, bool(action_state)
+ )
+
+ return bindings_map
+
+ @property
+ def is_active(self) -> bool:
+ """Is the screen active (i.e. visible and top of the stack)?"""
+ try:
+ return self.app.screen is self
+ except Exception:
+ return False
+
def render(self) -> RenderableType:
+ """Render method inherited from widget, used to render the screen's background.
+
+ Returns:
+ Background renderable.
+ """
background = self.styles.background
try:
base_screen = visible_screen_stack.get().pop()
@@ -270,10 +366,13 @@ def render(self) -> RenderableType:
base_screen = None
if base_screen is not None and background.a < 1:
+ # If background is translucent, render a background screen
return BackgroundScreen(base_screen, background)
if background.is_transparent:
+ # If the background is transparent, defer to App.render
return self.app.render()
+ # Render a screen of a solid color.
return Blank(background)
def get_offset(self, widget: Widget) -> Offset:
@@ -618,21 +717,22 @@ def set_focus(self, widget: Widget | None, scroll_visible: bool = True) -> None:
# Change focus
self.focused = widget
# Send focus event
+ widget.post_message(events.Focus())
+ focused = widget
+
if scroll_visible:
def scroll_to_center(widget: Widget) -> None:
"""Scroll to center (after a refresh)."""
- if widget.has_focus and not self.screen.can_view(widget):
- self.screen.scroll_to_center(widget, origin_visible=True)
+ if self.focused is widget and not self.can_view(widget):
+ self.scroll_to_center(widget, origin_visible=True)
- self.call_after_refresh(scroll_to_center, widget)
-
- widget.post_message(events.Focus())
- focused = widget
+ self.call_later(scroll_to_center, widget)
self.log.debug(widget, "was focused")
self._update_focus_styles(focused, blurred)
+ self.refresh_bindings()
def _extend_compose(self, widgets: list[Widget]) -> None:
"""Insert Textual's own internal widgets.
@@ -650,24 +750,32 @@ def _extend_compose(self, widgets: list[Widget]) -> None:
def _on_mount(self, event: events.Mount) -> None:
"""Set up the tooltip-clearing signal when we mount."""
- self.screen_layout_refresh_signal.subscribe(self, self._maybe_clear_tooltip)
+ self.screen_layout_refresh_signal.subscribe(
+ self, self._maybe_clear_tooltip, immediate=True
+ )
+ self.refresh_bindings()
async def _on_idle(self, event: events.Idle) -> None:
# Check for any widgets marked as 'dirty' (needs a repaint)
event.prevent_default()
- if not self.app._batch_count and self.is_current:
- if (
- self._layout_required
- or self._scroll_required
- or self._repaint_required
- or self._recompose_required
- or self._dirty_widgets
- ):
- self._update_timer.resume()
- return
-
- await self._invoke_and_clear_callbacks()
+ try:
+ if not self.app._batch_count and self.is_current:
+ if (
+ self._layout_required
+ or self._scroll_required
+ or self._repaint_required
+ or self._recompose_required
+ or self._dirty_widgets
+ ):
+ self._update_timer.resume()
+ return
+
+ await self._invoke_and_clear_callbacks()
+ finally:
+ if self._bindings_updated:
+ self._bindings_updated = False
+ self.app.call_later(self.bindings_updated_signal.publish, self)
def _compositor_refresh(self) -> None:
"""Perform a compositor refresh."""
@@ -714,6 +822,7 @@ def _compositor_refresh(self) -> None:
app.screen.refresh(*self._compositor._dirty_regions)
self._compositor._dirty_regions.clear()
self._dirty_widgets.clear()
+ app._update_mouse_over(self)
def _on_timer_update(self) -> None:
"""Called by the _update_timer."""
@@ -861,7 +970,7 @@ def _refresh_layout(self, size: Size | None = None, scroll: bool = False) -> Non
self._compositor_refresh()
if self.app._dom_ready:
- self.screen_layout_refresh_signal.publish()
+ self.screen_layout_refresh_signal.publish(self.screen)
else:
self.app.post_message(events.Ready())
self.app._dom_ready = True
@@ -966,7 +1075,7 @@ def _clear_tooltip(self) -> None:
self._tooltip_timer.stop()
tooltip.display = False
- def _maybe_clear_tooltip(self) -> None:
+ def _maybe_clear_tooltip(self, _) -> None:
"""Check if the widget under the mouse cursor still pertains to the tooltip.
If they differ, the tooltip will be removed.
@@ -1083,6 +1192,7 @@ def _forward_event(self, event: events.Event) -> None:
if event.is_forwarded:
return
event._set_forwarded()
+
if isinstance(event, (events.Enter, events.Leave)):
self.post_message(event)
@@ -1117,9 +1227,16 @@ def _forward_event(self, event: events.Event) -> None:
class _NoResult:
"""Class used to mark that there is no result."""
- def dismiss(self, result: ScreenResultType | Type[_NoResult] = _NoResult) -> None:
+ def dismiss(
+ self, result: ScreenResultType | Type[_NoResult] = _NoResult
+ ) -> AwaitComplete:
"""Dismiss the screen, optionally with a result.
+ !!! note
+
+ Only the active screen may be dismissed. If you try to dismiss a screen that isn't active,
+ this method will raise a `ScreenError`.
+
If `result` is provided and a callback was set when the screen was [pushed][textual.app.App.push_screen], then
the callback will be invoked with `result`.
@@ -1127,21 +1244,21 @@ def dismiss(self, result: ScreenResultType | Type[_NoResult] = _NoResult) -> Non
result: The optional result to be passed to the result callback.
Raises:
+ ScreenError: If the screen being dismissed is not active.
ScreenStackError: If trying to dismiss a screen that is not at the top of
the stack.
"""
- if self is not self.app.screen:
- from .app import ScreenStackError
+ if not self.is_active:
+ from .app import ScreenError
- raise ScreenStackError(
- f"Can't dismiss screen {self} that's not at the top of the stack."
- )
+ raise ScreenError("Screen is not active")
if result is not self._NoResult and self._result_callbacks:
self._result_callbacks[-1](cast(ScreenResultType, result))
- self.app.pop_screen()
+ await_pop = self.app.pop_screen()
+ return await_pop
- def action_dismiss(
+ async def action_dismiss(
self, result: ScreenResultType | Type[_NoResult] = _NoResult
) -> None:
"""A wrapper around [`dismiss`][textual.screen.Screen.dismiss] that can be called as an action.
@@ -1149,6 +1266,7 @@ def action_dismiss(
Args:
result: The optional result to be passed to the result callback.
"""
+ await self._flush_next_callbacks()
self.dismiss(result)
def can_view(self, widget: Widget) -> bool:
@@ -1204,7 +1322,7 @@ def __init__(
self._modal = True
-class _SystemModalScreen(ModalScreen[ScreenResultType], inherit_css=False):
+class SystemModalScreen(ModalScreen[ScreenResultType], inherit_css=False):
"""A variant of `ModalScreen` for internal use.
This version of `ModalScreen` allows us to build system-level screens;
diff --git a/src/textual/scrollbar.py b/src/textual/scrollbar.py
index 2700419192..a105aa8370 100644
--- a/src/textual/scrollbar.py
+++ b/src/textual/scrollbar.py
@@ -70,6 +70,13 @@ def __rich_repr__(self) -> rich.repr.Result:
class ScrollBarRender:
+ VERTICAL_BARS: ClassVar[list[str]] = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", " "]
+ """Glyphs used for vertical scrollbar ends, for smoother display."""
+ HORIZONTAL_BARS: ClassVar[list[str]] = ["▉", "▊", "▋", "▌", "▍", "▎", "▏", " "]
+ """Glyphs used for horizontal scrollbar ends, for smoother display."""
+ BLANK_GLYPH: ClassVar[str] = " "
+ """Glyph used for the main body of the scrollbar"""
+
def __init__(
self,
virtual_size: int = 100,
@@ -99,9 +106,9 @@ def render_bar(
bar_color: Color = Color.parse("bright_magenta"),
) -> Segments:
if vertical:
- bars = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", " "]
+ bars = cls.VERTICAL_BARS
else:
- bars = ["▉", "▊", "▋", "▌", "▍", "▎", "▏", " "]
+ bars = cls.HORIZONTAL_BARS
back = back_color
bar = bar_color
@@ -112,7 +119,7 @@ def render_bar(
_Segment = Segment
_Style = Style
- blank = " " * width_thickness
+ blank = cls.BLANK_GLYPH * width_thickness
foreground_meta = {"@mouse.down": "grab"}
if window_size and size and virtual_size and size != virtual_size:
diff --git a/src/textual/signal.py b/src/textual/signal.py
index a1a1d80b8a..5960ea687d 100644
--- a/src/textual/signal.py
+++ b/src/textual/signal.py
@@ -9,7 +9,7 @@
from __future__ import annotations
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Any, Awaitable, Callable, Generic, TypeVar, Union
from weakref import WeakKeyDictionary
import rich.repr
@@ -17,8 +17,13 @@
from textual import log
if TYPE_CHECKING:
- from ._types import IgnoreReturnCallbackType
from .dom import DOMNode
+ from .message_pump import MessagePump
+SignalT = TypeVar("SignalT")
+
+SignalCallbackType = Union[
+ Callable[[SignalT], Awaitable[Any]], Callable[[SignalT], Any]
+]
class SignalError(Exception):
@@ -26,7 +31,7 @@ class SignalError(Exception):
@rich.repr.auto(angular=True)
-class Signal:
+class Signal(Generic[SignalT]):
"""A signal that a widget may subscribe to, in order to invoke callbacks when an associated event occurs."""
def __init__(self, owner: DOMNode, name: str) -> None:
@@ -39,7 +44,7 @@ def __init__(self, owner: DOMNode, name: str) -> None:
self._owner = owner
self._name = name
self._subscriptions: WeakKeyDictionary[
- DOMNode, list[IgnoreReturnCallbackType]
+ MessagePump, list[SignalCallbackType]
] = WeakKeyDictionary()
def __rich_repr__(self) -> rich.repr.Result:
@@ -47,27 +52,47 @@ def __rich_repr__(self) -> rich.repr.Result:
yield "name", self._name
yield "subscriptions", list(self._subscriptions.keys())
- def subscribe(self, node: DOMNode, callback: IgnoreReturnCallbackType) -> None:
+ def subscribe(
+ self,
+ node: MessagePump,
+ callback: SignalCallbackType,
+ immediate: bool = False,
+ ) -> None:
"""Subscribe a node to this signal.
When the signal is published, the callback will be invoked.
Args:
node: Node to subscribe.
- callback: A callback function which takes no arguments, and returns anything (return type ignored).
+ callback: A callback function which takes a single argument and returns anything (return type ignored).
+ immediate: Invoke the callback immediately on publish if `True`, otherwise post it to the DOM node to be
+ called once existing messages have been processed.
Raises:
SignalError: Raised when subscribing a non-mounted widget.
"""
+
if not node.is_running:
raise SignalError(
f"Node must be running to subscribe to a signal (has {node} been mounted)?"
)
+
+ if immediate:
+
+ def signal_callback(data: object) -> None:
+ """Invoke the callback immediately."""
+ callback(data)
+
+ else:
+
+ def signal_callback(data: object) -> None:
+ """Post the callback to the node, to call at the next opertunity."""
+ node.call_next(callback, data)
+
callbacks = self._subscriptions.setdefault(node, [])
- if callback not in callbacks:
- callbacks.append(callback)
+ callbacks.append(signal_callback)
- def unsubscribe(self, node: DOMNode) -> None:
+ def unsubscribe(self, node: MessagePump) -> None:
"""Unsubscribe a node from this signal.
Args:
@@ -75,18 +100,29 @@ def unsubscribe(self, node: DOMNode) -> None:
"""
self._subscriptions.pop(node, None)
- def publish(self) -> None:
- """Publish the signal (invoke subscribed callbacks)."""
+ def publish(self, data: SignalT) -> None:
+ """Publish the signal (invoke subscribed callbacks).
+
+ Args:
+ data: An argument to pass to the callbacks.
+
+ """
+ # Don't publish if the DOM is not ready or shutting down
+ if not self._owner.is_attached or self._owner._pruning:
+ return
+ for ancestor_node in self._owner.ancestors_with_self:
+ if not ancestor_node.is_running:
+ return
for node, callbacks in list(self._subscriptions.items()):
- if not node.is_running:
+ if not (node.is_running and node.is_attached) or node._pruning:
# Removed nodes that are no longer running
self._subscriptions.pop(node)
else:
# Call callbacks
for callback in callbacks:
try:
- callback()
+ callback(data)
except Exception as error:
log.error(
f"error publishing signal to {node} ignored (callback={callback}); {error}"
diff --git a/src/textual/timer.py b/src/textual/timer.py
index 1d31af4b14..1cc8dcbdaa 100644
--- a/src/textual/timer.py
+++ b/src/textual/timer.py
@@ -7,8 +7,8 @@
from __future__ import annotations
import weakref
-from asyncio import CancelledError, Event, Task, create_task
-from typing import Any, Awaitable, Callable, Union
+from asyncio import CancelledError, Event, Task, create_task, gather
+from typing import Any, Awaitable, Callable, Iterable, Union
from rich.repr import Result, rich_repr
@@ -23,7 +23,7 @@
class EventTargetGone(Exception):
- pass
+ """Raised if the timer event target has been deleted prior to the timer event being sent."""
@rich_repr
@@ -83,12 +83,47 @@ def _start(self) -> None:
"""Start the timer."""
self._task = create_task(self._run_timer(), name=self.name)
- def stop(self) -> None:
- """Stop the timer."""
- if self._task is not None:
- self._active.set()
- self._task.cancel()
- self._task = None
+ def stop(self) -> Task:
+ """Stop the timer.
+
+ Returns:
+ A Task object. Await this to wait until the timer has completed.
+
+ """
+ if self._task is None:
+
+ async def noop() -> None:
+ """A dummy task."""
+
+ return create_task(noop())
+
+ self._active.set()
+ self._task.cancel()
+ return self._task
+
+ @classmethod
+ async def _stop_all(cls, timers: Iterable[Timer]) -> None:
+ """Stop a number of timers, and await their completion.
+
+ Args:
+ timers: A number of timers.
+ """
+
+ async def stop_timer(timer: Timer) -> None:
+ """Stop a timer and wait for it to finish.
+
+ Args:
+ timer: A Timer instance.
+ """
+ if timer._task is not None:
+ timer._active.set()
+ timer._task.cancel()
+ try:
+ await timer._task
+ except CancelledError:
+ pass
+
+ await gather(*[stop_timer(timer) for timer in list(timers)])
def pause(self) -> None:
"""Pause the timer.
diff --git a/src/textual/walk.py b/src/textual/walk.py
index a58bf0110f..0f6790c882 100644
--- a/src/textual/walk.py
+++ b/src/textual/walk.py
@@ -17,21 +17,22 @@
WalkType = TypeVar("WalkType", bound=DOMNode)
-@overload
-def walk_depth_first(
- root: DOMNode,
- *,
- with_root: bool = True,
-) -> Iterable[DOMNode]: ...
+if TYPE_CHECKING:
+ @overload
+ def walk_depth_first(
+ root: DOMNode,
+ *,
+ with_root: bool = True,
+ ) -> Iterable[DOMNode]: ...
-@overload
-def walk_depth_first(
- root: WalkType,
- filter_type: type[WalkType],
- *,
- with_root: bool = True,
-) -> Iterable[WalkType]: ...
+ @overload
+ def walk_depth_first(
+ root: WalkType,
+ filter_type: type[WalkType],
+ *,
+ with_root: bool = True,
+ ) -> Iterable[WalkType]: ...
def walk_depth_first(
@@ -65,31 +66,31 @@ def walk_depth_first(
if with_root and isinstance(root, check_type):
yield root
while stack:
- node = next(stack[-1], None)
- if node is None:
+ if (node := next(stack[-1], None)) is None:
pop()
else:
if isinstance(node, check_type):
yield node
- if node.children:
- push(iter(node.children))
+ if children := node._nodes:
+ push(iter(children))
-@overload
-def walk_breadth_first(
- root: DOMNode,
- *,
- with_root: bool = True,
-) -> Iterable[DOMNode]: ...
+if TYPE_CHECKING:
+ @overload
+ def walk_breadth_first(
+ root: DOMNode,
+ *,
+ with_root: bool = True,
+ ) -> Iterable[DOMNode]: ...
-@overload
-def walk_breadth_first(
- root: WalkType,
- filter_type: type[WalkType],
- *,
- with_root: bool = True,
-) -> Iterable[WalkType]: ...
+ @overload
+ def walk_breadth_first(
+ root: WalkType,
+ filter_type: type[WalkType],
+ *,
+ with_root: bool = True,
+ ) -> Iterable[WalkType]: ...
def walk_breadth_first(
@@ -127,4 +128,4 @@ def walk_breadth_first(
node = popleft()
if isinstance(node, check_type):
yield node
- extend(node.children)
+ extend(node._nodes)
diff --git a/src/textual/widget.py b/src/textual/widget.py
index e334c18ecf..b7296f6117 100644
--- a/src/textual/widget.py
+++ b/src/textual/widget.py
@@ -4,7 +4,7 @@
from __future__ import annotations
-from asyncio import Lock, create_task, wait
+from asyncio import create_task, gather, wait
from collections import Counter
from contextlib import asynccontextmanager
from fractions import Fraction
@@ -55,9 +55,11 @@
from ._styles_cache import StylesCache
from ._types import AnimationLevel
from .actions import SkipAction
+from .await_complete import AwaitComplete
from .await_remove import AwaitRemove
from .box_model import BoxModel
from .cache import FIFOCache
+from .color import Color
from .css.match import match
from .css.parse import parse_selectors
from .css.query import NoMatches, WrongType
@@ -75,11 +77,12 @@
)
from .layouts.vertical import VerticalLayout
from .message import Message
-from .messages import CallbackType
-from .notifications import Notification, SeverityLevel
+from .messages import CallbackType, Prune
+from .notifications import SeverityLevel
from .reactive import Reactive
from .render import measure
from .renderables.blank import Blank
+from .rlock import RLock
from .strip import Strip
from .walk import walk_depth_first
@@ -105,6 +108,8 @@
_NULL_STYLE = Style()
+_MOUSE_EVENTS_DISALLOW_IF_DISABLED = (events.MouseEvent, events.Enter, events.Leave)
+_MOUSE_EVENTS_ALLOW_IF_DISABLED = (events.MouseScrollDown, events.MouseScrollUp)
class AwaitMount:
@@ -134,6 +139,10 @@ async def await_mount() -> None:
if aws:
await wait(aws)
self._parent.refresh(layout=True)
+ try:
+ self._parent.app._update_mouse_over(self._parent.screen)
+ except NoScreen:
+ pass
return await_mount().__await__()
@@ -360,7 +369,7 @@ def __init__(
)
self._styles_cache = StylesCache()
- self._rich_style_cache: dict[str, tuple[Style, Style]] = {}
+ self._rich_style_cache: dict[tuple[str, ...], tuple[Style, Style]] = {}
self._tooltip: RenderableType | None = None
"""The tooltip content."""
@@ -391,13 +400,17 @@ def __init__(
if self.BORDER_SUBTITLE:
self.border_subtitle = self.BORDER_SUBTITLE
- self.lock = Lock()
+ self.lock = RLock()
"""`asyncio` lock to be used to synchronize the state of the widget.
Two different tasks might call methods on a widget at the same time, which
might result in a race condition.
This can be fixed by adding `async with widget.lock:` around the method calls.
"""
+ self._anchored: Widget | None = None
+ """An anchored child widget, or `None` if no child is anchored."""
+ self._anchor_animate: bool = False
+ """Flag to enable animation when scrolling anchored widgets."""
virtual_size: Reactive[Size] = Reactive(Size(0, 0), layout=True)
"""The virtual (scrollable) [size][textual.geometry.Size] of the widget."""
@@ -514,6 +527,40 @@ def opacity(self) -> float:
break
return opacity
+ @property
+ def is_anchored(self) -> bool:
+ """Is this widget anchored?"""
+ return self._parent is not None and self._parent is self
+
+ def anchor(self, *, animate: bool = False) -> None:
+ """Anchor the widget, which scrolls it into view (like [scroll_visible][textual.widget.Widget.scroll_visible]),
+ but also keeps it in view if the widget's size changes, or the size of its container changes.
+
+ !!! note
+
+ Anchored widgets will be un-anchored if the users scrolls the container.
+
+ Args:
+ animate: `True` if the scroll should animate, or `False` if it shouldn't.
+ """
+ if self._parent is not None and isinstance(self._parent, Widget):
+ self._parent._anchored = self
+ self._parent._anchor_animate = animate
+ self.check_idle()
+
+ def clear_anchor(self) -> None:
+ """Stop anchoring this widget (a no-op if this widget is not anchored)."""
+ if (
+ self._parent is not None
+ and isinstance(self._parent, Widget)
+ and self._parent._anchored is self
+ ):
+ self._parent._anchored = None
+
+ def _clear_anchor(self) -> None:
+ """Clear an anchored child."""
+ self._anchored = None
+
def _check_disabled(self) -> bool:
"""Check if the widget is disabled either explicitly by setting `disabled`,
or implicitly by setting `loading`.
@@ -625,15 +672,16 @@ def set_loading(self, loading: bool) -> Awaitable:
Returns:
An optional awaitable.
"""
-
+ LOADING_INDICATOR_CLASS = "-textual-loading-indicator"
+ LOADING_INDICATOR_QUERY = f".{LOADING_INDICATOR_CLASS}"
+ remove_indicator = self.query_children(LOADING_INDICATOR_QUERY).remove()
if loading:
loading_indicator = self.get_loading_widget()
- loading_indicator.add_class("-textual-loading-indicator")
+ loading_indicator.add_class(LOADING_INDICATOR_CLASS)
await_mount = self.mount(loading_indicator)
- return await_mount
+ return AwaitComplete(remove_indicator, await_mount).call_next(self)
else:
- await_remove = self.query(".-textual-loading-indicator").remove()
- return await_remove
+ return remove_indicator
async def _watch_loading(self, loading: bool) -> None:
"""Called when the 'loading' reactive is changed."""
@@ -641,11 +689,15 @@ async def _watch_loading(self, loading: bool) -> None:
ExpectType = TypeVar("ExpectType", bound="Widget")
- @overload
- def get_child_by_id(self, id: str) -> Widget: ...
+ if TYPE_CHECKING:
+
+ @overload
+ def get_child_by_id(self, id: str) -> Widget: ...
- @overload
- def get_child_by_id(self, id: str, expect_type: type[ExpectType]) -> ExpectType: ...
+ @overload
+ def get_child_by_id(
+ self, id: str, expect_type: type[ExpectType]
+ ) -> ExpectType: ...
def get_child_by_id(
self, id: str, expect_type: type[ExpectType] | None = None
@@ -675,13 +727,15 @@ def get_child_by_id(
)
return child
- @overload
- def get_widget_by_id(self, id: str) -> Widget: ...
+ if TYPE_CHECKING:
- @overload
- def get_widget_by_id(
- self, id: str, expect_type: type[ExpectType]
- ) -> ExpectType: ...
+ @overload
+ def get_widget_by_id(self, id: str) -> Widget: ...
+
+ @overload
+ def get_widget_by_id(
+ self, id: str, expect_type: type[ExpectType]
+ ) -> ExpectType: ...
def get_widget_by_id(
self, id: str, expect_type: type[ExpectType] | None = None
@@ -738,7 +792,7 @@ def get_child_by_type(self, expect_type: type[ExpectType]) -> ExpectType:
return child
raise NoMatches(f"No immediate child of type {expect_type}; {self._nodes}")
- def get_component_rich_style(self, name: str, *, partial: bool = False) -> Style:
+ def get_component_rich_style(self, *names: str, partial: bool = False) -> Style:
"""Get a *Rich* style for a component.
Args:
@@ -749,13 +803,21 @@ def get_component_rich_style(self, name: str, *, partial: bool = False) -> Style
A Rich style object.
"""
- if name not in self._rich_style_cache:
- component_styles = self.get_component_styles(name)
+ if names not in self._rich_style_cache:
+ component_styles = self.get_component_styles(*names)
style = component_styles.rich_style
+ text_opacity = component_styles.text_opacity
+ if text_opacity < 1 and style.bgcolor is not None:
+ style += Style.from_color(
+ (
+ Color.from_rich_color(style.bgcolor)
+ + component_styles.color.multiply_alpha(text_opacity)
+ ).rich_color
+ )
partial_style = component_styles.partial_rich_style
- self._rich_style_cache[name] = (style, partial_style)
+ self._rich_style_cache[names] = (style, partial_style)
- style, partial_style = self._rich_style_cache[name]
+ style, partial_style = self._rich_style_cache[names]
return partial_style if partial else style
@@ -890,13 +952,16 @@ def mount(
Only one of ``before`` or ``after`` can be provided. If both are
provided a ``MountError`` will be raised.
"""
+ if self._closing or self._pruning:
+ return AwaitMount(self, [])
+ if not self.is_attached:
+ raise MountError(f"Can't mount widget(s) before {self!r} is mounted")
# Check for duplicate IDs in the incoming widgets
- ids_to_mount = [widget.id for widget in widgets if widget.id is not None]
- unique_ids = set(ids_to_mount)
- num_unique_ids = len(unique_ids)
- num_widgets_with_ids = len(ids_to_mount)
- if num_unique_ids != num_widgets_with_ids:
- counter = Counter(widget.id for widget in widgets)
+ ids_to_mount = [
+ widget_id for widget in widgets if (widget_id := widget.id) is not None
+ ]
+ if len(set(ids_to_mount)) != len(ids_to_mount):
+ counter = Counter(ids_to_mount)
for widget_id, count in counter.items():
if count > 1:
raise MountError(
@@ -957,26 +1022,30 @@ def mount_all(
Only one of ``before`` or ``after`` can be provided. If both are
provided a ``MountError`` will be raised.
"""
+ if self.app._exit:
+ return AwaitMount(self, [])
await_mount = self.mount(*widgets, before=before, after=after)
return await_mount
- @overload
- def move_child(
- self,
- child: int | Widget,
- *,
- before: int | Widget,
- after: None = None,
- ) -> None: ...
-
- @overload
- def move_child(
- self,
- child: int | Widget,
- *,
- after: int | Widget,
- before: None = None,
- ) -> None: ...
+ if TYPE_CHECKING:
+
+ @overload
+ def move_child(
+ self,
+ child: int | Widget,
+ *,
+ before: int | Widget,
+ after: None = None,
+ ) -> None: ...
+
+ @overload
+ def move_child(
+ self,
+ child: int | Widget,
+ *,
+ after: int | Widget,
+ before: None = None,
+ ) -> None: ...
def move_child(
self,
@@ -1077,9 +1146,12 @@ async def recompose(self) -> None:
Recomposing will remove children and call `self.compose` again to remount.
"""
- if self._parent is not None:
- async with self.batch():
- await self.query("*").exclude(".-textual-system").remove()
+ if not self.is_attached or self._pruning:
+ return
+
+ async with self.batch():
+ await self.query("*").exclude(".-textual-system").remove()
+ if self.is_attached:
await self.mount_all(compose(self))
def _post_register(self, app: App) -> None:
@@ -1163,13 +1235,19 @@ def _get_box_model(
min_width -= gutter.width
content_width = max(content_width, min_width, Fraction(0))
- if styles.max_width is not None:
+ if styles.max_width is not None and not (
+ container.width == 0
+ and not styles.max_width.is_cells
+ and self._parent is not None
+ and self._parent.styles.is_auto_width
+ ):
# Restrict to maximum width, if set
max_width = styles.max_width.resolve(
container - margin.totals, viewport, width_fraction
)
if is_border_box:
max_width -= gutter.width
+
content_width = min(content_width, max_width)
content_width = max(Fraction(0), content_width)
@@ -1207,7 +1285,12 @@ def _get_box_model(
min_height -= gutter.height
content_height = max(content_height, min_height, Fraction(0))
- if styles.max_height is not None:
+ if styles.max_height is not None and not (
+ container.height == 0
+ and not styles.max_height.is_cells
+ and self._parent is not None
+ and self._parent.styles.is_auto_height
+ ):
# Restrict maximum height, if set
max_height = styles.max_height.resolve(
container - margin.totals, viewport, height_fraction
@@ -1233,9 +1316,11 @@ def get_content_width(self, container: Size, viewport: Size) -> int:
Returns:
The optimal width of the content.
"""
+
if self.is_container:
assert self._layout is not None
- return self._layout.get_content_width(self, container, viewport)
+ width = self._layout.get_content_width(self, container, viewport)
+ return width
cache_key = container.width
if self._content_width_cache[0] == cache_key:
@@ -1282,13 +1367,17 @@ def get_content_height(self, container: Size, viewport: Size, width: int) -> int
renderable = self.render()
if isinstance(renderable, Text):
- height = len(
- renderable.wrap(
- self._console,
- width,
- no_wrap=renderable.no_wrap,
- tab_size=renderable.tab_size or 8,
+ height = (
+ len(
+ renderable.wrap(
+ self._console,
+ width,
+ no_wrap=renderable.no_wrap,
+ tab_size=renderable.tab_size or 8,
+ )
)
+ if renderable
+ else 0
)
else:
options = self._console.options.update_width(width).update(
@@ -1557,6 +1646,15 @@ def size(self) -> Size:
"""
return self.content_region.size
+ @property
+ def scrollable_size(self) -> Size:
+ """The size of the scrollable content.
+
+ Returns:
+ Scrollable content size.
+ """
+ return self.scrollable_content_region.size
+
@property
def outer_size(self) -> Size:
"""The size of the widget (including padding and border).
@@ -1695,11 +1793,13 @@ def virtual_region_with_margin(self) -> Region:
@property
def _self_or_ancestors_disabled(self) -> bool:
"""Is this widget or any of its ancestors disabled?"""
- return any(
- node.disabled
- for node in self.ancestors_with_self
- if isinstance(node, Widget)
- )
+
+ node: Widget | None = self
+ while isinstance(node, Widget) and not node.is_dom_root:
+ if node.disabled:
+ return True
+ node = node._parent # type:ignore[assignment]
+ return False
@property
def focusable(self) -> bool:
@@ -2662,7 +2762,6 @@ def scroll_to_widget(
Returns:
`True` if any scrolling has occurred in any descendant, otherwise `False`.
"""
-
# Grow the region by the margin so to keep the margin in view.
region = widget.virtual_region_with_margin
scrolled = False
@@ -2674,7 +2773,7 @@ def scroll_to_widget(
else:
scroll_offset = container.scroll_to_region(
region,
- spacing=widget.gutter + widget.dock_gutter,
+ spacing=widget.dock_gutter,
animate=animate,
speed=speed,
duration=duration,
@@ -2695,6 +2794,8 @@ def scroll_to_widget(
region = (
(
region.translate(-scroll_offset)
+ .translate(container.styles.margin.top_left)
+ .translate(container.styles.border.spacing.top_left)
.translate(-widget.scroll_offset)
.translate(container.virtual_region_with_margin.offset)
)
@@ -2749,29 +2850,40 @@ def scroll_to_region(
if window in region and not (top or center):
return Offset()
+ def clamp_delta(delta: Offset) -> Offset:
+ """Clamp the delta to avoid scrolling out of range."""
+ scroll_x, scroll_y = self.scroll_offset
+ delta = Offset(
+ clamp(scroll_x + delta.x, 0, self.max_scroll_x) - scroll_x,
+ clamp(scroll_y + delta.y, 0, self.max_scroll_y) - scroll_y,
+ )
+ return delta
+
if center:
region_center_x, region_center_y = region.center
window_center_x, window_center_y = window.center
- center_delta = Offset(
- round(region_center_x - window_center_x),
- round(region_center_y - window_center_y),
+
+ delta = clamp_delta(
+ Offset(
+ int(region_center_x - window_center_x + 0.5),
+ int(region_center_y - window_center_y + 0.5),
+ )
)
- if origin_visible and region.offset not in window.translate(center_delta):
- center_delta = Region.get_scroll_to_visible(window, region, top=True)
- delta_x, delta_y = center_delta
+ if origin_visible and (region.offset not in window.translate(delta)):
+ delta = clamp_delta(
+ Region.get_scroll_to_visible(window, region, top=True)
+ )
else:
- delta_x, delta_y = Region.get_scroll_to_visible(window, region, top=top)
- scroll_x, scroll_y = self.scroll_offset
+ delta = clamp_delta(
+ Region.get_scroll_to_visible(window, region, top=top),
+ )
if not self.allow_horizontal_scroll and not force:
- delta_x = 0
+ delta = Offset(0, delta.y)
+
if not self.allow_vertical_scroll and not force:
- delta_y = 0
+ delta = Offset(delta.x, 0)
- delta = Offset(
- clamp(scroll_x + delta_x, 0, self.max_scroll_x) - scroll_x,
- clamp(scroll_y + delta_y, 0, self.max_scroll_y) - scroll_y,
- )
if delta:
if speed is None and duration is None:
duration = 0.2
@@ -2855,7 +2967,6 @@ def scroll_to_center(
on_complete: A callable to invoke when the animation is finished.
level: Minimum level required for the animation to take place (inclusive).
"""
-
self.call_after_refresh(
self.scroll_to_widget,
widget=widget,
@@ -3099,6 +3210,9 @@ def _get_rich_justify(self) -> JustifyMethod | None:
def post_render(self, renderable: RenderableType) -> ConsoleRenderable:
"""Applies style attributes to the default renderable.
+ This method is called by Textual itself.
+ It is unlikely you will need to call or implement this method.
+
Returns:
A new renderable.
"""
@@ -3133,19 +3247,24 @@ def watch_has_focus(self, value: bool) -> None:
"""Update from CSS if has focus state changes."""
self._update_styles()
- def watch_disabled(self) -> None:
+ def watch_disabled(self, disabled: bool) -> None:
"""Update the styles of the widget and its children when disabled is toggled."""
from .app import ScreenStackError
+ if disabled and self.mouse_over:
+ # Ensure widget gets a Leave if it is disabled while hovered
+ self._message_queue.put_nowait(events.Leave())
try:
+ screen = self.screen
if (
- self.disabled
- and self.app.focused is not None
- and self in self.app.focused.ancestors_with_self
+ disabled
+ and screen.focused is not None
+ and self in screen.focused.ancestors_with_self
):
- self.app.focused.blur()
- except (ScreenStackError, NoActiveAppError):
+ screen.focused.blur()
+ except (ScreenStackError, NoActiveAppError, NoScreen):
pass
+
self._update_styles()
def _size_updated(
@@ -3162,6 +3281,7 @@ def _size_updated(
Returns:
True if anything changed, or False if nothing changed.
"""
+
if (
self._size != size
or self.virtual_size != virtual_size
@@ -3288,6 +3408,15 @@ def get_style_at(self, x: int, y: int) -> Style:
return Style()
return self.screen.get_style_at(*screen_offset)
+ def suppress_click(self) -> None:
+ """Suppress a click event.
+
+ This will prevent a [Click][textual.events.Click] event being sent,
+ if called after a mouse down event and before the click itself.
+
+ """
+ self.app._mouse_down_widget = None
+
def _forward_event(self, event: events.Event) -> None:
event._set_forwarded()
self.post_message(event)
@@ -3326,8 +3455,7 @@ def refresh(
Returns:
The `Widget` instance.
"""
- if not self._is_mounted:
- return self
+
if layout:
self._layout_required = True
for ancestor in self.ancestors:
@@ -3335,6 +3463,11 @@ def refresh(
break
ancestor._clear_arrangement_cache()
+ if not self._is_mounted:
+ self._repaint_required = True
+ self.check_idle()
+ return self
+
if recompose:
self._recompose_required = True
self.call_next(self._check_recompose)
@@ -3355,26 +3488,36 @@ def remove(self) -> AwaitRemove:
Returns:
An awaitable object that waits for the widget to be removed.
"""
-
- await_remove = self.app._remove_nodes([self], self.parent)
+ await_remove = self.app._prune(self, parent=self._parent)
return await_remove
- def remove_children(self, selector: str | type[QueryType] = "*") -> AwaitRemove:
+ def remove_children(
+ self, selector: str | type[QueryType] | Iterable[Widget] = "*"
+ ) -> AwaitRemove:
"""Remove the immediate children of this Widget from the DOM.
Args:
- selector: A CSS selector to specify which direct children to remove.
+ selector: A CSS selector or iterable of widgets to remove.
Returns:
An awaitable object that waits for the direct children to be removed.
"""
- if not isinstance(selector, str):
+
+ if callable(selector) and issubclass(selector, Widget):
selector = selector.__name__
- parsed_selectors = parse_selectors(selector)
- children_to_remove = [
- child for child in self.children if match(parsed_selectors, child)
- ]
- await_remove = self.app._remove_nodes(children_to_remove, self)
+
+ children_to_remove: Iterable[Widget]
+
+ if isinstance(selector, str):
+ parsed_selectors = parse_selectors(selector)
+ children_to_remove = [
+ child for child in self.children if match(parsed_selectors, child)
+ ]
+ else:
+ children_to_remove = selector
+ await_remove = self.app._prune(
+ *children_to_remove, parent=cast(DOMNode, self._parent)
+ )
return await_remove
@asynccontextmanager
@@ -3462,9 +3605,30 @@ def post_message(self, message: Message) -> bool:
self.log.warning(self, f"IS NOT RUNNING, {message!r} not sent")
except NoActiveAppError:
pass
-
return super().post_message(message)
+ async def on_prune(self, event: messages.Prune) -> None:
+ """Close message loop when asked to prune."""
+ await self._close_messages(wait=False)
+
+ async def _message_loop_exit(self) -> None:
+ """Clean up DOM tree."""
+ parent = self._parent
+ # Post messages to children, asking them to prune
+ children = [*self.children, *self._get_virtual_dom()]
+ for node in children:
+ node.post_message(Prune())
+
+ # Wait for child nodes to exit
+ await gather(*[node._task for node in children if node._task is not None])
+ # Send unmount event
+ await self._dispatch_message(events.Unmount())
+ assert isinstance(parent, DOMNode)
+ # Finalize removal from DOM
+ parent._nodes._remove(self)
+ self.app._registry.discard(self)
+ self._detach()
+
async def _on_idle(self, event: events.Idle) -> None:
"""Called when there are no more events on the queue.
@@ -3473,6 +3637,11 @@ async def _on_idle(self, event: events.Idle) -> None:
"""
self._check_refresh()
+ if self.is_anchored:
+ self.scroll_visible(animate=self._anchor_animate)
+ if self._anchored:
+ self._anchored.scroll_visible(animate=self._anchor_animate)
+
def _check_refresh(self) -> None:
"""Check if a refresh was requested."""
if self._parent is not None and not self._closing:
@@ -3501,7 +3670,7 @@ def focus(self, scroll_visible: bool = True) -> Self:
The `Widget` instance.
"""
- def set_focus(widget: Widget):
+ def set_focus(widget: Widget) -> None:
"""Callback to set the focus."""
try:
widget.screen.set_focus(self, scroll_visible=scroll_visible)
@@ -3569,20 +3738,20 @@ def check_message_enabled(self, message: Message) -> bool:
`True` if the message will be sent, or `False` if it is disabled.
"""
# Do the normal checking and get out if that fails.
- if not super().check_message_enabled(message):
- return False
- message_type = type(message)
- if self._is_prevented(message_type):
+ if not super().check_message_enabled(message) or self._is_prevented(
+ type(message)
+ ):
return False
+
# Mouse scroll events should always go through, this allows mouse
# wheel scrolling to pass through disabled widgets.
- if isinstance(message, (events.MouseScrollDown, events.MouseScrollUp)):
+ if isinstance(message, _MOUSE_EVENTS_ALLOW_IF_DISABLED):
return True
# Otherwise, if this is any other mouse event, the widget receiving
# the event must not be disabled at this moment.
return (
not self._self_or_ancestors_disabled
- if isinstance(message, (events.MouseEvent, events.Enter, events.Leave))
+ if isinstance(message, _MOUSE_EVENTS_DISALLOW_IF_DISABLED)
else True
)
@@ -3636,7 +3805,8 @@ async def mount_composed_widgets(self, widgets: list[Widget]) -> None:
Args:
widgets: A list of child widgets.
"""
- await self.mount_all(widgets)
+ if widgets:
+ await self.mount_all(widgets)
def _extend_compose(self, widgets: list[Widget]) -> None:
"""Hook to extend composed widgets.
@@ -3673,45 +3843,54 @@ def _on_blur(self, event: events.Blur) -> None:
def _on_mouse_scroll_down(self, event: events.MouseScrollDown) -> None:
if event.ctrl or event.shift:
if self.allow_horizontal_scroll:
+ self._clear_anchor()
if self._scroll_right_for_pointer(animate=False):
event.stop()
else:
if self.allow_vertical_scroll:
+ self._clear_anchor()
if self._scroll_down_for_pointer(animate=False):
event.stop()
def _on_mouse_scroll_up(self, event: events.MouseScrollUp) -> None:
if event.ctrl or event.shift:
if self.allow_horizontal_scroll:
+ self._clear_anchor()
if self._scroll_left_for_pointer(animate=False):
event.stop()
else:
if self.allow_vertical_scroll:
+ self._clear_anchor()
if self._scroll_up_for_pointer(animate=False):
event.stop()
def _on_scroll_to(self, message: ScrollTo) -> None:
if self._allow_scroll:
+ self._clear_anchor()
self.scroll_to(message.x, message.y, animate=message.animate, duration=0.1)
message.stop()
def _on_scroll_up(self, event: ScrollUp) -> None:
if self.allow_vertical_scroll:
+ self._clear_anchor()
self.scroll_page_up()
event.stop()
def _on_scroll_down(self, event: ScrollDown) -> None:
if self.allow_vertical_scroll:
+ self._clear_anchor()
self.scroll_page_down()
event.stop()
def _on_scroll_left(self, event: ScrollLeft) -> None:
if self.allow_horizontal_scroll:
+ self._clear_anchor()
self.scroll_page_left()
event.stop()
def _on_scroll_right(self, event: ScrollRight) -> None:
if self.allow_horizontal_scroll:
+ self._clear_anchor()
self.scroll_page_right()
event.stop()
@@ -3738,50 +3917,70 @@ def _on_unmount(self) -> None:
def action_scroll_home(self) -> None:
if not self._allow_scroll:
raise SkipAction()
+ self._clear_anchor()
self.scroll_home()
def action_scroll_end(self) -> None:
if not self._allow_scroll:
raise SkipAction()
+ self._clear_anchor()
self.scroll_end()
def action_scroll_left(self) -> None:
if not self.allow_horizontal_scroll:
raise SkipAction()
+ self._clear_anchor()
self.scroll_left()
def action_scroll_right(self) -> None:
if not self.allow_horizontal_scroll:
raise SkipAction()
+ self._clear_anchor()
self.scroll_right()
def action_scroll_up(self) -> None:
if not self.allow_vertical_scroll:
raise SkipAction()
+ self._clear_anchor()
self.scroll_up()
def action_scroll_down(self) -> None:
if not self.allow_vertical_scroll:
raise SkipAction()
+ self._clear_anchor()
self.scroll_down()
def action_page_down(self) -> None:
if not self.allow_vertical_scroll:
raise SkipAction()
+ self._clear_anchor()
self.scroll_page_down()
def action_page_up(self) -> None:
if not self.allow_vertical_scroll:
raise SkipAction()
+ self._clear_anchor()
self.scroll_page_up()
+ def action_page_left(self) -> None:
+ if not self.allow_horizontal_scroll:
+ raise SkipAction()
+ self._clear_anchor()
+ self.scroll_page_left()
+
+ def action_page_right(self) -> None:
+ if not self.allow_horizontal_scroll:
+ raise SkipAction()
+ self._clear_anchor()
+ self.scroll_page_right()
+
def notify(
self,
message: str,
*,
title: str = "",
severity: SeverityLevel = "information",
- timeout: float = Notification.timeout,
+ timeout: float | None = None,
) -> None:
"""Create a notification.
@@ -3793,9 +3992,21 @@ def notify(
message: The message for the notification.
title: The title for the notification.
severity: The severity of the notification.
- timeout: The timeout (in seconds) for the notification.
+ timeout: The timeout (in seconds) for the notification, or `None` for default.
See [`App.notify`][textual.app.App.notify] for the full
documentation for this method.
"""
- return self.app.notify(message, title=title, severity=severity, timeout=timeout)
+ if timeout is None:
+ return self.app.notify(
+ message,
+ title=title,
+ severity=severity,
+ )
+ else:
+ return self.app.notify(
+ message,
+ title=title,
+ severity=severity,
+ timeout=timeout,
+ )
diff --git a/src/textual/widgets/__init__.py b/src/textual/widgets/__init__.py
index cd6e21f13b..a9505aa0dc 100644
--- a/src/textual/widgets/__init__.py
+++ b/src/textual/widgets/__init__.py
@@ -12,6 +12,7 @@
from ..widget import Widget
from ._button import Button
from ._checkbox import Checkbox
+ from ._classic_footer import ClassicFooter
from ._collapsible import Collapsible
from ._content_switcher import ContentSwitcher
from ._data_table import DataTable
@@ -49,6 +50,7 @@
__all__ = [
"Button",
"Checkbox",
+ "ClassicFooter",
"Collapsible",
"ContentSwitcher",
"DataTable",
@@ -70,6 +72,7 @@
"ProgressBar",
"RadioButton",
"RadioSet",
+ "RichLog",
"Rule",
"Select",
"SelectionList",
@@ -81,7 +84,6 @@
"TabPane",
"Tabs",
"TextArea",
- "RichLog",
"Tooltip",
"Tree",
"Welcome",
diff --git a/src/textual/widgets/__init__.pyi b/src/textual/widgets/__init__.pyi
index d4db2f8f52..93b3af4d66 100644
--- a/src/textual/widgets/__init__.pyi
+++ b/src/textual/widgets/__init__.pyi
@@ -1,6 +1,7 @@
# This stub file must re-export every classes exposed in the __init__.py's `__all__` list:
from ._button import Button as Button
from ._checkbox import Checkbox as Checkbox
+from ._classic_footer import ClassicFooter as ClassicFooter
from ._collapsible import Collapsible as Collapsible
from ._content_switcher import ContentSwitcher as ContentSwitcher
from ._data_table import DataTable as DataTable
diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py
index 532076c9f8..7024f2bac6 100644
--- a/src/textual/widgets/_button.py
+++ b/src/textual/widgets/_button.py
@@ -141,9 +141,7 @@ class Button(Widget, can_focus=True):
border-bottom: tall $error-lighten-2;
border-top: tall $error-darken-2;
}
-
}
-
}
"""
@@ -184,6 +182,7 @@ def __init__(
id: str | None = None,
classes: str | None = None,
disabled: bool = False,
+ tooltip: RenderableType | None = None,
):
"""Create a Button widget.
@@ -194,6 +193,7 @@ def __init__(
id: The ID of the button in the DOM.
classes: The CSS classes of the button.
disabled: Whether the button is disabled or not.
+ tooltip: Optional tooltip.
"""
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
@@ -202,8 +202,10 @@ def __init__(
self.label = label
self.variant = variant
- self.active_effect_duration = 0.3
+ self.active_effect_duration = 0.2
"""Amount of time in seconds the button 'press' animation lasts."""
+ if tooltip is not None:
+ self.tooltip = tooltip
def get_content_width(self, container: Size, viewport: Size) -> int:
try:
@@ -250,13 +252,17 @@ def post_render(self, renderable: RenderableType) -> ConsoleRenderable:
async def _on_click(self, event: events.Click) -> None:
event.stop()
- self.press()
+ if not self.has_class("-active"):
+ self.press()
def press(self) -> Self:
- """Respond to a button press.
+ """Animate the button and send the [Pressed][textual.widgets.Button.Pressed] message.
+
+ Can be used to simulate the button being pressed by a user.
Returns:
- The button instance."""
+ The button instance.
+ """
if self.disabled or not self.display:
return self
# Manage the "active" effect:
@@ -275,7 +281,8 @@ def _start_active_affect(self) -> None:
def action_press(self) -> None:
"""Activate a press of the button."""
- self.press()
+ if not self.has_class("-active"):
+ self.press()
@classmethod
def success(
diff --git a/src/textual/widgets/_classic_footer.py b/src/textual/widgets/_classic_footer.py
new file mode 100644
index 0000000000..d8df547300
--- /dev/null
+++ b/src/textual/widgets/_classic_footer.py
@@ -0,0 +1,161 @@
+from __future__ import annotations
+
+from collections import defaultdict
+from typing import TYPE_CHECKING, ClassVar, Optional
+
+import rich.repr
+from rich.text import Text
+
+from .. import events
+from ..binding import Binding
+from ..reactive import reactive
+from ..widget import Widget
+
+if TYPE_CHECKING:
+ from ..app import RenderResult
+ from ..screen import Screen
+
+
+@rich.repr.auto
+class ClassicFooter(Widget):
+ """A simple footer widget which docks itself to the bottom of the parent container."""
+
+ COMPONENT_CLASSES: ClassVar[set[str]] = {
+ "footer--description",
+ "footer--key",
+ "footer--highlight",
+ "footer--highlight-key",
+ }
+ """
+ | Class | Description |
+ | :- | :- |
+ | `classic-footer--description` | Targets the descriptions of the key bindings. |
+ | `classic-footer--highlight` | Targets the highlighted key binding. |
+ | `classic-footer--highlight-key` | Targets the key portion of the highlighted key binding. |
+ | `classic-footer--key` | Targets the key portions of the key bindings. |
+ """
+
+ __name__ = "Footer"
+
+ DEFAULT_CSS = """
+ ClassicFooter {
+ background: $accent;
+ color: $text;
+ dock: bottom;
+ height: 1;
+ }
+ ClassicFooter > .footer--highlight {
+ background: $accent-darken-1;
+ }
+
+ ClassicFooter > .footer--highlight-key {
+ background: $secondary;
+ text-style: bold;
+ }
+
+ ClassicFooter > .footer--key {
+ text-style: bold;
+ background: $accent-darken-2;
+ }
+ """
+
+ highlight_key: reactive[str | None] = reactive[Optional[str]](None)
+
+ def __init__(self) -> None:
+ super().__init__()
+ self._key_text: Text | None = None
+ self.auto_links = False
+
+ async def watch_highlight_key(self) -> None:
+ """If highlight key changes we need to regenerate the text."""
+ self._key_text = None
+ self.refresh()
+
+ def _on_mount(self, _: events.Mount) -> None:
+ self.screen.bindings_updated_signal.subscribe(self, self._bindings_changed)
+ self.log.warning(
+ "ClassicFooter is deprecated and will be removed in Textual 1.0"
+ )
+
+ def _bindings_changed(self, _screen: Screen) -> None:
+ self._key_text = None
+ self.refresh()
+
+ def _on_mouse_move(self, event: events.MouseMove) -> None:
+ """Store any key we are moving over."""
+ self.highlight_key = event.style.meta.get("key")
+
+ def _on_leave(self, _: events.Leave) -> None:
+ """Clear any highlight when the mouse leaves the widget"""
+ self.highlight_key = None
+
+ def __rich_repr__(self) -> rich.repr.Result:
+ yield from super().__rich_repr__()
+
+ def _make_key_text(self) -> Text:
+ """Create text containing all the keys."""
+ base_style = self.rich_style
+ text = Text(
+ style=self.rich_style,
+ no_wrap=True,
+ overflow="ellipsis",
+ justify="left",
+ end="",
+ )
+ highlight_style = self.get_component_rich_style("footer--highlight")
+ highlight_key_style = self.get_component_rich_style("footer--highlight-key")
+ key_style = self.get_component_rich_style("footer--key")
+ description_style = self.get_component_rich_style("footer--description")
+
+ bindings = [
+ (binding, enabled)
+ for (_, binding, enabled) in self.screen.active_bindings.values()
+ if binding.show
+ ]
+
+ action_to_bindings: defaultdict[str, list[tuple[Binding, bool]]] = defaultdict(
+ list
+ )
+ for binding, enabled in bindings:
+ action_to_bindings[binding.action].append((binding, enabled))
+
+ app_focus = self.app.app_focus
+ for _, _bindings in action_to_bindings.items():
+ binding, enabled = _bindings[0]
+ if binding.key_display is None:
+ key_display = self.app.get_key_display(binding.key)
+ if key_display is None:
+ key_display = binding.key.upper()
+ else:
+ key_display = binding.key_display
+ hovered = self.highlight_key == binding.key
+ key_text = Text.assemble(
+ (f" {key_display} ", highlight_key_style if hovered else key_style),
+ (
+ f" {binding.description} ",
+ highlight_style if hovered else base_style + description_style,
+ ),
+ meta=(
+ {
+ "@click": f"app.simulate_key('{binding.key}')",
+ "key": binding.key,
+ }
+ if enabled and app_focus
+ else {}
+ ),
+ )
+ if not enabled or not app_focus:
+ key_text.stylize("dim")
+ text.append_text(key_text)
+ return text
+
+ def notify_style_update(self) -> None:
+ self._key_text = None
+
+ def post_render(self, renderable):
+ return renderable
+
+ def render(self) -> RenderResult:
+ if self._key_text is None:
+ self._key_text = self._make_key_text()
+ return self._key_text
diff --git a/src/textual/widgets/_content_switcher.py b/src/textual/widgets/_content_switcher.py
index 8e63e7a502..7077232233 100644
--- a/src/textual/widgets/_content_switcher.py
+++ b/src/textual/widgets/_content_switcher.py
@@ -4,6 +4,7 @@
from typing import Optional
+from ..await_complete import AwaitComplete
from ..containers import Container
from ..css.query import NoMatches
from ..events import Mount
@@ -98,3 +99,34 @@ def watch_current(self, old: str | None, new: str | None) -> None:
pass
if new:
self.get_child_by_id(new).display = True
+
+ def add_content(
+ self, widget: Widget, *, id: str | None = None, set_current: bool = False
+ ) -> AwaitComplete:
+ """Add new content to the `ContentSwitcher`.
+
+ Args:
+ widget: A Widget to add.
+ id: ID for the widget, or `None` if the widget already has an ID.
+ set_current: Set the new widget as current (which will cause it to display).
+
+ Returns:
+ An awaitable to wait for the new content to be mounted.
+ """
+ if id is not None and widget.id != id:
+ widget.id = id
+
+ if not widget.id:
+ raise ValueError(
+ "Widget must have an ID (or set id parameter when calling add_content)"
+ )
+
+ async def _add_content() -> None:
+ """Add new widget and potentially change the current widget."""
+ widget.display = False
+ with self.app.batch_update():
+ await self.mount(widget)
+ if set_current:
+ self.current = widget.id
+
+ return AwaitComplete(_add_content())
diff --git a/src/textual/widgets/_data_table.py b/src/textual/widgets/_data_table.py
index b695c83529..107635e9e9 100644
--- a/src/textual/widgets/_data_table.py
+++ b/src/textual/widgets/_data_table.py
@@ -4,7 +4,7 @@
from dataclasses import dataclass
from itertools import chain, zip_longest
from operator import itemgetter
-from typing import Any, Callable, ClassVar, Generic, Iterable, NamedTuple, TypeVar, cast
+from typing import Any, Callable, ClassVar, Generic, Iterable, NamedTuple, TypeVar
import rich.repr
from rich.console import RenderableType
@@ -49,6 +49,8 @@
_DEFAULT_CELL_X_PADDING = 1
"""Default padding to use on each side of a column in the data table."""
+_EMPTY_TEXT = Text(no_wrap=True, end="")
+
class CellDoesNotExist(Exception):
"""The cell key/index was invalid.
@@ -152,22 +154,68 @@ def __rich_repr__(self):
yield "column_key", self.column_key
-def default_cell_formatter(obj: object) -> RenderableType:
+def _find_newline(string: str, number: int) -> int:
+ """Find newline number n (the nth newline) in a string.
+
+ Args:
+ string: The string to search.
+ number: The nth newline character to find.
+
+ Returns:
+ The index of the nth newline character, or -1 if not found.
+ """
+ if not string or number < 1:
+ return -1
+
+ pos = -1
+ for _ in range(number):
+ pos = string.find("\n", pos + 1)
+ if pos == -1:
+ break
+ return pos
+
+
+def default_cell_formatter(
+ obj: object, wrap: bool = True, height: int = 0
+) -> RenderableType:
"""Convert a cell into a Rich renderable for display.
Args:
obj: Data for a cell.
+ wrap: Enable or disable wrapping inside the cell.
+ height: The height of the cell, or `None` to render the entire cell.
+ This can be used to short-circuit rendering. e.g. If we know the cell
+ has a height of 1, we can render the cell as a single line of text
+ without any wrapping.
Returns:
A renderable to be displayed which represents the data.
"""
+ # Get the string which will be displayed in the cell.
+ possible_markup = False
if isinstance(obj, str):
- return Text.from_markup(obj)
- if isinstance(obj, float):
- return f"{obj:.2f}"
- if not is_renderable(obj):
- return str(obj)
- return cast(RenderableType, obj)
+ possible_markup = True
+ content = obj
+ elif isinstance(obj, float):
+ content = f"{obj:.2f}"
+ elif not is_renderable(obj):
+ content = str(obj)
+ else:
+ return obj
+
+ if height:
+ # Let's throw away lines which definitely won't appear in the cell
+ # after wrapping using the height constraint. A cell can only grow
+ # vertically after wrapping occurs, so this is a safe operation.
+ trim_position = _find_newline(content, height)
+ if trim_position != -1 and trim_position != len(content) - 1:
+ content = content[:trim_position]
+
+ if possible_markup:
+ text = Text.from_markup(content, end="")
+ text.no_wrap = not wrap
+ return text
+ return Text(content, no_wrap=not wrap, end="")
@dataclass
@@ -222,6 +270,10 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
Binding("left", "cursor_left", "Cursor Left", show=False),
Binding("pageup", "page_up", "Page Up", show=False),
Binding("pagedown", "page_down", "Page Down", show=False),
+ Binding("ctrl+home", "scroll_top", "Top", show=False),
+ Binding("ctrl+end", "scroll_bottom", "Bottom", show=False),
+ Binding("home", "scroll_home", "Home", show=False),
+ Binding("end", "scroll_end", "End", show=False),
]
"""
| Key(s) | Description |
@@ -231,6 +283,12 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
| down | Move the cursor down. |
| right | Move the cursor right. |
| left | Move the cursor left. |
+ | pageup | Move one page up. |
+ | pagedown | Move one page down. |
+ | ctrl+home | Move to the top. |
+ | ctrl+end | Move to the bottom. |
+ | home | Move to the home position (leftmost column). |
+ | end | Move to the end position (rightmost column). |
"""
COMPONENT_CLASSES: ClassVar[set[str]] = {
@@ -254,8 +312,8 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
| `datatable--header` | Target the header of the data table. |
| `datatable--header-cursor` | Target cells highlighted by the cursor. |
| `datatable--header-hover` | Target hovered header or row label cells. |
- | `datatable--even-row` | Target even rows (row indices start at 0). |
- | `datatable--odd-row` | Target odd rows (row indices start at 0). |
+ | `datatable--even-row` | Target even rows (row indices start at 0) if zebra_stripes. |
+ | `datatable--odd-row` | Target odd rows (row indices start at 0) if zebra_stripes. |
"""
DEFAULT_CSS = """
@@ -710,7 +768,8 @@ def __init__(
self.fixed_columns = fixed_columns
"""The number of columns to fix (prevented from scrolling)."""
self.zebra_stripes = zebra_stripes
- """Apply zebra effect on row backgrounds (light, dark, light, dark, ...)."""
+ """Apply alternating styles, datatable--even-row and datatable-odd-row, to create a zebra effect, e.g.,
+ alternating light and dark backgrounds."""
self.show_cursor = show_cursor
"""Show/hide both the keyboard and hover cursor."""
self.cursor_foreground_priority = cursor_foreground_priority
@@ -755,7 +814,7 @@ def _y_offsets(self) -> list[tuple[RowKey, int]]:
y-coordinate, we can index into this list to find which row that y-coordinate
lands on, and the y-offset *within* that row. The length of the returned list
is therefore the total height of all rows within the DataTable."""
- y_offsets = []
+ y_offsets: list[tuple[RowKey, int]] = []
if self._update_count in self._offset_cache:
y_offsets = self._offset_cache[self._update_count]
else:
@@ -1021,7 +1080,11 @@ def get_row_height(self, row_key: RowKey) -> int:
return self.rows[row_key].height
def notify_style_update(self) -> None:
- self._clear_caches()
+ self._row_render_cache.clear()
+ self._cell_render_cache.clear()
+ self._line_cache.clear()
+ self._styles_cache.clear()
+ self._get_styles_to_render_cell.cache_clear()
self.refresh()
def _on_resize(self, _: events.Resize) -> None:
@@ -1096,9 +1159,11 @@ def watch_cursor_coordinate(
elif self.cursor_type == "column":
self.refresh_column(old_coordinate.column)
self._highlight_column(new_coordinate.column)
- # If the coordinate was changed via `move_cursor`, give priority to its
- # scrolling because it may be animated.
- self.call_after_refresh(self._scroll_cursor_into_view)
+
+ if self._require_update_dimensions:
+ self.call_after_refresh(self._scroll_cursor_into_view)
+ else:
+ self._scroll_cursor_into_view()
def move_cursor(
self,
@@ -1106,6 +1171,7 @@ def move_cursor(
row: int | None = None,
column: int | None = None,
animate: bool = False,
+ scroll: bool = True,
) -> None:
"""Move the cursor to the given position.
@@ -1122,6 +1188,7 @@ def move_cursor(
row: The new row to move the cursor to.
column: The new column to move the cursor to.
animate: Whether to animate the change of coordinates.
+ scroll: Scroll the cursor into view after moving.
"""
cursor_row, cursor_column = self.cursor_coordinate
@@ -1137,7 +1204,11 @@ def move_cursor(
# of rows then tried to immediately move the cursor.
# We do this before setting `cursor_coordinate` because its watcher will also
# schedule a call to `_scroll_cursor_into_view` without optionally animating.
- self.call_after_refresh(self._scroll_cursor_into_view, animate=animate)
+ if scroll:
+ if self._require_update_dimensions:
+ self.call_after_refresh(self._scroll_cursor_into_view, animate=animate)
+ else:
+ self._scroll_cursor_into_view(animate=animate)
self.cursor_coordinate = destination
@@ -1249,19 +1320,37 @@ def _update_column_widths(self, updated_cells: set[CellKey]) -> None:
"""Update the widths of the columns based on the newly updated cell widths."""
for row_key, column_key in updated_cells:
column = self.columns.get(column_key)
- if column is None:
+ row = self.rows.get(row_key)
+ if column is None or row is None:
continue
console = self.app.console
label_width = measure(console, column.label, 1)
content_width = column.content_width
cell_value = self._data[row_key][column_key]
- new_content_width = measure(console, default_cell_formatter(cell_value), 1)
+ render_height = row.height
+ new_content_width = measure(
+ console,
+ default_cell_formatter(
+ cell_value,
+ wrap=row.height != 1,
+ height=render_height,
+ ),
+ 1,
+ )
if new_content_width < content_width:
cells_in_column = self.get_column(column_key)
cell_widths = [
- measure(console, default_cell_formatter(cell), 1)
+ measure(
+ console,
+ default_cell_formatter(
+ cell,
+ wrap=row.height != 1,
+ height=render_height,
+ ),
+ 1,
+ )
for cell in cells_in_column
]
column.content_width = max([*cell_widths, label_width])
@@ -1522,6 +1611,7 @@ def add_column(
self._updated_cells.add(CellKey(row_key, column_key))
self._require_update_dimensions = True
+ self._update_count += 1
self.check_idle()
return column_key
@@ -1556,6 +1646,9 @@ def add_row(
# If we don't do this, users will be required to call add_column(s)
# Before they call add_row.
+ if len(cells) > len(self.ordered_columns):
+ raise ValueError("More values provided than there are columns.")
+
row_index = self.row_count
# Map the key of this row to its current index
self._row_locations[row_key] = row_index
@@ -1563,7 +1656,9 @@ def add_row(
column.key: cell
for column, cell in zip_longest(self.ordered_columns, cells)
}
- label = Text.from_markup(label) if isinstance(label, str) else label
+
+ label = Text.from_markup(label, end="") if isinstance(label, str) else label
+
# Rows with auto-height get a height of 0 because 1) we need an integer height
# to do some intermediate computations and 2) because 0 doesn't impact the data
# table while we don't figure out how tall this row is.
@@ -1873,17 +1968,35 @@ def _get_row_renderables(self, row_index: int) -> RowRenderables:
return RowRenderables(None, header_row)
ordered_row = self.get_row_at(row_index)
- empty = Text()
-
- formatted_row_cells = [
- Text() if datum is None else default_cell_formatter(datum) or empty
+ row_key = self._row_locations.get_key(row_index)
+ if row_key is None:
+ return RowRenderables(None, [])
+ row_metadata = self.rows.get(row_key)
+ if row_metadata is None:
+ return RowRenderables(None, [])
+
+ formatted_row_cells: list[RenderableType] = [
+ (
+ _EMPTY_TEXT
+ if datum is None
+ else default_cell_formatter(
+ datum,
+ wrap=row_metadata.height != 1,
+ height=row_metadata.height,
+ )
+ or _EMPTY_TEXT
+ )
for datum, _ in zip_longest(ordered_row, range(len(self.columns)))
]
+
label = None
if self._should_render_row_labels:
- row_metadata = self.rows.get(self._row_locations.get_key(row_index))
label = (
- default_cell_formatter(row_metadata.label)
+ default_cell_formatter(
+ row_metadata.label,
+ wrap=row_metadata.height != 1,
+ height=row_metadata.height,
+ )
if row_metadata.label
else None
)
@@ -1959,19 +2072,25 @@ def _render_cell(
)
if is_header_cell:
- options = self.app.console.options.update_dimensions(
- width, self.header_height
- )
+ row_height = self.header_height
+ options = self.app.console.options.update_dimensions(width, row_height)
else:
- row = self.rows[row_key]
# If an auto-height row hasn't had its height calculated, we don't fix
# the value for `height` so that we can measure the height of the cell.
+ row = self.rows[row_key]
if row.auto_height and row.height == 0:
+ row_height = 0
options = self.app.console.options.update_width(width)
else:
+ row_height = row.height
options = self.app.console.options.update_dimensions(
- width, row.height
+ width, row_height
)
+
+ # If the row height is explicitly set to 1, then we don't wrap.
+ if row_height == 1:
+ options = options.update(no_wrap=True)
+
lines = self.app.console.render_lines(
Styled(
Padding(cell, (0, self.cell_padding)),
@@ -2418,7 +2537,7 @@ def _scroll_cursor_into_view(self, animate: bool = False) -> None:
else:
region = self._get_cell_region(self.cursor_coordinate)
- self.scroll_to_region(region, animate=animate, spacing=fixed_offset)
+ self.scroll_to_region(region, animate=animate, spacing=fixed_offset, force=True)
def _set_hover_cursor(self, active: bool) -> None:
"""Set whether the hover cursor (the faint cursor you see when you
@@ -2441,7 +2560,7 @@ def _set_hover_cursor(self, active: bool) -> None:
async def _on_click(self, event: events.Click) -> None:
self._set_hover_cursor(True)
meta = event.style.meta
- if not "row" in meta or not "column" in meta:
+ if "row" not in meta or "column" not in meta:
return
row_index = meta["row"]
@@ -2472,21 +2591,23 @@ def action_page_down(self) -> None:
"""Move the cursor one page down."""
self._set_hover_cursor(False)
if self.show_cursor and self.cursor_type in ("cell", "row"):
- height = self.size.height - (self.header_height if self.show_header else 0)
+ height = self.scrollable_content_region.height - (
+ self.header_height if self.show_header else 0
+ )
# Determine how many rows constitutes a "page"
offset = 0
rows_to_scroll = 0
- row_index, column_index = self.cursor_coordinate
+ row_index, _ = self.cursor_coordinate
for ordered_row in self.ordered_rows[row_index:]:
offset += ordered_row.height
+ rows_to_scroll += 1
if offset > height:
break
- rows_to_scroll += 1
- self.cursor_coordinate = Coordinate(
- row_index + rows_to_scroll - 1, column_index
- )
+ target_row = row_index + rows_to_scroll - 1
+ self.scroll_relative(y=height, animate=False, force=True)
+ self.move_cursor(row=target_row, scroll=False)
else:
super().action_page_down()
@@ -2494,44 +2615,74 @@ def action_page_up(self) -> None:
"""Move the cursor one page up."""
self._set_hover_cursor(False)
if self.show_cursor and self.cursor_type in ("cell", "row"):
- height = self.size.height - (self.header_height if self.show_header else 0)
+ height = self.scrollable_content_region.height - (
+ self.header_height if self.show_header else 0
+ )
# Determine how many rows constitutes a "page"
offset = 0
rows_to_scroll = 0
- row_index, column_index = self.cursor_coordinate
+ row_index, _ = self.cursor_coordinate
for ordered_row in self.ordered_rows[: row_index + 1]:
offset += ordered_row.height
+ rows_to_scroll += 1
if offset > height:
break
- rows_to_scroll += 1
- self.cursor_coordinate = Coordinate(
- row_index - rows_to_scroll + 1, column_index
- )
+ target_row = row_index - rows_to_scroll + 1
+ self.scroll_relative(y=-height, animate=False)
+ self.move_cursor(row=target_row, scroll=False)
else:
super().action_page_up()
- def action_scroll_home(self) -> None:
- """Scroll to the top of the data table."""
+ def action_page_left(self) -> None:
+ """Move the cursor one page left."""
+ self._set_hover_cursor(False)
+ super().scroll_page_left()
+
+ def action_page_right(self) -> None:
+ """Move the cursor one page right."""
+ self._set_hover_cursor(False)
+ super().scroll_page_right()
+
+ def action_scroll_top(self) -> None:
+ """Move the cursor and scroll to the top."""
self._set_hover_cursor(False)
cursor_type = self.cursor_type
if self.show_cursor and (cursor_type == "cell" or cursor_type == "row"):
- row_index, column_index = self.cursor_coordinate
+ _, column_index = self.cursor_coordinate
self.cursor_coordinate = Coordinate(0, column_index)
else:
super().action_scroll_home()
- def action_scroll_end(self) -> None:
- """Scroll to the bottom of the data table."""
+ def action_scroll_bottom(self) -> None:
+ """Move the cursor and scroll to the bottom."""
self._set_hover_cursor(False)
cursor_type = self.cursor_type
if self.show_cursor and (cursor_type == "cell" or cursor_type == "row"):
- row_index, column_index = self.cursor_coordinate
+ _, column_index = self.cursor_coordinate
self.cursor_coordinate = Coordinate(self.row_count - 1, column_index)
else:
super().action_scroll_end()
+ def action_scroll_home(self) -> None:
+ """Move the cursor and scroll to the leftmost column."""
+ self._set_hover_cursor(False)
+ cursor_type = self.cursor_type
+ if self.show_cursor and (cursor_type == "cell" or cursor_type == "column"):
+ self.move_cursor(column=0)
+ else:
+ self.scroll_x = 0
+
+ def action_scroll_end(self) -> None:
+ """Move the cursor and scroll to the rightmost column."""
+ self._set_hover_cursor(False)
+ cursor_type = self.cursor_type
+ if self.show_cursor and (cursor_type == "cell" or cursor_type == "column"):
+ self.move_cursor(column=len(self.columns) - 1)
+ else:
+ self.scroll_x = self.max_scroll_x
+
def action_cursor_up(self) -> None:
self._set_hover_cursor(False)
cursor_type = self.cursor_type
diff --git a/src/textual/widgets/_footer.py b/src/textual/widgets/_footer.py
index 5f3c60d925..09a743ac48 100644
--- a/src/textual/widgets/_footer.py
+++ b/src/textual/widgets/_footer.py
@@ -1,147 +1,200 @@
from __future__ import annotations
from collections import defaultdict
-from typing import TYPE_CHECKING, ClassVar, Optional
+from typing import TYPE_CHECKING
import rich.repr
from rich.text import Text
-from .. import events
-
-if TYPE_CHECKING:
- from ..app import RenderResult
-
+from ..app import ComposeResult
+from ..binding import Binding
+from ..containers import ScrollableContainer
from ..reactive import reactive
from ..widget import Widget
+if TYPE_CHECKING:
+ from ..screen import Screen
+
@rich.repr.auto
-class Footer(Widget):
- """A simple footer widget which docks itself to the bottom of the parent container."""
-
- COMPONENT_CLASSES: ClassVar[set[str]] = {
- "footer--description",
- "footer--key",
- "footer--highlight",
- "footer--highlight-key",
+class FooterKey(Widget):
+ COMPONENT_CLASSES = {
+ "footer-key--key",
+ "footer-key--description",
+ }
+
+ DEFAULT_CSS = """
+ FooterKey {
+ width: auto;
+ height: 1;
+ background: $panel;
+ color: $text-muted;
+ .footer-key--key {
+ color: $secondary;
+ background: $panel;
+ text-style: bold;
+ padding: 0 1;
+ }
+
+ .footer-key--description {
+ padding: 0 1 0 0;
+ }
+
+ &:light .footer-key--key {
+ color: $primary;
+ }
+
+ &:hover {
+ background: $panel-darken-2;
+ color: $text;
+ .footer-key--key {
+ background: $panel-darken-2;
+ }
+ }
+
+ &.-disabled {
+ text-style: dim;
+ background: $panel;
+ &:hover {
+ .footer-key--key {
+ background: $panel;
+ }
+ }
+ }
+
+ &.-compact {
+ .footer-key--key {
+ padding: 0;
+ }
+ .footer-key--description {
+ padding: 0 0 0 1;
+ }
+ }
}
"""
- | Class | Description |
- | :- | :- |
- | `footer--description` | Targets the descriptions of the key bindings. |
- | `footer--highlight` | Targets the highlighted key binding. |
- | `footer--highlight-key` | Targets the key portion of the highlighted key binding. |
- | `footer--key` | Targets the key portions of the key bindings. |
- """
+ upper_case_keys = reactive(False)
+ ctrl_to_caret = reactive(True)
+ compact = reactive(True)
+
+ def __init__(
+ self,
+ key: str,
+ key_display: str,
+ description: str,
+ action: str,
+ disabled: bool = False,
+ ) -> None:
+ self.key = key
+ self.key_display = key_display
+ self.description = description
+ self.action = action
+ self._disabled = disabled
+ super().__init__(classes="-disabled" if disabled else "")
+
+ def render(self) -> Text:
+ key_style = self.get_component_rich_style("footer-key--key")
+ description_style = self.get_component_rich_style("footer-key--description")
+ key_display = self.key_display
+ key_padding = self.get_component_styles("footer-key--key").padding
+ description_padding = self.get_component_styles(
+ "footer-key--description"
+ ).padding
+ if self.upper_case_keys:
+ key_display = key_display.upper()
+ if self.ctrl_to_caret and key_display.lower().startswith("ctrl+"):
+ key_display = "^" + key_display.split("+", 1)[1]
+ description = self.description
+ label_text = Text.assemble(
+ (
+ " " * key_padding.left + key_display + " " * key_padding.right,
+ key_style,
+ ),
+ (
+ " " * description_padding.left
+ + description
+ + " " * description_padding.right,
+ description_style,
+ ),
+ )
+ label_text.stylize_before(self.rich_style)
+ return label_text
+
+ async def on_mouse_down(self) -> None:
+ if self._disabled:
+ self.app.bell()
+ else:
+ self.app.simulate_key(self.key)
+
+ def _watch_compact(self, compact: bool) -> None:
+ self.set_class(compact, "-compact")
+
+
+@rich.repr.auto
+class Footer(ScrollableContainer, can_focus=False, can_focus_children=False):
DEFAULT_CSS = """
Footer {
- background: $accent;
+ layout: grid;
+ grid-columns: auto;
+ background: $panel;
color: $text;
dock: bottom;
height: 1;
- }
- Footer > .footer--highlight {
- background: $accent-darken-1;
- }
-
- Footer > .footer--highlight-key {
- background: $secondary;
- text-style: bold;
- }
-
- Footer > .footer--key {
- text-style: bold;
- background: $accent-darken-2;
+ scrollbar-size: 0 0;
+ &.-compact {
+ grid-gutter: 1;
+ }
}
"""
- highlight_key: reactive[str | None] = reactive[Optional[str]](None)
-
- def __init__(self) -> None:
- super().__init__()
- self._key_text: Text | None = None
- self.auto_links = False
-
- async def watch_highlight_key(self) -> None:
- """If highlight key changes we need to regenerate the text."""
- self._key_text = None
- self.refresh()
-
- def _on_mount(self, _: events.Mount) -> None:
- self.watch(self.screen, "focused", self._bindings_changed)
- self.watch(self.screen, "stack_updates", self._bindings_changed)
-
- def _bindings_changed(self, _: Widget | None) -> None:
- self._key_text = None
- self.refresh()
-
- def _on_mouse_move(self, event: events.MouseMove) -> None:
- """Store any key we are moving over."""
- self.highlight_key = event.style.meta.get("key")
-
- def _on_leave(self, _: events.Leave) -> None:
- """Clear any highlight when the mouse leaves the widget"""
- self.highlight_key = None
-
- def __rich_repr__(self) -> rich.repr.Result:
- yield from super().__rich_repr__()
-
- def _make_key_text(self) -> Text:
- """Create text containing all the keys."""
- base_style = self.rich_style
- text = Text(
- style=self.rich_style,
- no_wrap=True,
- overflow="ellipsis",
- justify="left",
- end="",
- )
- highlight_style = self.get_component_rich_style("footer--highlight")
- highlight_key_style = self.get_component_rich_style("footer--highlight-key")
- key_style = self.get_component_rich_style("footer--key")
- description_style = self.get_component_rich_style("footer--description")
-
+ upper_case_keys = reactive(False)
+ """Upper case key display."""
+ ctrl_to_caret = reactive(True)
+ """Convert 'ctrl+' prefix to '^'."""
+ compact = reactive(False)
+ """Display in compact style."""
+ _bindings_ready = reactive(False, repaint=False)
+ """True if the bindings are ready to be displayed."""
+
+ def compose(self) -> ComposeResult:
+ if not self._bindings_ready:
+ return
bindings = [
- binding
- for (_, binding) in self.app.namespace_bindings.values()
+ (binding, enabled)
+ for (_, binding, enabled) in self.screen.active_bindings.values()
if binding.show
]
-
- action_to_bindings = defaultdict(list)
- for binding in bindings:
- action_to_bindings[binding.action].append(binding)
-
- for _, bindings in action_to_bindings.items():
- binding = bindings[0]
- if binding.key_display is None:
- key_display = self.app.get_key_display(binding.key)
- if key_display is None:
- key_display = binding.key.upper()
- else:
- key_display = binding.key_display
- hovered = self.highlight_key == binding.key
- key_text = Text.assemble(
- (f" {key_display} ", highlight_key_style if hovered else key_style),
- (
- f" {binding.description} ",
- highlight_style if hovered else base_style + description_style,
- ),
- meta={
- "@click": f"app.check_bindings('{binding.key}')",
- "key": binding.key,
- },
+ action_to_bindings: defaultdict[str, list[tuple[Binding, bool]]] = defaultdict(
+ list
+ )
+ for binding, enabled in bindings:
+ action_to_bindings[binding.action].append((binding, enabled))
+
+ self.styles.grid_size_columns = len(action_to_bindings)
+ for multi_bindings in action_to_bindings.values():
+ binding, enabled = multi_bindings[0]
+ yield FooterKey(
+ binding.key,
+ binding.key_display or self.app.get_key_display(binding.key),
+ binding.description,
+ binding.action,
+ disabled=not enabled,
+ ).data_bind(
+ Footer.upper_case_keys,
+ Footer.ctrl_to_caret,
+ Footer.compact,
)
- text.append_text(key_text)
- return text
- def notify_style_update(self) -> None:
- self._key_text = None
+ def on_mount(self) -> None:
+ async def bindings_changed(screen: Screen) -> None:
+ self._bindings_ready = True
+ if self.is_attached and screen is self.screen:
+ await self.recompose()
+
+ self.screen.bindings_updated_signal.subscribe(self, bindings_changed)
- def post_render(self, renderable):
- return renderable
+ def on_unmount(self) -> None:
+ self.screen.bindings_updated_signal.unsubscribe(self)
- def render(self) -> RenderResult:
- if self._key_text is None:
- self._key_text = self._make_key_text()
- return self._key_text
+ def watch_compact(self, compact: bool) -> None:
+ self.set_class(compact, "-compact")
diff --git a/src/textual/widgets/_header.py b/src/textual/widgets/_header.py
index 62ebe8879c..3505514813 100644
--- a/src/textual/widgets/_header.py
+++ b/src/textual/widgets/_header.py
@@ -7,6 +7,7 @@
from rich.text import Text
from ..app import RenderResult
+from ..dom import NoScreen
from ..events import Click, Mount
from ..reactive import Reactive
from ..widget import Widget
@@ -34,7 +35,7 @@ class HeaderIcon(Widget):
async def on_click(self, event: Click) -> None:
"""Launch the command palette when icon is clicked."""
event.stop()
- await self.run_action("command_palette")
+ await self.run_action("app.command_palette")
def render(self) -> RenderResult:
"""Render the header icon.
@@ -77,6 +78,8 @@ class HeaderClock(HeaderClockSpace):
}
"""
+ time_format: Reactive[str] = Reactive("%X")
+
def _on_mount(self, _: Mount) -> None:
self.set_interval(1, callback=self.refresh, name=f"update header clock")
@@ -86,7 +89,7 @@ def render(self) -> RenderResult:
Returns:
The rendered clock.
"""
- return Text(datetime.now().time().strftime("%X"))
+ return Text(datetime.now().time().strftime(self.time_format))
class HeaderTitle(Widget):
@@ -139,6 +142,12 @@ class Header(Widget):
tall: Reactive[bool] = Reactive(False)
"""Set to `True` for a taller header or `False` for a single line header."""
+ icon: Reactive[str] = Reactive("⭘")
+ """A character for the icon at the top left."""
+
+ time_format: Reactive[str] = Reactive("%X")
+ """Time format of the clock."""
+
def __init__(
self,
show_clock: bool = False,
@@ -146,6 +155,8 @@ def __init__(
name: str | None = None,
id: str | None = None,
classes: str | None = None,
+ icon: str | None = None,
+ time_format: str | None = None,
):
"""Initialise the header widget.
@@ -154,14 +165,24 @@ def __init__(
name: The name of the header widget.
id: The ID of the header widget in the DOM.
classes: The CSS classes of the header widget.
+ icon: Single character to use as an icon, or `None` for default.
+ time_format: Time format (used by strftime) for clock, or `None` for default.
"""
super().__init__(name=name, id=id, classes=classes)
self._show_clock = show_clock
+ if icon is not None:
+ self.icon = icon
+ if time_format is not None:
+ self.time_format = time_format
def compose(self):
- yield HeaderIcon()
+ yield HeaderIcon().data_bind(Header.icon)
yield HeaderTitle()
- yield HeaderClock() if self._show_clock else HeaderClockSpace()
+ yield (
+ HeaderClock().data_bind(Header.time_format)
+ if self._show_clock
+ else HeaderClockSpace()
+ )
def watch_tall(self, tall: bool) -> None:
self.set_class(tall, "-tall")
@@ -193,10 +214,16 @@ def screen_sub_title(self) -> str:
def _on_mount(self, _: Mount) -> None:
async def set_title() -> None:
- self.query_one(HeaderTitle).text = self.screen_title
+ try:
+ self.query_one(HeaderTitle).text = self.screen_title
+ except NoScreen:
+ pass
async def set_sub_title() -> None:
- self.query_one(HeaderTitle).sub_text = self.screen_sub_title
+ try:
+ self.query_one(HeaderTitle).sub_text = self.screen_sub_title
+ except NoScreen:
+ pass
self.watch(self.app, "title", set_title)
self.watch(self.app, "sub_title", set_sub_title)
diff --git a/src/textual/widgets/_input.py b/src/textual/widgets/_input.py
index 0b3ae57ee1..8e7ba91381 100644
--- a/src/textual/widgets/_input.py
+++ b/src/textual/widgets/_input.py
@@ -5,7 +5,7 @@
from typing import TYPE_CHECKING, ClassVar, Iterable
from rich.cells import cell_len, get_character_cell_size
-from rich.console import Console, ConsoleOptions
+from rich.console import Console, ConsoleOptions, RenderableType
from rich.console import RenderResult as RichRenderResult
from rich.highlighter import Highlighter
from rich.segment import Segment
@@ -257,6 +257,7 @@ def __init__(
id: str | None = None,
classes: str | None = None,
disabled: bool = False,
+ tooltip: RenderableType | None = None,
) -> None:
"""Initialise the `Input` widget.
@@ -279,6 +280,7 @@ def __init__(
id: Optional ID for the widget.
classes: Optional initial classes for the widget.
disabled: Whether the input is disabled or not.
+ tooltip: Optional tooltip.
"""
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
@@ -335,6 +337,8 @@ def __init__(
if value is not None:
self.value = value
+ if tooltip is not None:
+ self.tooltip = tooltip
def _position_to_cell(self, position: int) -> int:
"""Convert an index within the value to cell position."""
diff --git a/src/textual/widgets/_label.py b/src/textual/widgets/_label.py
index d890d333bd..50ef2d28ab 100644
--- a/src/textual/widgets/_label.py
+++ b/src/textual/widgets/_label.py
@@ -10,5 +10,6 @@ class Label(Static):
Label {
width: auto;
height: auto;
+ min-height: 1;
}
"""
diff --git a/src/textual/widgets/_list_view.py b/src/textual/widgets/_list_view.py
index 69b1039bb3..a4243cdc87 100644
--- a/src/textual/widgets/_list_view.py
+++ b/src/textual/widgets/_list_view.py
@@ -119,9 +119,10 @@ def __init__(
)
# Set the index to the given initial index, or the first available index after.
self._index = _widget_navigation.find_next_enabled(
- self._nodes,
- anchor=initial_index - 1 if initial_index is not None else None,
+ children,
+ anchor=initial_index if initial_index is not None else None,
direction=1,
+ with_anchor=True,
)
def _on_mount(self, _: Mount) -> None:
@@ -216,6 +217,51 @@ def clear(self) -> AwaitRemove:
self.index = None
return await_remove
+ def insert(self, index: int, items: Iterable[ListItem]) -> AwaitMount:
+ """Insert new ListItem(s) to specified index.
+
+ Args:
+ index: index to insert new ListItem.
+ items: The ListItems to insert.
+
+ Returns:
+ An awaitable that yields control to the event loop
+ until the DOM has been updated with the new child item.
+ """
+ await_mount = self.mount(*items, before=index)
+ return await_mount
+
+ def pop(self, index: Optional[int] = None) -> AwaitRemove:
+ """Remove last ListItem from ListView or
+ Remove ListItem from ListView by index
+
+ Args:
+ index: index of ListItem to remove from ListView
+
+ Returns:
+ An awaitable that yields control to the event loop until
+ the DOM has been updated to reflect item being removed.
+ """
+ if index is None:
+ await_remove = self.query("ListItem").last().remove()
+ else:
+ await_remove = self.query("ListItem")[index].remove()
+ return await_remove
+
+ def remove_items(self, indices: Iterable[int]) -> AwaitRemove:
+ """Remove ListItems from ListView by indices
+
+ Args:
+ indices: index(s) of ListItems to remove from ListView
+
+ Returns:
+ An awaitable object that waits for the direct children to be removed.
+ """
+ items = self.query("ListItem")
+ items_to_remove = [items[index] for index in indices]
+ await_remove = self.remove_children(items_to_remove)
+ return await_remove
+
def action_select_cursor(self) -> None:
"""Select the current item in the list."""
selected_child = self.highlighted_child
diff --git a/src/textual/widgets/_markdown.py b/src/textual/widgets/_markdown.py
index 849e291bd4..6ff61de4c2 100644
--- a/src/textual/widgets/_markdown.py
+++ b/src/textual/widgets/_markdown.py
@@ -1,8 +1,11 @@
from __future__ import annotations
+import asyncio
import re
+from functools import partial
from pathlib import Path, PurePath
from typing import Callable, Iterable, Optional
+from urllib.parse import unquote
from markdown_it import MarkdownIt
from markdown_it.token import Token
@@ -48,6 +51,16 @@ def location(self) -> Path:
return Path(".")
return self.stack[self.index]
+ @property
+ def start(self) -> bool:
+ """Is the current location at the start of the stack?"""
+ return self.index == 0
+
+ @property
+ def end(self) -> bool:
+ """Is the current location at the end of the stack?"""
+ return self.index >= len(self.stack) - 1
+
def go(self, path: str | PurePath) -> Path:
"""Go to a new document.
@@ -221,6 +234,8 @@ class MarkdownHeader(MarkdownBlock):
DEFAULT_CSS = """
MarkdownHeader {
color: $text;
+ margin: 2 0 1 0;
+
}
"""
@@ -231,13 +246,10 @@ class MarkdownH1(MarkdownHeader):
DEFAULT_CSS = """
MarkdownH1 {
- background: $accent-darken-2;
- border: wide $background;
content-align: center middle;
-
- padding: 1;
text-style: bold;
- color: $text;
+ color: $success;
+ &:light {color: $primary;}
}
"""
@@ -248,13 +260,9 @@ class MarkdownH2(MarkdownHeader):
DEFAULT_CSS = """
MarkdownH2 {
- background: $panel;
- border: wide $background;
- text-align: center;
text-style: underline;
- color: $text;
- padding: 1;
- text-style: bold;
+ color: $success;
+ &:light {color: $primary;}
}
"""
@@ -264,11 +272,11 @@ class MarkdownH3(MarkdownHeader):
DEFAULT_CSS = """
MarkdownH3 {
- background: $surface;
text-style: bold;
- color: $text;
- border-bottom: wide $foreground;
+ color: $success;
+ margin: 1 0;
width: auto;
+ &:light {color: $primary;}
}
"""
@@ -278,8 +286,9 @@ class MarkdownH4(MarkdownHeader):
DEFAULT_CSS = """
MarkdownH4 {
- text-style: underline;
+ text-style: bold underline;
margin: 1 0;
+ color: $text;
}
"""
@@ -292,6 +301,7 @@ class MarkdownH5(MarkdownHeader):
text-style: bold;
color: $text;
margin: 1 0;
+
}
"""
@@ -338,10 +348,13 @@ class MarkdownBlockQuote(MarkdownBlock):
DEFAULT_CSS = """
MarkdownBlockQuote {
background: $boost;
- border-left: outer $success;
+ border-left: outer $success-darken-2;
margin: 1 0;
padding: 0 1;
}
+ MarkdownBlockQuote:light {
+ border-left: outer $primary;
+ }
MarkdownBlockQuote > BlockQuote {
margin-left: 2;
margin-top: 1;
@@ -475,6 +488,11 @@ def render(self) -> Table:
table.add_row(*row)
return table
+ async def action_link(self, href: str) -> None:
+ """Pass a link action on to the MarkdownTable parent."""
+ if isinstance(self.parent, MarkdownTable):
+ await self.parent.action_link(href)
+
class MarkdownTable(MarkdownBlock):
"""A Table markdown Block."""
@@ -482,9 +500,7 @@ class MarkdownTable(MarkdownBlock):
DEFAULT_CSS = """
MarkdownTable {
width: 100%;
- margin: 1 0;
background: $panel;
- border: wide $background;
}
"""
@@ -537,10 +553,13 @@ class MarkdownBullet(Widget):
width: auto;
color: $success;
text-style: bold;
+ &:light {
+ color: $primary;
+ }
}
"""
- symbol = reactive("\u25CF")
+ symbol = reactive("\u25cf")
"""The symbol for the bullet."""
def render(self) -> Text:
@@ -587,6 +606,8 @@ class MarkdownFence(MarkdownBlock):
height: auto;
max-height: 20;
color: rgb(210,210,210);
+
+
}
MarkdownFence > * {
@@ -651,8 +672,10 @@ class Markdown(Widget):
DEFAULT_CSS = """
Markdown {
height: auto;
- margin: 0 4 1 4;
+ margin: 0 2 1 2;
layout: vertical;
+ color: $text;
+ overflow-y: auto;
}
.em {
text-style: italic;
@@ -681,7 +704,7 @@ class Markdown(Widget):
| `strong` | Target text that is styled inline with strong. |
"""
- BULLETS = ["\u25CF ", "▪ ", "‣ ", "• ", "⭑ "]
+ BULLETS = ["\u25cf ", "▪ ", "‣ ", "• ", "⭑ "]
code_dark_theme: reactive[str] = reactive("material")
"""The theme to use for code blocks when in [dark mode][textual.app.App.dark]."""
@@ -759,7 +782,7 @@ def __init__(self, markdown: Markdown, href: str) -> None:
super().__init__()
self.markdown: Markdown = markdown
"""The `Markdown` widget containing the link clicked."""
- self.href: str = href
+ self.href: str = unquote(href)
"""The link that was selected."""
@property
@@ -771,9 +794,9 @@ def control(self) -> Markdown:
"""
return self.markdown
- def _on_mount(self, _: Mount) -> None:
+ async def _on_mount(self, _: Mount) -> None:
if self._markdown is not None:
- self.update(self._markdown)
+ await self.update(self._markdown)
def _watch_code_dark_theme(self) -> None:
"""React to the dark theme being changed."""
@@ -823,7 +846,7 @@ def goto_anchor(self, anchor: str) -> bool:
unique = TrackedSlugs()
for _, title, header_id in self._table_of_contents:
if unique.slug(title) == anchor:
- self.parent.scroll_to_widget(self.query_one(f"#{header_id}"), top=True)
+ self.query_one(f"#{header_id}").scroll_visible(top=True)
return True
return False
@@ -841,7 +864,10 @@ async def load(self, path: Path) -> None:
those that can be raised by calling [`Path.read_text`][pathlib.Path.read_text].
"""
path, anchor = self.sanitize_location(str(path))
- await self.update(path.read_text(encoding="utf-8"))
+ data = await asyncio.get_running_loop().run_in_executor(
+ None, partial(path.read_text, encoding="utf-8")
+ )
+ await self.update(data)
if anchor:
self.goto_anchor(anchor)
@@ -865,93 +891,143 @@ def update(self, markdown: str) -> AwaitComplete:
Returns:
An optionally awaitable object. Await this to ensure that all children have been mounted.
"""
- output: list[MarkdownBlock] = []
- stack: list[MarkdownBlock] = []
parser = (
MarkdownIt("gfm-like")
if self._parser_factory is None
else self._parser_factory()
)
- block_id: int = 0
- self._table_of_contents = []
-
- for token in parser.parse(markdown):
- if token.type == "heading_open":
- block_id += 1
- stack.append(HEADINGS[token.tag](self, id=f"block{block_id}"))
- elif token.type == "hr":
- output.append(MarkdownHorizontalRule(self))
- elif token.type == "paragraph_open":
- stack.append(MarkdownParagraph(self))
- elif token.type == "blockquote_open":
- stack.append(MarkdownBlockQuote(self))
- elif token.type == "bullet_list_open":
- stack.append(MarkdownBulletList(self))
- elif token.type == "ordered_list_open":
- stack.append(MarkdownOrderedList(self))
- elif token.type == "list_item_open":
- if token.info:
- stack.append(MarkdownOrderedListItem(self, token.info))
- else:
- item_count = sum(
- 1
- for block in stack
- if isinstance(block, MarkdownUnorderedListItem)
- )
- stack.append(
- MarkdownUnorderedListItem(
- self,
- self.BULLETS[item_count % len(self.BULLETS)],
- )
- )
+ table_of_contents = []
+
+ def parse_markdown(tokens) -> Iterable[MarkdownBlock]:
+ """Create a stream of MarkdownBlock widgets from markdown.
+
+ Args:
+ tokens: List of tokens
- elif token.type == "table_open":
- stack.append(MarkdownTable(self))
- elif token.type == "tbody_open":
- stack.append(MarkdownTBody(self))
- elif token.type == "thead_open":
- stack.append(MarkdownTHead(self))
- elif token.type == "tr_open":
- stack.append(MarkdownTR(self))
- elif token.type == "th_open":
- stack.append(MarkdownTH(self))
- elif token.type == "td_open":
- stack.append(MarkdownTD(self))
- elif token.type.endswith("_close"):
- block = stack.pop()
- if token.type == "heading_close":
- heading = block._text.plain
- level = int(token.tag[1:])
- self._table_of_contents.append((level, heading, block.id))
- if stack:
- stack[-1]._blocks.append(block)
+ Yields:
+ Widgets for mounting.
+ """
+
+ stack: list[MarkdownBlock] = []
+ stack_append = stack.append
+ block_id: int = 0
+
+ for token in tokens:
+ token_type = token.type
+ if token_type == "heading_open":
+ block_id += 1
+ stack_append(HEADINGS[token.tag](self, id=f"block{block_id}"))
+ elif token_type == "hr":
+ yield MarkdownHorizontalRule(self)
+ elif token_type == "paragraph_open":
+ stack_append(MarkdownParagraph(self))
+ elif token_type == "blockquote_open":
+ stack_append(MarkdownBlockQuote(self))
+ elif token_type == "bullet_list_open":
+ stack_append(MarkdownBulletList(self))
+ elif token_type == "ordered_list_open":
+ stack_append(MarkdownOrderedList(self))
+ elif token_type == "list_item_open":
+ if token.info:
+ stack_append(MarkdownOrderedListItem(self, token.info))
+ else:
+ item_count = sum(
+ 1
+ for block in stack
+ if isinstance(block, MarkdownUnorderedListItem)
+ )
+ stack_append(
+ MarkdownUnorderedListItem(
+ self,
+ self.BULLETS[item_count % len(self.BULLETS)],
+ )
+ )
+ elif token_type == "table_open":
+ stack_append(MarkdownTable(self))
+ elif token_type == "tbody_open":
+ stack_append(MarkdownTBody(self))
+ elif token_type == "thead_open":
+ stack_append(MarkdownTHead(self))
+ elif token_type == "tr_open":
+ stack_append(MarkdownTR(self))
+ elif token_type == "th_open":
+ stack_append(MarkdownTH(self))
+ elif token_type == "td_open":
+ stack_append(MarkdownTD(self))
+ elif token_type.endswith("_close"):
+ block = stack.pop()
+ if token.type == "heading_close":
+ heading = block._text.plain
+ level = int(token.tag[1:])
+ table_of_contents.append((level, heading, block.id))
+ if stack:
+ stack[-1]._blocks.append(block)
+ else:
+ yield block
+ elif token_type == "inline":
+ stack[-1].build_from_token(token)
+ elif token_type in ("fence", "code_block"):
+ fence = MarkdownFence(self, token.content.rstrip(), token.info)
+ if stack:
+ stack[-1]._blocks.append(fence)
+ else:
+ yield fence
else:
- output.append(block)
- elif token.type == "inline":
- stack[-1].build_from_token(token)
- elif token.type in ("fence", "code_block"):
- (stack[-1]._blocks if stack else output).append(
- MarkdownFence(self, token.content.rstrip(), token.info)
- )
- else:
- external = self.unhandled_token(token)
- if external is not None:
- (stack[-1]._blocks if stack else output).append(external)
-
- self.post_message(
- Markdown.TableOfContentsUpdated(self, self._table_of_contents).set_sender(
- self
- )
- )
+ external = self.unhandled_token(token)
+ if external is not None:
+ if stack:
+ stack[-1]._blocks.append(external)
+ else:
+ yield external
+
markdown_block = self.query("MarkdownBlock")
async def await_update() -> None:
- """Update in a single batch."""
+ """Update in batches."""
+ BATCH_SIZE = 200
+ batch: list[MarkdownBlock] = []
+ tokens = await asyncio.get_running_loop().run_in_executor(
+ None, parser.parse, markdown
+ )
- with self.app.batch_update():
- await markdown_block.remove()
- await self.mount_all(output)
+ # Lock so that you can't update with more than one document simultaneously
+ async with self.lock:
+ # Remove existing blocks for the first batch only
+ removed: bool = False
+
+ async def mount_batch(batch: list[MarkdownBlock]) -> None:
+ """Mount a single match of blocks.
+
+ Args:
+ batch: A list of blocks to mount.
+ """
+ nonlocal removed
+ if removed:
+ await self.mount_all(batch)
+ else:
+ with self.app.batch_update():
+ await markdown_block.remove()
+ await self.mount_all(batch)
+ removed = True
+
+ for block in parse_markdown(tokens):
+ batch.append(block)
+ if len(batch) == BATCH_SIZE:
+ await mount_batch(batch)
+ batch.clear()
+ if batch:
+ await mount_batch(batch)
+ if not removed:
+ await markdown_block.remove()
+
+ self._table_of_contents = table_of_contents
+
+ self.post_message(
+ Markdown.TableOfContentsUpdated(
+ self, self._table_of_contents
+ ).set_sender(self)
+ )
return AwaitComplete(await_update())
@@ -1066,6 +1142,9 @@ class MarkdownViewer(VerticalScroll, can_focus=True, can_focus_children=True):
navigator: var[Navigator] = var(Navigator)
+ class NavigatorUpdated(Message):
+ """Navigator has been changed (clicked link etc)."""
+
def __init__(
self,
markdown: str | None = None,
@@ -1101,9 +1180,9 @@ def table_of_contents(self) -> MarkdownTableOfContents:
"""The [table of contents][textual.widgets.markdown.MarkdownTableOfContents] widget."""
return self.query_one(MarkdownTableOfContents)
- def _on_mount(self, _: Mount) -> None:
+ async def _on_mount(self, _: Mount) -> None:
if self._markdown is not None:
- self.document.update(self._markdown)
+ await self.document.update(self._markdown)
async def go(self, location: str | PurePath) -> None:
"""Navigate to a new document path."""
@@ -1114,16 +1193,19 @@ async def go(self, location: str | PurePath) -> None:
else:
# We've been asked to go to a file, optionally with an anchor.
await self.document.load(self.navigator.go(location))
+ self.post_message(self.NavigatorUpdated())
async def back(self) -> None:
"""Go back one level in the history."""
if self.navigator.back():
await self.document.load(self.navigator.location)
+ self.post_message(self.NavigatorUpdated())
async def forward(self) -> None:
"""Go forward one level in the history."""
if self.navigator.forward():
await self.document.load(self.navigator.location)
+ self.post_message(self.NavigatorUpdated())
async def _on_markdown_link_clicked(self, message: Markdown.LinkClicked) -> None:
message.stop()
diff --git a/src/textual/widgets/_option_list.py b/src/textual/widgets/_option_list.py
index c59320afda..25fbaee6d4 100644
--- a/src/textual/widgets/_option_list.py
+++ b/src/textual/widgets/_option_list.py
@@ -1,31 +1,27 @@
-"""Provides the core of a classic vertical bounce-bar option list.
-
-Useful as a lightweight list view (not to be confused with ListView, which
-is much richer but uses widgets for the items) and as the base for various
-forms of bounce-bar menu.
-"""
-
from __future__ import annotations
-from typing import ClassVar, Iterable, NamedTuple
+from typing import TYPE_CHECKING, ClassVar, Iterable, NamedTuple
+import rich.repr
from rich.console import RenderableType
+from rich.measure import Measurement
from rich.padding import Padding
-from rich.repr import Result
from rich.rule import Rule
-from rich.style import Style
-from typing_extensions import Self, TypeAlias
+from rich.style import NULL_STYLE, Style
-from .. import _widget_navigation
+from .. import _widget_navigation, events
from .._widget_navigation import Direction
from ..binding import Binding, BindingType
-from ..events import Click, Idle, Leave, MouseMove
+from ..cache import LRUCache
from ..geometry import Region, Size
from ..message import Message
from ..reactive import reactive
from ..scroll_view import ScrollView
from ..strip import Strip
+if TYPE_CHECKING:
+ from typing_extensions import Self, TypeAlias
+
class DuplicateID(Exception):
"""Raised if a duplicate ID is used when adding options to an option list."""
@@ -35,6 +31,11 @@ class OptionDoesNotExist(Exception):
"""Raised when a request has been made for an option that doesn't exist."""
+class Separator:
+ """Class used to add a separator to an [OptionList][textual.widgets.OptionList]."""
+
+
+@rich.repr.auto
class Option:
"""Class that holds the details of an individual option."""
@@ -70,27 +71,13 @@ def id(self) -> str | None:
"""The optional ID for the option."""
return self.__id
- def __rich_repr__(self) -> Result:
+ def __rich_repr__(self) -> rich.repr.Result:
yield "prompt", self.prompt
yield "id", self.id, None
yield "disabled", self.disabled, False
-
-class Separator:
- """Class used to add a separator to an [OptionList][textual.widgets.OptionList]."""
-
-
-class Line(NamedTuple):
- """Class that holds a list of segments for the line of a option."""
-
- segments: Strip
- """The strip of segments that make up the line."""
-
- option_index: int | None = None
- """The index of the [Option][textual.widgets.option_list.Option] that this line is related to.
-
- If the line isn't related to an option this will be `None`.
- """
+ def __rich__(self) -> RenderableType:
+ return self.__prompt
class OptionLineSpan(NamedTuple):
@@ -108,14 +95,6 @@ class OptionLineSpan(NamedTuple):
line_count: int
"""The count of lines that make up the option."""
- def __contains__(self, line: object) -> bool:
- # For this named tuple `in` will have a very specific meaning; but
- # to keep mypy and friends happy we need to accept an object as the
- # parameter. So, let's keep the type checkers happy but only accept
- # an int.
- assert isinstance(line, int)
- return line >= self.first and line < (self.first + self.line_count)
-
OptionListContent: TypeAlias = "Option | Separator"
"""The type of an item of content in the option list.
@@ -156,24 +135,6 @@ class OptionList(ScrollView, can_focus=True):
| up | Move the highlight up. |
"""
- COMPONENT_CLASSES: ClassVar[set[str]] = {
- "option-list--option",
- "option-list--option-disabled",
- "option-list--option-highlighted",
- "option-list--option-hover",
- "option-list--option-hover-highlighted",
- "option-list--separator",
- }
- """
- | Class | Description |
- | :- | :- |
- | `option-list--option-disabled` | Target disabled options. |
- | `option-list--option-highlighted` | Target the highlighted option. |
- | `option-list--option-hover` | Target an option that has the mouse over it. |
- | `option-list--option-hover-highlighted` | Target a highlighted option that has the mouse over it. |
- | `option-list--separator` | Target the separators. |
- """
-
DEFAULT_CSS = """
OptionList {
height: auto;
@@ -224,6 +185,24 @@ 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-hover",
+ "option-list--option-hover-highlighted",
+ "option-list--separator",
+ }
+ """
+ | Class | Description |
+ | :- | :- |
+ | `option-list--option-disabled` | Target disabled options. |
+ | `option-list--option-highlighted` | Target the highlighted option. |
+ | `option-list--option-hover` | Target an option that has the mouse over it. |
+ | `option-list--option-hover-highlighted` | Target a highlighted option that has the mouse over it. |
+ | `option-list--separator` | Target the separators. |
+ """
+
highlighted: reactive[int | None] = reactive["int | None"](None)
"""The index of the currently-highlighted option, or `None` if no option is highlighted."""
@@ -256,7 +235,7 @@ def control(self) -> OptionList:
"""
return self.option_list
- def __rich_repr__(self) -> Result:
+ def __rich_repr__(self) -> rich.repr.Result:
yield "option_list", self.option_list
yield "option", self.option
yield "option_id", self.option_id
@@ -284,23 +263,10 @@ def __init__(
classes: str | None = None,
disabled: bool = False,
wrap: bool = True,
+ tooltip: RenderableType | None = None,
):
- """Initialise the option list.
-
- Args:
- *content: The content for the option list.
- name: The name of the option list.
- id: The ID of the option list in the DOM.
- classes: The CSS classes of the option list.
- disabled: Whether the option list is disabled or not.
- wrap: Should prompts be auto-wrapped?
- """
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
- # Internal refresh trackers. For things driven from on_idle.
- self._needs_refresh_content_tracking = False
- self._needs_to_scroll_to_highlight = False
-
self._wrap = wrap
"""Should we auto-wrap options?
@@ -330,73 +296,109 @@ def __init__(
"""
self._option_ids: dict[str, int] = {
- option.id: index for index, option in enumerate(self._options) if option.id
+ option.id: index
+ for index, option in enumerate(self._options)
+ if option.id is not None
}
"""A dictionary of option IDs and the option indexes they relate to."""
- self._lines: list[Line] = []
- """A list of all of the individual lines that make up the option list.
+ self._content_render_cache: LRUCache[tuple[int, Style, int], list[Strip]]
+ self._content_render_cache = LRUCache(256)
- Note that the size of this list will be at least the same as the number
- of options, and actually greater if any prompt of any option is
- multiple lines.
- """
+ self._lines: list[tuple[int, int]] | None = None
+ self._spans: list[OptionLineSpan] | None = None
- self._spans: list[OptionLineSpan] = []
- """A list of the locations and sizes of all options in the option list.
+ self._mouse_hovering_over: int | None = None
+ """Used to track what the mouse is hovering over."""
+
+ if tooltip is not None:
+ self.tooltip = tooltip
+
+ if self._options:
+ self.action_first()
+
+ def _left_gutter_width(self) -> int:
+ """Returns the size of any left gutter that should be taken into account.
- This will be the same size as the number of prompts; each entry in
- the list contains the line offset of the start of the prompt, and
- the count of the lines in the prompt.
+ Returns:
+ The width of the left gutter.
"""
+ return 0
- # Initial calculation of the content tracking.
- self._request_content_tracking_refresh()
+ def _on_mount(self):
+ self._populate()
- self._mouse_hovering_over: int | None = None
- """Used to track what the mouse is hovering over."""
+ def _refresh_lines(self) -> None:
+ self._lines = None
+ self._spans = None
+ self._content_render_cache.clear()
+ self.check_idle()
+
+ def notify_style_update(self) -> None:
+ self._content_render_cache.clear()
+
+ def _on_resize(self):
+ self._refresh_lines()
- # Finally, cause the highlighted property to settle down based on
- # the state of the option list in regard to its available options.
- self.action_first()
+ def on_idle(self):
+ if self._lines is None:
+ self._populate()
- def _request_content_tracking_refresh(
- self, rescroll_to_highlight: bool = False
+ def _add_lines(
+ self, new_content: list[OptionListContent], width: int, option_index=0
) -> None:
- """Request that the content tracking information gets refreshed.
+ """Add new lines.
Args:
- rescroll_to_highlight: Should the widget ensure the highlight is visible?
-
- Calling this method sets a flag to say the refresh should happen,
- and books the refresh call in for the next idle moment.
+ new_content: New content to add.
+ width: Width to render content.
+ option_index: Starting option index.
"""
- self._needs_refresh_content_tracking = True
- self._needs_to_scroll_to_highlight = rescroll_to_highlight
- self.check_idle()
+ assert self._lines is not None
+ assert self._spans is not None
+ style = NULL_STYLE
- async def _on_idle(self, _: Idle) -> None:
- """Perform content tracking data refresh when idle."""
- self._refresh_content_tracking()
- if self._needs_to_scroll_to_highlight:
- self._needs_to_scroll_to_highlight = False
- self.scroll_to_highlight()
+ for index, content in enumerate(new_content, len(self._lines)):
+ if isinstance(content, Option):
+ height = len(
+ self._render_option_content(
+ index, content, style, width - self._left_gutter_width()
+ )
+ )
+
+ self._spans.append(OptionLineSpan(len(self._lines), height))
+ self._lines.extend(
+ (option_index, y_offset) for y_offset in range(height)
+ )
+ option_index += 1
+ else:
+ self._lines.append(OptionLineSpan(-1, 0))
- def watch_show_vertical_scrollbar(self) -> None:
- """Handle the vertical scrollbar visibility status changing.
+ self.virtual_size = Size(width, len(self._lines))
- `show_vertical_scrollbar` is watched because it has an impact on the
- available width in which to render the renderables that make up the
- options in the list. If a vertical scrollbar appears or disappears
- we need to recalculate all the lines that make up the list.
- """
- self._request_content_tracking_refresh()
+ def _populate(self) -> None:
+ """Populate the lines data-structure."""
+ if self._lines is not None:
+ return
+ self._lines = []
+ self._spans = []
+
+ self._add_lines(
+ self._contents,
+ self.scrollable_content_region.width - self._left_gutter_width(),
+ )
+ self.refresh()
- def _on_resize(self) -> None:
- """Refresh the layout of the renderables in the list when resized."""
- self._request_content_tracking_refresh(rescroll_to_highlight=True)
+ def get_content_width(self, container: Size, viewport: Size) -> int:
+ """Get maximum width of options."""
+ console = self.app.console
+ options = console.options
+ return max(
+ Measurement.get(console, options, option.prompt).maximum
+ for option in self._options
+ )
- def _on_mouse_move(self, event: MouseMove) -> None:
+ def _on_mouse_move(self, event: events.MouseMove) -> None:
"""React to the mouse moving.
Args:
@@ -404,18 +406,22 @@ def _on_mouse_move(self, event: MouseMove) -> None:
"""
self._mouse_hovering_over = event.style.meta.get("option")
- def _on_leave(self, _: Leave) -> None:
+ def _on_leave(self, _: events.Leave) -> None:
"""React to the mouse leaving the widget."""
self._mouse_hovering_over = None
- async def _on_click(self, event: Click) -> None:
+ async def _on_click(self, event: events.Click) -> None:
"""React to the mouse being clicked on an item.
Args:
event: The click event.
"""
clicked_option: int | None = event.style.meta.get("option")
- if clicked_option is not None and not self._options[clicked_option].disabled:
+ if (
+ clicked_option is not None
+ and clicked_option >= 0
+ and not self._options[clicked_option].disabled
+ ):
self.highlighted = clicked_option
self.action_select()
@@ -434,97 +440,38 @@ def _make_content(self, content: NewOptionListContent) -> OptionListContent:
return Separator()
return Option(content)
- def _clear_content_tracking(self) -> None:
- """Clear down the content tracking information."""
- self._lines.clear()
- self._spans.clear()
-
- def _left_gutter_width(self) -> int:
- """Returns the size of any left gutter that should be taken into account.
-
- Returns:
- The width of the left gutter.
- """
- return 0
-
- def _refresh_content_tracking(self, force: bool = False) -> None:
- """Refresh the various forms of option list content tracking.
+ def _render_option_content(
+ self, option_index: int, renderable: RenderableType, style: Style, width: int
+ ) -> list[Strip]:
+ """Render content for option and style.
Args:
- force: Optionally force the refresh.
-
- Raises:
- DuplicateID: If there is an attempt to use a duplicate ID.
+ option_index: Option index to render.
+ renderable: The Option renderable.
+ style: The Rich style to render with.
+ width: The width of the renderable.
- Without a `force` the refresh will only take place if it has been
- requested via `_refresh_content_tracking`.
+ Returns:
+ A list of strips.
"""
+ cache_key = (option_index, style, width)
+ if (strips := self._content_render_cache.get(cache_key, None)) is not None:
+ return strips
- # If we don't need to refresh, don't bother.
- if not self._needs_refresh_content_tracking and not force:
- return
-
- # If we don't know our own width yet, we can't sensibly work out the
- # heights of the prompts of the options yet, so let's shortcut that
- # work. We'll be back here once we know our height.
- if not self.size.width:
- return
-
- self._clear_content_tracking()
- self._needs_refresh_content_tracking = False
-
- # Set up for doing less property access work inside the loop.
- lines_from = self.app.console.render_lines
- add_span = self._spans.append
- add_lines = self._lines.extend
-
- # Adjust the options for our purposes.
- options = self.app.console.options.update_width(
- self.scrollable_content_region.width - self._left_gutter_width()
- )
- options.no_wrap = not self._wrap
+ padding = self.get_component_styles("option-list--option").padding
+ console = self.app.console
+ options = console.options.update_width(width)
if not self._wrap:
- options.overflow = "ellipsis"
+ options = options.update(no_wrap=True, overflow="ellipsis")
+ if padding:
+ renderable = Padding(renderable, padding)
+ lines = self.app.console.render_lines(renderable, options, style=style)
- # Create a rule that can be used as a separator.
- separator = Strip(lines_from(Rule(style=""))[0])
+ style_meta = Style.from_meta({"option": option_index})
+ strips = [Strip(line, width).apply_style(style_meta) for line in lines]
- # Work through each item that makes up the content of the list,
- # break out the individual lines that will be used to draw it, and
- # also set up the tracking of the actual options.
- line = 0
- option_index = 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
- # work out the lines needed to show it.
- new_lines = [
- Line(
- Strip(prompt_line).apply_style(
- Style(meta={"option": option_index})
- ),
- option_index,
- )
- 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)))
- option_index += 1
- else:
- # The content isn't an option, so it must be a separator (if
- # there were to be other non-option content for an option
- # list it's in this if/else where we'd process it).
- new_lines = [Line(separator)]
- add_lines(new_lines)
- line += len(new_lines)
-
- # Now that we know how many lines make up the whole content of the
- # list, set the virtual size.
- self.virtual_size = Size(self.scrollable_content_region.width, len(self._lines))
+ self._content_render_cache[cache_key] = strips
+ return strips
def _duplicate_id_check(self, candidate_items: list[OptionListContent]) -> None:
"""Check the items to be added for any duplicates.
@@ -569,9 +516,15 @@ def add_options(self, items: Iterable[NewOptionListContent]) -> Self:
"""
# Only work if we have items to add; but don't make a fuss out of
# zero items to add, just carry on like nothing happened.
- if items:
+ if self._lines is None:
+ self._lines = []
+ if self._spans is None:
+ self._spans = []
+ new_items = list(items)
+ if new_items:
+ option_index = len(self._options)
# Turn any incoming values into valid content for the list.
- content = [self._make_content(item) for item in items]
+ content = [self._make_content(item) for item in new_items]
self._duplicate_id_check(content)
self._contents.extend(content)
# Pull out the content that is genuine options, create any new
@@ -585,7 +538,11 @@ def add_options(self, items: Iterable[NewOptionListContent]) -> Self:
self._option_ids[new_option.id] = new_option_index
self._options.extend(new_options)
- self._refresh_content_tracking(force=True)
+ self._add_lines(
+ content,
+ self.scrollable_content_region.width - self._left_gutter_width(),
+ option_index=option_index,
+ )
self.refresh()
return self
@@ -621,11 +578,10 @@ def _remove_option(self, index: int) -> None:
for option_id, option_index in self._option_ids.items()
if option_index != index
}
- self._refresh_content_tracking(force=True)
+ self._refresh_lines()
# Force a re-validation of the highlight.
self.highlighted = self.highlighted
self._mouse_hovering_over = None
- self.refresh()
def remove_option(self, option_id: str) -> Self:
"""Remove the option with the given ID.
@@ -673,8 +629,7 @@ def _replace_option_prompt(self, index: int, prompt: RenderableType) -> None:
OptionDoesNotExist: If there is no option with the given index.
"""
self.get_option_at_index(index).set_prompt(prompt)
- self._refresh_content_tracking(force=True)
- self.refresh()
+ self._refresh_lines()
def replace_option_prompt(self, option_id: str, prompt: RenderableType) -> Self:
"""Replace the prompt of the option with the given ID.
@@ -721,8 +676,7 @@ def clear_options(self) -> Self:
self._option_ids.clear()
self.highlighted = None
self._mouse_hovering_over = None
- self.virtual_size = Size(self.scrollable_content_region.width, 0)
- self._refresh_content_tracking(force=True)
+ self._refresh_lines()
return self
def _set_option_disabled(self, index: int, disabled: bool) -> Self:
@@ -862,80 +816,59 @@ def get_option_index(self, option_id: str) -> int:
) from None
def render_line(self, y: int) -> Strip:
- """Render a single line in the option list.
-
- Args:
- y: The Y offset of the line to render.
-
- Returns:
- A `Strip` instance for the caller to render.
- """
-
- scroll_x, scroll_y = self.scroll_offset
+ self._populate()
+ assert self._lines is not None
- # First off, work out which line we're working on, based off the
- # current scroll offset plus the line we're being asked to render.
+ _scroll_x, scroll_y = self.scroll_offset
line_number = scroll_y + y
+
try:
- line = self._lines[line_number]
+ option_index, y_offset = self._lines[line_number]
except IndexError:
- # An IndexError means we're drawing in an option list where
- # there's more list than there are options.
return Strip([])
- # Now that we know which line we're on, pull out the option index so
- # we have a "local" copy to refer to rather than needing to do a
- # property access multiple times.
- option_index = line.option_index
-
- # Knowing which line we're going to be drawing, we can now go pull
- # the relevant segments for the line of that particular prompt.
- strip = line.segments
-
- # If the line we're looking at isn't associated with an option, it
- # will be a separator, so let's exit early with that.
- if option_index is None:
- return strip.apply_style(
- self.get_component_rich_style("option-list--separator")
- )
-
- # At this point we know we're drawing actual content. To allow for
- # horizontal scrolling, let's crop the strip at the right locations.
- strip = strip.crop(scroll_x, scroll_x + self.scrollable_content_region.width)
+ renderable = (
+ Rule(style=self.get_component_rich_style("option-list--separator"))
+ if option_index == -1
+ else self._options[option_index]
+ )
- highlighted = self.highlighted
- mouse_over = self._mouse_hovering_over
- spans = self._spans
+ mouse_over = self._mouse_hovering_over == option_index
- # Handle drawing a disabled option.
- if self._options[option_index].disabled:
- return strip.apply_style(
- self.get_component_rich_style("option-list--option-disabled")
- )
+ component_class: str | None = None
- # Handle drawing a highlighted option.
- if highlighted is not None and line_number in spans[highlighted]:
- # Highlighted with the mouse over it?
- if option_index == mouse_over:
- return strip.apply_style(
- self.get_component_rich_style(
- "option-list--option-hover-highlighted"
- )
- )
- # Just a normal highlight.
- return strip.apply_style(
- self.get_component_rich_style("option-list--option-highlighted")
- )
-
- # Perhaps the line is within an otherwise-uninteresting option that
- # has the mouse hovering over it?
- if mouse_over is not None and line_number in spans[mouse_over]:
- return strip.apply_style(
- self.get_component_rich_style("option-list--option-hover")
- )
+ if option_index == -1:
+ component_class = "option-list--separator"
+ else:
+ try:
+ option = self._options[option_index]
+ except IndexError:
+ pass
+ else:
+ if option.disabled:
+ component_class = "option-list--option-disabled"
+ elif self.highlighted == option_index:
+ component_class = "option-list--option-highlighted"
+ elif mouse_over:
+ component_class = "option-list--option-hover"
+
+ style = (
+ self.get_component_rich_style(component_class)
+ if component_class
+ else self.rich_style
+ )
- # It's a normal option line.
- return strip.apply_style(self.rich_style)
+ strips = self._render_option_content(
+ option_index,
+ renderable,
+ style,
+ self.scrollable_content_region.width - self._left_gutter_width(),
+ )
+ try:
+ strip = strips[y_offset]
+ except IndexError:
+ return Strip([])
+ return strip
def scroll_to_highlight(self, top: bool = False) -> None:
"""Ensure that the highlighted option is in view.
@@ -944,19 +877,18 @@ def scroll_to_highlight(self, top: bool = False) -> None:
top: Scroll highlight to top of the list.
"""
highlighted = self.highlighted
- if highlighted is None:
+ if highlighted is None or self._spans is None:
return
+
try:
- span = self._spans[highlighted]
+ y, height = self._spans[highlighted]
except IndexError:
# Index error means we're being asked to scroll to a highlight
# before all the tracking information has been worked out.
# That's fine; let's just NoP that.
return
self.scroll_to_region(
- Region(
- 0, span.first, self.scrollable_content_region.width, span.line_count
- ),
+ Region(0, y, self.scrollable_content_region.width, height),
force=True,
animate=False,
top=top,
@@ -1019,6 +951,10 @@ def _page(self, direction: Direction) -> None:
# If we find ourselves in a position where we don't know where we're
# going, we need a fallback location. Where we go will depend on the
# direction.
+ self._populate()
+ assert self._spans is not None
+ assert self._lines is not None
+
fallback = self.action_first if direction == -1 else self.action_last
highlighted = self.highlighted
@@ -1037,7 +973,7 @@ def _page(self, direction: Direction) -> None:
try:
# Now that we've got a target line, let's figure out the
# index of the target option.
- target_option = self._lines[target_line].option_index
+ target_option: int | None = self._lines[target_line][0]
except IndexError:
# An index error suggests we've gone out of bounds, let's
# settle on whatever the call thinks is a good place to wrap
@@ -1079,3 +1015,14 @@ def action_select(self) -> None:
highlighted = self.highlighted
if highlighted is not None and not self._options[highlighted].disabled:
self.post_message(self.OptionSelected(self, highlighted))
+
+
+if __name__ == "__main__":
+ from textual.app import App, ComposeResult
+
+ class OptionApp(App):
+ def compose(self) -> ComposeResult:
+ yield OptionList("Foo", "Bar", "Baz")
+
+ app = OptionApp()
+ app.run()
diff --git a/src/textual/widgets/_radio_set.py b/src/textual/widgets/_radio_set.py
index 7ea1fea4f0..99439cade7 100644
--- a/src/textual/widgets/_radio_set.py
+++ b/src/textual/widgets/_radio_set.py
@@ -5,6 +5,7 @@
from typing import ClassVar, Optional
import rich.repr
+from rich.console import RenderableType
from .. import _widget_navigation
from ..binding import Binding, BindingType
@@ -121,6 +122,7 @@ def __init__(
id: str | None = None,
classes: str | None = None,
disabled: bool = False,
+ tooltip: RenderableType | None = None,
) -> None:
"""Initialise the radio set.
@@ -130,6 +132,7 @@ def __init__(
id: The ID of the radio set in the DOM.
classes: The CSS classes of the radio set.
disabled: Whether the radio set is disabled or not.
+ tooltip: Optional tooltip.
Note:
When a `str` label is provided, a
@@ -148,6 +151,8 @@ def __init__(
classes=classes,
disabled=disabled,
)
+ if tooltip is not None:
+ self.tooltip = tooltip
def _on_mount(self, _: Mount) -> None:
"""Perform some processing once mounted in the DOM."""
diff --git a/src/textual/widgets/_rule.py b/src/textual/widgets/_rule.py
index f172c0bda5..cb17595a05 100644
--- a/src/textual/widgets/_rule.py
+++ b/src/textual/widgets/_rule.py
@@ -1,8 +1,14 @@
from __future__ import annotations
-from rich.text import Text
+from typing import Iterable
+
+from rich.console import Console, ConsoleOptions
+from rich.segment import Segment
+from rich.style import Style
from typing_extensions import Literal
+from textual.geometry import Size
+
from ..app import RenderResult
from ..css._error_tools import friendly_list
from ..reactive import Reactive, reactive
@@ -72,6 +78,36 @@ class InvalidLineStyle(Exception):
"""Exception raised for an invalid rule line style."""
+class HorizontalRuleRenderable:
+ """Renders a horizontal rule."""
+
+ def __init__(self, character: str, style: Style, width: int):
+ self.character = character
+ self.style = style
+ self.width = width
+
+ def __rich_console__(
+ self, console: Console, options: ConsoleOptions
+ ) -> Iterable[Segment]:
+ yield Segment(self.width * self.character, self.style)
+
+
+class VerticalRuleRenderable:
+ """Renders a vertical rule."""
+
+ def __init__(self, character: str, style: Style, height: int):
+ self.character = character
+ self.style = style
+ self.height = height
+
+ def __rich_console__(
+ self, console: Console, options: ConsoleOptions
+ ) -> Iterable[Segment]:
+ segment = Segment(self.character, self.style)
+ new_line = Segment.line()
+ return ([segment, new_line] * self.height)[:-1]
+
+
class Rule(Widget, can_focus=False):
"""A rule widget to separate content, similar to a ` ` HTML tag."""
@@ -81,15 +117,15 @@ class Rule(Widget, can_focus=False):
}
Rule.-horizontal {
- min-height: 1;
- max-height: 1;
+ height: 1;
margin: 1 0;
+ width: 1fr;
}
Rule.-vertical {
- min-width: 1;
- max-width: 1;
+ width: 1;
margin: 0 2;
+ height: 1fr;
}
"""
@@ -122,15 +158,21 @@ def __init__(
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
self.orientation = orientation
self.line_style = line_style
+ self.expand = True
def render(self) -> RenderResult:
- rule_char: str
+ rule_character: str
+ style = self.rich_style
if self.orientation == "vertical":
- rule_char = _VERTICAL_LINE_CHARS[self.line_style]
- return Text(rule_char * self.size.height)
+ rule_character = _VERTICAL_LINE_CHARS[self.line_style]
+ return VerticalRuleRenderable(
+ rule_character, style, self.content_size.height
+ )
elif self.orientation == "horizontal":
- rule_char = _HORIZONTAL_LINE_CHARS[self.line_style]
- return Text(rule_char * self.size.width)
+ rule_character = _HORIZONTAL_LINE_CHARS[self.line_style]
+ return HorizontalRuleRenderable(
+ rule_character, style, self.content_size.width
+ )
else:
raise InvalidRuleOrientation(
f"Valid rule orientations are {friendly_list(_VALID_RULE_ORIENTATIONS)}"
@@ -156,6 +198,16 @@ def validate_line_style(self, style: LineStyle) -> LineStyle:
)
return style
+ def get_content_width(self, container: Size, viewport: Size) -> int:
+ if self.orientation == "horizontal":
+ return container.width
+ return 1
+
+ def get_content_height(self, container: Size, viewport: Size, width: int) -> int:
+ if self.orientation == "horizontal":
+ return 1
+ return container.height
+
@classmethod
def horizontal(
cls,
diff --git a/src/textual/widgets/_select.py b/src/textual/widgets/_select.py
index 43875e8fe8..6049dd3227 100644
--- a/src/textual/widgets/_select.py
+++ b/src/textual/widgets/_select.py
@@ -90,6 +90,7 @@ def action_dismiss(self) -> None:
def _on_blur(self, _event: events.Blur) -> None:
"""On blur we want to dismiss the overlay."""
self.post_message(self.Dismiss(lost_focus=True))
+ self.suppress_click()
def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
"""Inform parent when an option is selected."""
@@ -177,6 +178,7 @@ def _watch_has_value(self, has_value: bool) -> None:
async def _on_click(self, event: events.Click) -> None:
"""Inform ancestor we want to toggle."""
+ event.stop()
self.post_message(self.Toggle())
@@ -296,6 +298,7 @@ def __init__(
id: str | None = None,
classes: str | None = None,
disabled: bool = False,
+ tooltip: RenderableType | None = None,
):
"""Initialize the Select control.
@@ -313,6 +316,7 @@ def __init__(
id: The ID of the control in the DOM.
classes: The CSS classes of the control.
disabled: Whether the control is disabled or not.
+ tooltip: Optional tooltip.
Raises:
EmptySelectError: If no options are provided and `allow_blank` is `False`.
@@ -322,6 +326,8 @@ def __init__(
self.prompt = prompt
self._value = value
self._setup_variables_for_options(options)
+ if tooltip is not None:
+ self.tooltip = tooltip
@classmethod
def from_values(
diff --git a/src/textual/widgets/_selection_list.py b/src/textual/widgets/_selection_list.py
index 85f95b1326..3f9f961f22 100644
--- a/src/textual/widgets/_selection_list.py
+++ b/src/textual/widgets/_selection_list.py
@@ -646,6 +646,11 @@ def _remove_option(self, index: int) -> None:
option = self.get_option_at_index(index)
self._deselect(option.value)
del self._values[option.value]
+ # Decrement index of options after the one we just removed.
+ self._values = {
+ option_value: option_index - 1 if option_index > index else option_index
+ for option_value, option_index in self._values.items()
+ }
return super()._remove_option(index)
def add_options(
diff --git a/src/textual/widgets/_switch.py b/src/textual/widgets/_switch.py
index b6dbe5f6ea..257e56ebac 100644
--- a/src/textual/widgets/_switch.py
+++ b/src/textual/widgets/_switch.py
@@ -2,8 +2,12 @@
from typing import TYPE_CHECKING, ClassVar
+from rich.console import RenderableType
+
if TYPE_CHECKING:
from ..app import RenderResult
+ from typing_extensions import Self
+
from ..binding import Binding, BindingType
from ..events import Click
from ..geometry import Size
@@ -12,9 +16,6 @@
from ..scrollbar import ScrollBarRender
from ..widget import Widget
-if TYPE_CHECKING:
- from typing_extensions import Self
-
class Switch(Widget, can_focus=True):
"""A switch widget that represents a boolean value.
@@ -110,6 +111,7 @@ def __init__(
id: str | None = None,
classes: str | None = None,
disabled: bool = False,
+ tooltip: RenderableType | None = None,
):
"""Initialise the switch.
@@ -120,12 +122,15 @@ def __init__(
id: The ID of the switch in the DOM.
classes: The CSS classes of the switch.
disabled: Whether the switch is disabled or not.
+ tooltip: Optional tooltip.
"""
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
if value:
self.slider_pos = 1.0
self.set_reactive(Switch.value, value)
self._should_animate = animate
+ if tooltip is not None:
+ self.tooltip = tooltip
def watch_value(self, value: bool) -> None:
target_slider_pos = 1.0 if value else 0.0
diff --git a/src/textual/widgets/_tabbed_content.py b/src/textual/widgets/_tabbed_content.py
index 54b0c38799..b69e1f24c7 100644
--- a/src/textual/widgets/_tabbed_content.py
+++ b/src/textual/widgets/_tabbed_content.py
@@ -9,6 +9,7 @@
from rich.text import Text, TextType
from typing_extensions import Final
+from .. import events
from ..app import ComposeResult
from ..await_complete import AwaitComplete
from ..css.query import NoMatches
@@ -196,6 +197,10 @@ class Disabled(TabPaneMessage):
class Enabled(TabPaneMessage):
"""Sent when a tab pane is enabled via its reactive `disabled`."""
+ @dataclass
+ class Focused(TabPaneMessage):
+ """Sent when a child widget is focused."""
+
def __init__(
self,
title: TextType,
@@ -224,6 +229,10 @@ def _watch_disabled(self, disabled: bool) -> None:
"""Notify the parent `TabbedContent` that a tab pane was enabled/disabled."""
self.post_message(self.Disabled(self) if disabled else self.Enabled(self))
+ def _on_descendant_focus(self, event: events.DescendantFocus):
+ """Tell TabbedContent parent something is focused in this pane."""
+ self.post_message(self.Focused(self))
+
class TabbedContent(Widget):
"""A container with associated tabs to toggle content visibility."""
@@ -507,6 +516,12 @@ def _on_tabs_tab_activated(self, event: Tabs.TabActivated) -> None:
)
)
+ def _on_tab_pane_focused(self, event: TabPane.Focused) -> None:
+ """One of the panes contains a widget that was programmatically focused."""
+ event.stop()
+ if event.tab_pane.id is not None:
+ self.active = event.tab_pane.id
+
def _on_tabs_cleared(self, event: Tabs.Cleared) -> None:
"""Called when there are no active tabs. The tabs may have been cleared,
or they may all be hidden."""
diff --git a/src/textual/widgets/_tabs.py b/src/textual/widgets/_tabs.py
index 96d9c7c554..dce53e0e81 100644
--- a/src/textual/widgets/_tabs.py
+++ b/src/textual/widgets/_tabs.py
@@ -42,7 +42,7 @@ class Underline(Widget):
"""
| Class | Description |
| :- | :- |
- | `underline-bar` | Style of the bar (may be used to change the color). |
+ | `underline--bar` | Style of the bar (may be used to change the color). |
"""
highlight_start = reactive(0)
@@ -510,7 +510,7 @@ def remove_tab(self, tab_or_id: Tab | str | None) -> AwaitComplete:
An optionally awaitable object that waits for the tab to be removed.
"""
if not tab_or_id:
- return AwaitComplete(self.app._remove_nodes([], None))
+ return AwaitComplete()
if isinstance(tab_or_id, Tab):
remove_tab = tab_or_id
@@ -518,7 +518,7 @@ def remove_tab(self, tab_or_id: Tab | str | None) -> AwaitComplete:
try:
remove_tab = self.query_one(f"#tabs-list > #{tab_or_id}", Tab)
except NoMatches:
- return AwaitComplete(self.app._remove_nodes([], None))
+ return AwaitComplete()
removing_active_tab = remove_tab.has_class("-active")
next_tab = self._next_active
@@ -673,7 +673,7 @@ def _on_underline_clicked(self, event: Underline.Clicked) -> None:
offset = event.offset + (0, -1)
self.focus()
for tab in self.query(Tab):
- if offset in tab.region:
+ if offset in tab.region and not tab.disabled:
self._activate_tab(tab)
break
diff --git a/src/textual/widgets/_text_area.py b/src/textual/widgets/_text_area.py
index 37231b21aa..7b1c10cb6b 100644
--- a/src/textual/widgets/_text_area.py
+++ b/src/textual/widgets/_text_area.py
@@ -8,6 +8,7 @@
from pathlib import Path
from typing import TYPE_CHECKING, ClassVar, Iterable, Optional, Sequence, Tuple
+from rich.console import RenderableType
from rich.style import Style
from rich.text import Text
from typing_extensions import Literal
@@ -300,6 +301,9 @@ class TextArea(ScrollView):
Changing this value will immediately re-render the `TextArea`."""
+ line_number_start: Reactive[int] = reactive(1, init=False)
+ """The line number the first line should be."""
+
indent_width: Reactive[int] = reactive(4, init=False)
"""The width of tabs or the multiple of spaces to align to on pressing the `tab` key.
@@ -369,11 +373,13 @@ def __init__(
tab_behavior: Literal["focus", "indent"] = "focus",
read_only: bool = False,
show_line_numbers: bool = False,
+ line_number_start: int = 1,
max_checkpoints: int = 50,
name: str | None = None,
id: str | None = None,
classes: str | None = None,
disabled: bool = False,
+ tooltip: RenderableType | None = None,
) -> None:
"""Construct a new `TextArea`.
@@ -385,11 +391,13 @@ def __init__(
tab_behavior: If 'focus', pressing tab will switch focus. If 'indent', pressing tab will insert a tab.
read_only: Enable read-only mode. This prevents edits using the keyboard.
show_line_numbers: Show line numbers on the left edge.
+ line_number_start: What line number to start on.
max_checkpoints: The maximum number of undo history checkpoints to retain.
name: The name of the `TextArea` widget.
id: The ID of the widget, used to refer to it from Textual CSS.
classes: One or more Textual CSS compatible class names separated by spaces.
disabled: True if the widget is disabled.
+ tooltip: Optional tooltip.
"""
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
@@ -452,12 +460,16 @@ def __init__(
self.set_reactive(TextArea.soft_wrap, soft_wrap)
self.set_reactive(TextArea.read_only, read_only)
self.set_reactive(TextArea.show_line_numbers, show_line_numbers)
+ self.set_reactive(TextArea.line_number_start, line_number_start)
self.tab_behavior = tab_behavior
# When `app.dark` is toggled, reset the theme (since it caches values).
self.watch(self.app, "dark", self._app_dark_toggled, init=False)
+ if tooltip is not None:
+ self.tooltip = tooltip
+
@classmethod
def code_editor(
cls,
@@ -469,11 +481,13 @@ def code_editor(
tab_behavior: Literal["focus", "indent"] = "indent",
read_only: bool = False,
show_line_numbers: bool = True,
+ line_number_start: int = 1,
max_checkpoints: int = 50,
name: str | None = None,
id: str | None = None,
classes: str | None = None,
disabled: bool = False,
+ tooltip: RenderableType | None = None,
) -> TextArea:
"""Construct a new `TextArea` with sensible defaults for editing code.
@@ -487,10 +501,12 @@ def code_editor(
soft_wrap: Enable soft wrapping.
tab_behavior: If 'focus', pressing tab will switch focus. If 'indent', pressing tab will insert a tab.
show_line_numbers: Show line numbers on the left edge.
+ line_number_start: What line number to start on.
name: The name of the `TextArea` widget.
id: The ID of the widget, used to refer to it from Textual CSS.
classes: One or more Textual CSS compatible class names separated by spaces.
disabled: True if the widget is disabled.
+ tooltip: Optional tooltip
"""
return cls(
text,
@@ -500,11 +516,13 @@ def code_editor(
tab_behavior=tab_behavior,
read_only=read_only,
show_line_numbers=show_line_numbers,
+ line_number_start=line_number_start,
max_checkpoints=max_checkpoints,
name=name,
id=id,
classes=classes,
disabled=disabled,
+ tooltip=tooltip,
)
@staticmethod
@@ -682,6 +700,11 @@ def _watch_show_line_numbers(self) -> None:
self._rewrap_and_refresh_virtual_size()
self.scroll_cursor_visible()
+ def _watch_line_number_start(self) -> None:
+ """The line number gutter max size might change and contributes to virtual size, so recalculate."""
+ self._rewrap_and_refresh_virtual_size()
+ self.scroll_cursor_visible()
+
def _watch_indent_width(self) -> None:
"""Changing width of tabs will change the document display width."""
self._rewrap_and_refresh_virtual_size()
@@ -969,6 +992,21 @@ def _refresh_size(self) -> None:
width, height = self.document.get_size(self.indent_width)
self.virtual_size = Size(width + self.gutter_width + 1, height)
+ def get_line(self, line_index: int) -> Text:
+ """Retrieve the line at the given line index.
+
+ You can stylize the Text object returned here to apply additional
+ styling to TextArea content.
+
+ Args:
+ line_index: The index of the line.
+
+ Returns:
+ A `rich.Text` object containing the requested line.
+ """
+ line_string = self.document.get_line(line_index)
+ return Text(line_string, end="")
+
def render_line(self, y: int) -> Strip:
"""Render a single line of the TextArea. Called by Textual.
@@ -982,7 +1020,6 @@ def render_line(self, y: int) -> Strip:
if theme:
theme.apply_css(self)
- document = self.document
wrapped_document = self.wrapped_document
scroll_x, scroll_y = self.scroll_offset
@@ -1006,9 +1043,7 @@ def render_line(self, y: int) -> Strip:
line_index, section_offset = line_info
- # Get the line from the Document.
- line_string = document.get_line(line_index)
- line = Text(line_string, end="")
+ line = self.get_line(line_index)
line_character_count = len(line)
line.tab_size = self.indent_width
line.set_length(line_character_count + 1) # space at end for cursor
@@ -1058,7 +1093,7 @@ def render_line(self, y: int) -> Strip:
highlights = self._highlights
if highlights and theme:
- line_bytes = _utf8_encode(line_string)
+ line_bytes = _utf8_encode(line.plain)
byte_to_codepoint = build_byte_to_codepoint_dict(line_bytes)
get_highlight_from_theme = theme.syntax_styles.get
line_highlights = highlights[line_index]
@@ -1121,7 +1156,9 @@ def render_line(self, y: int) -> Strip:
gutter_style = theme.gutter_style
gutter_width_no_margin = gutter_width - 2
- gutter_content = str(line_index + 1) if section_offset == 0 else ""
+ gutter_content = (
+ str(line_index + self.line_number_start) if section_offset == 0 else ""
+ )
gutter = Text(
f"{gutter_content:>{gutter_width_no_margin}} ",
style=gutter_style or "",
@@ -1446,7 +1483,8 @@ def gutter_width(self) -> int:
# The longest number in the gutter plus two extra characters: `│ `.
gutter_margin = 2
gutter_width = (
- len(str(self.document.line_count)) + gutter_margin
+ len(str(self.document.line_count - 1 + self.line_number_start))
+ + gutter_margin
if self.show_line_numbers
else 0
)
@@ -1501,17 +1539,18 @@ async def _on_mouse_move(self, event: events.MouseMove) -> None:
def _end_mouse_selection(self) -> None:
"""Finalize the selection that has been made using the mouse."""
- self._selecting = False
- self.release_mouse()
- self.record_cursor_width()
- self._restart_blink()
+ if self._selecting:
+ self._selecting = False
+ self.release_mouse()
+ self.record_cursor_width()
+ self._restart_blink()
async def _on_mouse_up(self, event: events.MouseUp) -> None:
"""Finalize the selection that has been made using the mouse."""
self._end_mouse_selection()
async def _on_hide(self, event: events.Hide) -> None:
- """Finalize the selection that has been made using the mouse when thew widget is hidden."""
+ """Finalize the selection that has been made using the mouse when the widget is hidden."""
self._end_mouse_selection()
async def _on_paste(self, event: events.Paste) -> None:
diff --git a/src/textual/widgets/_toast.py b/src/textual/widgets/_toast.py
index a8198f4b53..0a057523f7 100644
--- a/src/textual/widgets/_toast.py
+++ b/src/textual/widgets/_toast.py
@@ -183,7 +183,6 @@ def show(self, notifications: Notifications) -> None:
Args:
notifications: The notifications to show.
"""
-
# Look for any stale toasts and remove them.
for toast in self.query(Toast):
if toast._notification not in notifications:
diff --git a/src/textual/widgets/_toggle_button.py b/src/textual/widgets/_toggle_button.py
index 6af1ea6a82..dca89cdbd0 100644
--- a/src/textual/widgets/_toggle_button.py
+++ b/src/textual/widgets/_toggle_button.py
@@ -7,6 +7,7 @@
from typing import TYPE_CHECKING, ClassVar
+from rich.console import RenderableType
from rich.style import Style
from rich.text import Text, TextType
@@ -130,6 +131,7 @@ def __init__(
id: str | None = None,
classes: str | None = None,
disabled: bool = False,
+ tooltip: RenderableType | None = None,
) -> None:
"""Initialise the toggle.
@@ -141,6 +143,7 @@ def __init__(
id: The ID of the toggle in the DOM.
classes: The CSS classes of the toggle.
disabled: Whether the button is disabled or not.
+ tooltip: RenderableType | None = None,
"""
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
self._button_first = button_first
@@ -148,6 +151,8 @@ def __init__(
with self.prevent(self.Changed):
self.value = value
self._label = self._make_label(label)
+ if tooltip is not None:
+ self.tooltip = tooltip
def _make_label(self, label: TextType) -> Text:
"""Make a `Text` label from a `TextType` value.
@@ -164,6 +169,7 @@ def _make_label(self, label: TextType) -> Text:
label = label.split()[0]
except IndexError:
pass
+
return label
@property
@@ -211,7 +217,9 @@ def render(self) -> RenderResult:
"""
button = self._button
label = self._label.copy()
- label.stylize(self.get_component_rich_style("toggle--label", partial=True))
+ label.stylize_before(
+ self.get_component_rich_style("toggle--label", partial=True)
+ )
spacer = " " if label else ""
return Text.assemble(
*(
diff --git a/src/textual/widgets/_tree.py b/src/textual/widgets/_tree.py
index 686382433d..9ee5227444 100644
--- a/src/textual/widgets/_tree.py
+++ b/src/textual/widgets/_tree.py
@@ -9,7 +9,7 @@
from rich.style import NULL_STYLE, Style
from rich.text import Text, TextType
-from .. import events
+from .. import events, on
from .._immutable_sequence_view import ImmutableSequenceView
from .._loop import loop_last
from .._segment_tools import line_pad
@@ -70,13 +70,13 @@ def _get_guide_width(self, guide_depth: int, show_root: bool) -> int:
Width in cells.
"""
if show_root:
- return 2 + (max(0, len(self.path) - 1)) * guide_depth
+ width = (max(0, len(self.path) - 1)) * guide_depth
else:
- guides = 2
+ width = 0
if len(self.path) > 1:
- guides += (len(self.path) - 1) * guide_depth
+ width += (len(self.path) - 1) * guide_depth
- return guides
+ return width
class TreeNodes(ImmutableSequenceView["TreeNode[TreeDataType]"]):
@@ -495,7 +495,7 @@ class Tree(Generic[TreeDataType], ScrollView, can_focus=True):
guide_depth = reactive(4, init=False)
"""The indent depth of tree nodes."""
auto_expand = var(True)
- """Auto expand tree nodes when clicked."""
+ """Auto expand tree nodes when they are selected."""
LINES: dict[str, tuple[str, str, str, str]] = {
"default": (
@@ -745,7 +745,7 @@ def reset(self, label: TextType, data: TreeDataType | None = None) -> Self:
self.root.data = data
return self
- def select_node(self, node: TreeNode[TreeDataType] | None) -> None:
+ def move_cursor(self, node: TreeNode[TreeDataType] | None) -> None:
"""Move the cursor to the given node, or reset cursor.
Args:
@@ -753,6 +753,23 @@ def select_node(self, node: TreeNode[TreeDataType] | None) -> None:
"""
self.cursor_line = -1 if node is None else node._line
+ def select_node(self, node: TreeNode[TreeDataType] | None) -> None:
+ """Move the cursor to the given node and select it, or reset cursor.
+
+ Args:
+ node: A tree node to move the cursor to and select, or None to reset cursor.
+ """
+ self.move_cursor(node)
+ if node is not None:
+ self.post_message(Tree.NodeSelected(node))
+
+ @on(NodeSelected)
+ def _expand_node_on_select(self, event: NodeSelected[TreeDataType]) -> None:
+ """When the node is selected, expand the node if `auto_expand` is True."""
+ node = event.node
+ if self.auto_expand:
+ self._toggle_node(node)
+
def get_node_at_line(self, line_no: int) -> TreeNode[TreeDataType] | None:
"""Get the node for a given line.
@@ -1089,6 +1106,8 @@ def get_guides(style: Style) -> tuple[str, str, str, str]:
else:
line_style = base_style
+ line_style += Style(meta={"line": y})
+
guides = Text(style=line_style)
guides_append = guides.append
@@ -1124,7 +1143,7 @@ def get_guides(style: Style) -> tuple[str, str, str, str]:
)
label = self.render_label(line.path[-1], line_style, label_style).copy()
- label.stylize(Style(meta={"node": line.node._id, "line": y}))
+ label.stylize(Style(meta={"node": line.node._id}))
guides.append(label)
segments = list(guides.render(self.app.console))
@@ -1231,6 +1250,4 @@ def action_select_cursor(self) -> None:
pass
else:
node = line.path[-1]
- if self.auto_expand:
- self._toggle_node(node)
- self.post_message(self.NodeSelected(node))
+ self.post_message(Tree.NodeSelected(node))
diff --git a/src/textual/worker.py b/src/textual/worker.py
index f7f10b60ae..d3455b9a61 100644
--- a/src/textual/worker.py
+++ b/src/textual/worker.py
@@ -293,7 +293,7 @@ async def do_work() -> ResultType:
return asyncio.run(do_work())
def run_coroutine(
- work: Callable[[], Coroutine[None, None, ResultType]]
+ work: Callable[[], Coroutine[None, None, ResultType]],
) -> ResultType:
"""Set the active worker and await coroutine."""
return run_awaitable(work())
diff --git a/tests/command_palette/test_events.py b/tests/command_palette/test_events.py
new file mode 100644
index 0000000000..47f5b8eee1
--- /dev/null
+++ b/tests/command_palette/test_events.py
@@ -0,0 +1,75 @@
+from typing import Union
+from unittest import mock
+
+from textual import on
+from textual.app import App
+from textual.command import CommandPalette, Hit, Hits, Provider
+
+CommandPaletteEvent = Union[
+ CommandPalette.Opened, CommandPalette.Closed, CommandPalette.OptionHighlighted
+]
+
+
+class SimpleSource(Provider):
+ async def search(self, query: str) -> Hits:
+ def goes_nowhere_does_nothing() -> None:
+ pass
+
+ yield Hit(1, query, goes_nowhere_does_nothing, query)
+
+
+class AppWithActiveCommandPalette(App[None]):
+ COMMANDS = {SimpleSource}
+
+ def __init__(self) -> None:
+ super().__init__()
+ self.events: list[CommandPaletteEvent] = []
+
+ def on_mount(self) -> None:
+ self.action_command_palette()
+
+ @on(CommandPalette.Opened)
+ @on(CommandPalette.Closed)
+ @on(CommandPalette.OptionHighlighted)
+ def record_event(
+ self,
+ event: CommandPaletteEvent,
+ ) -> None:
+ self.events.append(event)
+
+
+async def test_command_palette_opened_event():
+ app = AppWithActiveCommandPalette()
+ async with app.run_test():
+ assert app.events == [CommandPalette.Opened()]
+
+
+async def test_command_palette_closed_event():
+ app = AppWithActiveCommandPalette()
+ async with app.run_test() as pilot:
+ await pilot.press("escape")
+ assert app.events == [CommandPalette.Opened(), CommandPalette.Closed(False)]
+
+
+async def test_command_palette_closed_event_value():
+ app = AppWithActiveCommandPalette()
+ async with app.run_test() as pilot:
+ await pilot.press("a")
+ await pilot.press("down")
+ await pilot.press("enter")
+ assert app.events == [
+ CommandPalette.Opened(),
+ CommandPalette.OptionHighlighted(mock.ANY),
+ CommandPalette.Closed(True),
+ ]
+
+
+async def test_command_palette_option_highlighted_event():
+ app = AppWithActiveCommandPalette()
+ async with app.run_test() as pilot:
+ await pilot.press("a")
+ await pilot.press("down")
+ assert app.events == [
+ CommandPalette.Opened(),
+ CommandPalette.OptionHighlighted(mock.ANY),
+ ]
diff --git a/tests/css/test_screen_css.py b/tests/css/test_screen_css.py
index 54138fb8a5..61d26a6a38 100644
--- a/tests/css/test_screen_css.py
+++ b/tests/css/test_screen_css.py
@@ -210,7 +210,7 @@ async def test_screen_css_switch_screen_type_by_name():
class MyApp(SwitchBaseApp):
SCREENS = {"screenwithcss": ScreenWithCSS}
- def key_p(self):
+ async def key_p(self):
self.switch_screen("screenwithcss")
def key_o(self):
diff --git a/tests/deadlock.py b/tests/deadlock.py
new file mode 100644
index 0000000000..b27472b1df
--- /dev/null
+++ b/tests/deadlock.py
@@ -0,0 +1,21 @@
+"""
+Called by test_pipe.py
+
+"""
+
+from textual.app import App
+from textual.binding import Binding
+from textual.widgets import Footer
+
+
+class MyApp(App[None]):
+ BINDINGS = [
+ Binding(key="q", action="quit", description="Quit the app"),
+ ]
+
+ def compose(self):
+ yield Footer()
+
+
+app = MyApp()
+app.run()
diff --git a/tests/document/test_document.py b/tests/document/test_document.py
index ad1c82b834..ebf8474c8e 100644
--- a/tests/document/test_document.py
+++ b/tests/document/test_document.py
@@ -137,3 +137,16 @@ def test_location_from_index(text):
len(lines) - 1,
len(lines[-1]),
)
+
+
+@pytest.mark.parametrize(
+ "text", [TEXT, TEXT_NEWLINE, TEXT_WINDOWS, TEXT_WINDOWS_NEWLINE]
+)
+def test_document_end(text):
+ """The location is always what we expect."""
+ document = Document(text)
+ expected_line_number = (
+ len(text.splitlines()) if text.endswith("\n") else len(text.splitlines()) - 1
+ )
+ expected_pos = 0 if text.endswith("\n") else (len(text.splitlines()[-1]))
+ assert document.end == (expected_line_number, expected_pos)
diff --git a/tests/listview/test_listview_initial_index.py b/tests/listview/test_listview_initial_index.py
new file mode 100644
index 0000000000..515de4f044
--- /dev/null
+++ b/tests/listview/test_listview_initial_index.py
@@ -0,0 +1,42 @@
+import pytest
+
+from textual.app import App, ComposeResult
+from textual.widgets import Label, ListItem, ListView
+
+
+@pytest.mark.parametrize(
+ "initial_index,expected_index",
+ [
+ (0, 1),
+ (1, 1),
+ (2, 4),
+ (3, 4),
+ (4, 4),
+ (5, 5),
+ (6, 7),
+ (7, 7),
+ (8, 1),
+ ],
+)
+async def test_listview_initial_index(initial_index, expected_index) -> None:
+ """Regression test for https://github.com/Textualize/textual/issues/4449"""
+
+ class ListViewDisabledItemsApp(App[None]):
+ def compose(self) -> ComposeResult:
+ yield ListView(
+ ListItem(Label("0"), disabled=True),
+ ListItem(Label("1")),
+ ListItem(Label("2"), disabled=True),
+ ListItem(Label("3"), disabled=True),
+ ListItem(Label("4")),
+ ListItem(Label("5")),
+ ListItem(Label("6"), disabled=True),
+ ListItem(Label("7")),
+ ListItem(Label("8"), disabled=True),
+ initial_index=initial_index,
+ )
+
+ app = ListViewDisabledItemsApp()
+ async with app.run_test() as pilot:
+ list_view = pilot.app.query_one(ListView)
+ assert list_view._index == expected_index
diff --git a/tests/listview/test_listview_navigation.py b/tests/listview/test_listview_navigation.py
index 4a93bb2695..680b8ced68 100644
--- a/tests/listview/test_listview_navigation.py
+++ b/tests/listview/test_listview_navigation.py
@@ -35,7 +35,6 @@ async def test_keyboard_navigation_with_disabled_items() -> None:
await pilot.press("up")
assert app.highlighted == [
- None,
"1",
"4",
"5",
diff --git a/tests/listview/test_listview_remove_items.py b/tests/listview/test_listview_remove_items.py
new file mode 100644
index 0000000000..43501cdf19
--- /dev/null
+++ b/tests/listview/test_listview_remove_items.py
@@ -0,0 +1,32 @@
+from textual.app import App, ComposeResult
+from textual.widgets import ListView, ListItem, Label
+
+
+class ListViewApp(App[None]):
+ def compose(self) -> ComposeResult:
+ yield ListView(
+ ListItem(Label("0")),
+ ListItem(Label("1")),
+ ListItem(Label("2")),
+ ListItem(Label("3")),
+ ListItem(Label("4")),
+ ListItem(Label("5")),
+ ListItem(Label("6")),
+ ListItem(Label("7")),
+ ListItem(Label("8")),
+ )
+
+
+async def test_listview_remove_items() -> None:
+ """Regression test for https://github.com/Textualize/textual/issues/4735"""
+ app = ListViewApp()
+ async with app.run_test() as pilot:
+ listview = pilot.app.query_one(ListView)
+ assert len(listview) == 9
+ await listview.remove_items(range(4, 9))
+ assert len(listview) == 4
+
+
+if __name__ == "__main__":
+ app = ListViewApp()
+ app.run()
diff --git a/tests/option_list/test_option_list_mouse_click.py b/tests/option_list/test_option_list_mouse_click.py
new file mode 100644
index 0000000000..ec0182c470
--- /dev/null
+++ b/tests/option_list/test_option_list_mouse_click.py
@@ -0,0 +1,45 @@
+from __future__ import annotations
+
+from textual import on
+from textual.app import App, ComposeResult
+from textual.widgets import OptionList
+from textual.widgets.option_list import Option, Separator
+
+
+class OptionListApp(App[None]):
+ messages: list[str] = []
+
+ def compose(self) -> ComposeResult:
+ yield OptionList(
+ Option("0"),
+ Separator(),
+ Option("1"),
+ )
+
+ @on(OptionList.OptionMessage)
+ def log_option_message(
+ self,
+ event: OptionList.OptionMessage,
+ ) -> None:
+ self.messages.append(event.__class__.__name__)
+
+
+async def test_option_list_clicking_separator() -> None:
+ """Regression test for https://github.com/Textualize/textual/issues/4710"""
+ app = OptionListApp()
+ async with app.run_test() as pilot:
+ option_list = pilot.app.query_one(OptionList)
+ expected_messages = ["OptionHighlighted"] # Initial highlight
+ assert option_list.highlighted == 0
+ assert app.messages == expected_messages
+
+ # Select the second option with the mouse
+ await pilot.click(OptionList, offset=(3, 3))
+ expected_messages.extend(["OptionHighlighted", "OptionSelected"])
+ assert option_list.highlighted == 1
+ assert app.messages == expected_messages
+
+ # Click the separator - there should be no change
+ await pilot.click(OptionList, offset=(3, 2))
+ assert option_list.highlighted == 1
+ assert app.messages == expected_messages
diff --git a/tests/selection_list/test_selection_list_create.py b/tests/selection_list/test_selection_list_create.py
index e189a3c9d5..114bb69bd3 100644
--- a/tests/selection_list/test_selection_list_create.py
+++ b/tests/selection_list/test_selection_list_create.py
@@ -114,3 +114,12 @@ async def test_options_are_available_soon() -> None:
selection = Selection("", 0, id="some_id")
selection_list = SelectionList[int](selection)
assert selection_list.get_option("some_id") is selection
+
+
+async def test_removing_option_updates_indexes() -> None:
+ async with SelectionListApp().run_test() as pilot:
+ selections = pilot.app.query_one(SelectionList)
+ assert selections._values == {n: n for n in range(5)}
+
+ selections.remove_option_at_index(0)
+ assert selections._values == {n + 1: n for n in range(4)}
diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
index 5941436dd7..5efd95785d 100644
--- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
+++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
@@ -21,139 +21,139 @@
font-weight: 700;
}
- .terminal-904862513-matrix {
+ .terminal-1827861245-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-904862513-title {
+ .terminal-1827861245-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-904862513-r1 { fill: #cad1d6 }
- .terminal-904862513-r2 { fill: #7ae998 }
- .terminal-904862513-r3 { fill: #c5c8c6 }
- .terminal-904862513-r4 { fill: #4ebf71;font-weight: bold }
- .terminal-904862513-r5 { fill: #008139 }
- .terminal-904862513-r6 { fill: #e3dbce }
- .terminal-904862513-r7 { fill: #e1e1e1 }
- .terminal-904862513-r8 { fill: #e76580 }
- .terminal-904862513-r9 { fill: #f5e5e9;font-weight: bold }
- .terminal-904862513-r10 { fill: #780028 }
+ .terminal-1827861245-r1 { fill: #cad1d6 }
+ .terminal-1827861245-r2 { fill: #7ae998 }
+ .terminal-1827861245-r3 { fill: #c5c8c6 }
+ .terminal-1827861245-r4 { fill: #4ebf71;font-weight: bold }
+ .terminal-1827861245-r5 { fill: #008139 }
+ .terminal-1827861245-r6 { fill: #e3dbce }
+ .terminal-1827861245-r7 { fill: #e1e1e1 }
+ .terminal-1827861245-r8 { fill: #e76580 }
+ .terminal-1827861245-r9 { fill: #f5e5e9;font-weight: bold }
+ .terminal-1827861245-r10 { fill: #780028 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AlignContainersApp
+ AlignContainersApp
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- center
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
-
-
-
-
-
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- middle
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
-
-
-
-
-
-
-
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ center
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+
+
+
+
+
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ middle
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+
+
+
+
+
+
+
@@ -184,147 +184,147 @@
font-weight: 700;
}
- .terminal-1605463770-matrix {
+ .terminal-4256246556-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1605463770-title {
+ .terminal-4256246556-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1605463770-r1 { fill: #1f1f1f }
- .terminal-1605463770-r2 { fill: #c5c8c6 }
- .terminal-1605463770-r3 { fill: #aa3731 }
- .terminal-1605463770-r4 { fill: #c8837f }
- .terminal-1605463770-r5 { fill: #448c27 }
- .terminal-1605463770-r6 { fill: #8ab679 }
- .terminal-1605463770-r7 { fill: #cb9000 }
- .terminal-1605463770-r8 { fill: #dbb862 }
- .terminal-1605463770-r9 { fill: #325cc0 }
- .terminal-1605463770-r10 { fill: #8099d5 }
- .terminal-1605463770-r11 { fill: #7a3e9d }
- .terminal-1605463770-r12 { fill: #ab87c0 }
- .terminal-1605463770-r13 { fill: #0083b2 }
- .terminal-1605463770-r14 { fill: #62b0cc }
- .terminal-1605463770-r15 { fill: #f7f7f7 }
- .terminal-1605463770-r16 { fill: #f6f6f6 }
- .terminal-1605463770-r17 { fill: #000000 }
- .terminal-1605463770-r18 { fill: #626262 }
+ .terminal-4256246556-r1 { fill: #1f1f1f }
+ .terminal-4256246556-r2 { fill: #c5c8c6 }
+ .terminal-4256246556-r3 { fill: #aa3731 }
+ .terminal-4256246556-r4 { fill: #c8837f }
+ .terminal-4256246556-r5 { fill: #448c27 }
+ .terminal-4256246556-r6 { fill: #8ab679 }
+ .terminal-4256246556-r7 { fill: #cb9000 }
+ .terminal-4256246556-r8 { fill: #dbb862 }
+ .terminal-4256246556-r9 { fill: #325cc0 }
+ .terminal-4256246556-r10 { fill: #8099d5 }
+ .terminal-4256246556-r11 { fill: #7a3e9d }
+ .terminal-4256246556-r12 { fill: #ab87c0 }
+ .terminal-4256246556-r13 { fill: #0083b2 }
+ .terminal-4256246556-r14 { fill: #62b0cc }
+ .terminal-4256246556-r15 { fill: #f7f7f7 }
+ .terminal-4256246556-r16 { fill: #f6f6f6 }
+ .terminal-4256246556-r17 { fill: #000000 }
+ .terminal-4256246556-r18 { fill: #626262 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AnsiMappingApp
+ AnsiMappingApp
-
-
-
- Foreground & background
- red
- dim red
- green
- dim green
- yellow
- dim yellow
- blue
- dim blue
- magenta
- dim magenta
- cyan
- dim cyan
- white
- dim white
- black
- dim black
-
-
-
-
-
-
+
+
+
+ Foreground & background
+ red
+ dim red
+ green
+ dim green
+ yellow
+ dim yellow
+ blue
+ dim blue
+ magenta
+ dim magenta
+ cyan
+ dim cyan
+ white
+ dim white
+ black
+ dim black
+
+
+
+
+
+
@@ -355,145 +355,145 @@
font-weight: 700;
}
- .terminal-747464051-matrix {
+ .terminal-1802641872-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-747464051-title {
+ .terminal-1802641872-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-747464051-r1 { fill: #e1e1e1 }
- .terminal-747464051-r2 { fill: #c5c8c6 }
- .terminal-747464051-r3 { fill: #f4005f }
- .terminal-747464051-r4 { fill: #9e0c45 }
- .terminal-747464051-r5 { fill: #98e024 }
- .terminal-747464051-r6 { fill: #679221 }
- .terminal-747464051-r7 { fill: #fd971f }
- .terminal-747464051-r8 { fill: #a3661e }
- .terminal-747464051-r9 { fill: #9d65ff }
- .terminal-747464051-r10 { fill: #6a48a5 }
- .terminal-747464051-r11 { fill: #58d1eb }
- .terminal-747464051-r12 { fill: #408999 }
- .terminal-747464051-r13 { fill: #c4c5b5 }
- .terminal-747464051-r14 { fill: #818278 }
- .terminal-747464051-r15 { fill: #1a1a1a }
- .terminal-747464051-r16 { fill: #1b1b1b }
+ .terminal-1802641872-r1 { fill: #e1e1e1 }
+ .terminal-1802641872-r2 { fill: #c5c8c6 }
+ .terminal-1802641872-r3 { fill: #f4005f }
+ .terminal-1802641872-r4 { fill: #9e0c45 }
+ .terminal-1802641872-r5 { fill: #98e024 }
+ .terminal-1802641872-r6 { fill: #679221 }
+ .terminal-1802641872-r7 { fill: #fd971f }
+ .terminal-1802641872-r8 { fill: #a3661e }
+ .terminal-1802641872-r9 { fill: #9d65ff }
+ .terminal-1802641872-r10 { fill: #6a48a5 }
+ .terminal-1802641872-r11 { fill: #58d1eb }
+ .terminal-1802641872-r12 { fill: #408999 }
+ .terminal-1802641872-r13 { fill: #c4c5b5 }
+ .terminal-1802641872-r14 { fill: #818278 }
+ .terminal-1802641872-r15 { fill: #1a1a1a }
+ .terminal-1802641872-r16 { fill: #1b1b1b }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AnsiMappingApp
+ AnsiMappingApp
-
-
-
- Foreground & background
- red
- dim red
- green
- dim green
- yellow
- dim yellow
- blue
- dim blue
- magenta
- dim magenta
- cyan
- dim cyan
- white
- dim white
- black
- dim black
-
-
-
-
-
-
+
+
+
+ Foreground & background
+ red
+ dim red
+ green
+ dim green
+ yellow
+ dim yellow
+ blue
+ dim blue
+ magenta
+ dim magenta
+ cyan
+ dim cyan
+ white
+ dim white
+ black
+ dim black
+
+
+
+
+
+
@@ -524,134 +524,134 @@
font-weight: 700;
}
- .terminal-530450620-matrix {
+ .terminal-2159330678-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-530450620-title {
+ .terminal-2159330678-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-530450620-r1 { fill: #e1e1e1 }
- .terminal-530450620-r2 { fill: #c5c8c6 }
- .terminal-530450620-r3 { fill: #1e1e1e }
- .terminal-530450620-r4 { fill: #121212 }
- .terminal-530450620-r5 { fill: #e2e2e2 }
+ .terminal-2159330678-r1 { fill: #e1e1e1 }
+ .terminal-2159330678-r2 { fill: #c5c8c6 }
+ .terminal-2159330678-r3 { fill: #1e1e1e }
+ .terminal-2159330678-r4 { fill: #121212 }
+ .terminal-2159330678-r5 { fill: #e2e2e2 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AppBlurApp
+ AppBlurApp
-
-
-
-
-
-
-
-
-
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ This should be the blur style ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ This should also be the blur style ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ This should be the blur style ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ This should also be the blur style ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
@@ -682,141 +682,141 @@
font-weight: 700;
}
- .terminal-3685857257-matrix {
+ .terminal-3141096165-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3685857257-title {
+ .terminal-3141096165-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3685857257-r1 { fill: #00ffff }
- .terminal-3685857257-r2 { fill: #c5c8c6 }
- .terminal-3685857257-r3 { fill: #e1e1e1 }
- .terminal-3685857257-r4 { fill: #008000 }
- .terminal-3685857257-r5 { fill: #ff0000 }
- .terminal-3685857257-r6 { fill: #e1e1e1;font-weight: bold }
- .terminal-3685857257-r7 { fill: #dde6ed }
+ .terminal-3141096165-r1 { fill: #00ffff }
+ .terminal-3141096165-r2 { fill: #c5c8c6 }
+ .terminal-3141096165-r3 { fill: #e1e1e1 }
+ .terminal-3141096165-r4 { fill: #008000 }
+ .terminal-3141096165-r5 { fill: #ff0000 }
+ .terminal-3141096165-r6 { fill: #e1e1e1;font-weight: bold }
+ .terminal-3141096165-r7 { fill: #dde6ed }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- FRApp
+ FRApp
-
-
-
- ┌ ────────────────────────────────────────────────────────────────────────────── ┐
- │ ┌ ──────────────────────────── ┐ │
- │ │ Hello one line │ │
- │ │ ┌ ────────────────────────── ┐ │ │
- │ │ │ Widget #child │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ └ ────────────────────────── ┘ │ │
- │ │ │ │
- │ │ Two │ │
- │ │ Lines with 1x2 margin │ │
- │ │ │ │
- │ └ ──────────────────────────── ┘ │
- └ ────────────────────────────────────────────────────────────────────────────── ┘
+
+
+
+ ┌──────────────────────────────────────────────────────────────────────────────┐
+ │ ┌────────────────────────────┐ │
+ │ │ Hello one line │ │
+ │ │ ┌──────────────────────────┐ │ │
+ │ │ │ Widget #child │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ └──────────────────────────┘ │ │
+ │ │ │ │
+ │ │ Two │ │
+ │ │ Lines with 1x2 margin │ │
+ │ │ │ │
+ │ └────────────────────────────┘ │
+ └──────────────────────────────────────────────────────────────────────────────┘
@@ -846,136 +846,136 @@
font-weight: 700;
}
- .terminal-2376794324-matrix {
+ .terminal-2558549670-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2376794324-title {
+ .terminal-2558549670-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2376794324-r1 { fill: #008000 }
- .terminal-2376794324-r2 { fill: #c5c8c6 }
- .terminal-2376794324-r3 { fill: #e1e1e1 }
- .terminal-2376794324-r4 { fill: #1e1e1e }
- .terminal-2376794324-r5 { fill: #121212 }
- .terminal-2376794324-r6 { fill: #e2e2e2 }
+ .terminal-2558549670-r1 { fill: #008000 }
+ .terminal-2558549670-r2 { fill: #c5c8c6 }
+ .terminal-2558549670-r3 { fill: #e1e1e1 }
+ .terminal-2558549670-r4 { fill: #1e1e1e }
+ .terminal-2558549670-r5 { fill: #121212 }
+ .terminal-2558549670-r6 { fill: #e2e2e2 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- GridApp
+ GridApp
-
-
-
- ┌ ────────────────────────────────────────────────────────────────────────────── ┐
- │ foo ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ │
- │ ▊ ▎ │
- │ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ │
- │ Longer label ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ │
- │ ▊ ▎ │
- │ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ │
- └ ────────────────────────────────────────────────────────────────────────────── ┘
- ┌ ────────────────────────────────────────────────────────────────────────────── ┐
- │ foo ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ │
- │ ▊ ▎ │
- │ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ │
- │ Longer label ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ │
- │ ▊ ▎ │
- │ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ │
- └ ────────────────────────────────────────────────────────────────────────────── ┘
- ┌ ────────────────────────────────────────────────────────────────────────────── ┐
- │ foo bar foo bar foo bar foo ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ │
- │ bar foo bar foo bar foo bar ▊ ▎ │
- │ foo bar foo bar foo bar ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ │
- │ Longer label ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ │
- │ ▊ ▎ │
- │ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ │
- └ ────────────────────────────────────────────────────────────────────────────── ┘
+
+
+
+ ┌──────────────────────────────────────────────────────────────────────────────┐
+ │ foo ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ │
+ │ ▊ ▎ │
+ │ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ │
+ │ Longer label ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ │
+ │ ▊ ▎ │
+ │ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ │
+ └──────────────────────────────────────────────────────────────────────────────┘
+ ┌──────────────────────────────────────────────────────────────────────────────┐
+ │ foo ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ │
+ │ ▊ ▎ │
+ │ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ │
+ │ Longer label ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ │
+ │ ▊ ▎ │
+ │ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ │
+ └──────────────────────────────────────────────────────────────────────────────┘
+ ┌──────────────────────────────────────────────────────────────────────────────┐
+ │ foo bar foo bar foo bar foo ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ │
+ │ bar foo bar foo bar foo bar ▊ ▎ │
+ │ foo bar foo bar foo bar ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ │
+ │ Longer label ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ │
+ │ ▊ ▎ │
+ │ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ │
+ └──────────────────────────────────────────────────────────────────────────────┘
@@ -1005,136 +1005,303 @@
font-weight: 700;
}
- .terminal-3669917786-matrix {
+ .terminal-3513309286-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3669917786-title {
+ .terminal-3513309286-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3669917786-r1 { fill: #c5c8c6 }
- .terminal-3669917786-r2 { fill: #e3e3e3 }
- .terminal-3669917786-r3 { fill: #e1e1e1 }
- .terminal-3669917786-r4 { fill: #ff0000 }
- .terminal-3669917786-r5 { fill: #dde8f3;font-weight: bold }
- .terminal-3669917786-r6 { fill: #ddedf9 }
+ .terminal-3513309286-r1 { fill: #c5c8c6 }
+ .terminal-3513309286-r2 { fill: #e3e3e3 }
+ .terminal-3513309286-r3 { fill: #e1e1e1 }
+ .terminal-3513309286-r4 { fill: #ff0000 }
+ .terminal-3513309286-r5 { fill: #fea62b;font-weight: bold }
+ .terminal-3513309286-r6 { fill: #a7a9ab }
+ .terminal-3513309286-r7 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- GridHeightAuto
+ GridHeightAuto
-
-
-
- ⭘ GridHeightAuto
- Here is some text before the grid
- ┌ ────────────────────────────────────────────────────────────────────────────── ┐
- │ Cell #0 Cell #1 Cell #2 │
- │ Cell #3 Cell #4 Cell #5 │
- │ Cell #6 Cell #7 Cell #8 │
- └ ────────────────────────────────────────────────────────────────────────────── ┘
- Here is some text after the grid
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- G Grid V Vertical H Horizontal C Container
+
+
+
+ ⭘ GridHeightAuto
+ Here is some text before the grid
+ ┌──────────────────────────────────────────────────────────────────────────────┐
+ │ Cell #0 Cell #1 Cell #2 │
+ │ Cell #3 Cell #4 Cell #5 │
+ │ Cell #6 Cell #7 Cell #8 │
+ └──────────────────────────────────────────────────────────────────────────────┘
+ Here is some text after the grid
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ g Grid v Vertical h Horizontal c Container
+
+
+
+
+ '''
+# ---
+# name: test_auto_tab_active
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ExampleApp
+
+
+
+
+
+
+
+
+
+
+ Parent 1 Parent 2
+ ━━━━━━━━━━━━╸ ━━━━━━━━ ╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+
+ Child 2.1 Child 2.2
+ ━━━━━━━━━━━━━╸ ━━━━━━━━━ ╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Button 2.2
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+
+
+
+
+
+
+
+
+
+
+
+ SPACE Focus button 2.2
@@ -1164,202 +1331,202 @@
font-weight: 700;
}
- .terminal-3752476664-matrix {
+ .terminal-1765028414-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3752476664-title {
+ .terminal-1765028414-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3752476664-r1 { fill: #c5c8c6 }
- .terminal-3752476664-r2 { fill: #e3e3e3 }
- .terminal-3752476664-r3 { fill: #004578 }
- .terminal-3752476664-r4 { fill: #e1e1e1 }
- .terminal-3752476664-r5 { fill: #632ca6 }
- .terminal-3752476664-r6 { fill: #dde6ed;font-weight: bold }
- .terminal-3752476664-r7 { fill: #14191f }
- .terminal-3752476664-r8 { fill: #23568b }
+ .terminal-1765028414-r1 { fill: #c5c8c6 }
+ .terminal-1765028414-r2 { fill: #e3e3e3 }
+ .terminal-1765028414-r3 { fill: #004578 }
+ .terminal-1765028414-r4 { fill: #e1e1e1 }
+ .terminal-1765028414-r5 { fill: #632ca6 }
+ .terminal-1765028414-r6 { fill: #dde6ed;font-weight: bold }
+ .terminal-1765028414-r7 { fill: #14191f }
+ .terminal-1765028414-r8 { fill: #23568b }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MyApp
+ MyApp
-
-
-
- ⭘ MyApp
- ╭ ────────────────── ╮ ╭ ────────────────────────────────────────────────────────────────────────────────────────────────── ╮
- │ ok │ │ test │
- │ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ │ │ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ │
- │ │ │ ╭ ─ 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
+ ╭──────────────────╮╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
+ │ ok ││ test │
+ │ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ ││ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ │
+ │ ││╭─ 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 │
+ │ ││╰──────────────────────────────────────────╯╰──────────────────────────────────────────╯╰─────────│
+ │ ││ ▋ │
+ ╰──────────────────╯╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
@@ -1389,136 +1556,136 @@
font-weight: 700;
}
- .terminal-1625062503-matrix {
+ .terminal-2582506136-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1625062503-title {
+ .terminal-2582506136-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-2582506136-r1 { fill: #c5c8c6 }
+ .terminal-2582506136-r2 { fill: #e3e3e3 }
+ .terminal-2582506136-r3 { fill: #1e1e1e }
+ .terminal-2582506136-r4 { fill: #0178d4 }
+ .terminal-2582506136-r5 { fill: #e1e1e1 }
+ .terminal-2582506136-r6 { fill: #e2e2e2 }
+ .terminal-2582506136-r7 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- InputWidthAutoApp
+ InputWidthAutoApp
-
-
-
- ⭘ InputWidthAutoApp
- ▊ ▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ Hello ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ⭘ InputWidthAutoApp
+ ▊ ▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ Hello ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1549,135 +1716,135 @@
font-weight: 700;
}
- .terminal-1381838495-matrix {
+ .terminal-4093147427-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1381838495-title {
+ .terminal-4093147427-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1381838495-r1 { fill: #454a50 }
- .terminal-1381838495-r2 { fill: #e1e1e1 }
- .terminal-1381838495-r3 { fill: #c5c8c6 }
- .terminal-1381838495-r4 { fill: #24292f;font-weight: bold }
- .terminal-1381838495-r5 { fill: #000000 }
- .terminal-1381838495-r6 { fill: #e2e3e3;font-weight: bold }
+ .terminal-4093147427-r1 { fill: #454a50 }
+ .terminal-4093147427-r2 { fill: #e1e1e1 }
+ .terminal-4093147427-r3 { fill: #c5c8c6 }
+ .terminal-4093147427-r4 { fill: #24292f;font-weight: bold }
+ .terminal-4093147427-r5 { fill: #000000 }
+ .terminal-4093147427-r6 { fill: #e2e3e3;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ButtonApp
+ ButtonApp
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-
-
-
- Hello
-
-
-
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-
-
- Hello
- World !!
-
-
-
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
-
-
-
-
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+
+
+
+ Hello
+
+
+
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+
+
+ Hello
+ World !!
+
+
+
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+
+
+
+
@@ -1685,6 +1852,164 @@
'''
# ---
+# name: test_bindings_screen_overrides_show
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ HideBindingApp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ p Binding shown
+
+
+
+
+ '''
+# ---
# name: test_blur_on_disabled
'''
@@ -1708,134 +2033,134 @@
font-weight: 700;
}
- .terminal-3271357708-matrix {
+ .terminal-3770003934-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3271357708-title {
+ .terminal-3770003934-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3271357708-r1 { fill: #1e1e1e }
- .terminal-3271357708-r2 { fill: #171717 }
- .terminal-3271357708-r3 { fill: #c5c8c6 }
- .terminal-3271357708-r4 { fill: #a7a7a7 }
- .terminal-3271357708-r5 { fill: #e1e1e1 }
+ .terminal-3770003934-r1 { fill: #1e1e1e }
+ .terminal-3770003934-r2 { fill: #171717 }
+ .terminal-3770003934-r3 { fill: #c5c8c6 }
+ .terminal-3770003934-r4 { fill: #a7a7a7 }
+ .terminal-3770003934-r5 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- BlurApp
+ BlurApp
-
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ foo ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ foo ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1866,137 +2191,137 @@
font-weight: 700;
}
- .terminal-812768284-matrix {
+ .terminal-29072690-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-812768284-title {
+ .terminal-29072690-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-812768284-r1 { fill: #1e1e1e }
- .terminal-812768284-r2 { fill: #c5c8c6 }
- .terminal-812768284-r3 { fill: #e1e1e1 }
- .terminal-812768284-r4 { fill: #183118 }
- .terminal-812768284-r5 { fill: #124512 }
- .terminal-812768284-r6 { fill: #0c580c }
- .terminal-812768284-r7 { fill: #066c06 }
- .terminal-812768284-r8 { fill: #008000 }
+ .terminal-29072690-r1 { fill: #1e1e1e }
+ .terminal-29072690-r2 { fill: #c5c8c6 }
+ .terminal-29072690-r3 { fill: #e1e1e1 }
+ .terminal-29072690-r4 { fill: #183118 }
+ .terminal-29072690-r5 { fill: #124512 }
+ .terminal-29072690-r6 { fill: #0c580c }
+ .terminal-29072690-r7 { fill: #066c06 }
+ .terminal-29072690-r8 { fill: #008000 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- BorderAlphaApp
+ BorderAlphaApp
-
-
-
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
-
-
-
-
-
+
+
+
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+
+
+
+
+
@@ -2027,133 +2352,133 @@
font-weight: 700;
}
- .terminal-1480993901-matrix {
+ .terminal-1229229535-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1480993901-title {
+ .terminal-1229229535-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1480993901-r1 { fill: #ffffff }
- .terminal-1480993901-r2 { fill: #e1e1e1 }
- .terminal-1480993901-r3 { fill: #c5c8c6 }
- .terminal-1480993901-r4 { fill: #e2e3e3;font-weight: bold }
+ .terminal-1229229535-r1 { fill: #ffffff }
+ .terminal-1229229535-r2 { fill: #e1e1e1 }
+ .terminal-1229229535-r3 { fill: #c5c8c6 }
+ .terminal-1229229535-r4 { fill: #e2e3e3;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ButtonIssue
+ ButtonIssue
-
-
-
- ┌ ────────────── ┐
- │ Test │
- └ ────────────── ┘
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ┌──────────────┐
+ │ Test │
+ └──────────────┘
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2184,136 +2509,136 @@
font-weight: 700;
}
- .terminal-898715-matrix {
+ .terminal-3701416180-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-898715-title {
+ .terminal-3701416180-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-898715-r1 { fill: #ff0000 }
- .terminal-898715-r2 { fill: #e1e1e1 }
- .terminal-898715-r3 { fill: #c5c8c6 }
- .terminal-898715-r4 { fill: #454a50 }
- .terminal-898715-r5 { fill: #24292f;font-weight: bold }
- .terminal-898715-r6 { fill: #000000 }
- .terminal-898715-r7 { fill: #e2e3e3;font-weight: bold }
+ .terminal-3701416180-r1 { fill: #ff0000 }
+ .terminal-3701416180-r2 { fill: #e1e1e1 }
+ .terminal-3701416180-r3 { fill: #c5c8c6 }
+ .terminal-3701416180-r4 { fill: #454a50 }
+ .terminal-3701416180-r5 { fill: #24292f;font-weight: bold }
+ .terminal-3701416180-r6 { fill: #000000 }
+ .terminal-3701416180-r7 { fill: #e2e3e3;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- HorizontalWidthAutoApp
+ HorizontalWidthAutoApp
-
-
-
- ┌ ──────────────────────────── ┐
- │ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ │
- │ This is a very wide button │
- │ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ │
- └ ──────────────────────────── ┘
- ┌ ──────────────────────────────────────────────────────── ┐
- │ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ │
- │ This is a very wide button This is a very wide button │
- │ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ │
- └ ──────────────────────────────────────────────────────── ┘
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ┌────────────────────────────┐
+ │ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ │
+ │ This is a very wide button │
+ │ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ │
+ └────────────────────────────┘
+ ┌────────────────────────────────────────────────────────┐
+ │ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ │
+ │ This is a very wide button This is a very wide button │
+ │ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ │
+ └────────────────────────────────────────────────────────┘
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2344,141 +2669,141 @@
font-weight: 700;
}
- .terminal-2679207537-matrix {
+ .terminal-3060571111-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2679207537-title {
+ .terminal-3060571111-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2679207537-r1 { fill: #454a50 }
- .terminal-2679207537-r2 { fill: #e1e1e1 }
- .terminal-2679207537-r3 { fill: #c5c8c6 }
- .terminal-2679207537-r4 { fill: #24292f;font-weight: bold }
- .terminal-2679207537-r5 { fill: #24292f;font-weight: bold;font-style: italic; }
- .terminal-2679207537-r6 { fill: #000000 }
- .terminal-2679207537-r7 { fill: #e2e3e3;font-weight: bold }
- .terminal-2679207537-r8 { fill: #f4005f;font-weight: bold;font-style: italic; }
- .terminal-2679207537-r9 { fill: #303336 }
- .terminal-2679207537-r10 { fill: #a7a7a7;font-weight: bold }
- .terminal-2679207537-r11 { fill: #620909;font-weight: bold;font-style: italic; }
- .terminal-2679207537-r12 { fill: #0f0f0f }
+ .terminal-3060571111-r1 { fill: #454a50 }
+ .terminal-3060571111-r2 { fill: #e1e1e1 }
+ .terminal-3060571111-r3 { fill: #c5c8c6 }
+ .terminal-3060571111-r4 { fill: #24292f;font-weight: bold }
+ .terminal-3060571111-r5 { fill: #24292f;font-weight: bold;font-style: italic; }
+ .terminal-3060571111-r6 { fill: #000000 }
+ .terminal-3060571111-r7 { fill: #e2e3e3;font-weight: bold }
+ .terminal-3060571111-r8 { fill: #f4005f;font-weight: bold;font-style: italic; }
+ .terminal-3060571111-r9 { fill: #303336 }
+ .terminal-3060571111-r10 { fill: #a7a7a7;font-weight: bold }
+ .terminal-3060571111-r11 { fill: #620909;font-weight: bold;font-style: italic; }
+ .terminal-3060571111-r12 { fill: #0f0f0f }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ButtonsWithMarkupApp
+ ButtonsWithMarkupApp
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Focused Button
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Blurred Button
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Disabled Button
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Focused Button
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Blurred Button
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Disabled Button
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2509,134 +2834,134 @@
font-weight: 700;
}
- .terminal-3403690919-matrix {
+ .terminal-4065186018-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3403690919-title {
+ .terminal-4065186018-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3403690919-r1 { fill: #454a50 }
- .terminal-3403690919-r2 { fill: #e1e1e1 }
- .terminal-3403690919-r3 { fill: #c5c8c6 }
- .terminal-3403690919-r4 { fill: #24292f;font-weight: bold }
- .terminal-3403690919-r5 { fill: #000000 }
+ .terminal-4065186018-r1 { fill: #454a50 }
+ .terminal-4065186018-r2 { fill: #e1e1e1 }
+ .terminal-4065186018-r3 { fill: #c5c8c6 }
+ .terminal-4065186018-r4 { fill: #24292f;font-weight: bold }
+ .terminal-4065186018-r5 { fill: #000000 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ButtonWithMultilineLabelApp
+ ButtonWithMultilineLabelApp
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Button
- with
- multi-line
- label
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Button
+ with
+ multi-line
+ label
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2667,162 +2992,162 @@
font-weight: 700;
}
- .terminal-3236763676-matrix {
+ .terminal-1520326498-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3236763676-title {
+ .terminal-1520326498-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3236763676-r1 { fill: #e1e1e1 }
- .terminal-3236763676-r2 { fill: #c5c8c6 }
- .terminal-3236763676-r3 { fill: #e1e1e1;font-weight: bold }
- .terminal-3236763676-r4 { fill: #454a50 }
- .terminal-3236763676-r5 { fill: #303336 }
- .terminal-3236763676-r6 { fill: #24292f;font-weight: bold }
- .terminal-3236763676-r7 { fill: #a7a7a7;font-weight: bold }
- .terminal-3236763676-r8 { fill: #000000 }
- .terminal-3236763676-r9 { fill: #0f0f0f }
- .terminal-3236763676-r10 { fill: #507bb3 }
- .terminal-3236763676-r11 { fill: #364b66 }
- .terminal-3236763676-r12 { fill: #dde6ed;font-weight: bold }
- .terminal-3236763676-r13 { fill: #a5a9ac;font-weight: bold }
- .terminal-3236763676-r14 { fill: #001541 }
- .terminal-3236763676-r15 { fill: #0f192e }
- .terminal-3236763676-r16 { fill: #7ae998 }
- .terminal-3236763676-r17 { fill: #4a8159 }
- .terminal-3236763676-r18 { fill: #0a180e;font-weight: bold }
- .terminal-3236763676-r19 { fill: #0e1510;font-weight: bold }
- .terminal-3236763676-r20 { fill: #008139 }
- .terminal-3236763676-r21 { fill: #0f4e2a }
- .terminal-3236763676-r22 { fill: #ffcf56 }
- .terminal-3236763676-r23 { fill: #8b7439 }
- .terminal-3236763676-r24 { fill: #211505;font-weight: bold }
- .terminal-3236763676-r25 { fill: #19140c;font-weight: bold }
- .terminal-3236763676-r26 { fill: #b86b00 }
- .terminal-3236763676-r27 { fill: #68430f }
- .terminal-3236763676-r28 { fill: #e76580 }
- .terminal-3236763676-r29 { fill: #80404d }
- .terminal-3236763676-r30 { fill: #f5e5e9;font-weight: bold }
- .terminal-3236763676-r31 { fill: #b0a8aa;font-weight: bold }
- .terminal-3236763676-r32 { fill: #780028 }
- .terminal-3236763676-r33 { fill: #4a0f22 }
+ .terminal-1520326498-r1 { fill: #e1e1e1 }
+ .terminal-1520326498-r2 { fill: #c5c8c6 }
+ .terminal-1520326498-r3 { fill: #e1e1e1;font-weight: bold }
+ .terminal-1520326498-r4 { fill: #454a50 }
+ .terminal-1520326498-r5 { fill: #303336 }
+ .terminal-1520326498-r6 { fill: #24292f;font-weight: bold }
+ .terminal-1520326498-r7 { fill: #a7a7a7;font-weight: bold }
+ .terminal-1520326498-r8 { fill: #000000 }
+ .terminal-1520326498-r9 { fill: #0f0f0f }
+ .terminal-1520326498-r10 { fill: #507bb3 }
+ .terminal-1520326498-r11 { fill: #364b66 }
+ .terminal-1520326498-r12 { fill: #dde6ed;font-weight: bold }
+ .terminal-1520326498-r13 { fill: #a5a9ac;font-weight: bold }
+ .terminal-1520326498-r14 { fill: #001541 }
+ .terminal-1520326498-r15 { fill: #0f192e }
+ .terminal-1520326498-r16 { fill: #7ae998 }
+ .terminal-1520326498-r17 { fill: #4a8159 }
+ .terminal-1520326498-r18 { fill: #0a180e;font-weight: bold }
+ .terminal-1520326498-r19 { fill: #0e1510;font-weight: bold }
+ .terminal-1520326498-r20 { fill: #008139 }
+ .terminal-1520326498-r21 { fill: #0f4e2a }
+ .terminal-1520326498-r22 { fill: #ffcf56 }
+ .terminal-1520326498-r23 { fill: #8b7439 }
+ .terminal-1520326498-r24 { fill: #211505;font-weight: bold }
+ .terminal-1520326498-r25 { fill: #19140c;font-weight: bold }
+ .terminal-1520326498-r26 { fill: #b86b00 }
+ .terminal-1520326498-r27 { fill: #68430f }
+ .terminal-1520326498-r28 { fill: #e76580 }
+ .terminal-1520326498-r29 { fill: #80404d }
+ .terminal-1520326498-r30 { fill: #f5e5e9;font-weight: bold }
+ .terminal-1520326498-r31 { fill: #b0a8aa;font-weight: bold }
+ .terminal-1520326498-r32 { fill: #780028 }
+ .terminal-1520326498-r33 { fill: #4a0f22 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ButtonsApp
+ ButtonsApp
-
-
-
-
- Standard Buttons Disabled Buttons
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Default Default
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Primary! Primary!
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Success! Success!
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Warning! Warning!
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Error! Error!
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
+
+
+
+
+ Standard Buttons Disabled Buttons
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Default Default
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Primary! Primary!
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Success! Success!
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Warning! Warning!
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Error! Error!
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
@@ -3020,136 +3345,138 @@
font-weight: 700;
}
- .terminal-4171601622-matrix {
+ .terminal-4248372996-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-4171601622-title {
+ .terminal-4248372996-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-4171601622-r1 { fill: #121212 }
- .terminal-4171601622-r2 { fill: #c5c8c6 }
- .terminal-4171601622-r3 { fill: #ddedf9 }
- .terminal-4171601622-r4 { fill: #e2e2e2 }
- .terminal-4171601622-r5 { fill: #e1e1e1 }
- .terminal-4171601622-r6 { fill: #dde8f3;font-weight: bold }
+ .terminal-4248372996-r1 { fill: #121212 }
+ .terminal-4248372996-r2 { fill: #c5c8c6 }
+ .terminal-4248372996-r3 { fill: #ddedf9 }
+ .terminal-4248372996-r4 { fill: #e2e2e2 }
+ .terminal-4248372996-r5 { fill: #e1e1e1 }
+ .terminal-4248372996-r6 { fill: #fea62b;font-weight: bold }
+ .terminal-4248372996-r7 { fill: #a7a9ab }
+ .terminal-4248372996-r8 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- CollapsibleApp
+ CollapsibleApp
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- ▶ Leto
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- ▶ Jessica
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- ▶ Paul
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- C Collapse All E Expand All
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ ▶ Leto
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ ▶ Jessica
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ ▶ Paul
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ c Collapse All e Expand All
@@ -3179,134 +3506,134 @@
font-weight: 700;
}
- .terminal-3510718873-matrix {
+ .terminal-693385943-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3510718873-title {
+ .terminal-693385943-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3510718873-r1 { fill: #121212 }
- .terminal-3510718873-r2 { fill: #c5c8c6 }
- .terminal-3510718873-r3 { fill: #ddedf9 }
- .terminal-3510718873-r4 { fill: #e2e2e2 }
- .terminal-3510718873-r5 { fill: #e1e1e1 }
+ .terminal-693385943-r1 { fill: #121212 }
+ .terminal-693385943-r2 { fill: #c5c8c6 }
+ .terminal-693385943-r3 { fill: #ddedf9 }
+ .terminal-693385943-r4 { fill: #e2e2e2 }
+ .terminal-693385943-r5 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- CollapsibleApp
+ CollapsibleApp
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- >>> Toggle v Toggle
-
- Hello, world.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ >>> Toggle v Toggle
+
+ Hello, world.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -3337,137 +3664,140 @@
font-weight: 700;
}
- .terminal-4078801769-matrix {
+ .terminal-1549040424-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-4078801769-title {
+ .terminal-1549040424-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-4078801769-r1 { fill: #121212 }
- .terminal-4078801769-r2 { fill: #e1e1e1 }
- .terminal-4078801769-r3 { fill: #c5c8c6 }
- .terminal-4078801769-r4 { fill: #ddedf9 }
- .terminal-4078801769-r5 { fill: #e2e2e2 }
- .terminal-4078801769-r6 { fill: #0053aa }
- .terminal-4078801769-r7 { fill: #dde8f3;font-weight: bold }
+ .terminal-1549040424-r1 { fill: #121212 }
+ .terminal-1549040424-r2 { fill: #e1e1e1 }
+ .terminal-1549040424-r3 { fill: #c5c8c6 }
+ .terminal-1549040424-r4 { fill: #ddedf9 }
+ .terminal-1549040424-r5 { fill: #e2e2e2 }
+ .terminal-1549040424-r6 { fill: #4ebf71;font-weight: bold }
+ .terminal-1549040424-r7 { fill: #14191f }
+ .terminal-1549040424-r8 { fill: #fea62b;font-weight: bold }
+ .terminal-1549040424-r9 { fill: #a7a9ab }
+ .terminal-1549040424-r10 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- CollapsibleApp
+ CollapsibleApp
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- ▼ Leto
-
- # Duke Leto I Atreides
-
- Head of House Atreides.
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- ▼ Jessica
-
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▎ ▊
- ▎ Lady Jessica ▊
- ▎ ▊
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
- Bene Gesserit and concubine of Leto, and mother of Paul and Alia.
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- ▼ Paul
-
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- C Coll apse All E Expand All
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ ▼ Leto
+
+ # Duke Leto I Atreides
+
+ Head of House Atreides.
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ ▼ Jessica
+
+
+
+ Lady Jessica
+
+ Bene Gesserit and concubine of Leto, and mother of Paul and Alia.
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ ▼ Paul ▆▆
+
+
+
+ c Collapse All e Expand All
@@ -3497,135 +3827,135 @@
font-weight: 700;
}
- .terminal-3576423522-matrix {
+ .terminal-2781425159-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3576423522-title {
+ .terminal-2781425159-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3576423522-r1 { fill: #121212 }
- .terminal-3576423522-r2 { fill: #c5c8c6 }
- .terminal-3576423522-r3 { fill: #ddedf9 }
- .terminal-3576423522-r4 { fill: #e2e2e2 }
- .terminal-3576423522-r5 { fill: #e3e3e3 }
- .terminal-3576423522-r6 { fill: #e1e1e1 }
+ .terminal-2781425159-r1 { fill: #121212 }
+ .terminal-2781425159-r2 { fill: #c5c8c6 }
+ .terminal-2781425159-r3 { fill: #ddedf9 }
+ .terminal-2781425159-r4 { fill: #e2e2e2 }
+ .terminal-2781425159-r5 { fill: #e3e3e3 }
+ .terminal-2781425159-r6 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- CollapsibleApp
+ CollapsibleApp
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- ▼ Toggle
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- ▶ Toggle
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ ▼ Toggle
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ ▶ Toggle
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -3656,137 +3986,139 @@
font-weight: 700;
}
- .terminal-4121784704-matrix {
+ .terminal-844098826-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-4121784704-title {
+ .terminal-844098826-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-4121784704-r1 { fill: #121212 }
- .terminal-4121784704-r2 { fill: #c5c8c6 }
- .terminal-4121784704-r3 { fill: #ddedf9 }
- .terminal-4121784704-r4 { fill: #e2e2e2 }
- .terminal-4121784704-r5 { fill: #0053aa }
- .terminal-4121784704-r6 { fill: #dde8f3;font-weight: bold }
- .terminal-4121784704-r7 { fill: #e1e1e1 }
+ .terminal-844098826-r1 { fill: #121212 }
+ .terminal-844098826-r2 { fill: #c5c8c6 }
+ .terminal-844098826-r3 { fill: #ddedf9 }
+ .terminal-844098826-r4 { fill: #e2e2e2 }
+ .terminal-844098826-r5 { fill: #4ebf71;font-weight: bold }
+ .terminal-844098826-r6 { fill: #e1e1e1 }
+ .terminal-844098826-r7 { fill: #fea62b;font-weight: bold }
+ .terminal-844098826-r8 { fill: #a7a9ab }
+ .terminal-844098826-r9 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- CollapsibleApp
+ CollapsibleApp
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- ▼ Leto
-
- # Duke Leto I Atreides
-
- Head of House Atreides.
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- ▼ Jessica
-
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▎ ▊
- ▎ Lady Jessica ▊
- ▎ ▊
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
- Bene Gesserit and concubine of Leto, and mother of Paul and Alia.
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- ▶ Paul
-
-
- C Collapse All E Expand All
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ ▼ Leto
+
+ # Duke Leto I Atreides
+
+ Head of House Atreides.
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ ▼ Jessica
+
+
+
+ Lady Jessica
+
+ Bene Gesserit and concubine of Leto, and mother of Paul and Alia.
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ ▶ Paul
+
+
+
+ c Collapse All e Expand All
@@ -3816,133 +4148,133 @@
font-weight: 700;
}
- .terminal-2207022363-matrix {
+ .terminal-4204346594-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2207022363-title {
+ .terminal-4204346594-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2207022363-r1 { fill: #ff0000 }
- .terminal-2207022363-r2 { fill: #c5c8c6 }
- .terminal-2207022363-r3 { fill: #008000 }
- .terminal-2207022363-r4 { fill: #e1e1e1 }
+ .terminal-4204346594-r1 { fill: #ff0000 }
+ .terminal-4204346594-r2 { fill: #c5c8c6 }
+ .terminal-4204346594-r3 { fill: #008000 }
+ .terminal-4204346594-r4 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- HeightApp
+ HeightApp
-
-
-
- ┌ ────────────────────────────────────────────────────────────────────────────── ┐
- │ ┌ ──────────────────── ┐ ┌ ──────────────── ┐ ┌ ────────────────────── ┐ │
- │ │ As tall as container │ │ This has default │ │ I have a static height │ │
- │ │ │ │ height │ │ │ │
- │ │ │ │ but a │ │ │ │
- │ │ │ │ few lines │ │ │ │
- │ │ │ └ ──────────────── ┘ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ └ ──────────────────── ┘ └ ────────────────────── ┘ │
- └ ────────────────────────────────────────────────────────────────────────────── ┘
-
-
-
-
-
+
+
+
+ ┌──────────────────────────────────────────────────────────────────────────────┐
+ │ ┌────────────────────┐┌────────────────┐┌──────────────────────┐ │
+ │ │ As tall as container ││ This has default ││ I have a static height │ │
+ │ │ ││ height ││ │ │
+ │ │ ││ but a ││ │ │
+ │ │ ││ few lines ││ │ │
+ │ │ │└────────────────┘│ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ └────────────────────┘ └──────────────────────┘ │
+ └──────────────────────────────────────────────────────────────────────────────┘
+
+
+
+
+
@@ -3973,137 +4305,138 @@
font-weight: 700;
}
- .terminal-454793765-matrix {
+ .terminal-1919115509-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-454793765-title {
+ .terminal-1919115509-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-454793765-r1 { fill: #a2a2a2 }
- .terminal-454793765-r2 { fill: #c5c8c6 }
- .terminal-454793765-r3 { fill: #004578 }
- .terminal-454793765-r4 { fill: #e2e3e3 }
- .terminal-454793765-r5 { fill: #00ff00 }
- .terminal-454793765-r6 { fill: #000000 }
- .terminal-454793765-r7 { fill: #1e1e1e }
- .terminal-454793765-r8 { fill: #fea62b;font-weight: bold }
+ .terminal-1919115509-r1 { fill: #646464 }
+ .terminal-1919115509-r2 { fill: #c5c8c6 }
+ .terminal-1919115509-r3 { fill: #004578 }
+ .terminal-1919115509-r4 { fill: #dfe1e2 }
+ .terminal-1919115509-r5 { fill: #00ff00 }
+ .terminal-1919115509-r6 { fill: #000000 }
+ .terminal-1919115509-r7 { fill: #1e1e1e }
+ .terminal-1919115509-r8 { fill: #dfe1e2;font-weight: bold }
+ .terminal-1919115509-r9 { fill: #fea62b;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- CommandPaletteApp
+ CommandPaletteApp
-
-
-
-
-
-
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
-
- 🔎 A
-
-
- This is a test of this code 9
- This is a test of this code 8
- This is a test of this code 7
- This is a test of this code 6
- This is a test of this code 5
- This is a test of this code 4
- This is a test of this code 3
- This is a test of this code 2
- This is a test of this code 1
- This is a test of this code 0
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
-
-
-
+
+
+
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+
+ 🔎 A
+
+
+ This is a test of this code 0
+ This is a test of this code 1
+ This is a test of this code 2
+ This is a test of this code 3
+ This is a test of this code 4
+ This is a test of this code 5
+ This is a test of this code 6
+ This is a test of this code 7
+ This is a test of this code 8
+ This is a test of this code 9
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+
+
+
@@ -4134,137 +4467,138 @@
font-weight: 700;
}
- .terminal-929804574-matrix {
+ .terminal-1425100236-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-929804574-title {
+ .terminal-1425100236-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-929804574-r1 { fill: #a2a2a2 }
- .terminal-929804574-r2 { fill: #c5c8c6 }
- .terminal-929804574-r3 { fill: #004578 }
- .terminal-929804574-r4 { fill: #e2e3e3 }
- .terminal-929804574-r5 { fill: #00ff00 }
- .terminal-929804574-r6 { fill: #000000 }
- .terminal-929804574-r7 { fill: #1e1e1e }
- .terminal-929804574-r8 { fill: #777a7e }
+ .terminal-1425100236-r1 { fill: #646464 }
+ .terminal-1425100236-r2 { fill: #c5c8c6 }
+ .terminal-1425100236-r3 { fill: #004578 }
+ .terminal-1425100236-r4 { fill: #dfe1e2 }
+ .terminal-1425100236-r5 { fill: #00ff00 }
+ .terminal-1425100236-r6 { fill: #000000 }
+ .terminal-1425100236-r7 { fill: #1e1e1e }
+ .terminal-1425100236-r8 { fill: #697278 }
+ .terminal-1425100236-r9 { fill: #dfe1e2;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- CommandPaletteApp
+ CommandPaletteApp
-
-
-
-
-
-
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
-
- 🔎 C ommand Palette Search...
-
-
- This is a test of this code 0
- This is a test of this code 1
- This is a test of this code 2
- This is a test of this code 3
- This is a test of this code 4
- This is a test of this code 5
- This is a test of this code 6
- This is a test of this code 7
- This is a test of this code 8
- This is a test of this code 9
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
-
-
-
+
+
+
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+
+ 🔎 S earch for commands…
+
+
+ This is a test of this code 0
+ This is a test of this code 1
+ This is a test of this code 2
+ This is a test of this code 3
+ This is a test of this code 4
+ This is a test of this code 5
+ This is a test of this code 6
+ This is a test of this code 7
+ This is a test of this code 8
+ This is a test of this code 9
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+
+
+
@@ -4272,7 +4606,7 @@
'''
# ---
-# name: test_content_switcher_example_initial
+# name: test_component_text_opacity
'''
@@ -4295,150 +4629,141 @@
font-weight: 700;
}
- .terminal-3026307999-matrix {
+ .terminal-2090419717-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3026307999-title {
+ .terminal-2090419717-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3026307999-r1 { fill: #c5c8c6 }
- .terminal-3026307999-r2 { fill: #e1e1e1 }
- .terminal-3026307999-r3 { fill: #454a50 }
- .terminal-3026307999-r4 { fill: #24292f;font-weight: bold }
- .terminal-3026307999-r5 { fill: #e2e3e3;font-weight: bold }
- .terminal-3026307999-r6 { fill: #000000 }
- .terminal-3026307999-r7 { fill: #004578 }
- .terminal-3026307999-r8 { fill: #dde6ed;font-weight: bold }
- .terminal-3026307999-r9 { fill: #dde6ed }
- .terminal-3026307999-r10 { fill: #211505 }
- .terminal-3026307999-r11 { fill: #e2e3e3 }
+ .terminal-2090419717-r1 { fill: #7f7fff }
+ .terminal-2090419717-r2 { fill: #c5c8c6 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ContentSwitcherApp
+ TestApp
-
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- DataTable Markdown
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
- ╭ ──────────────────────────────────────────────────────────────────── ╮
- │ Book Year │
- │ Dune 1965 │
- │ Dune Messiah 1969 │
- │ Children of Dune 1976 │
- │ God Emperor of Dune 1981 │
- │ Heretics of Dune 1984 │
- │ Chapterhouse: Dune 1985 │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- ╰ ──────────────────────────────────────────────────────────────────── ╯
-
+
+
+
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+ WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
'''
# ---
-# name: test_content_switcher_example_switch
+# name: test_content_switcher_example_initial
'''
-
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
+
+ ContentSwitcherApp
+
+
+
+
+
+
+
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ DataTable Markdown
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ╭────────────────────────────────────────────────────────────────────╮
+ │ Book Year │
+ │ Dune 1965 │
+ │ Dune Messiah 1969 │
+ │ Children of Dune 1976 │
+ │ God Emperor of Dune 1981 │
+ │ Heretics of Dune 1984 │
+ │ Chapterhouse: Dune 1985 │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ ╰────────────────────────────────────────────────────────────────────╯
+
+
+
+
+
+ '''
+# ---
+# name: test_content_switcher_example_switch
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ContentSwitcherApp
-
-
-
-
-
-
-
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- DataTable Markdown
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
- ╭ ───────────────────────────────────────── ╮
- │ ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁ │
- │ ▎ ▊ │
- │ ▎ Three Flavours Cornetto ▊ │
- │ ▎ ▊ │
- │ ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔ │
- │ The Three Flavours Cornetto │
- │ trilogy is an anthology series │
- │ of British comedic genre films │
- │ directed by Edgar Wright. │
- │ │
- │ Shaun of the Dead │
- │ │
- │ ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁ │
- │ ▎ ▊ │
- │ ▎ UK ▊ │
- │ ▎ Release ▊ │
- │ ▎ Flavour Date Director ▊ │
- │ ▎ ━━━━━━━━━━━━━━━━━━━━━━━━━━━ ▊ │
- │ ▎ Strawbe… 2004-04… Edgar ▊ │
- │ ▎ Wright ▊ │
- │ ▎ ▊ │
- │ ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔ │
- │ │
- │ Hot Fuzz │
- │ │
- │ ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁ │
- │ ▎ ▊ │
- │ ▎ UK ▊ │
- │ ▎ Release ▊ │
- │ ▎ Flavour Date Director ▊ │
- │ ▎ ━━━━━━━━━━━━━━━━━━━━━━━━━━━ ▊ │
- │ ▎ Classico 2007-02… Edgar ▊ │
- │ ▎ Wright ▊ │
- │ ▎ ▊ │
- │ ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔ │
- │ │
- │ The World's End │
- │ │
- │ ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁ │
- │ ▎ ▊ │
- │ ▎ UK ▊ │
- │ ▎ Release ▊ │
- │ ▎ Flavour Date Director ▊ │
- ╰ ───────────────────────────────────────── ╯
+ ContentSwitcherApp
+
+
+
+
+
+
+
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ DataTable Markdown
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ╭─────────────────────────────────────────╮
+ │ │
+ │ │
+ │ Three Flavours Cornetto │
+ │ │
+ │ The Three Flavours Cornetto trilogy │
+ │ is an anthology series of British │
+ │ comedic genre films directed by Edgar │
+ │ Wright. │
+ │ │
+ │ │
+ │ Shaun of the Dead │
+ │ │
+ │ │
+ │ UK Release │
+ │ Flavour Date Director │
+ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
+ │ Strawberry 2004-04-09 Edgar │
+ │ Wright │
+ │ │
+ │ │
+ │ │
+ │ Hot Fuzz │
+ │ │
+ │ │
+ │ UK Release │
+ │ Flavour Date Director │
+ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
+ │ Classico 2007-02-17 Edgar Wright │
+ │ │
+ │ │
+ │ │
+ │ The World's End │
+ │ │
+ │ │
+ │ UK Release │
+ │ Flavour Date Director │
+ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
+ │ Mint 2013-07-19 Edgar Wright │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ ╰─────────────────────────────────────────╯
@@ -4729,131 +5215,131 @@
font-weight: 700;
}
- .terminal-1055651203-matrix {
+ .terminal-3958682080-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1055651203-title {
+ .terminal-3958682080-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1055651203-r1 { fill: #e1e1e1 }
- .terminal-1055651203-r2 { fill: #c5c8c6 }
+ .terminal-3958682080-r1 { fill: #e1e1e1 }
+ .terminal-3958682080-r2 { fill: #c5c8c6 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- HotReloadingApp
+ HotReloadingApp
-
-
-
- Hello, world!
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Hello, world!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -4884,131 +5370,131 @@
font-weight: 700;
}
- .terminal-1055651203-matrix {
+ .terminal-3958682080-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1055651203-title {
+ .terminal-3958682080-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1055651203-r1 { fill: #e1e1e1 }
- .terminal-1055651203-r2 { fill: #c5c8c6 }
+ .terminal-3958682080-r1 { fill: #e1e1e1 }
+ .terminal-3958682080-r2 { fill: #c5c8c6 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- HotReloadingApp
+ HotReloadingApp
-
-
-
- Hello, world!
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Hello, world!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -5039,134 +5525,134 @@
font-weight: 700;
}
- .terminal-1567237307-matrix {
+ .terminal-4107518032-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1567237307-title {
+ .terminal-4107518032-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1567237307-r1 { fill: #e1e1e1 }
- .terminal-1567237307-r2 { fill: #c5c8c6 }
- .terminal-1567237307-r3 { fill: #ffffff }
- .terminal-1567237307-r4 { fill: #e5f2e5 }
- .terminal-1567237307-r5 { fill: #e5f2e5;font-weight: bold }
+ .terminal-4107518032-r1 { fill: #e1e1e1 }
+ .terminal-4107518032-r2 { fill: #c5c8c6 }
+ .terminal-4107518032-r3 { fill: #ffffff }
+ .terminal-4107518032-r4 { fill: #e5f2e5 }
+ .terminal-4107518032-r5 { fill: #e5f2e5;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AlignApp
+ AlignApp
-
-
-
-
-
-
-
-
-
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ ┃
- ┃ Vertical alignment with Textual ┃
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
-
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ ┃
- ┃ Take note, browsers. ┃
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ ┃
+ ┃ Vertical alignment with Textual ┃
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ ┃
+ ┃ Take note, browsers. ┃
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+
+
+
+
+
+
@@ -5197,135 +5683,135 @@
font-weight: 700;
}
- .terminal-1945469710-matrix {
+ .terminal-800662067-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1945469710-title {
+ .terminal-800662067-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1945469710-r1 { fill: #808080 }
- .terminal-1945469710-r2 { fill: #e1e1e1 }
- .terminal-1945469710-r3 { fill: #c5c8c6 }
- .terminal-1945469710-r4 { fill: #ddedf9 }
- .terminal-1945469710-r5 { fill: #e2e2e2 }
+ .terminal-800662067-r1 { fill: #808080 }
+ .terminal-800662067-r2 { fill: #e1e1e1 }
+ .terminal-800662067-r3 { fill: #c5c8c6 }
+ .terminal-800662067-r4 { fill: #ddedf9 }
+ .terminal-800662067-r5 { fill: #e2e2e2 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AlignAllApp
+ AlignAllApp
-
-
-
- ┌ ──────────────────────── ┐ ┌ ──────────────────────── ┐ ┌ ──────────────────────── ┐
- │ left top │ │ center top │ │ right top │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- └ ──────────────────────── ┘ └ ──────────────────────── ┘ └ ──────────────────────── ┘
-
- ┌ ──────────────────────── ┐ ┌ ──────────────────────── ┐ ┌ ──────────────────────── ┐
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ left middle │ │ center middle │ │ right middle │
- │ │ │ │ │ │
- │ │ │ │ │ │
- └ ──────────────────────── ┘ └ ──────────────────────── ┘ └ ──────────────────────── ┘
-
- ┌ ──────────────────────── ┐ ┌ ──────────────────────── ┐ ┌ ──────────────────────── ┐
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ left bottom │ │ center bottom │ │ right bottom │
- └ ──────────────────────── ┘ └ ──────────────────────── ┘ └ ──────────────────────── ┘
+
+
+
+ ┌────────────────────────┐ ┌────────────────────────┐ ┌────────────────────────┐
+ │ left top │ │ center top │ │ right top │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ └────────────────────────┘ └────────────────────────┘ └────────────────────────┘
+
+ ┌────────────────────────┐ ┌────────────────────────┐ ┌────────────────────────┐
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ left middle │ │ center middle │ │ right middle │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ └────────────────────────┘ └────────────────────────┘ └────────────────────────┘
+
+ ┌────────────────────────┐ ┌────────────────────────┐ ┌────────────────────────┐
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ │ │ │ │ │
+ │ left bottom │ │ center bottom │ │ right bottom │
+ └────────────────────────┘ └────────────────────────┘ └────────────────────────┘
@@ -5674,134 +6160,134 @@
font-weight: 700;
}
- .terminal-1839441138-matrix {
+ .terminal-3021441172-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1839441138-title {
+ .terminal-3021441172-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1839441138-r1 { fill: #ffffff }
- .terminal-1839441138-r2 { fill: #c5c8c6 }
- .terminal-1839441138-r3 { fill: #ff0000 }
- .terminal-1839441138-r4 { fill: #008000 }
- .terminal-1839441138-r5 { fill: #0000ff }
+ .terminal-3021441172-r1 { fill: #ffffff }
+ .terminal-3021441172-r2 { fill: #c5c8c6 }
+ .terminal-3021441172-r3 { fill: #ff0000 }
+ .terminal-3021441172-r4 { fill: #008000 }
+ .terminal-3021441172-r5 { fill: #0000ff }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- BorderApp
+ BorderApp
-
-
-
-
- ┌ ──────────────────────────────────────────────────────────────────────────── ┐
- │ │
- │ My border is solid red │
- │ │
- └ ──────────────────────────────────────────────────────────────────────────── ┘
-
- ┏ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ ┓
- ╏ ╏
- ╏ My border is dashed green ╏
- ╏ ╏
- ┗ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ ┛
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ ▎
- ▊ My border is tall blue ▎
- ▊ ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
+
+
+
+
+ ┌────────────────────────────────────────────────────────────────────────────┐
+ │ │
+ │ My border is solid red │
+ │ │
+ └────────────────────────────────────────────────────────────────────────────┘
+
+ ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓
+ ╏ ╏
+ ╏ My border is dashed green ╏
+ ╏ ╏
+ ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ ▎
+ ▊ My border is tall blue ▎
+ ▊ ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
@@ -5832,133 +6318,133 @@
font-weight: 700;
}
- .terminal-2091190527-matrix {
+ .terminal-251619606-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2091190527-title {
+ .terminal-251619606-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2091190527-r1 { fill: #e1e1e1 }
- .terminal-2091190527-r2 { fill: #c5c8c6 }
- .terminal-2091190527-r3 { fill: #0178d4 }
- .terminal-2091190527-r4 { fill: #1e1e1e }
+ .terminal-251619606-r1 { fill: #e1e1e1 }
+ .terminal-251619606-r2 { fill: #c5c8c6 }
+ .terminal-251619606-r3 { fill: #0178d4 }
+ .terminal-251619606-r4 { fill: #1e1e1e }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AllBordersApp
+ AllBordersApp
-
-
-
-
- + ---------------- + ┏ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ ┓ ╔ ═════════════════ ╗
- | ascii | blank ╏ dashed ╏ ║ double ║
- + ---------------- + ┗ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ ┛ ╚ ═════════════════ ╝
-
-
-
- ┏ ━━━━━━━━━━━━━━━━ ┓ ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔ ▗ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▖
- ┃ heavy ┃ hidden/none hkey ▐ inner ▌
- ┗ ━━━━━━━━━━━━━━━━ ┛ ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁ ▝ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▘
-
-
-
- ▛ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▜ ▊ █████████████████ ▎ ╭ ──────────────── ╮ ┌ ───────────────── ┐
- ▌ outer ▐ ▊ panel ▎ │ round │ │ solid │
- ▙ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▟ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ ╰ ──────────────── ╯ └ ───────────────── ┘
-
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ █ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ █ ▏ ▕ ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▊ tall ▎ █ thick █ ▏ vkey ▕ ▎ wide ▊
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ █ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ █ ▏ ▕ ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
-
+
+
+
+
+ +----------------+ ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓ ╔═════════════════╗
+ | ascii | blank ╏ dashed ╏ ║ double ║
+ +----------------+ ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛ ╚═════════════════╝
+
+
+
+ ┏━━━━━━━━━━━━━━━━┓ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▗▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▖
+ ┃ heavy ┃ hidden/none hkey ▐ inner ▌
+ ┗━━━━━━━━━━━━━━━━┛ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▝▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▘
+
+
+
+ ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜ ▊ █████████████████▎ ╭────────────────╮ ┌─────────────────┐
+ ▌ outer ▐ ▊ panel ▎ │ round │ │ solid │
+ ▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ ╰────────────────╯ └─────────────────┘
+
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█ ▏ ▕ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▊ tall ▎ █ thick █ ▏ vkey ▕ ▎ wide ▊
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ █▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█ ▏ ▕ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+
@@ -5989,141 +6475,141 @@
font-weight: 700;
}
- .terminal-2586053582-matrix {
+ .terminal-2407668749-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2586053582-title {
+ .terminal-2407668749-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2586053582-r1 { fill: #e1e1e1 }
- .terminal-2586053582-r2 { fill: #c5c8c6 }
- .terminal-2586053582-r3 { fill: #fea62b }
- .terminal-2586053582-r4 { fill: #fea62b;font-weight: bold }
- .terminal-2586053582-r5 { fill: #fea62b;font-weight: bold;font-style: italic; }
- .terminal-2586053582-r6 { fill: #f4005f;font-weight: bold }
- .terminal-2586053582-r7 { fill: #1e1e1e }
- .terminal-2586053582-r8 { fill: #1e1e1e;text-decoration: underline; }
- .terminal-2586053582-r9 { fill: #fea62b;text-decoration: underline; }
- .terminal-2586053582-r10 { fill: #1a1a1a;text-decoration: underline; }
- .terminal-2586053582-r11 { fill: #4ebf71 }
- .terminal-2586053582-r12 { fill: #b93c5b }
+ .terminal-2407668749-r1 { fill: #e1e1e1 }
+ .terminal-2407668749-r2 { fill: #c5c8c6 }
+ .terminal-2407668749-r3 { fill: #fea62b }
+ .terminal-2407668749-r4 { fill: #fea62b;font-weight: bold }
+ .terminal-2407668749-r5 { fill: #fea62b;font-weight: bold;font-style: italic; }
+ .terminal-2407668749-r6 { fill: #f4005f;font-weight: bold }
+ .terminal-2407668749-r7 { fill: #1e1e1e }
+ .terminal-2407668749-r8 { fill: #1e1e1e;text-decoration: underline; }
+ .terminal-2407668749-r9 { fill: #fea62b;text-decoration: underline; }
+ .terminal-2407668749-r10 { fill: #1a1a1a;text-decoration: underline; }
+ .terminal-2407668749-r11 { fill: #4ebf71 }
+ .terminal-2407668749-r12 { fill: #b93c5b }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- BorderSubTitleAlignAll
+ BorderSubTitleAlignAll
-
-
-
-
-
- ▏ Border title ▕ ╭ ─ Lef… ─ ╮ ▁ ▁▁▁▁ Left ▁▁▁▁ ▁
- ▏ This is the story of ▕ │ a Python │ ▎ developer that ▊
- ▏ Border subtitle ▕ ╰ ─ Cen… ─ ╯ ▔ ▔▔▔▔ @@@ ▔▔▔▔▔ ▔
-
-
-
-
-
- + -------------- + ─ Title ─────────────────
- | had to fill up | nine labels and ended up redoing it
- + - Left ------- + ────────────── Subtitle ─
-
-
-
-
- ─ Title, but really looo… ─
- ─ Title, but r… ─ ─ Title, but reall… ─
- because the first try had some labels that were too long.
- ─ Subtitle, bu… ─ ─ Subtitle, but re… ─
- ─ Subtitle, but really l… ─
-
+
+
+
+
+
+ ▏ Border title ▕ ╭─ Lef… ─╮ ▁▁▁▁▁ Left ▁▁▁▁▁
+ ▏ This is the story of ▕ │ a Python │ ▎ developer that ▊
+ ▏ Border subtitle ▕ ╰─ Cen… ─╯ ▔▔▔▔▔ @@@ ▔▔▔▔▔▔
+
+
+
+
+
+ +--------------+ ─Title─────────────────
+ | had to fill up | nine labels and ended up redoing it
+ +- Left -------+ ──────────────Subtitle─
+
+
+
+
+ ─Title, but really looo…─
+ ─Title, but r…─ ─Title, but reall…─
+ because the first try had some labels that were too long.
+ ─Subtitle, bu…─ ─Subtitle, but re…─
+ ─Subtitle, but really l…─
+
@@ -6154,134 +6640,134 @@
font-weight: 700;
}
- .terminal-1601354540-matrix {
+ .terminal-1369800768-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1601354540-title {
+ .terminal-1369800768-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1601354540-r1 { fill: #e1e1e1 }
- .terminal-1601354540-r2 { fill: #c5c8c6 }
- .terminal-1601354540-r3 { fill: #fea62b }
- .terminal-1601354540-r4 { fill: #ffffff }
- .terminal-1601354540-r5 { fill: #1e1e1e }
+ .terminal-1369800768-r1 { fill: #e1e1e1 }
+ .terminal-1369800768-r2 { fill: #c5c8c6 }
+ .terminal-1369800768-r3 { fill: #fea62b }
+ .terminal-1369800768-r4 { fill: #ffffff }
+ .terminal-1369800768-r5 { fill: #1e1e1e }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- BorderSubtitleAlignApp
+ BorderSubtitleAlignApp
-
-
-
-
- ┌ ──────────────────────────────────────────────────────────────────────────── ┐
- │ │
- │ My subtitle is on the left. │
- │ │
- └ ─ < Left ─────────────────────────────────────────────────────────────────── ┘
-
- ┏ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ ┓
- ╏ ╏
- ╏ My subtitle is centered ╏
- ╏ ╏
- ┗ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ Centered! ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ ┛
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ ▎
- ▊ My subtitle is on the right ▎
- ▊ ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ Right > ▁ ▎
-
-
-
-
-
+
+
+
+
+ ┌────────────────────────────────────────────────────────────────────────────┐
+ │ │
+ │ My subtitle is on the left. │
+ │ │
+ └─ < Left ───────────────────────────────────────────────────────────────────┘
+
+ ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓
+ ╏ ╏
+ ╏ My subtitle is centered ╏
+ ╏ ╏
+ ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ Centered! ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎
+ ▊ ▎
+ ▊ My subtitle is on the right ▎
+ ▊ ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ Right > ▁▎
+
+
+
+
+
@@ -6312,134 +6798,134 @@
font-weight: 700;
}
- .terminal-2047325817-matrix {
+ .terminal-3570826125-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2047325817-title {
+ .terminal-3570826125-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2047325817-r1 { fill: #e1e1e1 }
- .terminal-2047325817-r2 { fill: #c5c8c6 }
- .terminal-2047325817-r3 { fill: #fea62b }
- .terminal-2047325817-r4 { fill: #ffffff }
- .terminal-2047325817-r5 { fill: #1e1e1e }
+ .terminal-3570826125-r1 { fill: #e1e1e1 }
+ .terminal-3570826125-r2 { fill: #c5c8c6 }
+ .terminal-3570826125-r3 { fill: #fea62b }
+ .terminal-3570826125-r4 { fill: #ffffff }
+ .terminal-3570826125-r5 { fill: #1e1e1e }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- BorderTitleAlignApp
+ BorderTitleAlignApp
-
-
-
-
- ┌ ─ < Left ─────────────────────────────────────────────────────────────────── ┐
- │ │
- │ My title is on the left. │
- │ │
- └ ──────────────────────────────────────────────────────────────────────────── ┘
-
- ┏ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ Centered! ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ ┓
- ╏ ╏
- ╏ My title is centered ╏
- ╏ ╏
- ┗ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ ┛
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ Right > ▔ ▎
- ▊ ▎
- ▊ My title is on the right ▎
- ▊ ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
+
+
+
+
+ ┌─ < Left ───────────────────────────────────────────────────────────────────┐
+ │ │
+ │ My title is on the left. │
+ │ │
+ └────────────────────────────────────────────────────────────────────────────┘
+
+ ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ Centered! ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓
+ ╏ ╏
+ ╏ My title is centered ╏
+ ╏ ╏
+ ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ Right > ▔▎
+ ▊ ▎
+ ▊ My title is on the right ▎
+ ▊ ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎
+
+
+
+
+
@@ -6470,134 +6956,134 @@
font-weight: 700;
}
- .terminal-2286355719-matrix {
+ .terminal-2865494641-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2286355719-title {
+ .terminal-2865494641-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2286355719-r1 { fill: #e1e1e1 }
- .terminal-2286355719-r2 { fill: #c5c8c6 }
- .terminal-2286355719-r3 { fill: #ff0000 }
- .terminal-2286355719-r4 { fill: #008000;font-weight: bold }
- .terminal-2286355719-r5 { fill: #ff00ff;font-style: italic; }
+ .terminal-2865494641-r1 { fill: #e1e1e1 }
+ .terminal-2865494641-r2 { fill: #c5c8c6 }
+ .terminal-2865494641-r3 { fill: #ff0000 }
+ .terminal-2865494641-r4 { fill: #008000;font-weight: bold }
+ .terminal-2865494641-r5 { fill: #ff00ff;font-style: italic; }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- BorderTitleApp
+ BorderTitleApp
-
-
-
-
-
-
-
-
-
- ┏ ━ Textual Rocks ━━━━━━━━━━━━━ ┓
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┃ Hello, World! ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┗ ━━━━━━━━━━━━━ Textual Rocks ━ ┛
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ ┏━ Textual Rocks ━━━━━━━━━━━━━┓
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ Hello, World! ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┗━━━━━━━━━━━━━ Textual Rocks ━┛
+
+
+
+
+
+
@@ -6628,132 +7114,132 @@
font-weight: 700;
}
- .terminal-3266307003-matrix {
+ .terminal-467154456-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3266307003-title {
+ .terminal-467154456-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3266307003-r1 { fill: #000000 }
- .terminal-3266307003-r2 { fill: #c5c8c6 }
- .terminal-3266307003-r3 { fill: #ccccff }
+ .terminal-467154456-r1 { fill: #000000 }
+ .terminal-467154456-r2 { fill: #c5c8c6 }
+ .terminal-467154456-r3 { fill: #ccccff }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- BoxSizingApp
+ BoxSizingApp
-
-
-
-
-
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▎ ▊
- ▎ I'm using border-box! ▊
- ▎ ▊
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
-
-
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▎ ▊
- ▎ I'm using content-box! ▊
- ▎ ▊
- ▎ ▊
- ▎ ▊
- ▎ ▊
- ▎ ▊
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
-
-
-
-
-
+
+
+
+
+
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▎ ▊
+ ▎ I'm using border-box! ▊
+ ▎ ▊
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+
+
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▎ ▊
+ ▎ I'm using content-box! ▊
+ ▎ ▊
+ ▎ ▊
+ ▎ ▊
+ ▎ ▊
+ ▎ ▊
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+
+
+
+
+
@@ -7262,133 +7748,133 @@
font-weight: 700;
}
- .terminal-1585086532-matrix {
+ .terminal-2684005981-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1585086532-title {
+ .terminal-2684005981-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1585086532-r1 { fill: #c5c8c6 }
- .terminal-1585086532-r2 { fill: #ffffff }
- .terminal-1585086532-r3 { fill: #ffffff;font-style: italic; }
- .terminal-1585086532-r4 { fill: #ffffff;font-weight: bold }
+ .terminal-2684005981-r1 { fill: #c5c8c6 }
+ .terminal-2684005981-r2 { fill: #ffffff }
+ .terminal-2684005981-r3 { fill: #ffffff;font-style: italic; }
+ .terminal-2684005981-r4 { fill: #ffffff;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ContentAlignApp
+ ContentAlignApp
-
-
-
-
- With content-align you can...
-
-
-
-
-
-
-
-
-
- ... Easily align content ...
-
-
-
-
-
-
-
-
-
-
- ...Horizontally and vertically!
+
+
+
+
+ With content-align you can...
+
+
+
+
+
+
+
+
+
+ ... Easily align content ...
+
+
+
+
+
+
+
+
+
+
+ ...Horizontally and vertically!
@@ -7575,132 +8061,132 @@
font-weight: 700;
}
- .terminal-3544266701-matrix {
+ .terminal-2110623858-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3544266701-title {
+ .terminal-2110623858-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3544266701-r1 { fill: #0000ff }
- .terminal-3544266701-r2 { fill: #c5c8c6 }
- .terminal-3544266701-r3 { fill: #ddeedd }
+ .terminal-2110623858-r1 { fill: #0000ff }
+ .terminal-2110623858-r2 { fill: #c5c8c6 }
+ .terminal-2110623858-r3 { fill: #ddeedd }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- DisplayApp
+ DisplayApp
-
-
-
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ Widget 1 ┃
- ┃ ┃
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ Widget 3 ┃
- ┃ ┃
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃Widget 1 ┃
+ ┃ ┃
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃Widget 3 ┃
+ ┃ ┃
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -7731,132 +8217,132 @@
font-weight: 700;
}
- .terminal-1440050744-matrix {
+ .terminal-766040431-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1440050744-title {
+ .terminal-766040431-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1440050744-r1 { fill: #e1e1e1 }
- .terminal-1440050744-r2 { fill: #c5c8c6 }
- .terminal-1440050744-r3 { fill: #ffffff }
+ .terminal-766040431-r1 { fill: #e1e1e1 }
+ .terminal-766040431-r2 { fill: #c5c8c6 }
+ .terminal-766040431-r3 { fill: #ffffff }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- DockAllApp
+ DockAllApp
-
-
-
-
-
-
- ╭ ────────────────────────────────────────────────────────── ╮
- │ top │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ left right │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ bottom │
- ╰ ────────────────────────────────────────────────────────── ╯
-
-
+
+
+
+
+
+
+ ╭──────────────────────────────────────────────────────────╮
+ │ top │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ left right │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ bottom │
+ ╰──────────────────────────────────────────────────────────╯
+
+
@@ -7887,133 +8373,133 @@
font-weight: 700;
}
- .terminal-2927206876-matrix {
+ .terminal-3791676016-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2927206876-title {
+ .terminal-3791676016-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2927206876-r1 { fill: #c5c8c6 }
- .terminal-2927206876-r2 { fill: #e1e1e1 }
- .terminal-2927206876-r3 { fill: #731077 }
- .terminal-2927206876-r4 { fill: #161c1d }
+ .terminal-3791676016-r1 { fill: #c5c8c6 }
+ .terminal-3791676016-r2 { fill: #e1e1e1 }
+ .terminal-3791676016-r3 { fill: #731077 }
+ .terminal-3791676016-r4 { fill: #161c1d }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- GridApp
+ GridApp
-
-
-
-
- Grid cell 1 Grid cell 2
-
- row-span: 3;
- column-span: 2;
-
-
- Grid cell 3
-
-
-
-
-
- Grid cell 4
-
-
-
-
-
- Grid cell 5 Grid cell 6 Grid cell 7
-
-
-
+
+
+
+
+ Grid cell 1 Grid cell 2
+
+ row-span: 3;
+ column-span: 2;
+
+
+ Grid cell 3
+
+
+
+
+
+ Grid cell 4
+
+
+
+
+
+ Grid cell 5 Grid cell 6 Grid cell 7
+
+
+
@@ -8044,133 +8530,133 @@
font-weight: 700;
}
- .terminal-3216047084-matrix {
+ .terminal-3072634976-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3216047084-title {
+ .terminal-3072634976-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3216047084-r1 { fill: #ffffff }
- .terminal-3216047084-r2 { fill: #c5c8c6 }
- .terminal-3216047084-r3 { fill: #e1e1e1 }
+ .terminal-3072634976-r1 { fill: #ffffff }
+ .terminal-3072634976-r2 { fill: #c5c8c6 }
+ .terminal-3072634976-r3 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MyApp
+ MyApp
-
-
-
- ╭ ────────── ╮ ╭ ────────────── ╮ ╭ ────────────────────── ╮ ╭ ────────── ╮ ╭ ────────────── ╮
- │ 1fr │ │ width = 16 │ │ 2fr │ │ 1fr │ │ width = 16 │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- ╰ ────────── ╯ ╰ ────────────── ╯ ╰ ────────────────────── ╯ ╰ ────────── ╯ ╰ ────────────── ╯
- ╭ ────────── ╮ ╭ ────────────── ╮ ╭ ────────────────────── ╮ ╭ ────────── ╮ ╭ ────────────── ╮
- │ 1fr │ │ width = 16 │ │ 2fr │ │ 1fr │ │ width = 16 │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- ╰ ────────── ╯ ╰ ────────────── ╯ ╰ ────────────────────── ╯ ╰ ────────── ╯ ╰ ────────────── ╯
+
+
+
+ ╭──────────╮╭──────────────╮╭──────────────────────╮╭──────────╮╭──────────────╮
+ │ 1fr ││ width = 16 ││ 2fr ││ 1fr ││ width = 16 │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ ╰──────────╯╰──────────────╯╰──────────────────────╯╰──────────╯╰──────────────╯
+ ╭──────────╮╭──────────────╮╭──────────────────────╮╭──────────╮╭──────────────╮
+ │ 1fr ││ width = 16 ││ 2fr ││ 1fr ││ width = 16 │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ ╰──────────╯╰──────────────╯╰──────────────────────╯╰──────────╯╰──────────────╯
@@ -8200,133 +8686,133 @@
font-weight: 700;
}
- .terminal-1194212456-matrix {
+ .terminal-3574968865-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1194212456-title {
+ .terminal-3574968865-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1194212456-r1 { fill: #ffffff }
- .terminal-1194212456-r2 { fill: #e1e1e1 }
- .terminal-1194212456-r3 { fill: #c5c8c6 }
+ .terminal-3574968865-r1 { fill: #ffffff }
+ .terminal-3574968865-r2 { fill: #e1e1e1 }
+ .terminal-3574968865-r3 { fill: #c5c8c6 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MyApp
+ MyApp
-
-
-
- ╭ ───────────────────────────────────── ╮ ╭ ───────────────────────────────────── ╮
- │ │ │ │
- │ 1 │ │ 2 │
- │ │ │ │
- ╰ ───────────────────────────────────── ╯ ╰ ───────────────────────────────────── ╯
-
- ╭ ───────────────────────────────────── ╮ ╭ ───────────────────────────────────── ╮
- │ │ │ │
- │ 3 │ │ 4 │
- │ │ │ │
- ╰ ───────────────────────────────────── ╯ ╰ ───────────────────────────────────── ╯
-
- ╭ ───────────────────────────────────── ╮ ╭ ───────────────────────────────────── ╮
- │ │ │ │
- │ 5 │ │ 6 │
- │ │ │ │
- ╰ ───────────────────────────────────── ╯ ╰ ───────────────────────────────────── ╯
-
- ╭ ───────────────────────────────────── ╮ ╭ ───────────────────────────────────── ╮
- │ │ │ │
- │ 7 │ │ 8 │
- │ │ │ │
- │ │ │ │
- ╰ ───────────────────────────────────── ╯ ╰ ───────────────────────────────────── ╯
+
+
+
+ ╭─────────────────────────────────────╮ ╭─────────────────────────────────────╮
+ │ │ │ │
+ │ 1 │ │ 2 │
+ │ │ │ │
+ ╰─────────────────────────────────────╯ ╰─────────────────────────────────────╯
+
+ ╭─────────────────────────────────────╮ ╭─────────────────────────────────────╮
+ │ │ │ │
+ │ 3 │ │ 4 │
+ │ │ │ │
+ ╰─────────────────────────────────────╯ ╰─────────────────────────────────────╯
+
+ ╭─────────────────────────────────────╮ ╭─────────────────────────────────────╮
+ │ │ │ │
+ │ 5 │ │ 6 │
+ │ │ │ │
+ ╰─────────────────────────────────────╯ ╰─────────────────────────────────────╯
+
+ ╭─────────────────────────────────────╮ ╭─────────────────────────────────────╮
+ │ │ │ │
+ │ 7 │ │ 8 │
+ │ │ │ │
+ │ │ │ │
+ ╰─────────────────────────────────────╯ ╰─────────────────────────────────────╯
@@ -8356,133 +8842,133 @@
font-weight: 700;
}
- .terminal-986618502-matrix {
+ .terminal-3421668871-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-986618502-title {
+ .terminal-3421668871-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-986618502-r1 { fill: #ffffff }
- .terminal-986618502-r2 { fill: #c5c8c6 }
- .terminal-986618502-r3 { fill: #e1e1e1 }
+ .terminal-3421668871-r1 { fill: #ffffff }
+ .terminal-3421668871-r2 { fill: #c5c8c6 }
+ .terminal-3421668871-r3 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MyApp
+ MyApp
-
-
-
- ╭ ────────────────────────────────────── ╮ ╭ ────────────────────────────────────── ╮
- │ 1fr │ │ 1fr │
- ╰ ────────────────────────────────────── ╯ ╰ ────────────────────────────────────── ╯
- ╭ ────────────────────────────────────── ╮ ╭ ────────────────────────────────────── ╮
- │ │ │ │
- │ height = 6 │ │ height = 6 │
- │ │ │ │
- │ │ │ │
- ╰ ────────────────────────────────────── ╯ ╰ ────────────────────────────────────── ╯
- ╭ ────────────────────────────────────── ╮ ╭ ────────────────────────────────────── ╮
- │ │ │ │
- │ 25% │ │ 25% │
- │ │ │ │
- │ │ │ │
- ╰ ────────────────────────────────────── ╯ ╰ ────────────────────────────────────── ╯
- ╭ ────────────────────────────────────── ╮ ╭ ────────────────────────────────────── ╮
- │ 1fr │ │ 1fr │
- ╰ ────────────────────────────────────── ╯ ╰ ────────────────────────────────────── ╯
- ╭ ────────────────────────────────────── ╮ ╭ ────────────────────────────────────── ╮
- │ │ │ │
- │ height = 6 │ │ height = 6 │
- │ │ │ │
- │ │ │ │
- ╰ ────────────────────────────────────── ╯ ╰ ────────────────────────────────────── ╯
+
+
+
+ ╭──────────────────────────────────────╮╭──────────────────────────────────────╮
+ │ 1fr ││ 1fr │
+ ╰──────────────────────────────────────╯╰──────────────────────────────────────╯
+ ╭──────────────────────────────────────╮╭──────────────────────────────────────╮
+ │ ││ │
+ │ height = 6 ││ height = 6 │
+ │ ││ │
+ │ ││ │
+ ╰──────────────────────────────────────╯╰──────────────────────────────────────╯
+ ╭──────────────────────────────────────╮╭──────────────────────────────────────╮
+ │ ││ │
+ │ 25% ││ 25% │
+ │ ││ │
+ │ ││ │
+ ╰──────────────────────────────────────╯╰──────────────────────────────────────╯
+ ╭──────────────────────────────────────╮╭──────────────────────────────────────╮
+ │ 1fr ││ 1fr │
+ ╰──────────────────────────────────────╯╰──────────────────────────────────────╯
+ ╭──────────────────────────────────────╮╭──────────────────────────────────────╮
+ │ ││ │
+ │ height = 6 ││ height = 6 │
+ │ ││ │
+ │ ││ │
+ ╰──────────────────────────────────────╯╰──────────────────────────────────────╯
@@ -8512,132 +8998,132 @@
font-weight: 700;
}
- .terminal-3270198448-matrix {
+ .terminal-4060527209-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3270198448-title {
+ .terminal-4060527209-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3270198448-r1 { fill: #ffffff }
- .terminal-3270198448-r2 { fill: #c5c8c6 }
- .terminal-3270198448-r3 { fill: #e1e1e1 }
+ .terminal-4060527209-r1 { fill: #ffffff }
+ .terminal-4060527209-r2 { fill: #c5c8c6 }
+ .terminal-4060527209-r3 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MyApp
+ MyApp
-
-
-
- ╭ ────────────────────────────────────── ╮ ╭ ────────────────────────────────────── ╮
- │ │ │ │
- │ 1 │ │ 2 │
- │ │ │ │
- │ │ │ │
- ╰ ────────────────────────────────────── ╯ ╰ ────────────────────────────────────── ╯
- ╭ ────────────────────────────────────── ╮ ╭ ────────────────────────────────────── ╮
- │ │ │ │
- │ 3 │ │ 4 │
- │ │ │ │
- │ │ │ │
- ╰ ────────────────────────────────────── ╯ ╰ ────────────────────────────────────── ╯
- ╭ ────────────────────────────────────── ╮
- │ │
- │ 5 │
- │ │
- │ │
- ╰ ────────────────────────────────────── ╯
-
-
-
-
-
+
+
+
+ ╭──────────────────────────────────────╮╭──────────────────────────────────────╮
+ │ ││ │
+ │ 1 ││ 2 │
+ │ ││ │
+ │ ││ │
+ ╰──────────────────────────────────────╯╰──────────────────────────────────────╯
+ ╭──────────────────────────────────────╮╭──────────────────────────────────────╮
+ │ ││ │
+ │ 3 ││ 4 │
+ │ ││ │
+ │ ││ │
+ ╰──────────────────────────────────────╯╰──────────────────────────────────────╯
+ ╭──────────────────────────────────────╮
+ │ │
+ │ 5 │
+ │ │
+ │ │
+ ╰──────────────────────────────────────╯
+
+
+
+
+
@@ -8668,133 +9154,294 @@
font-weight: 700;
}
- .terminal-4208206220-matrix {
+ .terminal-1640924670-matrix {
+ font-family: Fira Code, monospace;
+ font-size: 20px;
+ line-height: 24.4px;
+ font-variant-east-asian: full-width;
+ }
+
+ .terminal-1640924670-title {
+ font-size: 18px;
+ font-weight: bold;
+ font-family: arial;
+ }
+
+ .terminal-1640924670-r1 { fill: #ffffff }
+ .terminal-1640924670-r2 { fill: #c5c8c6 }
+ .terminal-1640924670-r3 { fill: #e1e1e1 }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MyApp
+
+
+
+
+
+
+
+
+
+ ╭──────────────────────────────────────╮╭──────────────────────────────────────╮
+ │ ││ │
+ │ ││ │
+ │ 1 ││ 2 │
+ │ ││ │
+ │ ││ │
+ │ ││ │
+ ╰──────────────────────────────────────╯╰──────────────────────────────────────╯
+ ╭──────────────────────────────────────╮╭──────────────────────────────────────╮
+ │ ││ │
+ │ ││ │
+ │ 3 ││ 4 │
+ │ ││ │
+ │ ││ │
+ │ ││ │
+ ╰──────────────────────────────────────╯╰──────────────────────────────────────╯
+ ╭──────────────────────────────────────╮
+ │ │
+ │ │
+ │ 5 │
+ │ │
+ │ │
+ │ │
+ ╰──────────────────────────────────────╯
+
+
+
+
+ '''
+# ---
+# name: test_css_property[hatch.py]
+ '''
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MyApp
+ HatchApp
-
-
-
- ╭ ────────────────────────────────────── ╮ ╭ ────────────────────────────────────── ╮
- │ │ │ │
- │ │ │ │
- │ 1 │ │ 2 │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- ╰ ────────────────────────────────────── ╯ ╰ ────────────────────────────────────── ╯
- ╭ ────────────────────────────────────── ╮ ╭ ────────────────────────────────────── ╮
- │ │ │ │
- │ │ │ │
- │ 3 │ │ 4 │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- ╰ ────────────────────────────────────── ╯ ╰ ────────────────────────────────────── ╯
- ╭ ────────────────────────────────────── ╮
- │ │
- │ │
- │ 5 │
- │ │
- │ │
- │ │
- ╰ ────────────────────────────────────── ╯
+
+
+
+ ┌─ cross ──────┐┌─ horizontal ─┐┌─ custom ─────┐┌─ left ───────┐┌─ right ──────┐
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ │ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ││ ────────────── ││ TTTTTTTTTTTTTT ││ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ││ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ │
+ └──────────────┘└──────────────┘└──────────────┘└──────────────┘└──────────────┘
@@ -9144,136 +9791,136 @@
font-weight: 700;
}
- .terminal-3762053931-matrix {
+ .terminal-1885421407-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3762053931-title {
+ .terminal-1885421407-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3762053931-r1 { fill: #c5c8c6 }
- .terminal-3762053931-r2 { fill: #008000 }
- .terminal-3762053931-r3 { fill: #e8e0e7 }
- .terminal-3762053931-r4 { fill: #eae3e5 }
- .terminal-3762053931-r5 { fill: #1e1e1e }
- .terminal-3762053931-r6 { fill: #ede6e6 }
- .terminal-3762053931-r7 { fill: #efeedf }
+ .terminal-1885421407-r1 { fill: #c5c8c6 }
+ .terminal-1885421407-r2 { fill: #008000 }
+ .terminal-1885421407-r3 { fill: #e8e0e7 }
+ .terminal-1885421407-r4 { fill: #eae3e5 }
+ .terminal-1885421407-r5 { fill: #1e1e1e }
+ .terminal-1885421407-r6 { fill: #ede6e6 }
+ .terminal-1885421407-r7 { fill: #efeedf }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- KeylineApp
+ KeylineApp
-
-
-
-
-
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┳ ━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ ┃ ┃
- ┃ ┃ ┃
- ┃ #foo ┃ ┃
- ┃ ┃ ┃
- ┃ ┃ ┃
- ┣ ━━━━━━━━━━━━━━━━━━━━━━━ ┳ ━━━━━━━━━━━━━━━━━━━━━━━ ┫ #bar ┃
- ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃
- ┃ Placeholder ┃ ┃ ┃
- ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃
- ┣ ━━━━━━━━━━━━━━━━━━━━━━━ ┻ ━━━━━━━━━━━━━━━━━━━━━━━ ┻ ━━━━━━━━━━━━━━━━━━━━━━━━ ┫
- ┃ ┃
- ┃ ┃
- ┃ #baz ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
-
+
+
+
+
+
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ ┃ ┃
+ ┃ ┃ ┃
+ ┃ #foo ┃ ┃
+ ┃ ┃ ┃
+ ┃ ┃ ┃
+ ┣━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┫ #bar ┃
+ ┃ ┃ ┃ ┃
+ ┃ ┃ ┃ ┃
+ ┃ Placeholder ┃ ┃ ┃
+ ┃ ┃ ┃ ┃
+ ┃ ┃ ┃ ┃
+ ┣━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┫
+ ┃ ┃
+ ┃ ┃
+ ┃ #baz ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+
@@ -9304,135 +9951,135 @@
font-weight: 700;
}
- .terminal-1481425640-matrix {
+ .terminal-1481807543-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1481425640-title {
+ .terminal-1481807543-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1481425640-r1 { fill: #fea62b }
- .terminal-1481425640-r2 { fill: #c5c8c6 }
- .terminal-1481425640-r3 { fill: #e8e0e7 }
- .terminal-1481425640-r4 { fill: #eae3e5 }
- .terminal-1481425640-r5 { fill: #ede6e6 }
+ .terminal-1481807543-r1 { fill: #fea62b }
+ .terminal-1481807543-r2 { fill: #c5c8c6 }
+ .terminal-1481807543-r3 { fill: #e8e0e7 }
+ .terminal-1481807543-r4 { fill: #eae3e5 }
+ .terminal-1481807543-r5 { fill: #ede6e6 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- KeylineApp
+ KeylineApp
-
-
-
- ┌ ───────────────────────── ┬ ───────────────────────── ┬ ────────────────────────── ┐
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ Placeholder │ Placeholder │ Placeholder │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- └ ───────────────────────── ┴ ───────────────────────── ┴ ────────────────────────── ┘
+
+
+
+ ┌─────────────────────────┬─────────────────────────┬──────────────────────────┐
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ Placeholder │ Placeholder │ Placeholder │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ └─────────────────────────┴─────────────────────────┴──────────────────────────┘
@@ -9620,134 +10267,134 @@
font-weight: 700;
}
- .terminal-687058265-matrix {
+ .terminal-3791839841-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-687058265-title {
+ .terminal-3791839841-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-687058265-r1 { fill: #e1e1e1 }
- .terminal-687058265-r2 { fill: #c5c8c6 }
- .terminal-687058265-r3 { fill: #ffdddd;text-decoration: underline; }
- .terminal-687058265-r4 { fill: #121201;text-decoration: underline; }
- .terminal-687058265-r5 { fill: #ddedf9;text-decoration: underline; }
+ .terminal-3791839841-r1 { fill: #e1e1e1 }
+ .terminal-3791839841-r2 { fill: #c5c8c6 }
+ .terminal-3791839841-r3 { fill: #ffdddd;text-decoration: underline; }
+ .terminal-3791839841-r4 { fill: #121201;text-decoration: underline; }
+ .terminal-3791839841-r5 { fill: #ddedf9;text-decoration: underline; }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- LinkBackgroundApp
+ LinkBackgroundApp
-
-
-
- Visit the Textualize website.
- Click here for the bell sound.
- You can also click here for the bell sound.
- Exit this application.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Visit the Textualize website.
+ Click here for the bell sound.
+ You can also click here for the bell sound.
+ Exit this application.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -9778,132 +10425,132 @@
font-weight: 700;
}
- .terminal-1076604876-matrix {
+ .terminal-213509332-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1076604876-title {
+ .terminal-213509332-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1076604876-r1 { fill: #e1e1e1 }
- .terminal-1076604876-r2 { fill: #c5c8c6 }
- .terminal-1076604876-r3 { fill: #e1e1e1;text-decoration: underline; }
+ .terminal-213509332-r1 { fill: #e1e1e1 }
+ .terminal-213509332-r2 { fill: #c5c8c6 }
+ .terminal-213509332-r3 { fill: #e1e1e1;text-decoration: underline; }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- LinkHoverBackgroundApp
+ LinkHoverBackgroundApp
-
-
-
- Visit the Textualize website.
- Click here for the bell sound.
- You can also click here for the bell sound.
- Exit this application.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Visit the Textualize website.
+ Click here for the bell sound.
+ You can also click here for the bell sound.
+ Exit this application.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -9934,134 +10581,134 @@
font-weight: 700;
}
- .terminal-3021056461-matrix {
+ .terminal-1781128917-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3021056461-title {
+ .terminal-1781128917-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3021056461-r1 { fill: #e1e1e1 }
- .terminal-3021056461-r2 { fill: #c5c8c6 }
- .terminal-3021056461-r3 { fill: #ff0000;text-decoration: underline; }
- .terminal-3021056461-r4 { fill: #8e8e0f;text-decoration: underline; }
- .terminal-3021056461-r5 { fill: #0178d4;text-decoration: underline; }
+ .terminal-1781128917-r1 { fill: #e1e1e1 }
+ .terminal-1781128917-r2 { fill: #c5c8c6 }
+ .terminal-1781128917-r3 { fill: #ff0000;text-decoration: underline; }
+ .terminal-1781128917-r4 { fill: #8e8e0f;text-decoration: underline; }
+ .terminal-1781128917-r5 { fill: #0178d4;text-decoration: underline; }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- LinkColorApp
+ LinkColorApp
-
-
-
- Visit the Textualize website.
- Click here for the bell sound.
- You can also click here for the bell sound.
- Exit this application.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Visit the Textualize website.
+ Click here for the bell sound.
+ You can also click here for the bell sound.
+ Exit this application.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -10092,132 +10739,132 @@
font-weight: 700;
}
- .terminal-3576933835-matrix {
+ .terminal-2559238867-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3576933835-title {
+ .terminal-2559238867-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3576933835-r1 { fill: #e1e1e1 }
- .terminal-3576933835-r2 { fill: #c5c8c6 }
- .terminal-3576933835-r3 { fill: #e1e1e1;text-decoration: underline; }
+ .terminal-2559238867-r1 { fill: #e1e1e1 }
+ .terminal-2559238867-r2 { fill: #c5c8c6 }
+ .terminal-2559238867-r3 { fill: #e1e1e1;text-decoration: underline; }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- LinkHoverColorApp
+ LinkHoverColorApp
-
-
-
- Visit the Textualize website.
- Click here for the bell sound.
- You can also click here for the bell sound.
- Exit this application.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Visit the Textualize website.
+ Click here for the bell sound.
+ You can also click here for the bell sound.
+ Exit this application.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -10248,134 +10895,134 @@
font-weight: 700;
}
- .terminal-2410786847-matrix {
+ .terminal-4258391335-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2410786847-title {
+ .terminal-4258391335-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2410786847-r1 { fill: #e1e1e1 }
- .terminal-2410786847-r2 { fill: #c5c8c6 }
- .terminal-2410786847-r3 { fill: #e1e1e1;font-weight: bold;font-style: italic; }
- .terminal-2410786847-r4 { fill: #1e1e1e;text-decoration: line-through; }
- .terminal-2410786847-r5 { fill: #e1e1e1;font-weight: bold }
+ .terminal-4258391335-r1 { fill: #e1e1e1 }
+ .terminal-4258391335-r2 { fill: #c5c8c6 }
+ .terminal-4258391335-r3 { fill: #e1e1e1;font-weight: bold;font-style: italic; }
+ .terminal-4258391335-r4 { fill: #1e1e1e;text-decoration: line-through; }
+ .terminal-4258391335-r5 { fill: #e1e1e1;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- LinkStyleApp
+ LinkStyleApp
-
-
-
- Visit the Textualize website.
- Click here for the bell sound.
- You can also click here for the bell sound.
- Exit this application.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Visit the Textualize website.
+ Click here for the bell sound.
+ You can also click here for the bell sound.
+ Exit this application.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -10406,132 +11053,132 @@
font-weight: 700;
}
- .terminal-3588337117-matrix {
+ .terminal-2570642149-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3588337117-title {
+ .terminal-2570642149-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3588337117-r1 { fill: #e1e1e1 }
- .terminal-3588337117-r2 { fill: #c5c8c6 }
- .terminal-3588337117-r3 { fill: #e1e1e1;text-decoration: underline; }
+ .terminal-2570642149-r1 { fill: #e1e1e1 }
+ .terminal-2570642149-r2 { fill: #c5c8c6 }
+ .terminal-2570642149-r3 { fill: #e1e1e1;text-decoration: underline; }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- LinkHoverStyleApp
+ LinkHoverStyleApp
-
-
-
- Visit the Textualize website.
- Click here for the bell sound.
- You can also click here for the bell sound.
- Exit this application.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Visit the Textualize website.
+ Click here for the bell sound.
+ You can also click here for the bell sound.
+ Exit this application.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -10719,133 +11366,133 @@
font-weight: 700;
}
- .terminal-3234129636-matrix {
+ .terminal-1776342825-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3234129636-title {
+ .terminal-1776342825-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3234129636-r1 { fill: #000000 }
- .terminal-3234129636-r2 { fill: #c5c8c6 }
- .terminal-3234129636-r3 { fill: #0000ff }
- .terminal-3234129636-r4 { fill: #ccccff }
+ .terminal-1776342825-r1 { fill: #000000 }
+ .terminal-1776342825-r2 { fill: #c5c8c6 }
+ .terminal-1776342825-r3 { fill: #0000ff }
+ .terminal-1776342825-r4 { fill: #ccccff }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MarginApp
+ MarginApp
-
-
-
-
-
-
-
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▎ I must not fear. ▊
- ▎ Fear is the mind-killer. ▊
- ▎ Fear is the little-death that brings total obliteration. ▊
- ▎ I will face my fear. ▊
- ▎ I will permit it to pass over me and through me. ▊
- ▎ And when it has gone past, I will turn the inner eye to see ▊
- ▎ its path. ▊
- ▎ Where the fear has gone there will be nothing. Only I will ▊
- ▎ remain. ▊
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▎ I must not fear. ▊
+ ▎ Fear is the mind-killer. ▊
+ ▎ Fear is the little-death that brings total obliteration. ▊
+ ▎ I will face my fear. ▊
+ ▎ I will permit it to pass over me and through me. ▊
+ ▎ And when it has gone past, I will turn the inner eye to see ▊
+ ▎ its path. ▊
+ ▎ Where the fear has gone there will be nothing. Only I will ▊
+ ▎ remain. ▊
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+
+
+
+
+
+
+
+
@@ -10876,141 +11523,141 @@
font-weight: 700;
}
- .terminal-670958431-matrix {
+ .terminal-2957478392-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-670958431-title {
+ .terminal-2957478392-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-670958431-r1 { fill: #ffffff }
- .terminal-670958431-r2 { fill: #e0e0e0 }
- .terminal-670958431-r3 { fill: #c5c8c6 }
- .terminal-670958431-r4 { fill: #ece5e5 }
- .terminal-670958431-r5 { fill: #eee8e3 }
- .terminal-670958431-r6 { fill: #e7e0e6 }
- .terminal-670958431-r7 { fill: #eae2e4 }
- .terminal-670958431-r8 { fill: #e3ede7 }
- .terminal-670958431-r9 { fill: #e8ede4 }
- .terminal-670958431-r10 { fill: #e1eceb }
- .terminal-670958431-r11 { fill: #eeeddf }
+ .terminal-2957478392-r1 { fill: #ffffff }
+ .terminal-2957478392-r2 { fill: #e0e0e0 }
+ .terminal-2957478392-r3 { fill: #c5c8c6 }
+ .terminal-2957478392-r4 { fill: #ece5e5 }
+ .terminal-2957478392-r5 { fill: #eee8e3 }
+ .terminal-2957478392-r6 { fill: #e7e0e6 }
+ .terminal-2957478392-r7 { fill: #eae2e4 }
+ .terminal-2957478392-r8 { fill: #e3ede7 }
+ .terminal-2957478392-r9 { fill: #e8ede4 }
+ .terminal-2957478392-r10 { fill: #e1eceb }
+ .terminal-2957478392-r11 { fill: #eeeddf }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MarginAllApp
+ MarginAllApp
-
-
-
- ╭ ──────────────── ╮ ╭ ───────────────── ╮ ╭ ──────────────── ╮ ╭ ───────────────── ╮
- │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │
- │ │ │ │ │ margin │ │ margin: 1 │
- │ no margin │ │ margin: 1 │ │ : 1 5 │ │ 1 2 6 │
- │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │
- ╰ ──────────────── ╯ ╰ ───────────────── ╯ ╰ ──────────────── ╯ ╰ ───────────────── ╯
-
- ╭ ──────────────── ╮ ╭ ───────────────── ╮ ╭ ──────────────── ╮ ╭ ───────────────── ╮
- │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │
- │ │ │ │ │ margin-bottom: 4 │ │ │
- │ │ │ │ │ │ │ │
- │ │ │ margin-right: │ │ │ │ margin-left: 3 │
- │ │ │ 3 │ │ │ │ │
- │ margin-top: 4 │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │
- ╰ ──────────────── ╯ ╰ ───────────────── ╯ ╰ ──────────────── ╯ ╰ ───────────────── ╯
+
+
+
+ ╭────────────────╮ ╭─────────────────╮ ╭────────────────╮ ╭─────────────────╮
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ margin │ │ margin: 1 │
+ │ no margin │ │ margin: 1 │ │ : 1 5 │ │ 1 2 6 │
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │
+ ╰────────────────╯ ╰─────────────────╯ ╰────────────────╯ ╰─────────────────╯
+
+ ╭────────────────╮ ╭─────────────────╮ ╭────────────────╮ ╭─────────────────╮
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ margin-bottom: 4 │ │ │
+ │ │ │ │ │ │ │ │
+ │ │ │ margin-right: │ │ │ │ margin-left: 3 │
+ │ │ │ 3 │ │ │ │ │
+ │ margin-top: 4 │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │
+ ╰────────────────╯ ╰─────────────────╯ ╰────────────────╯ ╰─────────────────╯
@@ -11518,135 +12165,135 @@
font-weight: 700;
}
- .terminal-1031378205-matrix {
+ .terminal-438685870-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1031378205-title {
+ .terminal-438685870-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1031378205-r1 { fill: #c5c8c6 }
- .terminal-1031378205-r2 { fill: #e1e1e1 }
- .terminal-1031378205-r3 { fill: #e8e0e7 }
- .terminal-1031378205-r4 { fill: #eae3e5 }
- .terminal-1031378205-r5 { fill: #ede6e6 }
- .terminal-1031378205-r6 { fill: #efe9e4 }
+ .terminal-438685870-r1 { fill: #c5c8c6 }
+ .terminal-438685870-r2 { fill: #e1e1e1 }
+ .terminal-438685870-r3 { fill: #e8e0e7 }
+ .terminal-438685870-r4 { fill: #eae3e5 }
+ .terminal-438685870-r5 { fill: #ede6e6 }
+ .terminal-438685870-r6 { fill: #efe9e4 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MinWidthApp
+ MinWidthApp
-
-
-
-
-
- min-width: 25%
-
-
-
-
- min-width: 75%
-
-
-
-
-
- min-width: 100
-
-
-
-
-
- min-width: 400h
-
-
-
+
+
+
+
+
+ min-width: 25%
+
+
+
+
+ min-width: 75%
+
+
+
+
+
+ min-width: 100
+
+
+
+
+
+ min-width: 400h
+
+
+
@@ -11677,134 +12324,134 @@
font-weight: 700;
}
- .terminal-3520697079-matrix {
+ .terminal-3730833227-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3520697079-title {
+ .terminal-3730833227-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .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 }
+ .terminal-3730833227-r1 { fill: #000000 }
+ .terminal-3730833227-r2 { fill: #0000ff }
+ .terminal-3730833227-r3 { fill: #c5c8c6 }
+ .terminal-3730833227-r4 { fill: #ff0000 }
+ .terminal-3730833227-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) ▐
+ ▌ ▐
+ ▌ ▐
+ ▌ ▐
+ ▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟
+
+
+
@@ -11835,141 +12482,141 @@
font-weight: 700;
}
- .terminal-2483518667-matrix {
+ .terminal-618549268-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2483518667-title {
+ .terminal-618549268-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2483518667-r1 { fill: #000000 }
- .terminal-2483518667-r2 { fill: #c5c8c6 }
- .terminal-2483518667-r3 { fill: #000000;font-weight: bold }
- .terminal-2483518667-r4 { fill: #01090f }
- .terminal-2483518667-r5 { fill: #373838;font-weight: bold }
- .terminal-2483518667-r6 { fill: #07243f }
- .terminal-2483518667-r7 { fill: #6f7474;font-weight: bold }
- .terminal-2483518667-r8 { fill: #10518f }
- .terminal-2483518667-r9 { fill: #a8b3b2;font-weight: bold }
- .terminal-2483518667-r10 { fill: #1e90ff }
- .terminal-2483518667-r11 { fill: #e2f4f3;font-weight: bold }
+ .terminal-618549268-r1 { fill: #000000 }
+ .terminal-618549268-r2 { fill: #c5c8c6 }
+ .terminal-618549268-r3 { fill: #000000;font-weight: bold }
+ .terminal-618549268-r4 { fill: #01090f }
+ .terminal-618549268-r5 { fill: #373838;font-weight: bold }
+ .terminal-618549268-r6 { fill: #07243f }
+ .terminal-618549268-r7 { fill: #6f7474;font-weight: bold }
+ .terminal-618549268-r8 { fill: #10518f }
+ .terminal-618549268-r9 { fill: #a8b3b2;font-weight: bold }
+ .terminal-618549268-r10 { fill: #1e90ff }
+ .terminal-618549268-r11 { fill: #e2f4f3;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- OpacityApp
+ OpacityApp
-
-
-
- ▛ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▜
- ▌ opacity: 0% ▐
- ▌ ▐
- ▙ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▟
- ▛ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▜
- ▌ ▐
- ▌ opacity: 25% ▐
- ▌ ▐
- ▙ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▟
- ▛ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▜
- ▌ ▐
- ▌ opacity: 50% ▐
- ▌ ▐
- ▙ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▟
- ▛ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▜
- ▌ ▐
- ▌ opacity: 75% ▐
- ▌ ▐
- ▙ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▟
- ▛ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▜
- ▌ ▐
- ▌ opacity: 100% ▐
- ▌ ▐
- ▙ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▟
+
+
+
+ ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜
+ ▌ opacity: 0% ▐
+ ▌ ▐
+ ▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟
+ ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜
+ ▌ ▐
+ ▌ opacity: 25% ▐
+ ▌ ▐
+ ▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟
+ ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜
+ ▌ ▐
+ ▌ opacity: 50% ▐
+ ▌ ▐
+ ▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟
+ ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜
+ ▌ ▐
+ ▌ opacity: 75% ▐
+ ▌ ▐
+ ▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟
+ ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜
+ ▌ ▐
+ ▌ opacity: 100% ▐
+ ▌ ▐
+ ▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟
@@ -11999,133 +12646,133 @@
font-weight: 700;
}
- .terminal-3556982422-matrix {
+ .terminal-2596745327-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3556982422-title {
+ .terminal-2596745327-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3556982422-r1 { fill: #000000 }
- .terminal-3556982422-r2 { fill: #c5c8c6 }
- .terminal-3556982422-r3 { fill: #008000 }
- .terminal-3556982422-r4 { fill: #cce5cc }
+ .terminal-2596745327-r1 { fill: #000000 }
+ .terminal-2596745327-r2 { fill: #c5c8c6 }
+ .terminal-2596745327-r3 { fill: #008000 }
+ .terminal-2596745327-r4 { fill: #cce5cc }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- OutlineApp
+ OutlineApp
-
-
-
-
-
-
-
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▎ ear is the mind-killer. ▊
- ▎ ear is the little-death that brings total obliteration. ▊
- ▎ will face my fear. ▊
- ▎ will permit it to pass over me and through me. ▊
- ▎ nd when it has gone past, I will turn the inner eye to see its ▊
- ▎ ath. ▊
- ▎ here the fear has gone there will be nothing. Only I will ▊
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▎ ear is the mind-killer. ▊
+ ▎ ear is the little-death that brings total obliteration. ▊
+ ▎ will face my fear. ▊
+ ▎ will permit it to pass over me and through me. ▊
+ ▎ nd when it has gone past, I will turn the inner eye to see its ▊
+ ▎ ath. ▊
+ ▎ here the fear has gone there will be nothing. Only I will ▊
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+
+
+
+
+
+
+
+
+
+
@@ -12156,133 +12803,133 @@
font-weight: 700;
}
- .terminal-2040973868-matrix {
+ .terminal-119716715-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2040973868-title {
+ .terminal-119716715-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2040973868-r1 { fill: #e1e1e1 }
- .terminal-2040973868-r2 { fill: #0178d4 }
- .terminal-2040973868-r3 { fill: #c5c8c6 }
- .terminal-2040973868-r4 { fill: #1e1e1e }
+ .terminal-119716715-r1 { fill: #e1e1e1 }
+ .terminal-119716715-r2 { fill: #0178d4 }
+ .terminal-119716715-r3 { fill: #c5c8c6 }
+ .terminal-119716715-r4 { fill: #1e1e1e }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AllOutlinesApp
+ AllOutlinesApp
-
-
-
- + ------------------ + ┏ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ ┓
- | ascii | blank ╏ dashed ╏
- + ------------------ + ┗ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ ┛
-
-
- ╔ ══════════════════ ╗ ┏ ━━━━━━━━━━━━━━━━━━ ┓
- ║ double ║ ┃ heavy ┃ hidden/none
- ╚ ══════════════════ ╝ ┗ ━━━━━━━━━━━━━━━━━━ ┛
-
-
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔ ▗ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▖
- hkey ▐ inner ▌ none
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁ ▝ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▘
-
-
- ▛ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▜ ╭ ────────────────── ╮ ┌ ────────────────── ┐
- ▌ outer ▐ │ round │ │ solid │
- ▙ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▟ ╰ ────────────────── ╯ └ ────────────────── ┘
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ ▏ ▕ ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▊ tall ▎ ▏ vkey ▕ ▎ wide ▊
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ ▏ ▕ ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
+
+
+
+ +------------------+ ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓
+ | ascii | blank ╏ dashed ╏
+ +------------------+ ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
+
+
+ ╔══════════════════╗ ┏━━━━━━━━━━━━━━━━━━┓
+ ║ double ║ ┃ heavy ┃ hidden/none
+ ╚══════════════════╝ ┗━━━━━━━━━━━━━━━━━━┛
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▗▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▖
+ hkey ▐ inner ▌ none
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▝▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▘
+
+
+ ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜ ╭──────────────────╮ ┌──────────────────┐
+ ▌ outer ▐ │ round │ │ solid │
+ ▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟ ╰──────────────────╯ └──────────────────┘
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ ▏ ▕ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▊ tall ▎ ▏ vkey ▕ ▎ wide ▊
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ ▏ ▕ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
@@ -12313,134 +12960,134 @@
font-weight: 700;
}
- .terminal-1454950069-matrix {
+ .terminal-55418416-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1454950069-title {
+ .terminal-55418416-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1454950069-r1 { fill: #b93c5b }
- .terminal-1454950069-r2 { fill: #e1e1e1 }
- .terminal-1454950069-r3 { fill: #c5c8c6 }
- .terminal-1454950069-r4 { fill: #4ebf71 }
+ .terminal-55418416-r1 { fill: #b93c5b }
+ .terminal-55418416-r2 { fill: #e1e1e1 }
+ .terminal-55418416-r3 { fill: #c5c8c6 }
+ .terminal-55418416-r4 { fill: #4ebf71 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- OutlineBorderApp
+ OutlineBorderApp
-
-
-
- ╭ ─────────────────────────────────────────────────────────────────── ╮
- │ ear is the mind-killer. │
- │ ear is the little-death that brings total obliteration. │
- │ will face my fear. │
- │ will permit it to pass over me and through me. │
- │ nd when it has gone past, I will turn the inner eye to see its path │
- │ here the fear has gone there will be nothing. Only I will remain. │
- ╰ ─────────────────────────────────────────────────────────────────── ╯
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ I must not fear. ┃
- ┃ Fear is the mind-killer. ┃
- ┃ Fear is the little-death that brings total obliteration. ┃
- ┃ I will face my fear. ┃
- ┃ I will permit it to pass over me and through me. ┃
- ┃ And when it has gone past, I will turn the inner eye to see its path. ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
- ╭ ───────────────────────────────────────────────────────────────────── ╮
- │ I must not fear. │
- │ Fear is the mind-killer. │
- │ Fear is the little-death that brings total obliteration. │
- │ I will face my fear. │
- │ I will permit it to pass over me and through me. │
- │ And when it has gone past, I will turn the inner eye to see its path. │
- ╰ ───────────────────────────────────────────────────────────────────── ╯
+
+
+
+ ╭───────────────────────────────────────────────────────────────────╮
+ │ ear is the mind-killer. │
+ │ ear is the little-death that brings total obliteration. │
+ │ will face my fear. │
+ │ will permit it to pass over me and through me. │
+ │ nd when it has gone past, I will turn the inner eye to see its path │
+ │ here the fear has gone there will be nothing. Only I will remain. │
+ ╰───────────────────────────────────────────────────────────────────╯
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ I must not fear. ┃
+ ┃ Fear is the mind-killer. ┃
+ ┃ Fear is the little-death that brings total obliteration. ┃
+ ┃ I will face my fear. ┃
+ ┃ I will permit it to pass over me and through me. ┃
+ ┃ And when it has gone past, I will turn the inner eye to see its path. ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ ╭─────────────────────────────────────────────────────────────────────╮
+ │ I must not fear. │
+ │ Fear is the mind-killer. │
+ │ Fear is the little-death that brings total obliteration. │
+ │ I will face my fear. │
+ │ I will permit it to pass over me and through me. │
+ │ And when it has gone past, I will turn the inner eye to see its path. │
+ ╰─────────────────────────────────────────────────────────────────────╯
@@ -12470,136 +13117,136 @@
font-weight: 700;
}
- .terminal-1076088988-matrix {
+ .terminal-2364259351-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1076088988-title {
+ .terminal-2364259351-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1076088988-r1 { fill: #000000 }
- .terminal-1076088988-r2 { fill: #c5c8c6 }
- .terminal-1076088988-r3 { fill: #008000 }
- .terminal-1076088988-r4 { fill: #e5f0e5 }
- .terminal-1076088988-r5 { fill: #036a03 }
- .terminal-1076088988-r6 { fill: #14191f }
+ .terminal-2364259351-r1 { fill: #000000 }
+ .terminal-2364259351-r2 { fill: #c5c8c6 }
+ .terminal-2364259351-r3 { fill: #008000 }
+ .terminal-2364259351-r4 { fill: #e5f0e5 }
+ .terminal-2364259351-r5 { fill: #036a03 }
+ .terminal-2364259351-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 me ▊ ▎ I 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 me ▊ ▎ and 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 me ▊ ▎ I 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 me ▊ ▎ and through me. ▊
@@ -12629,131 +13276,131 @@
font-weight: 700;
}
- .terminal-3291669704-matrix {
+ .terminal-1142797465-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3291669704-title {
+ .terminal-1142797465-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3291669704-r1 { fill: #c5c8c6 }
- .terminal-3291669704-r2 { fill: #0000ff }
+ .terminal-1142797465-r1 { fill: #c5c8c6 }
+ .terminal-1142797465-r2 { fill: #0000ff }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- PaddingApp
+ PaddingApp
-
-
-
-
-
-
-
- I must not fear.
- Fear is the mind-killer.
- Fear is the little-death that brings total obliteration.
- I will face my fear.
- I will permit it to pass over me and through me.
- And when it has gone past, I will turn the inner eye to see its
- path.
- Where the fear has gone there will be nothing. Only I will
- remain.
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ I must not fear.
+ Fear is the mind-killer.
+ Fear is the little-death that brings total obliteration.
+ I will face my fear.
+ I will permit it to pass over me and through me.
+ And when it has gone past, I will turn the inner eye to see its
+ path.
+ Where the fear has gone there will be nothing. Only I will
+ remain.
+
+
+
+
+
+
+
+
+
+
@@ -12784,139 +13431,139 @@
font-weight: 700;
}
- .terminal-1905931680-matrix {
+ .terminal-2663771085-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1905931680-title {
+ .terminal-2663771085-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1905931680-r1 { fill: #e7e0e6 }
- .terminal-1905931680-r2 { fill: #e0e0e0 }
- .terminal-1905931680-r3 { fill: #c5c8c6 }
- .terminal-1905931680-r4 { fill: #eae2e4 }
- .terminal-1905931680-r5 { fill: #ece5e5 }
- .terminal-1905931680-r6 { fill: #eee8e3 }
- .terminal-1905931680-r7 { fill: #e8ede4 }
- .terminal-1905931680-r8 { fill: #e3ede7 }
- .terminal-1905931680-r9 { fill: #e1eceb }
- .terminal-1905931680-r10 { fill: #eeeddf }
+ .terminal-2663771085-r1 { fill: #e7e0e6 }
+ .terminal-2663771085-r2 { fill: #e0e0e0 }
+ .terminal-2663771085-r3 { fill: #c5c8c6 }
+ .terminal-2663771085-r4 { fill: #eae2e4 }
+ .terminal-2663771085-r5 { fill: #ece5e5 }
+ .terminal-2663771085-r6 { fill: #eee8e3 }
+ .terminal-2663771085-r7 { fill: #e8ede4 }
+ .terminal-2663771085-r8 { fill: #e3ede7 }
+ .terminal-2663771085-r9 { fill: #e1eceb }
+ .terminal-2663771085-r10 { fill: #eeeddf }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- PaddingAllApp
+ PaddingAllApp
-
-
-
- no padding
- padding: 1 padding: padding: 1 1
- 1 5 2 6
-
-
-
-
-
-
-
-
-
- padding-right: 3 padding-bottom: 4 padding-left: 3
-
-
-
- padding-top: 4
-
-
-
-
-
-
+
+
+
+ no padding
+ padding: 1 padding: padding: 1 1
+ 1 5 2 6
+
+
+
+
+
+
+
+
+
+ padding-right: 3 padding-bottom: 4 padding-left: 3
+
+
+
+ padding-top: 4
+
+
+
+
+
+
@@ -13109,132 +13756,132 @@
font-weight: 700;
}
- .terminal-3484348706-matrix {
+ .terminal-3108861724-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3484348706-title {
+ .terminal-3108861724-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3484348706-r1 { fill: #e1e1e1 }
- .terminal-3484348706-r2 { fill: #c5c8c6 }
- .terminal-3484348706-r3 { fill: #14191f }
+ .terminal-3108861724-r1 { fill: #e1e1e1 }
+ .terminal-3108861724-r2 { fill: #c5c8c6 }
+ .terminal-3108861724-r3 { fill: #14191f }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ScrollbarCornerColorApp
+ ScrollbarCornerColorApp
-
-
-
- I must not fear. Fear is the mind-killer. Fear is the little-death that brings
- I must not fear.
- Fear is the mind-killer.
- Fear is the little-death that brings total obliteration.
- I will face my fear.
- I will permit it to pass over me and through me.
- And when it has gone past, I will turn the inner eye to see its path.
- Where the fear has gone there will be nothing. Only I will remain. ▅▅
- I must not fear.
- Fear is the mind-killer.
- Fear is the little-death that brings total obliteration.
- I will face my fear.
- I will permit it to pass over me and through me.
- And when it has gone past, I will turn the inner eye to see its path.
- Where the fear has gone there will be nothing. Only I will remain.
- I must not fear.
- Fear is the mind-killer.
- Fear is the little-death that brings total obliteration.
- I will face my fear.
- I will permit it to pass over me and through me.
- And when it has gone past, I will turn the inner eye to see its path.
- Where the fear has gone there will be nothing. Only I will remain.
- I must not fear.
+
+
+
+ I must not fear. Fear is the mind-killer. Fear is the little-death that brings
+ I must not fear.
+ Fear is the mind-killer.
+ Fear is the little-death that brings total obliteration.
+ I will face my fear.
+ I will permit it to pass over me and through me.
+ And when it has gone past, I will turn the inner eye to see its path.
+ Where the fear has gone there will be nothing. Only I will remain. ▅▅
+ I must not fear.
+ Fear is the mind-killer.
+ Fear is the little-death that brings total obliteration.
+ I will face my fear.
+ I will permit it to pass over me and through me.
+ And when it has gone past, I will turn the inner eye to see its path.
+ Where the fear has gone there will be nothing. Only I will remain.
+ I must not fear.
+ Fear is the mind-killer.
+ Fear is the little-death that brings total obliteration.
+ I will face my fear.
+ I will permit it to pass over me and through me.
+ And when it has gone past, I will turn the inner eye to see its path.
+ Where the fear has gone there will be nothing. Only I will remain.
+ I must not fear.
@@ -13421,132 +14068,132 @@
font-weight: 700;
}
- .terminal-1624210960-matrix {
+ .terminal-3590949634-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1624210960-title {
+ .terminal-3590949634-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1624210960-r1 { fill: #c5c8c6 }
- .terminal-1624210960-r2 { fill: #3333ff }
- .terminal-1624210960-r3 { fill: #14191f }
+ .terminal-3590949634-r1 { fill: #c5c8c6 }
+ .terminal-3590949634-r2 { fill: #3333ff }
+ .terminal-3590949634-r3 { fill: #14191f }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ScrollbarApp
+ ScrollbarApp
-
-
-
-
-
- I must not fear.
- Fear is the mind-killer.
- Fear is the little-death that brings total obliteration. ▁▁▁▁
- I will face my fear.
- I will permit it to pass over me and through me.
- And when it has gone past, I will turn the inner eye to see its path.
- Where the fear has gone there will be nothing. Only I will remain.
- I must not fear.
- Fear is the mind-killer.
- Fear is the little-death that brings total obliteration.
- I will face my fear.
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ I must not fear.
+ Fear is the mind-killer.
+ Fear is the little-death that brings total obliteration. ▁▁▁▁
+ I will face my fear.
+ I will permit it to pass over me and through me.
+ And when it has gone past, I will turn the inner eye to see its path.
+ Where the fear has gone there will be nothing. Only I will remain.
+ I must not fear.
+ Fear is the mind-killer.
+ Fear is the little-death that brings total obliteration.
+ I will face my fear.
+
+
+
+
+
+
+
+
+
+
@@ -13577,136 +14224,136 @@
font-weight: 700;
}
- .terminal-709381566-matrix {
+ .terminal-4177949326-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-709381566-title {
+ .terminal-4177949326-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-709381566-r1 { fill: #e7e0e0 }
- .terminal-709381566-r2 { fill: #c5c8c6 }
- .terminal-709381566-r3 { fill: #e0e4e0 }
- .terminal-709381566-r4 { fill: #e0e0e7 }
- .terminal-709381566-r5 { fill: #14191f }
- .terminal-709381566-r6 { fill: #23568b }
+ .terminal-4177949326-r1 { fill: #e7e0e0 }
+ .terminal-4177949326-r2 { fill: #c5c8c6 }
+ .terminal-4177949326-r3 { fill: #e0e4e0 }
+ .terminal-4177949326-r4 { fill: #e0e0e7 }
+ .terminal-4177949326-r5 { fill: #14191f }
+ .terminal-4177949326-r6 { fill: #23568b }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ScrollbarApp
+ ScrollbarApp
-
-
-
- I must not fear. I must not fear. I must not fear.
- Fear is the mind-killer. Fear is the mind-killer. Fear is the mind-killer.
- Fear is the little-death Fear is the little-death t Fear is the little-death
- I will face my fear. I will face my fear. I will face my fear.
- I will permit it to pass I will permit it to pass o I will permit it to pass
- And when it has gone past And when it has gone past, And when it has gone past
- Where the fear has gone t Where the fear has gone th Where the fear has gone t
- I must not fear. I must not fear. I must not fear.
- Fear is the mind-killer. Fear is the mind-killer. Fear is the mind-killer.
- Fear is the little-death Fear is the little-death t Fear is the little-death
- I will face my fear. ▇ I will face my fear. I will face my fear. ▇▇
- I will permit it to pass I will permit it to pass o I will permit it to pass
- And when it has gone past And when it has gone past, And when it has gone past
- Where the fear has gone t Where the fear has gone th Where the fear has gone t
- I must not fear. I must not fear. ▂ I must not fear.
- Fear is the mind-killer. Fear is the mind-killer. Fear is the mind-killer.
- Fear is the little-death Fear is the little-death t Fear is the little-death
- I will face my fear. I will face my fear. I will face my fear.
- I will permit it to pass I will permit it to pass o I will permit it to pass
- ▏ And when it has gone past, ▏
- ▏ Where the fear has gone th ▏
- ▏ I must not fear. ▏
- ▏ Fear is the mind-killer. ▏
- ▏ ▉ ▏
+
+
+
+ I must not fear. I must not fear. I must not fear.
+ Fear is the mind-killer. Fear is the mind-killer. Fear is the mind-killer.
+ Fear is the little-death Fear is the little-death t Fear is the little-death
+ I will face my fear. I will face my fear. I will face my fear.
+ I will permit it to pass I will permit it to pass o I will permit it to pass
+ And when it has gone past And when it has gone past, And when it has gone past
+ Where the fear has gone t Where the fear has gone th Where the fear has gone t
+ I must not fear. I must not fear. I must not fear.
+ Fear is the mind-killer. Fear is the mind-killer. Fear is the mind-killer.
+ Fear is the little-death Fear is the little-death t Fear is the little-death
+ I will face my fear. ▇ I will face my fear. I will face my fear. ▇▇
+ I will permit it to pass I will permit it to pass o I will permit it to pass
+ And when it has gone past And when it has gone past, And when it has gone past
+ Where the fear has gone t Where the fear has gone th Where the fear has gone t
+ I must not fear. I must not fear. ▂ I must not fear.
+ Fear is the mind-killer. Fear is the mind-killer. Fear is the mind-killer.
+ Fear is the little-death Fear is the little-death t Fear is the little-death
+ I will face my fear. I will face my fear. I will face my fear.
+ I will permit it to pass I will permit it to pass o I will permit it to pass
+ ▏ And when it has gone past, ▏
+ ▏ Where the fear has gone th ▏
+ ▏ I must not fear. ▏
+ ▏ Fear is the mind-killer. ▏
+ ▏ ▉ ▏
@@ -13736,136 +14383,136 @@
font-weight: 700;
}
- .terminal-3248687298-matrix {
+ .terminal-2700719567-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3248687298-title {
+ .terminal-2700719567-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3248687298-r1 { fill: #e1e1e1 }
- .terminal-3248687298-r2 { fill: #c5c8c6 }
- .terminal-3248687298-r3 { fill: #14191f }
- .terminal-3248687298-r4 { fill: #ff0000 }
- .terminal-3248687298-r5 { fill: #23568b }
- .terminal-3248687298-r6 { fill: #008000 }
+ .terminal-2700719567-r1 { fill: #e1e1e1 }
+ .terminal-2700719567-r2 { fill: #c5c8c6 }
+ .terminal-2700719567-r3 { fill: #14191f }
+ .terminal-2700719567-r4 { fill: #ff0000 }
+ .terminal-2700719567-r5 { fill: #23568b }
+ .terminal-2700719567-r6 { fill: #008000 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ScrollbarApp
+ ScrollbarApp
-
-
-
- I must not fear. I must not fear.
- Fear is the mind-killer. Fear is the mind-killer.
- Fear is the little-death that brings t Fear is the little-death that brings t
- I will face my fear. I will face my fear.
- I will permit it to pass over me and t I will permit it to pass over me and t
- And when it has gone past, I will turn And when it has gone past, I will turn
- see its path. see its path.
- Where the fear has gone there will be Where the fear has gone there will be
- will remain. will 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 brings t Fear is the little-death that brings t
- I will face my fear. I will face my fear.
- I will permit it to pass over me and t I will permit it to pass over me and t
- And when it has gone past, I will turn And when it has gone past, I will turn
- see its path. ▃▃ see its path. ▃▃
- Where the fear has gone there will be Where the fear has gone there will be
- will remain. will 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 brings t Fear is the little-death that brings t
- I will face my fear. I will face my fear.
- I will permit it to pass over me and t I will permit it to pass over me and t
- ▍ ▍
+
+
+
+ I must not fear. I must not fear.
+ Fear is the mind-killer. Fear is the mind-killer.
+ Fear is the little-death that brings t Fear is the little-death that brings t
+ I will face my fear. I will face my fear.
+ I will permit it to pass over me and t I will permit it to pass over me and t
+ And when it has gone past, I will turn And when it has gone past, I will turn
+ see its path. see its path.
+ Where the fear has gone there will be Where the fear has gone there will be
+ will remain. will 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 brings t Fear is the little-death that brings t
+ I will face my fear. I will face my fear.
+ I will permit it to pass over me and t I will permit it to pass over me and t
+ And when it has gone past, I will turn And when it has gone past, I will turn
+ see its path. ▃▃ see its path. ▃▃
+ Where the fear has gone there will be Where the fear has gone there will be
+ will remain. will 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 brings t Fear is the little-death that brings t
+ I will face my fear. I will face my fear.
+ I will permit it to pass over me and t I will permit it to pass over me and t
+ ▍ ▍
@@ -13895,133 +14542,133 @@
font-weight: 700;
}
- .terminal-3828898399-matrix {
+ .terminal-295079783-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3828898399-title {
+ .terminal-295079783-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3828898399-r1 { fill: #e1e1e1 }
- .terminal-3828898399-r2 { fill: #c5c8c6 }
- .terminal-3828898399-r3 { fill: #0000ff }
+ .terminal-295079783-r1 { fill: #e1e1e1 }
+ .terminal-295079783-r2 { fill: #c5c8c6 }
+ .terminal-295079783-r3 { fill: #0000ff }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- Scrollbar2App
+ Scrollbar2App
-
-
-
- I must not fear.
- Fear is the mind-killer.
- Fear is the little-death that brings total obliteration.
- I will face my fear.
- I will permit it to pass over me and through me.
- And when it has gone past, I will turn the inner eye to see its path.
- Where the fear has gone there will be nothing. Only I will remain.
- I must not fear.
- Fear is the mind-killer. ▇▇
- Fear is the little-death that brings total obliteration.
- I will face my fear.
- I will permit it to pass over me and through me.
- And when it has gone past, I will turn the inner eye to see its path.
- Where the fear has gone there will be nothing. Only I will remain.
- I must not fear.
- Fear is the mind-killer.
- Fear is the little-death that brings total obliteration.
- I will face my fear.
- I will permit it to pass over me and through me.
- And when it has gone past, I will turn the inner eye to see its path.
- Where the fear has gone there will be nothing. Only I will remain.
- I must not fear.
- Fear is the mind-killer.
- Fear is the little-death that brings total obliteration.
+
+
+
+ I must not fear.
+ Fear is the mind-killer.
+ Fear is the little-death that brings total obliteration.
+ I will face my fear.
+ I will permit it to pass over me and through me.
+ And when it has gone past, I will turn the inner eye to see its path.
+ Where the fear has gone there will be nothing. Only I will remain.
+ I must not fear.
+ Fear is the mind-killer. ▇▇
+ Fear is the little-death that brings total obliteration.
+ I will face my fear.
+ I will permit it to pass over me and through me.
+ And when it has gone past, I will turn the inner eye to see its path.
+ Where the fear has gone there will be nothing. Only I will remain.
+ I must not fear.
+ Fear is the mind-killer.
+ Fear is the little-death that brings total obliteration.
+ I will face my fear.
+ I will permit it to pass over me and through me.
+ And when it has gone past, I will turn the inner eye to see its path.
+ Where the fear has gone there will be nothing. Only I will remain.
+ I must not fear.
+ Fear is the mind-killer.
+ Fear is the little-death that brings total obliteration.
@@ -14051,138 +14698,138 @@
font-weight: 700;
}
- .terminal-3840959336-matrix {
+ .terminal-2525861690-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3840959336-title {
+ .terminal-2525861690-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3840959336-r1 { fill: #c5c8c6 }
- .terminal-3840959336-r2 { fill: #161c1d;font-weight: bold }
- .terminal-3840959336-r3 { fill: #161c1d }
- .terminal-3840959336-r4 { fill: #f8e9e9 }
- .terminal-3840959336-r5 { fill: #f8e9e9;font-weight: bold }
- .terminal-3840959336-r6 { fill: #132013 }
- .terminal-3840959336-r7 { fill: #132013;font-weight: bold }
- .terminal-3840959336-r8 { fill: #1c0e13;font-weight: bold }
- .terminal-3840959336-r9 { fill: #1c0e13 }
+ .terminal-2525861690-r1 { fill: #c5c8c6 }
+ .terminal-2525861690-r2 { fill: #161c1d;font-weight: bold }
+ .terminal-2525861690-r3 { fill: #161c1d }
+ .terminal-2525861690-r4 { fill: #f8e9e9 }
+ .terminal-2525861690-r5 { fill: #f8e9e9;font-weight: bold }
+ .terminal-2525861690-r6 { fill: #132013 }
+ .terminal-2525861690-r7 { fill: #132013;font-weight: bold }
+ .terminal-2525861690-r8 { fill: #1c0e13;font-weight: bold }
+ .terminal-2525861690-r9 { fill: #1c0e13 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- TextAlign
+ TextAlign
-
-
-
-
- Left aligned Center aligned
- I must not fear. Fear is the I must not fear. Fear is the
- mind-killer. Fear is the mind-killer. Fear is the
- little-death that brings total little-death that brings total
- obliteration. I will face my fear. I obliteration. I will face my fear. I
- will permit it to pass over me and will permit it to pass over me and
- through me. through me.
-
-
-
-
-
- Right aligned Justified
- I must not fear. Fear is the I must not fear. Fear is the
- mind-killer. Fear is the mind-killer. Fear is the
- little-death that brings total little-death that brings total
- obliteration. I will face my fear. I obliteration. I will face my fear. I
- will permit it to pass over me and will permit it to pass over me and
- through me. through me.
-
-
-
+
+
+
+
+ Left aligned Center aligned
+ I must not fear. Fear is the I must not fear. Fear is the
+ mind-killer. Fear is the mind-killer. Fear is the
+ little-death that brings total little-death that brings total
+ obliteration. I will face my fear. I obliteration. I will face my fear. I
+ will permit it to pass over me and will permit it to pass over me and
+ through me. through me.
+
+
+
+
+
+ Right aligned Justified
+ I must not fear. Fear is the I must not fear. Fear is the
+ mind-killer. Fear is the mind-killer. Fear is the
+ little-death that brings total little-death that brings total
+ obliteration. I will face my fear. I obliteration. I will face my fear. I
+ will permit it to pass over me and will permit it to pass over me and
+ through me. through me.
+
+
+
@@ -14529,138 +15176,138 @@
font-weight: 700;
}
- .terminal-3534356536-matrix {
+ .terminal-3629813941-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3534356536-title {
+ .terminal-3629813941-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3534356536-r1 { fill: #e1e1e1 }
- .terminal-3534356536-r2 { fill: #c5c8c6 }
- .terminal-3534356536-r3 { fill: #e1e1e1;font-weight: bold }
- .terminal-3534356536-r4 { fill: #e1e1e1;font-style: italic; }
- .terminal-3534356536-r5 { fill: #1e1e1e }
- .terminal-3534356536-r6 { fill: #e1e1e1;text-decoration: line-through; }
- .terminal-3534356536-r7 { fill: #e1e1e1;text-decoration: underline; }
- .terminal-3534356536-r8 { fill: #e1e1e1;font-weight: bold;font-style: italic; }
- .terminal-3534356536-r9 { fill: #1e1e1e;text-decoration: line-through; }
+ .terminal-3629813941-r1 { fill: #e1e1e1 }
+ .terminal-3629813941-r2 { fill: #c5c8c6 }
+ .terminal-3629813941-r3 { fill: #e1e1e1;font-weight: bold }
+ .terminal-3629813941-r4 { fill: #e1e1e1;font-style: italic; }
+ .terminal-3629813941-r5 { fill: #1e1e1e }
+ .terminal-3629813941-r6 { fill: #e1e1e1;text-decoration: line-through; }
+ .terminal-3629813941-r7 { fill: #e1e1e1;text-decoration: underline; }
+ .terminal-3629813941-r8 { fill: #e1e1e1;font-weight: bold;font-style: italic; }
+ .terminal-3629813941-r9 { fill: #1e1e1e;text-decoration: line-through; }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AllTextStyleApp
+ AllTextStyleApp
-
-
-
-
- none bold italic reverse
- I must not fear. I must not fear. I must not fear. I must not fear.
- Fear is the Fear is the Fear is the Fear is the
- mind-killer. mind-killer. mind-killer. mind-killer.
- Fear is the Fear is the Fear is the Fear is the
- little-death that little-death that little-death that little-death that
- brings total brings total brings total brings total
- obliteration. obliteration. obliteration. obliteration.
- I will face my I will face my I will face my I will face my
- fear. fear. fear. fear.
-
- strike underline bold italic reverse strike
- I must not fear. I must not fear. I must not fear. I must not fear.
- Fear is the Fear is the Fear is the Fear is the
- mind-killer. mind-killer. mind-killer. mind-killer.
- Fear is the Fear is the Fear is the Fear is the
- little-death that little-death that little-death that little-death that
- brings total brings total brings total brings total
- obliteration. obliteration. obliteration. obliteration.
- I will face my I will face my I will face my I will face my
- fear. fear. fear. fear.
- I will permit it I will permit it I will permit it I will permit it
+
+
+
+
+ none bold italic reverse
+ I must not fear. I must not fear. I must not fear. I must not fear.
+ Fear is the Fear is the Fear is the Fear is the
+ mind-killer. mind-killer. mind-killer. mind-killer.
+ Fear is the Fear is the Fear is the Fear is the
+ little-death that little-death that little-death that little-death that
+ brings total brings total brings total brings total
+ obliteration. obliteration. obliteration. obliteration.
+ I will face my I will face my I will face my I will face my
+ fear. fear. fear. fear.
+
+ strike underline bold italic reverse strike
+ I must not fear. I must not fear. I must not fear. I must not fear.
+ Fear is the Fear is the Fear is the Fear is the
+ mind-killer. mind-killer. mind-killer. mind-killer.
+ Fear is the Fear is the Fear is the Fear is the
+ little-death that little-death that little-death that little-death that
+ brings total brings total brings total brings total
+ obliteration. obliteration. obliteration. obliteration.
+ I will face my I will face my I will face my I will face my
+ fear. fear. fear. fear.
+ I will permit it I will permit it I will permit it I will permit it
@@ -14855,132 +15502,132 @@
font-weight: 700;
}
- .terminal-398211359-matrix {
+ .terminal-3114242500-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-398211359-title {
+ .terminal-3114242500-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-398211359-r1 { fill: #0000ff }
- .terminal-398211359-r2 { fill: #c5c8c6 }
- .terminal-398211359-r3 { fill: #ddeedd }
+ .terminal-3114242500-r1 { fill: #0000ff }
+ .terminal-3114242500-r2 { fill: #c5c8c6 }
+ .terminal-3114242500-r3 { fill: #ddeedd }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- VisibilityApp
+ VisibilityApp
-
-
-
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ Widget 1 ┃
- ┃ ┃
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
-
-
-
-
-
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ Widget 3 ┃
- ┃ ┃
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
-
-
-
-
-
-
-
-
+
+
+
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃Widget 1 ┃
+ ┃ ┃
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+
+
+
+
+
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃Widget 3 ┃
+ ┃ ┃
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+
+
+
+
+
+
+
+
@@ -15328,148 +15975,148 @@
font-weight: 700;
}
- .terminal-3780599318-matrix {
+ .terminal-2486976152-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3780599318-title {
+ .terminal-2486976152-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3780599318-r1 { fill: #c5c8c6 }
- .terminal-3780599318-r2 { fill: #e8e0e7 }
- .terminal-3780599318-r3 { fill: #eae3e5 }
- .terminal-3780599318-r4 { fill: #ede6e6 }
- .terminal-3780599318-r5 { fill: #efe9e4 }
- .terminal-3780599318-r6 { fill: #efeedf }
- .terminal-3780599318-r7 { fill: #e9eee5 }
- .terminal-3780599318-r8 { fill: #e4eee8 }
- .terminal-3780599318-r9 { fill: #e2edeb }
- .terminal-3780599318-r10 { fill: #dfebed }
- .terminal-3780599318-r11 { fill: #ddedf9 }
+ .terminal-2486976152-r1 { fill: #c5c8c6 }
+ .terminal-2486976152-r2 { fill: #e8e0e7 }
+ .terminal-2486976152-r3 { fill: #eae3e5 }
+ .terminal-2486976152-r4 { fill: #ede6e6 }
+ .terminal-2486976152-r5 { fill: #efe9e4 }
+ .terminal-2486976152-r6 { fill: #efeedf }
+ .terminal-2486976152-r7 { fill: #e9eee5 }
+ .terminal-2486976152-r8 { fill: #e4eee8 }
+ .terminal-2486976152-r9 { fill: #e2edeb }
+ .terminal-2486976152-r10 { fill: #dfebed }
+ .terminal-2486976152-r11 { fill: #ddedf9 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- WidthComparisonApp
+ WidthComparisonApp
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- #cells #percent #w #h #vw #vh #auto #fr1 #fr3
-
-
-
-
-
-
-
-
-
-
-
- ····•···· •····•···· •····•·· ··•··· ·•····•····• ····•· ···•· ···•·· ··•····•····•····•
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #cells #percent #w #h #vw #vh #auto #fr1 #fr3
+
+
+
+
+
+
+
+
+
+
+
+ ····•····•····•····•····•····•····•····•····•····•····•····•····•····•····•····•
'''
# ---
-# name: test_datatable_add_column
+# name: test_data_table_in_tabs
'''
@@ -15492,125 +16139,286 @@
font-weight: 700;
}
- .terminal-2146794738-matrix {
+ .terminal-971656130-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2146794738-title {
+ .terminal-971656130-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2146794738-r1 { fill: #dde6ed;font-weight: bold }
- .terminal-2146794738-r2 { fill: #dde6ed }
- .terminal-2146794738-r3 { fill: #c5c8c6 }
- .terminal-2146794738-r4 { fill: #211505 }
- .terminal-2146794738-r5 { fill: #e1e1e1 }
+ .terminal-971656130-r1 { fill: #c5c8c6 }
+ .terminal-971656130-r2 { fill: #e1e1e1 }
+ .terminal-971656130-r3 { fill: #e1e1e1;font-weight: bold }
+ .terminal-971656130-r4 { fill: #474747 }
+ .terminal-971656130-r5 { fill: #0178d4 }
+ .terminal-971656130-r6 { fill: #dde6ed;font-weight: bold }
+ .terminal-971656130-r7 { fill: #dde6ed }
+ .terminal-971656130-r8 { fill: #211505 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AddColumn
+ Dashboard
-
-
-
- Movies No Default With Default Long Default
- Severance ABC 01234567890123456789
- Foundation ABC 01234567890123456789
- Dark Hello! ABC 01234567890123456789
- The Boys ABC 01234567890123456789
- The Last of Us ABC 01234567890123456789
- Lost in Space ABC 01234567890123456789
- Altered Carbon ABC 01234567890123456789
-
-
-
-
-
-
+
+
+
+
+ Workflows
+ ━╸ ━━━━━━━━━ ╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ Id Description Status Result Id
+ 1 2 3 4
+ a b c d
+ fee fy fo fum
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ '''
+# ---
+# name: test_datatable_add_column
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ AddColumn
+
+
+
+
+
+
+
+
+
+ Movies No Default With Default Long Default
+ Severance ABC 01234567890123456789
+ Foundation ABC 01234567890123456789
+ Dark Hello! ABC 01234567890123456789
+ The Boys ABC 01234567890123456789
+ The Last of Us ABC 01234567890123456789
+ Lost in Space ABC 01234567890123456789
+ Altered Carbon ABC 01234567890123456789
+
+
+
+
+
+
@@ -15966,134 +16774,134 @@
font-weight: 700;
}
- .terminal-1699433504-matrix {
+ .terminal-3480025555-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1699433504-title {
+ .terminal-3480025555-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1699433504-r1 { fill: #e1e1e1 }
- .terminal-1699433504-r2 { fill: #c5c8c6 }
- .terminal-1699433504-r3 { fill: #dde6ed;font-weight: bold }
- .terminal-1699433504-r4 { fill: #dde6ed }
- .terminal-1699433504-r5 { fill: #211505 }
+ .terminal-3480025555-r1 { fill: #e1e1e1 }
+ .terminal-3480025555-r2 { fill: #c5c8c6 }
+ .terminal-3480025555-r3 { fill: #dde6ed;font-weight: bold }
+ .terminal-3480025555-r4 { fill: #dde6ed }
+ .terminal-3480025555-r5 { fill: #211505 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- TableApp
+ TableApp
-
-
-
-
- one two three
- value value val
-
- one two three
- value value val
-
- one two three
- value value val
-
- one two three
- value value val
-
- one two three
- value value val
-
-
-
-
-
-
-
-
+
+
+
+
+ one two three
+ value value val
+
+ one two three
+ value value val
+
+ one two three
+ value value val
+
+ one two three
+ value value val
+
+ one two three
+ value value val
+
+
+
+
+
+
+
+
@@ -16124,134 +16932,134 @@
font-weight: 700;
}
- .terminal-236473376-matrix {
+ .terminal-2017065427-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-236473376-title {
+ .terminal-2017065427-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-236473376-r1 { fill: #e1e1e1 }
- .terminal-236473376-r2 { fill: #c5c8c6 }
- .terminal-236473376-r3 { fill: #dde6ed;font-weight: bold }
- .terminal-236473376-r4 { fill: #dde6ed }
- .terminal-236473376-r5 { fill: #211505 }
+ .terminal-2017065427-r1 { fill: #e1e1e1 }
+ .terminal-2017065427-r2 { fill: #c5c8c6 }
+ .terminal-2017065427-r3 { fill: #dde6ed;font-weight: bold }
+ .terminal-2017065427-r4 { fill: #dde6ed }
+ .terminal-2017065427-r5 { fill: #211505 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- TableApp
+ TableApp
-
-
-
-
- one two three
- value value val
-
- one two three
- value value val
-
- one two three
- value value val
-
- one two three
- value value val
-
- one two three
- value value val
-
-
-
-
-
-
-
-
+
+
+
+
+ one two three
+ value value val
+
+ one two three
+ value value val
+
+ one two three
+ value value val
+
+ one two three
+ value value val
+
+ one two three
+ value value val
+
+
+
+
+
+
+
+
@@ -17397,137 +18205,137 @@
font-weight: 700;
}
- .terminal-3159150741-matrix {
+ .terminal-535151098-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3159150741-title {
+ .terminal-535151098-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3159150741-r1 { fill: #e1e1e1 }
- .terminal-3159150741-r2 { fill: #c5c8c6 }
- .terminal-3159150741-r3 { fill: #dde6ed;font-weight: bold }
- .terminal-3159150741-r4 { fill: #dde6ed }
- .terminal-3159150741-r5 { fill: #fea62b;font-weight: bold;font-style: italic; }
- .terminal-3159150741-r6 { fill: #e1e2e3 }
- .terminal-3159150741-r7 { fill: #f4005f }
- .terminal-3159150741-r8 { fill: #f4005f;font-weight: bold;font-style: italic; }
+ .terminal-535151098-r1 { fill: #e1e1e1 }
+ .terminal-535151098-r2 { fill: #c5c8c6 }
+ .terminal-535151098-r3 { fill: #dde6ed;font-weight: bold }
+ .terminal-535151098-r4 { fill: #dde6ed }
+ .terminal-535151098-r5 { fill: #fea62b;font-weight: bold;font-style: italic; }
+ .terminal-535151098-r6 { fill: #e1e2e3 }
+ .terminal-535151098-r7 { fill: #f4005f }
+ .terminal-535151098-r8 { fill: #f4005f;font-weight: bold;font-style: italic; }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- DataTableCursorStyles
+ DataTableCursorStyles
-
-
-
- Foreground is 'css', background is 'css':
- Movies
- Severance
- Foundation
- Dark
-
- Foreground is 'css', background is 'renderable':
- Movies
- Severance
- Foundation
- Dark
-
- Foreground is 'renderable', background is 'renderable':
- Movies
- Severance
- Foundation
- Dark
-
- Foreground is 'renderable', background is 'css':
- Movies
- Severance
- Foundation
- Dark
+
+
+
+ Foreground is 'css', background is 'css':
+ Movies
+ Severance
+ Foundation
+ Dark
+
+ Foreground is 'css', background is 'renderable':
+ Movies
+ Severance
+ Foundation
+ Dark
+
+ Foreground is 'renderable', background is 'renderable':
+ Movies
+ Severance
+ Foundation
+ Dark
+
+ Foreground is 'renderable', background is 'css':
+ Movies
+ Severance
+ Foundation
+ Dark
@@ -17558,168 +18366,169 @@
font-weight: 700;
}
- .terminal-123171118-matrix {
+ .terminal-353476698-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-123171118-title {
+ .terminal-353476698-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-123171118-r1 { fill: #c5c8c6 }
- .terminal-123171118-r2 { fill: #e3e3e3 }
- .terminal-123171118-r3 { fill: #e1e1e1 }
- .terminal-123171118-r4 { fill: #e2e2e2 }
- .terminal-123171118-r5 { fill: #14191f }
- .terminal-123171118-r6 { fill: #004578 }
- .terminal-123171118-r7 { fill: #262626 }
- .terminal-123171118-r8 { fill: #e2e2e2;font-weight: bold;text-decoration: underline; }
- .terminal-123171118-r9 { fill: #e2e2e2;font-weight: bold }
- .terminal-123171118-r10 { fill: #7ae998 }
- .terminal-123171118-r11 { fill: #4ebf71;font-weight: bold }
- .terminal-123171118-r12 { fill: #008139 }
- .terminal-123171118-r13 { fill: #dde8f3;font-weight: bold }
- .terminal-123171118-r14 { fill: #ddedf9 }
+ .terminal-353476698-r1 { fill: #c5c8c6 }
+ .terminal-353476698-r2 { fill: #e3e3e3 }
+ .terminal-353476698-r3 { fill: #e1e1e1 }
+ .terminal-353476698-r4 { fill: #e2e2e2 }
+ .terminal-353476698-r5 { fill: #14191f }
+ .terminal-353476698-r6 { fill: #004578 }
+ .terminal-353476698-r7 { fill: #262626 }
+ .terminal-353476698-r8 { fill: #e2e2e2;font-weight: bold;text-decoration: underline; }
+ .terminal-353476698-r9 { fill: #e2e2e2;font-weight: bold }
+ .terminal-353476698-r10 { fill: #7ae998 }
+ .terminal-353476698-r11 { fill: #4ebf71;font-weight: bold }
+ .terminal-353476698-r12 { fill: #008139 }
+ .terminal-353476698-r13 { fill: #fea62b;font-weight: bold }
+ .terminal-353476698-r14 { fill: #a7a9ab }
+ .terminal-353476698-r15 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- Textual Demo
+ Textual Demo
-
-
-
- ⭘ Textual Demo
-
-
- TOP
-
- ▆▆
-
- Widgets
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▎ ▊
- ▎ ▊
- Rich content ▎ Textual Demo ▊
- ▎ ▊
- ▎ Welcome ! Textual is a framework for creating sophisticated ▊
- ▎ applications with the terminal. ▊
- CSS ▎ ▊
- ▎ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▊
- ▎ Start ▊
- ▎ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▊
- ▎ ▊
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
-
-
-
-
-
-
-
-
- CTRL+B Sidebar CTRL+T Toggle Dark mode CTRL+S Screenshot F1 Notes CTRL+Q Quit
+
+
+
+ ⭘ Textual Demo
+
+
+ TOP
+
+ ▆▆
+
+ Widgets
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▎ ▊
+ ▎ ▊
+ Rich content ▎ Textual Demo ▊
+ ▎ ▊
+ ▎ Welcome ! Textual is a framework for creating sophisticated ▊
+ ▎ applications with the terminal. ▊
+ CSS ▎ ▊
+ ▎ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▊
+ ▎ Start ▊
+ ▎ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▊
+ ▎ ▊
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+
+
+
+
+
+
+
+
+ ^b Sidebar ^t Toggle Dark mode ^s Screenshot f1 Notes ^q Quit
@@ -17749,132 +18558,132 @@
font-weight: 700;
}
- .terminal-993607346-matrix {
+ .terminal-1348330842-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-993607346-title {
+ .terminal-1348330842-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-993607346-r1 { fill: #e1e1e1;font-weight: bold }
- .terminal-993607346-r2 { fill: #c5c8c6 }
- .terminal-993607346-r3 { fill: #e1e1e1 }
+ .terminal-1348330842-r1 { fill: #e1e1e1;font-weight: bold }
+ .terminal-1348330842-r2 { fill: #c5c8c6 }
+ .terminal-1348330842-r3 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- DigitApp
+ DigitApp
-
-
-
- ╺━┓ ┓ ╻ ╻╺━┓╺━┓
- ━┫ ┃ ┗━┫┏━┛ ┃
- ╺━┛.╺┻╸ ╹┗━╸ ╹
- ┏━┓ ┓ ╺━┓╺━┓╻ ╻┏━╸┏━╸╺━┓┏━┓┏━┓
- ┃ ┃ ┃ ┏━┛ ━┫┗━┫┗━┓┣━┓ ┃┣━┫┗━┫╺╋╸╺━╸
- ┗━┛╺┻╸┗━╸╺━┛ ╹╺━┛┗━┛ ╹┗━┛╺━┛ .,
- ╺━┓ ┓ ┏━┓ ^ ╻ ╻
- ━┫ × ┃ ┃ ┃ ┗━┫
- ╺━┛ ╺┻╸┗━┛ ╹
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ╺━┓ ┓ ╻ ╻╺━┓╺━┓
+ ━┫ ┃ ┗━┫┏━┛ ┃
+ ╺━┛.╺┻╸ ╹┗━╸ ╹
+ ┏━┓ ┓ ╺━┓╺━┓╻ ╻┏━╸┏━╸╺━┓┏━┓┏━┓
+ ┃ ┃ ┃ ┏━┛ ━┫┗━┫┗━┓┣━┓ ┃┣━┫┗━┫╺╋╸╺━╸
+ ┗━┛╺┻╸┗━╸╺━┛ ╹╺━┛┗━┛ ╹┗━┛╺━┛ .,
+ ╺━┓ ┓ ┏━┓ ^ ╻ ╻
+ ━┫ × ┃ ┃ ┃ ┗━┫
+ ╺━┛ ╺┻╸┗━┛ ╹
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -17905,136 +18714,136 @@
font-weight: 700;
}
- .terminal-1907613470-matrix {
+ .terminal-2123922789-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1907613470-title {
+ .terminal-2123922789-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1907613470-r1 { fill: #e2e3e3 }
- .terminal-1907613470-r2 { fill: #e2e3e3;font-weight: bold }
- .terminal-1907613470-r3 { fill: #c5c8c6 }
- .terminal-1907613470-r4 { fill: #008139 }
- .terminal-1907613470-r5 { fill: #211505;font-weight: bold }
- .terminal-1907613470-r6 { fill: #fea62b;font-weight: bold }
- .terminal-1907613470-r7 { fill: #e2e3e3;font-style: italic; }
+ .terminal-2123922789-r1 { fill: #e2e3e3 }
+ .terminal-2123922789-r2 { fill: #e2e3e3;font-weight: bold }
+ .terminal-2123922789-r3 { fill: #c5c8c6 }
+ .terminal-2123922789-r4 { fill: #008139 }
+ .terminal-2123922789-r5 { fill: #211505;font-weight: bold }
+ .terminal-2123922789-r6 { fill: #fea62b;font-weight: bold }
+ .terminal-2123922789-r7 { fill: #e2e3e3;font-style: italic; }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- DirectoryTreeReloadApp
+ DirectoryTreeReloadApp
-
-
-
- 📂 test_directory_tree_reloading0
- ├── 📂 b1
- │ ├── 📂 c1
- │ │ ┣━━ 📂 d1
- │ │ ┃ ┣━━ 📄 f1 .txt
- │ │ ┃ ┗━━ 📄 f2 .txt
- │ │ ┣━━ 📄 f1 .txt
- │ │ ┗━━ 📄 f2 .txt
- │ ├── 📄 f1 .txt
- │ └── 📄 f2 .txt
- ├── 📄 f1 .txt
- └── 📄 f2 .txt
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ 📂 test_directory_tree_reloading0
+ ├── 📂 b1
+ │ ├── 📂 c1
+ │ │ ┣━━ 📂 d1
+ │ │ ┃ ┣━━ 📄 f1 .txt
+ │ │ ┃ ┗━━ 📄 f2 .txt
+ │ │ ┣━━ 📄 f1 .txt
+ │ │ ┗━━ 📄 f2 .txt
+ │ ├── 📄 f1 .txt
+ │ └── 📄 f2 .txt
+ ├── 📄 f1 .txt
+ └── 📄 f2 .txt
+
+
+
+
+
+
+
+
+
+
+
@@ -18065,162 +18874,162 @@
font-weight: 700;
}
- .terminal-3209943725-matrix {
+ .terminal-3864303289-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3209943725-title {
+ .terminal-3864303289-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3209943725-r1 { fill: #454a50 }
- .terminal-3209943725-r2 { fill: #507bb3 }
- .terminal-3209943725-r3 { fill: #7ae998 }
- .terminal-3209943725-r4 { fill: #ffcf56 }
- .terminal-3209943725-r5 { fill: #e76580 }
- .terminal-3209943725-r6 { fill: #c5c8c6 }
- .terminal-3209943725-r7 { fill: #24292f;font-weight: bold }
- .terminal-3209943725-r8 { fill: #dde6ed;font-weight: bold }
- .terminal-3209943725-r9 { fill: #0a180e;font-weight: bold }
- .terminal-3209943725-r10 { fill: #211505;font-weight: bold }
- .terminal-3209943725-r11 { fill: #f5e5e9;font-weight: bold }
- .terminal-3209943725-r12 { fill: #000000 }
- .terminal-3209943725-r13 { fill: #001541 }
- .terminal-3209943725-r14 { fill: #008139 }
- .terminal-3209943725-r15 { fill: #b86b00 }
- .terminal-3209943725-r16 { fill: #780028 }
- .terminal-3209943725-r17 { fill: #303336 }
- .terminal-3209943725-r18 { fill: #364b66 }
- .terminal-3209943725-r19 { fill: #4a8159 }
- .terminal-3209943725-r20 { fill: #8b7439 }
- .terminal-3209943725-r21 { fill: #80404d }
- .terminal-3209943725-r22 { fill: #a7a7a7;font-weight: bold }
- .terminal-3209943725-r23 { fill: #a5a9ac;font-weight: bold }
- .terminal-3209943725-r24 { fill: #0e1510;font-weight: bold }
- .terminal-3209943725-r25 { fill: #19140c;font-weight: bold }
- .terminal-3209943725-r26 { fill: #b0a8aa;font-weight: bold }
- .terminal-3209943725-r27 { fill: #0f0f0f }
- .terminal-3209943725-r28 { fill: #0f192e }
- .terminal-3209943725-r29 { fill: #0f4e2a }
- .terminal-3209943725-r30 { fill: #68430f }
- .terminal-3209943725-r31 { fill: #4a0f22 }
- .terminal-3209943725-r32 { fill: #e2e3e3;font-weight: bold }
+ .terminal-3864303289-r1 { fill: #454a50 }
+ .terminal-3864303289-r2 { fill: #507bb3 }
+ .terminal-3864303289-r3 { fill: #7ae998 }
+ .terminal-3864303289-r4 { fill: #ffcf56 }
+ .terminal-3864303289-r5 { fill: #e76580 }
+ .terminal-3864303289-r6 { fill: #c5c8c6 }
+ .terminal-3864303289-r7 { fill: #24292f;font-weight: bold }
+ .terminal-3864303289-r8 { fill: #dde6ed;font-weight: bold }
+ .terminal-3864303289-r9 { fill: #0a180e;font-weight: bold }
+ .terminal-3864303289-r10 { fill: #211505;font-weight: bold }
+ .terminal-3864303289-r11 { fill: #f5e5e9;font-weight: bold }
+ .terminal-3864303289-r12 { fill: #000000 }
+ .terminal-3864303289-r13 { fill: #001541 }
+ .terminal-3864303289-r14 { fill: #008139 }
+ .terminal-3864303289-r15 { fill: #b86b00 }
+ .terminal-3864303289-r16 { fill: #780028 }
+ .terminal-3864303289-r17 { fill: #303336 }
+ .terminal-3864303289-r18 { fill: #364b66 }
+ .terminal-3864303289-r19 { fill: #4a8159 }
+ .terminal-3864303289-r20 { fill: #8b7439 }
+ .terminal-3864303289-r21 { fill: #80404d }
+ .terminal-3864303289-r22 { fill: #a7a7a7;font-weight: bold }
+ .terminal-3864303289-r23 { fill: #a5a9ac;font-weight: bold }
+ .terminal-3864303289-r24 { fill: #0e1510;font-weight: bold }
+ .terminal-3864303289-r25 { fill: #19140c;font-weight: bold }
+ .terminal-3864303289-r26 { fill: #b0a8aa;font-weight: bold }
+ .terminal-3864303289-r27 { fill: #0f0f0f }
+ .terminal-3864303289-r28 { fill: #0f192e }
+ .terminal-3864303289-r29 { fill: #0f4e2a }
+ .terminal-3864303289-r30 { fill: #68430f }
+ .terminal-3864303289-r31 { fill: #4a0f22 }
+ .terminal-3864303289-r32 { fill: #e2e3e3;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- WidgetDisableTestApp
+ WidgetDisableTestApp
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Button Button Button Button Button
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Button Button Button Button Button
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Button Button Button Button Button
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Button Button Button Button Button
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Button Button Button Button Button
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Button Button Button Button Button
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Button Button Button Button Button
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Button Button Button Button Button
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Button Button Button Button Button
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Button Button Button Button Button
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Button Button Button Button Button
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Button Button Button Button Button
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Button Button Button Button Button
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Button Button Button Button Button
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Button Button Button Button Button
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Button Button Button Button Button
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
@@ -18407,141 +19216,142 @@
font-weight: 700;
}
- .terminal-3671710802-matrix {
+ .terminal-3926104244-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3671710802-title {
+ .terminal-3926104244-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3671710802-r1 { fill: #c5c8c6 }
- .terminal-3671710802-r2 { fill: #1e1e1e }
- .terminal-3671710802-r3 { fill: #1f1f1f }
- .terminal-3671710802-r4 { fill: #ff0000 }
- .terminal-3671710802-r5 { fill: #dde8f3;font-weight: bold }
- .terminal-3671710802-r6 { fill: #ddedf9 }
- .terminal-3671710802-r7 { fill: #c7cdd2 }
+ .terminal-3926104244-r1 { fill: #c5c8c6 }
+ .terminal-3926104244-r2 { fill: #1e1e1e }
+ .terminal-3926104244-r3 { fill: #1f1f1f }
+ .terminal-3926104244-r4 { fill: #ff0000 }
+ .terminal-3926104244-r5 { fill: #004578;font-weight: bold }
+ .terminal-3926104244-r6 { fill: #585a5c }
+ .terminal-3926104244-r7 { fill: #1c1d1e }
+ .terminal-3926104244-r8 { 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 Qu it
-
-
- ▇▇
+
+
+
+ ⭘ TestApp
+ ┌─────────┐
+ │ this │
+ │ is │
+ │ a │
+ │ sample │
+ │ sentence │
+ │ and │
+ │ here │
+ │ are │
+ │ some │
+ │ wordsthis │
+ │ is │
+ │ a │
+ │ sample │
+ │ sentence │
+ │ and │
+ │ here │
+ │ are │
+ │ some │
+ │ words │
+ ^q Quit
+
+
+ ▇▇
@@ -18571,140 +19381,141 @@
font-weight: 700;
}
- .terminal-2802609302-matrix {
+ .terminal-1826994938-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2802609302-title {
+ .terminal-1826994938-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2802609302-r1 { fill: #c5c8c6 }
- .terminal-2802609302-r2 { fill: #1e1e1e }
- .terminal-2802609302-r3 { fill: #1f1f1f }
- .terminal-2802609302-r4 { fill: #ff0000 }
- .terminal-2802609302-r5 { fill: #c7cdd2 }
- .terminal-2802609302-r6 { fill: #dde8f3;font-weight: bold }
- .terminal-2802609302-r7 { fill: #ddedf9 }
+ .terminal-1826994938-r1 { fill: #c5c8c6 }
+ .terminal-1826994938-r2 { fill: #1e1e1e }
+ .terminal-1826994938-r3 { fill: #1f1f1f }
+ .terminal-1826994938-r4 { fill: #ff0000 }
+ .terminal-1826994938-r5 { fill: #c7cdd2 }
+ .terminal-1826994938-r6 { fill: #004578;font-weight: bold }
+ .terminal-1826994938-r7 { fill: #585a5c }
+ .terminal-1826994938-r8 { fill: #1c1d1e }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- TestApp
+ TestApp
-
-
-
- ⭘ TestApp
- ┌ ───────── ┐
- │ this │
- │ is │
- │ a │
- │ sample │
- │ sentence │
- │ and │
- │ here │
- │ are │
- │ some │
- │ wordsthis │
- │ is │
- │ a │ ▅▅
- │ sample │
- │ sentence │
- │ and │
- │ here │
- │ are │
- │ some │
- │ words │
- CTRL+Q Qu it
-
-
+
+
+
+ ⭘ TestApp
+ ┌─────────┐
+ │ this │
+ │ is │
+ │ a │
+ │ sample │
+ │ sentence │
+ │ and │
+ │ here │
+ │ are │
+ │ some │
+ │ wordsthis │
+ │ is │
+ │ a │ ▅▅
+ │ sample │
+ │ sentence │
+ │ and │
+ │ here │
+ │ are │
+ │ some │
+ │ words │
+ ^q Quit
+
+
@@ -18735,141 +19546,141 @@
font-weight: 700;
}
- .terminal-1431734718-matrix {
+ .terminal-1300400438-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1431734718-title {
+ .terminal-1300400438-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1431734718-r1 { fill: #1e1e1e }
- .terminal-1431734718-r2 { fill: #e1e1e1 }
- .terminal-1431734718-r3 { fill: #c5c8c6 }
- .terminal-1431734718-r4 { fill: #434343 }
- .terminal-1431734718-r5 { fill: #262626;font-weight: bold }
- .terminal-1431734718-r6 { fill: #e2e2e2 }
- .terminal-1431734718-r7 { fill: #23568b }
- .terminal-1431734718-r8 { fill: #ddedf9 }
+ .terminal-1300400438-r1 { fill: #1e1e1e }
+ .terminal-1300400438-r2 { fill: #e1e1e1 }
+ .terminal-1300400438-r3 { fill: #c5c8c6 }
+ .terminal-1300400438-r4 { fill: #434343 }
+ .terminal-1300400438-r5 { fill: #262626;font-weight: bold }
+ .terminal-1300400438-r6 { fill: #e2e2e2 }
+ .terminal-1300400438-r7 { fill: #23568b }
+ .terminal-1300400438-r8 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ScrollOffByOne
+ ScrollOffByOne
-
-
-
- ▊ ▔▔▔▔▔▔▔▔ ▎
- ▊ ▐ X ▌ 92 ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- ▊ ▐ X ▌ 93 ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- ▊ ▐ X ▌ 94 ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- ▊ ▐ X ▌ 95 ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- ▊ ▐ X ▌ 96 ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- ▊ ▐ X ▌ 97 ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- ▊ ▐ X ▌ 98 ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- ▊ ▐ X ▌ 99 ▎ ▁▁
- ▊ ▁▁▁▁▁▁▁▁ ▎
+
+
+
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ ▊ ▐ X ▌ 92 ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ ▊ ▐ X ▌ 93 ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ ▊ ▐ X ▌ 94 ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ ▊ ▐ X ▌ 95 ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ ▊ ▐ X ▌ 96 ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ ▊ ▐ X ▌ 97 ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ ▊ ▐ X ▌ 98 ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ ▊ ▐ X ▌ 99 ▎ ▁▁
+ ▊ ▁▁▁▁▁▁▁▁ ▎
@@ -18877,6 +19688,166 @@
'''
# ---
+# name: test_dynamic_bindings
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ BindingsApp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ a A c C
+
+
+
+
+ '''
+# ---
# name: test_example_calculator
'''
@@ -18900,144 +19871,144 @@
font-weight: 700;
}
- .terminal-710708527-matrix {
+ .terminal-64523855-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-710708527-title {
+ .terminal-64523855-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-710708527-r1 { fill: #e1e1e1 }
- .terminal-710708527-r2 { fill: #c5c8c6 }
- .terminal-710708527-r3 { fill: #e5ebf2;font-weight: bold }
- .terminal-710708527-r4 { fill: #507bb3 }
- .terminal-710708527-r5 { fill: #ffcf56 }
- .terminal-710708527-r6 { fill: #004578;font-weight: bold }
- .terminal-710708527-r7 { fill: #dde6ed;font-weight: bold }
- .terminal-710708527-r8 { fill: #211505;font-weight: bold }
- .terminal-710708527-r9 { fill: #001541 }
- .terminal-710708527-r10 { fill: #b86b00 }
- .terminal-710708527-r11 { fill: #454a50 }
- .terminal-710708527-r12 { fill: #e2e3e3;font-weight: bold }
- .terminal-710708527-r13 { fill: #000000 }
- .terminal-710708527-r14 { fill: #14191f }
+ .terminal-64523855-r1 { fill: #e1e1e1 }
+ .terminal-64523855-r2 { fill: #c5c8c6 }
+ .terminal-64523855-r3 { fill: #e5ebf2;font-weight: bold }
+ .terminal-64523855-r4 { fill: #507bb3 }
+ .terminal-64523855-r5 { fill: #ffcf56 }
+ .terminal-64523855-r6 { fill: #004578;font-weight: bold }
+ .terminal-64523855-r7 { fill: #dde6ed;font-weight: bold }
+ .terminal-64523855-r8 { fill: #211505;font-weight: bold }
+ .terminal-64523855-r9 { fill: #001541 }
+ .terminal-64523855-r10 { fill: #b86b00 }
+ .terminal-64523855-r11 { fill: #454a50 }
+ .terminal-64523855-r12 { fill: #e2e3e3;font-weight: bold }
+ .terminal-64523855-r13 { fill: #000000 }
+ .terminal-64523855-r14 { fill: #14191f }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- CalculatorApp
+ CalculatorApp
-
-
-
-
-
- ┏━┓
- ┃ ┃
- ┗━┛
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- AC +/- % ÷
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- 7 8 9 ×
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- 4 5 6 -
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- 1 2 3 +
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▅▅
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+
+
+
+
+
+ ┏━┓
+ ┃ ┃
+ ┗━┛
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ AC +/- % ÷
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ 7 8 9 ×
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ 4 5 6 -
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ 1 2 3 +
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▅▅
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
@@ -19067,133 +20038,133 @@
font-weight: 700;
}
- .terminal-3687631720-matrix {
+ .terminal-112858016-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3687631720-title {
+ .terminal-112858016-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3687631720-r1 { fill: #c5c8c6 }
- .terminal-3687631720-r2 { fill: #e3e3e3 }
- .terminal-3687631720-r3 { fill: #e1e1e1 }
- .terminal-3687631720-r4 { fill: #ffdddd }
+ .terminal-112858016-r1 { fill: #c5c8c6 }
+ .terminal-112858016-r2 { fill: #e3e3e3 }
+ .terminal-112858016-r3 { fill: #e1e1e1 }
+ .terminal-112858016-r4 { fill: #eedddd }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- Press ctrl + \ and type a color
+ Press ctrl + \ and type a color
-
-
-
- ⭘ Press ctrl + \ and type a color
-
-
-
-
- red
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ⭘ Press ctrl + \ and type a color
+
+
+
+
+ ansi_red
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -19363,7 +20334,1615 @@
'''
# ---
-# name: test_example_five_by_five
+# name: test_example_five_by_five
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5x5 -- A little annoying puzzle
+
+
+
+
+
+
+
+
+
+ 5x5 -- A little annoying puzzle Moves: 0 Filled: 5
+ ╭──────────────╮╭──────────────╮╭──────────────╮╭──────────────╮╭──────────────╮
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ ╰──────────────╯╰──────────────╯╰──────────────╯╰──────────────╯╰──────────────╯
+ ╭──────────────╮╭──────────────╮ ╭──────────────╮ ╭──────────────╮╭──────────────╮
+ │ ││ │ │ │ │ ││ │
+ │ ││ │ │ │ │ ││ │
+ ╰──────────────╯╰──────────────╯ ╰──────────────╯ ╰──────────────╯╰──────────────╯
+ ╭──────────────╮ ╭──────────────╮╭──────────────╮╭──────────────╮ ╭──────────────╮
+ │ │ │ ││ ││ │ │ │
+ │ │ │ ││ ││ │ │ │
+ │ │ │ ││ ││ │ │ │
+ ╰──────────────╯ ╰──────────────╯╰──────────────╯╰──────────────╯ ╰──────────────╯
+ ╭──────────────╮╭──────────────╮ ╭──────────────╮ ╭──────────────╮╭──────────────╮
+ │ ││ │ │ │ │ ││ │
+ │ ││ │ │ │ │ ││ │
+ ╰──────────────╯╰──────────────╯ ╰──────────────╯ ╰──────────────╯╰──────────────╯
+ ╭──────────────╮╭──────────────╮╭──────────────╮╭──────────────╮╭──────────────╮
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ │ ││ ││ ││ ││ │
+ ╰──────────────╯╰──────────────╯╰──────────────╯╰──────────────╯╰──────────────╯
+ n New Game ? Help q Quit ^d Toggle Dark Mode
+
+
+
+
+ '''
+# ---
+# name: test_example_json_tree
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TreeApp
+
+
+
+
+
+
+
+
+
+ ⭘ TreeApp
+ ▼ Root
+ └── ▼ {} JSON ▁▁
+ ├── code = '5060292302201'
+ ├── ▼ {} product
+ │ ┣━━ _id = '5060292302201'
+ │ ┣━━ ▶ [] _keywords
+ │ ┣━━ ▶ [] added_countries_tags
+ │ ┣━━ ▶ [] additives_debug_tags
+ │ ┣━━ additives_n = 2
+ │ ┣━━ additives_old_n = 2
+ │ ┣━━ ▶ [] additives_old_tags
+ │ ┣━━ ▶ [] additives_original_tags
+ │ ┣━━ ▶ [] additives_prev_original_tags
+ │ ┣━━ ▶ [] additives_tags
+ │ ┣━━ additives_tags_n = None
+ │ ┣━━ allergens = 'en:milk'
+ │ ┣━━ ▶ [] allergens_debug_tags
+ │ ┣━━ allergens_from_ingredients = 'en:milk, milk'
+ │ ┣━━ allergens_from_user = '(en) en:milk'
+ │ ┣━━ ▶ [] allergens_hierarchy
+ │ ┣━━ ▶ [] allergens_tags
+
+ a Add node c Clear t Toggle root
+
+
+
+
+ '''
+# ---
+# name: test_example_markdown
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MarkdownApp
+
+
+
+
+
+
+
+
+
+ ▊
+ ▼ Ⅰ Textual Markdown Browser ▊
+ └── Ⅱ Do You Want to Know More? ▊ Textual Markdown Browser
+ ▊
+ ▊ Welcome fellow adventurer! If you ran
+ ▊ markdown.py from the terminal you are
+ ▊ viewing demo.md with Textual's built in
+ ▊ Markdown widget.
+ ▊
+ ▊ The widget supports much of the Markdown
+ ▊ spec. There is also an optional Table of
+ ▊ Contents sidebar which you will see to
+ ▊ your left.
+ ▊
+ ▊
+ ▊ Do You Want to Know More?
+ ▊
+ ▊ See example.md for more examples of what
+ ▊ this can do.
+ ▊
+ ▊
+ ▊
+ ▊
+ t TOC b Back f Forward
+
+
+
+
+ '''
+# ---
+# name: test_example_merlin
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MerlinApp
+
+
+
+
+
+
+
+
+
+
+
+ ┏━┓ ┏━┓┏━┓ ┏━┓┏━┓
+ ┃ ┃ : ┃ ┃┃ ┃ : ┃ ┃┃ ┃
+ ┗━┛ ┗━┛┗━┛ ┗━┛┗━┛
+
+
+ █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
+ █ █
+ █ 7 8 9 █
+ █ ▊ ▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔ ▎ █
+ █ ▊ ▎ ▊ ▎ ▊ ▎ █
+ █ ▊ ▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁ ▎ █
+ █ █
+ █ 4 5 6 █
+ █ ▊ ▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔ ▎ █
+ █ ▊ ▎ ▊ ▎ ▊ ▎ █
+ █ ▊ ▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁ ▎ █
+ █ █
+ █ 1 2 3 █
+ █ ▊ ▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔ ▎ █
+ █ ▊ ▎ ▊ ▎ ▊ ▎ █
+ █ ▊ ▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁ ▎ █
+ █ █ ▇▇
+
+
+
+
+ '''
+# ---
+# name: test_example_pride
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PrideApp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ '''
+# ---
+# name: test_focus_component_class
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
+
+
+
+ '''
+# ---
+# name: test_footer_classic_styling
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ClassicFooterStylingApp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CTRL+T Toggle Dark mode CTRL+Q Quit
+
+
+
+
+ '''
+# ---
+# name: test_footer_compact
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ToggleCompactFooterApp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Compact Footer
+
+
+
+
+
+
+
+
+
+
+
+ ^t Toggle Compact Footer ^q Quit
+
+
+
+
+ '''
+# ---
+# name: test_footer_compact_with_hover
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ToggleCompactFooterApp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Compact Footer
+
+
+
+
+
+
+
+
+
+
+
+ ^t Toggle Compact Footer ^q Quit
+
+
+
+
+ '''
+# ---
+# name: test_footer_render
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ FooterApp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ q Quit the app ? Show help screen delete Delete the thing
+
+
+
+
+ '''
+# ---
+# name: test_footer_standard_after_reactive_change
'''
@@ -19386,146 +21965,142 @@
font-weight: 700;
}
- .terminal-1546987173-matrix {
+ .terminal-3950236293-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1546987173-title {
+ .terminal-3950236293-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1546987173-r1 { fill: #e4e5e6 }
- .terminal-1546987173-r2 { fill: #c5c8c6 }
- .terminal-1546987173-r3 { fill: #0d0d0d }
- .terminal-1546987173-r4 { fill: #e1e1e1;font-weight: bold }
- .terminal-1546987173-r5 { fill: #e7920d }
- .terminal-1546987173-r6 { fill: #211505;font-weight: bold }
- .terminal-1546987173-r7 { fill: #fea62b;font-weight: bold }
- .terminal-1546987173-r8 { fill: #dde8f3;font-weight: bold }
- .terminal-1546987173-r9 { fill: #ddedf9 }
+ .terminal-3950236293-r1 { fill: #e1e1e1 }
+ .terminal-3950236293-r2 { fill: #c5c8c6 }
+ .terminal-3950236293-r3 { fill: #fea62b;font-weight: bold }
+ .terminal-3950236293-r4 { fill: #a7a9ab }
+ .terminal-3950236293-r5 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- 5x5 -- A little annoying puzzle
+ ToggleCompactFooterApp
-
-
-
- 5x5 -- A little annoying puzzle Moves: 0 Filled: 5
- ╭ ────────────── ╮ ╭ ────────────── ╮ ╭ ────────────── ╮ ╭ ────────────── ╮ ╭ ────────────── ╮
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- ╰ ────────────── ╯ ╰ ────────────── ╯ ╰ ────────────── ╯ ╰ ────────────── ╯ ╰ ────────────── ╯
- ╭ ────────────── ╮ ╭ ────────────── ╮ ╭ ────────────── ╮ ╭ ────────────── ╮ ╭ ────────────── ╮
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- ╰ ────────────── ╯ ╰ ────────────── ╯ ╰ ────────────── ╯ ╰ ────────────── ╯ ╰ ────────────── ╯
- ╭ ────────────── ╮ ╭ ────────────── ╮ ╭ ────────────── ╮ ╭ ────────────── ╮ ╭ ────────────── ╮
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- ╰ ────────────── ╯ ╰ ────────────── ╯ ╰ ────────────── ╯ ╰ ────────────── ╯ ╰ ────────────── ╯
- ╭ ────────────── ╮ ╭ ────────────── ╮ ╭ ────────────── ╮ ╭ ────────────── ╮ ╭ ────────────── ╮
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- ╰ ────────────── ╯ ╰ ────────────── ╯ ╰ ────────────── ╯ ╰ ────────────── ╯ ╰ ────────────── ╯
- ╭ ────────────── ╮ ╭ ────────────── ╮ ╭ ────────────── ╮ ╭ ────────────── ╮ ╭ ────────────── ╮
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │
- ╰ ────────────── ╯ ╰ ────────────── ╯ ╰ ────────────── ╯ ╰ ────────────── ╯ ╰ ────────────── ╯
- N New Game ? Help Q Quit CTRL+D Toggle Dark Mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Standard Footer
+
+
+
+
+
+
+
+
+
+
+
+ ^t Toggle Compact Footer ^q Quit
'''
# ---
-# name: test_example_json_tree
+# name: test_footer_standard_with_hover
'''
@@ -19548,153 +22123,145 @@
font-weight: 700;
}
- .terminal-3545091782-matrix {
+ .terminal-2873348574-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3545091782-title {
+ .terminal-2873348574-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3545091782-r1 { fill: #c5c8c6 }
- .terminal-3545091782-r2 { fill: #e3e3e3 }
- .terminal-3545091782-r3 { fill: #e2e3e3 }
- .terminal-3545091782-r4 { fill: #008139 }
- .terminal-3545091782-r5 { fill: #14191f }
- .terminal-3545091782-r6 { fill: #e2e3e3;font-weight: bold }
- .terminal-3545091782-r7 { fill: #98e024 }
- .terminal-3545091782-r8 { fill: #211505;font-weight: bold }
- .terminal-3545091782-r9 { fill: #fea62b;font-weight: bold }
- .terminal-3545091782-r10 { fill: #58d1eb;font-weight: bold }
- .terminal-3545091782-r11 { fill: #f4005f;font-style: italic; }
- .terminal-3545091782-r12 { fill: #23568b }
- .terminal-3545091782-r13 { fill: #dde8f3;font-weight: bold }
- .terminal-3545091782-r14 { fill: #ddedf9 }
+ .terminal-2873348574-r1 { fill: #e1e1e1 }
+ .terminal-2873348574-r2 { fill: #c5c8c6 }
+ .terminal-2873348574-r3 { fill: #fea62b;font-weight: bold }
+ .terminal-2873348574-r4 { fill: #dddedf }
+ .terminal-2873348574-r5 { fill: #a7a9ab }
+ .terminal-2873348574-r6 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- TreeApp
+ ToggleCompactFooterApp
-
-
-
- ⭘ TreeApp
- ▼ Root
- └── ▼ {} JSON ▁▁
- ├── code = '5060292302201'
- ├── ▼ {} product
- │ ┣━━ _id = '5060292302201'
- │ ┣━━ ▶ [] _keywords
- │ ┣━━ ▶ [] added_countries_tags
- │ ┣━━ ▶ [] additives_debug_tags
- │ ┣━━ additives_n = 2
- │ ┣━━ additives_old_n = 2
- │ ┣━━ ▶ [] additives_old_tags
- │ ┣━━ ▶ [] additives_original_tags
- │ ┣━━ ▶ [] additives_prev_original_tags
- │ ┣━━ ▶ [] additives_tags
- │ ┣━━ additives_tags_n = None
- │ ┣━━ allergens = 'en:milk'
- │ ┣━━ ▶ [] allergens_debug_tags
- │ ┣━━ allergens_from_ingredients = 'en:milk, milk'
- │ ┣━━ allergens_from_user = ' ( en ) en:milk'
- │ ┣━━ ▶ [] allergens_hierarchy
- │ ┣━━ ▶ [] allergens_tags
- ▉
- A Add node C Clear T Toggle root
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Standard Footer
+
+
+
+
+
+
+
+
+
+
+
+ ^t Toggle Compact Footer ^q Quit
'''
# ---
-# name: test_example_markdown
+# name: test_fr_margins
'''
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
- MarkdownApp
+ TestApp
-
-
-
- ▊ ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▼ Ⅰ Textual Markdown Browser ▊ ▎ ▊
- └── Ⅱ Do You Want to Know More? ▊ ▎ Textual Markdown Browser ▊
- ▊ ▎ ▊
- ▊ ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
- ▊ Welcome fellow adventurer! If you
- ▊ ran markdown.py from the terminal
- ▊ you are viewing demo.md with
- ▊ Textual's built in Markdown
- ▊ widget.
- ▊
- ▊ The widget supports much of the
- ▊ Markdown spec. There is also an
- ▊ optional Table of Contents sidebar
- ▊ which you will see to your left.
- ▊
- ▊ ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▊ ▎ ▊
- ▊ ▎ Do You Want to Know More? ▊
- ▊ ▎ ▊
- ▊ ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
- ▊ See example.md for more examples ▆▆
- ▊ of what this can do.
- T TOC B Back F Forward
+
+
+
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ ┃
+ ┃ ┃
+ ┃ Hello ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ World ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ !! ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
'''
# ---
-# name: test_example_merlin
+# name: test_fr_unit_with_min
'''
@@ -19883,147 +22443,143 @@
font-weight: 700;
}
- .terminal-1091601628-matrix {
+ .terminal-302077504-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1091601628-title {
+ .terminal-302077504-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1091601628-r1 { fill: #e1e1e1 }
- .terminal-1091601628-r2 { fill: #c5c8c6 }
- .terminal-1091601628-r3 { fill: #fea62b;font-weight: bold }
- .terminal-1091601628-r4 { fill: #004578 }
- .terminal-1091601628-r5 { fill: #e1e1e1;font-weight: bold }
- .terminal-1091601628-r6 { fill: #1e1e1e }
- .terminal-1091601628-r7 { fill: #0178d4 }
- .terminal-1091601628-r8 { fill: #e2e2e2 }
- .terminal-1091601628-r9 { fill: #737373;font-weight: bold }
- .terminal-1091601628-r10 { fill: #14191f }
+ .terminal-302077504-r1 { fill: #c5c8c6 }
+ .terminal-302077504-r2 { fill: #e3e3e3 }
+ .terminal-302077504-r3 { fill: #ddddff }
+ .terminal-302077504-r4 { fill: #e3e4e5 }
+ .terminal-302077504-r5 { fill: #e2e3e3 }
+ .terminal-302077504-r6 { fill: #14191f }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MerlinApp
+ ScreenSplitApp
-
-
-
-
-
- ┏━┓ ┏━┓┏━┓ ┏━┓┏━┓
- ┃ ┃ : ┃ ┃┃ ┃ : ┃ ┃┃ ┃
- ┗━┛ ┗━┛┗━┛ ┗━┛┗━┛
-
-
- █ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ █
- █ █
- █ 7 8 9 █
- █ ▊ ▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔ ▎ █
- █ ▊ ▎ ▊ ▎ ▊ ▎ █
- █ ▊ ▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁ ▎ █
- █ █
- █ 4 5 6 █
- █ ▊ ▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔ ▎ █
- █ ▊ ▎ ▊ ▎ ▊ ▎ █
- █ ▊ ▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁ ▎ █
- █ █
- █ 1 2 3 █
- █ ▊ ▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔ ▎ █
- █ ▊ ▎ ▊ ▎ ▊ ▎ █
- █ ▊ ▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁ ▎ █
- █ █ ▇▇
+
+
+
+ ⭘ ScreenSplitApp
+ This is content This is content number 0
+ number 0 This is content number 1
+ This is content ▄▄ This is content number 2
+ number 1 This is content number 3
+ This is content This is content number 4 ▁▁
+ number 2 This is content number 5
+ This is content This is content number 6
+ number 3 This is content number 7
+ This is content This is content number 8
+ number 4 This is content number 9
+ This is content This is content number 10
+ number 5 This is content number 11
+ This is content This is content number 12
+ number 6 This is content number 13
+ This is content This is content number 14
+ number 7 This is content number 15
+ This is content This is content number 16
+ number 8 This is content number 17
+ This is content This is content number 18
+ number 9 This is content number 19
+ This is content This is content number 20
+ number 10 This is content number 21
+
'''
# ---
-# name: test_example_pride
+# name: test_fr_units
'''
@@ -20046,144 +22602,140 @@
font-weight: 700;
}
- .terminal-3094371209-matrix {
+ .terminal-1610428255-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3094371209-title {
+ .terminal-1610428255-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3094371209-r1 { fill: #ffdddd }
- .terminal-3094371209-r2 { fill: #c5c8c6 }
- .terminal-3094371209-r3 { fill: #fff3dd }
- .terminal-3094371209-r4 { fill: #ffffdd }
- .terminal-3094371209-r5 { fill: #ddeedd }
- .terminal-3094371209-r6 { fill: #ddddff }
- .terminal-3094371209-r7 { fill: #eeddee }
+ .terminal-1610428255-r1 { fill: #ffffff }
+ .terminal-1610428255-r2 { fill: #c5c8c6 }
+ .terminal-1610428255-r3 { fill: #e2e2e2 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- PrideApp
+ FRApp
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ HEADER ┃
+ ┃ ┃
+ ┃ ┃
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ ┏━━━━━━━━┓┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓┏━━━━━━┓
+ ┃ foo ┃┃ bar ┃┃ baz ┃
+ ┃ ┃┃ ┃┃ ┃
+ ┃ ┃┃ ┃┃ ┃
+ ┃ ┃┃ ┃┃ ┃
+ ┃ ┃┃ ┃┃ ┃
+ ┃ ┃┃ ┃┃ ┃
+ ┃ ┃┃ ┃┃ ┃
+ ┃ ┃┃ ┃┃ ┃
+ ┃ ┃┃ ┃┃ ┃
+ ┃ ┃┃ ┃┃ ┃
+ ┃ ┃┃ ┃┃ ┃
+ ┃ ┃┃ ┃┃ ┃
+ ┗━━━━━━━━┛┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛┗━━━━━━┛
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ FOOTER ┃
+ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
'''
# ---
-# name: test_focus_component_class
+# name: test_grid_auto
'''
@@ -20206,135 +22758,141 @@
font-weight: 700;
}
- .terminal-3936062011-matrix {
+ .terminal-1332404670-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3936062011-title {
+ .terminal-1332404670-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3936062011-r1 { fill: #c5c8c6 }
- .terminal-3936062011-r2 { fill: #e3e3e3 }
- .terminal-3936062011-r3 { fill: #ffdddd }
- .terminal-3936062011-r4 { fill: #e1e1e1 }
- .terminal-3936062011-r5 { fill: #14191f }
- .terminal-3936062011-r6 { fill: #ddedf9 }
+ .terminal-1332404670-r1 { fill: #008000 }
+ .terminal-1332404670-r2 { fill: #e1e1e1 }
+ .terminal-1332404670-r3 { fill: #c5c8c6 }
+ .terminal-1332404670-r4 { fill: #e6def6 }
+ .terminal-1332404670-r5 { fill: #e8e1f3 }
+ .terminal-1332404670-r6 { fill: #ebe4f4 }
+ .terminal-1332404670-r7 { fill: #ede7f2 }
+ .terminal-1332404670-r8 { fill: #edecee }
+ .terminal-1332404670-r9 { fill: #e7ecf3 }
+ .terminal-1332404670-r10 { fill: #e2ecf7 }
+ .terminal-1332404670-r11 { fill: #e0ebfa }
+ .terminal-1332404670-r12 { fill: #dde9fb }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- StyleBugApp
+ KeylineApp
-
-
-
- ⭘ 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
+
+
+
+ ┌──┬──┬──┐
+ │ a │ b │ c │
+ ├──┼──┼──┤
+ │ d │ e │ f │
+ ├──┼──┼──┤
+ │ g │ h │ i │
+ └──┴──┴──┘
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -20342,7 +22900,7 @@
'''
# ---
-# name: test_footer_render
+# name: test_grid_gutter
'''
@@ -20365,143 +22923,147 @@
font-weight: 700;
}
- .terminal-1971839132-matrix {
+ .terminal-3470684325-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1971839132-title {
+ .terminal-3470684325-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1971839132-r1 { fill: #e1e1e1 }
- .terminal-1971839132-r2 { fill: #c5c8c6 }
- .terminal-1971839132-r3 { fill: #dde8f3;font-weight: bold }
- .terminal-1971839132-r4 { fill: #ddedf9 }
+ .terminal-3470684325-r1 { fill: #e1e1e1 }
+ .terminal-3470684325-r2 { fill: #c5c8c6 }
+ .terminal-3470684325-r3 { fill: #0178d4 }
+ .terminal-3470684325-r4 { fill: #e1e1e1;font-weight: bold }
+ .terminal-3470684325-r5 { fill: #474747 }
+ .terminal-3470684325-r6 { fill: #1e1e1e }
+ .terminal-3470684325-r7 { fill: #121212 }
+ .terminal-3470684325-r8 { fill: #e1e1e1;font-style: italic; }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- FooterApp
+ Demonstrator
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Q Quit the app ? Show help screen DELETE Delete the thing
+
+
+
+
+
+ ┌──────────────────────────────────────────────────────────┐
+ │ │
+ │ Information │
+ │ ━╸ ━━━━━━━━━━━ ╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
+ │ │
+ │ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ │
+ │ ▊ aaa naa aaaaa aaa aaaan, aaa aaa, aaaa?", aa aaa ▎ │
+ │ ▊ aaaaanaaa anaaaaaaana aaaaaaaa aaaaaana aaa ▎ │
+ │ ▊ aaaaa aa aaa, aa aaaaaaaaa aaa aaaa, "aaaa, an ▎ │
+ │ ▊ aaaa aaa aaaa, a aa". "aaaa, naa aaaaaaaaaaa, ▎ │
+ │ ▊ aaa a aaaa aaaaaanaa aaaa aa a aaa!", aaa ▎ │
+ │ ▊ anaaaa, aaaaa aaaaaaaa aanaaaaa. "Na! aaa naa. ▎ │
+ │ ▊ aaaaa. aa aaaaa naa. aaaaa aa na aaa.", aaa ▎ │
+ │ ▊ aaaaaaaa aaaanaaaaa DONE. ▎ │
+ │ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ │
+ │ │
+ │ │
+ │ │
+ │ │
+ └──────────────────────────────────────────────────────────┘
+
+
'''
# ---
-# name: test_fr_margins
+# name: test_grid_layout_basic
'''
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
-
- TestApp
+ GridLayoutExample
-
-
-
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ ┃
- ┃ ┃
- ┃ Hello ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┃ World ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┃ !! ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
+
+
+
+ ┌────────────────────────┐┌─────────────────────────┐┌─────────────────────────┐
+ │ One ││ Two ││ Three │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ └────────────────────────┘└─────────────────────────┘└─────────────────────────┘
+ ┌────────────────────────┐┌─────────────────────────┐┌─────────────────────────┐
+ │ Four ││ Five ││ Six │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ └────────────────────────┘└─────────────────────────┘└─────────────────────────┘
'''
# ---
-# name: test_fr_unit_with_min
+# name: test_grid_layout_basic_overflow
'''
@@ -20683,144 +23240,140 @@
font-weight: 700;
}
- .terminal-2211506176-matrix {
+ .terminal-3369516122-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2211506176-title {
+ .terminal-3369516122-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2211506176-r1 { fill: #c5c8c6 }
- .terminal-2211506176-r2 { fill: #e3e3e3 }
- .terminal-2211506176-r3 { fill: #ddddff }
- .terminal-2211506176-r4 { fill: #e3e4e5 }
- .terminal-2211506176-r5 { fill: #e2e3e3 }
- .terminal-2211506176-r6 { fill: #14191f }
- .terminal-2211506176-r7 { fill: #ddedf9 }
+ .terminal-3369516122-r1 { fill: #008000 }
+ .terminal-3369516122-r2 { fill: #c5c8c6 }
+ .terminal-3369516122-r3 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ScreenSplitApp
+ GridLayoutExample
-
-
-
- ⭘ ScreenSplitApp
- This is content This is content number 0
- number 0 This is content number 1
- This is content ▄▄ This is content number 2
- number 1 This is content number 3
- This is content This is content number 4 ▁▁
- number 2 This is content number 5
- This is content This is content number 6
- number 3 This is content number 7
- This is content This is content number 8
- number 4 This is content number 9
- This is content This is content number 10
- number 5 This is content number 11
- This is content This is content number 12
- number 6 This is content number 13
- This is content This is content number 14
- number 7 This is content number 15
- This is content This is content number 16
- number 8 This is content number 17
- This is content This is content number 18
- number 9 This is content number 19
- This is content This is content number 20
- number 10 This is content number 21
-
+
+
+
+ ┌────────────────────────┐┌─────────────────────────┐┌─────────────────────────┐
+ │ One ││ Two ││ Three │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ └────────────────────────┘└─────────────────────────┘└─────────────────────────┘
+ ┌────────────────────────┐┌─────────────────────────┐┌─────────────────────────┐
+ │ Four ││ Five ││ Six │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ └────────────────────────┘└─────────────────────────┘└─────────────────────────┘
+ ┌────────────────────────┐
+ │ Seven │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ └────────────────────────┘
'''
# ---
-# name: test_fr_units
+# name: test_grid_layout_gutter
'''
@@ -20843,140 +23396,140 @@
font-weight: 700;
}
- .terminal-230484307-matrix {
+ .terminal-721777988-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-230484307-title {
+ .terminal-721777988-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-230484307-r1 { fill: #ffffff }
- .terminal-230484307-r2 { fill: #c5c8c6 }
- .terminal-230484307-r3 { fill: #e2e2e2 }
+ .terminal-721777988-r1 { fill: #efddef }
+ .terminal-721777988-r2 { fill: #c5c8c6 }
+ .terminal-721777988-r3 { fill: #f0fcf0 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- FRApp
+ GridLayoutExample
-
-
-
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ HEADER ┃
- ┃ ┃
- ┃ ┃
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
- ┏ ━━━━━━━━ ┓ ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓ ┏ ━━━━━━ ┓
- ┃ foo ┃ ┃ bar ┃ ┃ baz ┃
- ┃ ┃ ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃ ┃ ┃
- ┗ ━━━━━━━━ ┛ ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛ ┗ ━━━━━━ ┛
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ FOOTER ┃
- ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
+
+
+
+ One Two Three
+
+
+
+
+
+
+
+
+
+
+
+ Four Five Six
+
+
+
+
+
+
+
+
+
+
+
'''
# ---
-# name: test_grid_layout_basic
+# name: test_hatch
'''
@@ -20999,140 +23552,146 @@
font-weight: 700;
}
- .terminal-3077119198-matrix {
+ .terminal-531352656-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3077119198-title {
+ .terminal-531352656-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3077119198-r1 { fill: #008000 }
- .terminal-3077119198-r2 { fill: #c5c8c6 }
- .terminal-3077119198-r3 { fill: #e1e1e1 }
+ .terminal-531352656-r1 { fill: #6a5acd }
+ .terminal-531352656-r2 { fill: #c5c8c6 }
+ .terminal-531352656-r3 { fill: #4ebf71 }
+ .terminal-531352656-r4 { fill: #fea62b }
+ .terminal-531352656-r5 { fill: #b93c5b }
+ .terminal-531352656-r6 { fill: #ff0000 }
+ .terminal-531352656-r7 { fill: #004578 }
+ .terminal-531352656-r8 { fill: #366e47 }
+ .terminal-531352656-r9 { fill: #ff00ff;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- GridLayoutExample
+ HatchApp
-
-
-
- ┌ ──────────────────────── ┐ ┌ ───────────────────────── ┐ ┌ ───────────────────────── ┐
- │ One │ │ Two │ │ Three │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- └ ──────────────────────── ┘ └ ───────────────────────── ┘ └ ───────────────────────── ┘
- ┌ ──────────────────────── ┐ ┌ ───────────────────────── ┐ ┌ ───────────────────────── ┐
- │ Four │ │ Five │ │ Six │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- └ ──────────────────────── ┘ └ ───────────────────────── ┘ └ ───────────────────────── ┘
+
+
+
+ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱
+ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳ ──────────────────────────────────────────────────────── ╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳ ── ┌─ Hello World ────────────────────────────────────┐ ── ╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳ ── │ ││││││││││││││││││││││││││││││││││││││││││││││││││ │ ── ╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳ ── │ ││││││││││││││││││││││││││││││││││││││││││││││││││ │ ── ╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳ ── │ ││││ ┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼ ││││ │ ── ╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳ ── │ ││││ ┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼ Hatched ┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼ ││││ │ ── ╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳ ── │ ││││ ┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼ ││││ │ ── ╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳ ── │ ││││ ┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼ ││││ │ ── ╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳ ── │ ││││││││││││││││││││││││││││││││││││││││││││││││││ │ ── ╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳ ── │ ││││││││││││││││││││││││││││││││││││││││││││││││││ │ ── ╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳ ── └──────────────────────────────────────────────────┘ ── ╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳ ──────────────────────────────────────────────────────── ╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲ ╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳╳ ╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱ ╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲ ╱╱╱╱
+ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱
+ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱
'''
# ---
-# name: test_grid_layout_basic_overflow
+# name: test_header_render
'''
@@ -21155,140 +23714,140 @@
font-weight: 700;
}
- .terminal-1958232742-matrix {
+ .terminal-2289354751-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1958232742-title {
+ .terminal-2289354751-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1958232742-r1 { fill: #008000 }
- .terminal-1958232742-r2 { fill: #c5c8c6 }
- .terminal-1958232742-r3 { fill: #e1e1e1 }
+ .terminal-2289354751-r1 { fill: #c5c8c6 }
+ .terminal-2289354751-r2 { fill: #e3e3e3 }
+ .terminal-2289354751-r3 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- GridLayoutExample
+ HeaderApp
-
-
-
- ┌ ──────────────────────── ┐ ┌ ───────────────────────── ┐ ┌ ───────────────────────── ┐
- │ One │ │ Two │ │ Three │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- └ ──────────────────────── ┘ └ ───────────────────────── ┘ └ ───────────────────────── ┘
- ┌ ──────────────────────── ┐ ┌ ───────────────────────── ┐ ┌ ───────────────────────── ┐
- │ Four │ │ Five │ │ Six │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- └ ──────────────────────── ┘ └ ───────────────────────── ┘ └ ───────────────────────── ┘
- ┌ ──────────────────────── ┐
- │ Seven │
- │ │
- │ │
- │ │
- │ │
- │ │
- └ ──────────────────────── ┘
+
+
+
+ ⭘ HeaderApp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
'''
# ---
-# name: test_grid_layout_gutter
+# name: test_horizontal_layout
'''
@@ -21311,140 +23870,140 @@
font-weight: 700;
}
- .terminal-721777988-matrix {
+ .terminal-2888370753-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-721777988-title {
+ .terminal-2888370753-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-721777988-r1 { fill: #efddef }
- .terminal-721777988-r2 { fill: #c5c8c6 }
- .terminal-721777988-r3 { fill: #f0fcf0 }
+ .terminal-2888370753-r1 { fill: #008000 }
+ .terminal-2888370753-r2 { fill: #c5c8c6 }
+ .terminal-2888370753-r3 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- GridLayoutExample
+ HorizontalLayoutExample
-
-
-
- One Two Three
-
-
-
-
-
-
-
-
-
-
-
- Four Five Six
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ┌────────────────────────┐┌─────────────────────────┐┌─────────────────────────┐
+ │ One ││ Two ││ Three │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ │ ││ ││ │
+ └────────────────────────┘└─────────────────────────┘└─────────────────────────┘
'''
# ---
-# name: test_header_render
+# name: test_horizontal_layout_width_auto_dock
'''
@@ -21467,132 +24026,135 @@
font-weight: 700;
}
- .terminal-4077214022-matrix {
+ .terminal-2824857946-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-4077214022-title {
+ .terminal-2824857946-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-2824857946-r1 { fill: #e1f0ff }
+ .terminal-2824857946-r2 { fill: #e7e5ef }
+ .terminal-2824857946-r3 { fill: #e1e1e1 }
+ .terminal-2824857946-r4 { fill: #c5c8c6 }
+ .terminal-2824857946-r5 { fill: #ebf0e2 }
+ .terminal-2824857946-r6 { fill: #f7e0ef }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- HeaderApp
+ HorizontalAutoWidth
-
-
-
- ⭘ HeaderApp
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Docke
+ d Widget 1 Widget 2
+ left
+ 1 Docked left 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -21600,7 +24162,7 @@
'''
# ---
-# name: test_horizontal_layout
+# name: test_input_and_focus
'''
@@ -21623,140 +24185,143 @@
font-weight: 700;
}
- .terminal-1769115774-matrix {
+ .terminal-4091889985-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1769115774-title {
+ .terminal-4091889985-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1769115774-r1 { fill: #008000 }
- .terminal-1769115774-r2 { fill: #c5c8c6 }
- .terminal-1769115774-r3 { fill: #e1e1e1 }
+ .terminal-4091889985-r1 { fill: #1e1e1e }
+ .terminal-4091889985-r2 { fill: #121212 }
+ .terminal-4091889985-r3 { fill: #c5c8c6 }
+ .terminal-4091889985-r4 { fill: #e2e2e2 }
+ .terminal-4091889985-r5 { fill: #0178d4 }
+ .terminal-4091889985-r6 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- HorizontalLayoutExample
+ InputApp
-
-
-
- ┌ ──────────────────────── ┐ ┌ ───────────────────────── ┐ ┌ ───────────────────────── ┐
- │ One │ │ Two │ │ Three │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- │ │ │ │ │ │
- └ ──────────────────────── ┘ └ ───────────────────────── ┘ └ ───────────────────────── ┘
+
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ Darren ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ Burns ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
'''
# ---
-# name: test_horizontal_layout_width_auto_dock
+# name: test_input_percentage_width
'''
@@ -21779,143 +24344,143 @@
font-weight: 700;
}
- .terminal-1672359278-matrix {
+ .terminal-3624006926-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1672359278-title {
+ .terminal-3624006926-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1672359278-r1 { fill: #e1f0ff }
- .terminal-1672359278-r2 { fill: #e7e5ef }
- .terminal-1672359278-r3 { fill: #e1e1e1 }
- .terminal-1672359278-r4 { fill: #c5c8c6 }
- .terminal-1672359278-r5 { fill: #ebf0e2 }
- .terminal-1672359278-r6 { fill: #f7e0ef }
+ .terminal-3624006926-r1 { fill: #1e1e1e }
+ .terminal-3624006926-r2 { fill: #e1e1e1 }
+ .terminal-3624006926-r3 { fill: #c5c8c6 }
+ .terminal-3624006926-r4 { fill: #ff0000 }
+ .terminal-3624006926-r5 { fill: #e2e2e2 }
+ .terminal-3624006926-r6 { fill: #e2e3e3;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- HorizontalAutoWidth
+ InputVsTextArea
-
-
-
- Docke
- d Widget 1 Widget 2
- left
- 1 Do cked left 2
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789
+ ┌──────────────────────────────────────┐
+ │ │
+ │ │
+ │ │
+ └──────────────────────────────────────┘
+ ┌──────────────────────────────────────┐
+ │ │
+ │ │
+ │ │
+ │ │
+ └──────────────────────────────────────┘
+ ┌──────────────────────────────────────┐
+ │ │
+ │ │
+ │ │
+ │ │
+ └──────────────────────────────────────┘
+ ┌──────────────────────────────────────┐
+ │ │
+ │ Button │
+ │ │
+ │ │
+ └──────────────────────────────────────┘
'''
# ---
-# name: test_input_and_focus
+# name: test_input_suggestions
'''
@@ -21938,135 +24503,137 @@
font-weight: 700;
}
- .terminal-596216952-matrix {
+ .terminal-2025105416-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-596216952-title {
+ .terminal-2025105416-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-596216952-r1 { fill: #1e1e1e }
- .terminal-596216952-r2 { fill: #121212 }
- .terminal-596216952-r3 { fill: #c5c8c6 }
- .terminal-596216952-r4 { fill: #e2e2e2 }
- .terminal-596216952-r5 { fill: #0178d4 }
- .terminal-596216952-r6 { fill: #e1e1e1 }
+ .terminal-2025105416-r1 { fill: #1e1e1e }
+ .terminal-2025105416-r2 { fill: #0178d4 }
+ .terminal-2025105416-r3 { fill: #c5c8c6 }
+ .terminal-2025105416-r4 { fill: #e2e2e2 }
+ .terminal-2025105416-r5 { fill: #1e1e1e;font-style: italic; }
+ .terminal-2025105416-r6 { fill: #ff0000;font-style: italic; }
+ .terminal-2025105416-r7 { fill: #121212 }
+ .terminal-2025105416-r8 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- InputApp
+ FruitsApp
-
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ Darren ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ Burns ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ strawb e rry ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ straw ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ p ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ b ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ a ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
@@ -22074,7 +24641,7 @@
'''
# ---
-# name: test_input_percentage_width
+# name: test_input_validation
'''
@@ -22097,143 +24664,146 @@
font-weight: 700;
}
- .terminal-2024488306-matrix {
+ .terminal-1896989657-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2024488306-title {
+ .terminal-1896989657-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2024488306-r1 { fill: #1e1e1e }
- .terminal-2024488306-r2 { fill: #e1e1e1 }
- .terminal-2024488306-r3 { fill: #c5c8c6 }
- .terminal-2024488306-r4 { fill: #ff0000 }
- .terminal-2024488306-r5 { fill: #e2e2e2 }
- .terminal-2024488306-r6 { fill: #e2e3e3;font-weight: bold }
+ .terminal-1896989657-r1 { fill: #e1e1e1 }
+ .terminal-1896989657-r2 { fill: #c5c8c6 }
+ .terminal-1896989657-r3 { fill: #1e1e1e }
+ .terminal-1896989657-r4 { fill: #7b3042 }
+ .terminal-1896989657-r5 { fill: #e2e2e2 }
+ .terminal-1896989657-r6 { fill: #3a7e4f }
+ .terminal-1896989657-r7 { fill: #b93c5b }
+ .terminal-1896989657-r8 { fill: #121212 }
+ .terminal-1896989657-r9 { fill: #787878 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- InputVsTextArea
+ InputApp
-
-
-
- 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789
- ┌ ────────────────────────────────────── ┐
- │ │
- │ │
- │ │
- └ ────────────────────────────────────── ┘
- ┌ ────────────────────────────────────── ┐
- │ │
- │ │
- │ │
- │ │
- └ ────────────────────────────────────── ┘
- ┌ ────────────────────────────────────── ┐
- │ │
- │ │
- │ │
- │ │
- └ ────────────────────────────────────── ┘
- ┌ ────────────────────────────────────── ┐
- │ │
- │ Button │
- │ │
- │ │
- └ ────────────────────────────────────── ┘
+
+
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ -2 ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ 3 ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ -2 ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ Enter a number between 1 and 5 ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
'''
# ---
-# name: test_input_suggestions
+# name: test_key_display
'''
@@ -22256,145 +24826,142 @@
font-weight: 700;
}
- .terminal-2577839347-matrix {
+ .terminal-1780802408-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2577839347-title {
+ .terminal-1780802408-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2577839347-r1 { fill: #1e1e1e }
- .terminal-2577839347-r2 { fill: #0178d4 }
- .terminal-2577839347-r3 { fill: #c5c8c6 }
- .terminal-2577839347-r4 { fill: #e2e2e2 }
- .terminal-2577839347-r5 { fill: #1e1e1e;font-style: italic; }
- .terminal-2577839347-r6 { fill: #ff0000;font-style: italic; }
- .terminal-2577839347-r7 { fill: #121212 }
- .terminal-2577839347-r8 { fill: #e1e1e1 }
+ .terminal-1780802408-r1 { fill: #e1e1e1 }
+ .terminal-1780802408-r2 { fill: #c5c8c6 }
+ .terminal-1780802408-r3 { fill: #fea62b;font-weight: bold }
+ .terminal-1780802408-r4 { fill: #a7a9ab }
+ .terminal-1780802408-r5 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- FruitsApp
+ KeyDisplayApp
-
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ strawb e rry ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ straw ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ p ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ b ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ a ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ? Question ^q Quit app Escape! Escape a Letter A
'''
# ---
-# name: test_input_validation
+# name: test_keyline
'''
@@ -22417,146 +24984,143 @@
font-weight: 700;
}
- .terminal-922438230-matrix {
+ .terminal-2719940520-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-922438230-title {
+ .terminal-2719940520-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-922438230-r1 { fill: #e1e1e1 }
- .terminal-922438230-r2 { fill: #c5c8c6 }
- .terminal-922438230-r3 { fill: #1e1e1e }
- .terminal-922438230-r4 { fill: #7b3042 }
- .terminal-922438230-r5 { fill: #e2e2e2 }
- .terminal-922438230-r6 { fill: #3a7e4f }
- .terminal-922438230-r7 { fill: #b93c5b }
- .terminal-922438230-r8 { fill: #121212 }
- .terminal-922438230-r9 { fill: #787878 }
+ .terminal-2719940520-r1 { fill: #ff0000 }
+ .terminal-2719940520-r2 { fill: #c5c8c6 }
+ .terminal-2719940520-r3 { fill: #e1e1e1 }
+ .terminal-2719940520-r4 { fill: #008000 }
+ .terminal-2719940520-r5 { fill: #ff00ff }
+ .terminal-2719940520-r6 { fill: #1e1e1e }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- InputApp
+ KeylineApp
-
-
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ -2 ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ 3 ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ -2 ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ Enter a number between 1 and 5 ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
+
+
+
+ ┌──────────────────────────────────────────────────────────────────────────────┐
+ │ 1 │
+ ├──────────────────────────────────────────────────────────────────────────────┤
+ │ 2 │
+ ├──────────────────────────────────────────────────────────────────────────────┤
+ │ 3 │
+ │ │
+ └──────────────────────────────────────────────────────────────────────────────┘
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ 4 ┃ 5 ┃ 6 ┃
+ ┃ ┃ ┃ ┃
+ ┃ ┃ ┃ ┃
+ ┃ ┃ ┃ ┃
+ ┃ ┃ ┃ ┃
+ ┃ ┃ ┃ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ ╔══════════════════════════════════════╦═══════════════════════════════════════╗
+ ║ 7 ║ 8 ║
+ ║ ║ ║
+ ╠══════════════════════════════════════╬═══════════════════════════════════════╝
+ ║ 9 ║
+ ║ ║
+ ║ ║
+ ╚══════════════════════════════════════╝
'''
# ---
-# name: test_key_display
+# name: test_label_widths
'''
@@ -22579,141 +25143,142 @@
font-weight: 700;
}
- .terminal-1765381587-matrix {
+ .terminal-1889976810-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1765381587-title {
+ .terminal-1889976810-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1765381587-r1 { fill: #e1e1e1 }
- .terminal-1765381587-r2 { fill: #c5c8c6 }
- .terminal-1765381587-r3 { fill: #dde8f3;font-weight: bold }
- .terminal-1765381587-r4 { fill: #ddedf9 }
+ .terminal-1889976810-r1 { fill: #1f1f1f }
+ .terminal-1889976810-r2 { fill: #c5c8c6 }
+ .terminal-1889976810-r3 { fill: #00ff00 }
+ .terminal-1889976810-r4 { fill: #1b1b1b }
+ .terminal-1889976810-r5 { fill: #121e12 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- KeyDisplayApp
+ LabelWrap
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ? Question ^q Quit app Escape! Escape A Letter A
+
+
+
+
+
+
+
+
+
+ Apple Banana Cherry Mango Fig Guava Pineapple:Dragon Unicorn Centaur Phoenix Ch
+
+
+ Apple Banana Cherry Mango Fig Guava Pineapple:Dragon Unicorn Centaur Phoenix
+ Chimera Castle
+
+
+ ╭────────────────────────────────────────────────────────────────────────────╮
+ │ Apple Banana Cherry Mango Fig Guava Pineapple:Dragon Unicorn Centaur │
+ │ Phoenix Chimera Castle │
+ ╰────────────────────────────────────────────────────────────────────────────╯
+
+
+
+
+
+
+
'''
# ---
-# name: test_keyline
+# name: test_layer_fix
'''
@@ -22736,143 +25301,144 @@
font-weight: 700;
}
- .terminal-1393283672-matrix {
+ .terminal-3315312229-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1393283672-title {
+ .terminal-3315312229-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1393283672-r1 { fill: #ff0000 }
- .terminal-1393283672-r2 { fill: #c5c8c6 }
- .terminal-1393283672-r3 { fill: #e1e1e1 }
- .terminal-1393283672-r4 { fill: #008000 }
- .terminal-1393283672-r5 { fill: #ff00ff }
- .terminal-1393283672-r6 { fill: #1e1e1e }
+ .terminal-3315312229-r1 { fill: #c5c8c6 }
+ .terminal-3315312229-r2 { fill: #e3e3e3 }
+ .terminal-3315312229-r3 { fill: #e1e1e1 }
+ .terminal-3315312229-r4 { fill: #ff0000 }
+ .terminal-3315312229-r5 { fill: #fea62b;font-weight: bold }
+ .terminal-3315312229-r6 { fill: #a7a9ab }
+ .terminal-3315312229-r7 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- KeylineApp
+ DialogIssueApp
-
-
-
- ┌ ────────────────────────────────────────────────────────────────────────────── ┐
- │ 1 │
- ├ ────────────────────────────────────────────────────────────────────────────── ┤
- │ 2 │
- ├ ────────────────────────────────────────────────────────────────────────────── ┤
- │ 3 │
- │ │
- └ ────────────────────────────────────────────────────────────────────────────── ┘
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━ ┳ ━━━━━━━━━━━━━━━━━━━━━━━━━ ┳ ━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ 4 ┃ 5 ┃ 6 ┃
- ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃
- ┃ ┃ ┃ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━ ┻ ━━━━━━━━━━━━━━━━━━━━━━━━━ ┻ ━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
- ╔ ══════════════════════════════════════ ╦ ═══════════════════════════════════════ ╗
- ║ 7 ║ 8 ║
- ║ ║ ║
- ╠ ══════════════════════════════════════ ╬ ═══════════════════════════════════════ ╝
- ║ 9 ║
- ║ ║
- ║ ║
- ╚ ══════════════════════════════════════ ╝
+
+
+
+ ⭘ DialogIssueApp
+
+
+
+
+
+ ╭──────────────────────────────────────╮
+ │ │
+ │ │
+ │ │
+ │ │
+ │ This should not cause a scrollbar to a │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ ╰──────────────────────────────────────╯
+
+
+
+
+
+ d Toggle the dialog
'''
# ---
-# name: test_label_widths
+# name: test_layers
'''
@@ -22895,134 +25461,133 @@
font-weight: 700;
}
- .terminal-248448564-matrix {
+ .terminal-3008465429-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-248448564-title {
+ .terminal-3008465429-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-248448564-r1 { fill: #1f1f1f }
- .terminal-248448564-r2 { fill: #c5c8c6 }
- .terminal-248448564-r3 { fill: #00ff00 }
- .terminal-248448564-r4 { fill: #1b1b1b }
- .terminal-248448564-r5 { fill: #121e12 }
+ .terminal-3008465429-r1 { fill: #e1e1e1 }
+ .terminal-3008465429-r2 { fill: #c5c8c6 }
+ .terminal-3008465429-r3 { fill: #ddefef }
+ .terminal-3008465429-r4 { fill: #211500 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- LabelWrap
+ LayersExample
-
-
-
-
-
-
-
-
-
- Apple Banana Cherry Mango Fig Guava Pineapple:Dragon Unicorn Centaur Phoenix Ch
-
-
- Apple Banana Cherry Mango Fig Guava Pineapple:Dragon Unicorn Centaur Phoenix
- Chimera Castle
-
-
- ╭────────────────────────────────────────────────────────────────────────────╮
- │ Apple Banana Cherry Mango Fig Guava Pineapple:Dragon Unicorn Centaur │
- │ Phoenix Chimera Castle │
- ╰────────────────────────────────────────────────────────────────────────────╯
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ box1 (layer = above)
+
+
+
+
+
+ box2 (layer = below)
+
+
+
+
+
@@ -23030,7 +25595,7 @@
'''
# ---
-# name: test_layer_fix
+# name: test_layout_containers
'''
@@ -23053,143 +25618,151 @@
font-weight: 700;
}
- .terminal-3085198851-matrix {
+ .terminal-3138754671-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3085198851-title {
+ .terminal-3138754671-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3085198851-r1 { fill: #c5c8c6 }
- .terminal-3085198851-r2 { fill: #e3e3e3 }
- .terminal-3085198851-r3 { fill: #e1e1e1 }
- .terminal-3085198851-r4 { fill: #ff0000 }
- .terminal-3085198851-r5 { fill: #dde8f3;font-weight: bold }
- .terminal-3085198851-r6 { fill: #ddedf9 }
+ .terminal-3138754671-r1 { fill: #7ae998 }
+ .terminal-3138754671-r2 { fill: #e76580 }
+ .terminal-3138754671-r3 { fill: #1e1e1e }
+ .terminal-3138754671-r4 { fill: #121212 }
+ .terminal-3138754671-r5 { fill: #c5c8c6 }
+ .terminal-3138754671-r6 { fill: #4ebf71;font-weight: bold }
+ .terminal-3138754671-r7 { fill: #f5e5e9;font-weight: bold }
+ .terminal-3138754671-r8 { fill: #e2e2e2 }
+ .terminal-3138754671-r9 { fill: #0a180e;font-weight: bold }
+ .terminal-3138754671-r10 { fill: #008139 }
+ .terminal-3138754671-r11 { fill: #780028 }
+ .terminal-3138754671-r12 { fill: #e1e1e1 }
+ .terminal-3138754671-r13 { fill: #23568b }
+ .terminal-3138754671-r14 { fill: #14191f }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- DialogIssueApp
+ MyApp
-
-
-
- ⭘ DialogIssueApp
-
-
-
-
-
- ╭ ────────────────────────────────────── ╮
- │ │
- │ │
- │ │
- │ │
- │ This should not cause a scrollbar to a │
- │ │
- │ │
- │ │
- │ │
- │ │
- ╰ ────────────────────────────────────── ╯
-
-
-
-
-
- D Toggle the dialog
+
+
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▊ ▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▊ ▔▔▔▔▔▔▔
+ Accept Decline ▊ Accept Decline ▊
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▊ ▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▊ ▁▁▁▁▁▁▁
+
+
+
+
+
+
+
+
+ ▌
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Accept Accept
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Decline Decline
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▆▆
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ ▊ ▊
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+ 0 0
+
+ 1000000 1000000
'''
# ---
-# name: test_layers
+# name: test_line_api_scrollbars
'''
@@ -23212,133 +25785,132 @@
font-weight: 700;
}
- .terminal-3301495769-matrix {
+ .terminal-1120593594-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3301495769-title {
+ .terminal-1120593594-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3301495769-r1 { fill: #e1e1e1 }
- .terminal-3301495769-r2 { fill: #c5c8c6 }
- .terminal-3301495769-r3 { fill: #ddefef }
- .terminal-3301495769-r4 { fill: #211500 }
+ .terminal-1120593594-r1 { fill: #e1e1e1 }
+ .terminal-1120593594-r2 { fill: #c5c8c6 }
+ .terminal-1120593594-r3 { fill: #23568b }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- LayersExample
+ ScrollViewApp
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- box1 (layer = above)
-
-
-
-
-
- box2 (layer = below)
-
-
-
-
-
+
+
+
+
+
+ 11 01234567
+ 12 01234567
+ 13 01234567
+ 14 01234567
+ 15 01234567 ▁▁
+ 16 01234567
+ 17 01234567
+ 18 01234567
+ 19 01234567
+ ▍
+ 11 01234567
+ 12 01234567
+ 13 01234567
+ 14 01234567
+ 15 01234567 ▁▁
+ 16 01234567
+ 17 01234567
+ 18 01234567
+ 19 01234567
+ ▍
+
@@ -23346,7 +25918,7 @@
'''
# ---
-# name: test_layout_containers
+# name: test_list_view
'''
@@ -23369,151 +25941,142 @@
font-weight: 700;
}
- .terminal-3525357221-matrix {
+ .terminal-3775001870-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3525357221-title {
+ .terminal-3775001870-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3525357221-r1 { fill: #7ae998 }
- .terminal-3525357221-r2 { fill: #e76580 }
- .terminal-3525357221-r3 { fill: #1e1e1e }
- .terminal-3525357221-r4 { fill: #121212 }
- .terminal-3525357221-r5 { fill: #c5c8c6 }
- .terminal-3525357221-r6 { fill: #4ebf71;font-weight: bold }
- .terminal-3525357221-r7 { fill: #f5e5e9;font-weight: bold }
- .terminal-3525357221-r8 { fill: #e2e2e2 }
- .terminal-3525357221-r9 { fill: #0a180e;font-weight: bold }
- .terminal-3525357221-r10 { fill: #008139 }
- .terminal-3525357221-r11 { fill: #780028 }
- .terminal-3525357221-r12 { fill: #e1e1e1 }
- .terminal-3525357221-r13 { fill: #23568b }
- .terminal-3525357221-r14 { fill: #14191f }
+ .terminal-3775001870-r1 { fill: #e1e1e1 }
+ .terminal-3775001870-r2 { fill: #c5c8c6 }
+ .terminal-3775001870-r3 { fill: #e4e5e6 }
+ .terminal-3775001870-r4 { fill: #ddedf9 }
+ .terminal-3775001870-r5 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MyApp
+ ListViewExample
-
-
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▊ ▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▊ ▔▔▔▔▔▔▔
- Accept Decline ▊ Accept Decline ▊
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▊ ▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▊ ▁▁▁▁▁▁▁
-
-
-
-
-
-
-
-
- ▌
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Accept Accept
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Decline Decline
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▆▆
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- ▊ ▊
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
- 0 0
-
- 1000000 1000000
+
+
+
+
+
+
+
+
+
+
+
+ One
+
+
+ Two
+
+
+ Three
+
+
+
+
+
+
+
+
+
'''
# ---
-# name: test_line_api_scrollbars
+# name: test_listview_index
'''
@@ -23536,132 +26099,134 @@
font-weight: 700;
}
- .terminal-3512435366-matrix {
+ .terminal-1356381627-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3512435366-title {
+ .terminal-1356381627-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3512435366-r1 { fill: #e1e1e1 }
- .terminal-3512435366-r2 { fill: #c5c8c6 }
- .terminal-3512435366-r3 { fill: #23568b }
+ .terminal-1356381627-r1 { fill: #e4e5e6 }
+ .terminal-1356381627-r2 { fill: #e1e1e1 }
+ .terminal-1356381627-r3 { fill: #c5c8c6 }
+ .terminal-1356381627-r4 { fill: #23568b }
+ .terminal-1356381627-r5 { fill: #ddedf9 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ScrollViewApp
+ ListViewIndexApp
-
-
-
-
-
- 11 01234567
- 12 01234567
- 13 01234567
- 14 01234567
- 15 01234567 ▁▁
- 16 01234567
- 17 01234567
- 18 01234567
- 19 01234567
- ▍
- 11 01234567
- 12 01234567
- 13 01234567
- 14 01234567
- 15 01234567 ▁▁
- 16 01234567
- 17 01234567
- 18 01234567
- 19 01234567
- ▍
-
+
+
+
+ 10
+ 12
+ 14
+ 16 ▆▆
+ 18
+ 20
+ 22
+ 24
+ 26
+ 28
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -23669,7 +26234,7 @@
'''
# ---
-# name: test_list_view
+# name: test_loading_indicator
'''
@@ -23692,141 +26257,143 @@
font-weight: 700;
}
- .terminal-552007062-matrix {
+ .terminal-1966602932-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-552007062-title {
+ .terminal-1966602932-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-552007062-r1 { fill: #e1e1e1 }
- .terminal-552007062-r2 { fill: #c5c8c6 }
- .terminal-552007062-r3 { fill: #e4e5e6 }
- .terminal-552007062-r4 { fill: #ddedf9 }
+ .terminal-1966602932-r1 { fill: #1e1e1e }
+ .terminal-1966602932-r2 { fill: #0178d4 }
+ .terminal-1966602932-r3 { fill: #c5c8c6 }
+ .terminal-1966602932-r4 { fill: #e2e2e2;font-weight: bold }
+ .terminal-1966602932-r5 { fill: #e2e2e2 }
+ .terminal-1966602932-r6 { fill: #14191f }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ListViewExample
+ LoadingOverlayRedux
-
-
-
-
-
-
-
-
-
-
-
- One
-
-
- Two
-
-
- Three
-
-
-
-
-
-
-
-
-
+
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ ▎ ▊ bar ▎
+ ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▄▄ ▎
+ ▊ ▎ ▊ bar ▎
+ ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ ▎ ▊ bar ▎
+ ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ ▎ ▊ bar ▎
+ ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ ▎ ▊ bar ▎
+ ▊ Loading! ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ ▎ ▊ bar ▎
+ ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ ▎ ▊ bar ▎
+ ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ ▎ ▊ bar ▎
+ ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ ▎ ▊ bar ▎
+ ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ ▎ ▊ bar ▎
+ ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ ▎ ▊ bar ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
'''
# ---
-# name: test_listview_index
+# name: test_loading_indicator_disables_widget
'''
@@ -23849,142 +26416,144 @@
font-weight: 700;
}
- .terminal-1219584187-matrix {
+ .terminal-1951874799-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1219584187-title {
+ .terminal-1951874799-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1219584187-r1 { fill: #e4e5e6 }
- .terminal-1219584187-r2 { fill: #e1e1e1 }
- .terminal-1219584187-r3 { fill: #c5c8c6 }
- .terminal-1219584187-r4 { fill: #23568b }
- .terminal-1219584187-r5 { fill: #ddedf9 }
+ .terminal-1951874799-r1 { fill: #1e1e1e }
+ .terminal-1951874799-r2 { fill: #0178d4 }
+ .terminal-1951874799-r3 { fill: #c5c8c6 }
+ .terminal-1951874799-r4 { fill: #ddedf9;font-weight: bold }
+ .terminal-1951874799-r5 { fill: #e2e2e2 }
+ .terminal-1951874799-r6 { fill: #e2e2e2;font-weight: bold }
+ .terminal-1951874799-r7 { fill: #14191f }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ListViewIndexApp
+ LoadingOverlayRedux
-
-
-
- 10
- 12
- 14
- 16 ▆▆
- 18
- 20
- 22
- 24
- 26
- 28
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ world hello world hello world ▎ ▊ bar ▎
+ ▊ hello world hello world hello ▄▄ ▎ ▊ foo barfoo barfoo barfoo barfoo ▄▄ ▎
+ ▊ world hello world hello world ▎ ▊ bar ▎
+ ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ world hello world hello world ▎ ▊ bar ▎
+ ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ world hello world hello world ▎ ▊ bar ▎
+ ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ world hello world hello world ▎ ▊ bar ▎
+ ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ world hello world hello world ▎ ▊ bar ▎
+ ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ world hello world hello world ▎ ▊ bar ▎
+ ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ world hello world hello world ▎ ▊ bar ▎
+ ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ world hello world hello world ▎ ▊ bar ▎
+ ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ world hello world hello world ▎ ▊ bar ▎
+ ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
+ ▊ world hello world hello world ▎ ▊ bar ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
'''
# ---
-# name: test_loading_indicator
+# name: test_log_write
'''
@@ -24007,143 +26576,139 @@
font-weight: 700;
}
- .terminal-1550026580-matrix {
+ .terminal-3821190285-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1550026580-title {
+ .terminal-3821190285-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1550026580-r1 { fill: #1e1e1e }
- .terminal-1550026580-r2 { fill: #0178d4 }
- .terminal-1550026580-r3 { fill: #c5c8c6 }
- .terminal-1550026580-r4 { fill: #e2e2e2;font-weight: bold }
- .terminal-1550026580-r5 { fill: #e2e2e2 }
- .terminal-1550026580-r6 { fill: #14191f }
+ .terminal-3821190285-r1 { fill: #e1e1e1 }
+ .terminal-3821190285-r2 { fill: #c5c8c6 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- LoadingOverlayRedux
+ LogApp
-
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ ▎ ▊ bar ▎
- ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▄▄ ▎
- ▊ ▎ ▊ bar ▎
- ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ ▎ ▊ bar ▎
- ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ ▎ ▊ bar ▎
- ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ ▎ ▊ bar ▎
- ▊ Loading! ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ ▎ ▊ bar ▎
- ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ ▎ ▊ bar ▎
- ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ ▎ ▊ bar ▎
- ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ ▎ ▊ bar ▎
- ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ ▎ ▊ bar ▎
- ▊ ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ ▎ ▊ bar ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+ Hello, World!
+ What's up?
+ FOO
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
'''
# ---
-# name: test_loading_indicator_disables_widget
+# name: test_log_write_lines
'''
@@ -24166,144 +26731,141 @@
font-weight: 700;
}
- .terminal-3594715516-matrix {
+ .terminal-903827020-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3594715516-title {
+ .terminal-903827020-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3594715516-r1 { fill: #1e1e1e }
- .terminal-3594715516-r2 { fill: #0178d4 }
- .terminal-3594715516-r3 { fill: #c5c8c6 }
- .terminal-3594715516-r4 { fill: #ddedf9;font-weight: bold }
- .terminal-3594715516-r5 { fill: #e2e2e2 }
- .terminal-3594715516-r6 { fill: #e2e2e2;font-weight: bold }
- .terminal-3594715516-r7 { fill: #14191f }
+ .terminal-903827020-r1 { fill: #e1e1e1 }
+ .terminal-903827020-r2 { fill: #c5c8c6 }
+ .terminal-903827020-r3 { fill: #14191f }
+ .terminal-903827020-r4 { fill: #23568b }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- LoadingOverlayRedux
+ LogApp
-
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ world hello world hello world ▎ ▊ bar ▎
- ▊ hello world hello world hello ▄▄ ▎ ▊ foo barfoo barfoo barfoo barfoo ▄▄ ▎
- ▊ world hello world hello world ▎ ▊ bar ▎
- ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ world hello world hello world ▎ ▊ bar ▎
- ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ world hello world hello world ▎ ▊ bar ▎
- ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ world hello world hello world ▎ ▊ bar ▎
- ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ world hello world hello world ▎ ▊ bar ▎
- ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ world hello world hello world ▎ ▊ bar ▎
- ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ world hello world hello world ▎ ▊ bar ▎
- ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ world hello world hello world ▎ ▊ bar ▎
- ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ world hello world hello world ▎ ▊ bar ▎
- ▊ hello world hello world hello ▎ ▊ foo barfoo barfoo barfoo barfoo ▎
- ▊ world hello world hello world ▎ ▊ bar ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+ I must not fear. And when it has go Hello, World Fear is the mind-k
+ Fear is the mind-k Where the fear has Fear is the little
+ Fear is the little I must not fear. I will face my fea
+ I will face my fea ▁▁ Fear is the mind-k I will permit it t
+ I will permit it t Fear is the little And when it has go
+ And when it has go I will face my fea Where the fear has
+ Where the fear has I will permit it t
+ I must not fear. And when it has go
+ Fear is the mind-k Where the fear has
+ Fear is the little I must not fear.
+ I will face my fea Fear is the mind-k
+ I will permit it t Fear is the little
+ And when it has go I will face my fea
+ Where the fear has I will permit it t
+ I must not fear. And when it has go
+ Fear is the mind-k Where the fear has
+ Fear is the little I must not fear.
+ I will face my fea Fear is the mind-k
+ I will permit it t Fear is the little
+ And when it has go I will face my fea ▇▇
+ Where the fear has I will permit it t
+ I must not fear. And when it has go
+ Fear is the mind-k Where the fear has
+ ▊ ▊ ▊
'''
# ---
-# name: test_log_write
+# name: test_margin_multiple
'''
@@ -24326,131 +26888,134 @@
font-weight: 700;
}
- .terminal-2521368986-matrix {
+ .terminal-3867713494-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2521368986-title {
+ .terminal-3867713494-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2521368986-r1 { fill: #e1e1e1 }
- .terminal-2521368986-r2 { fill: #c5c8c6 }
+ .terminal-3867713494-r1 { fill: #ffff00 }
+ .terminal-3867713494-r2 { fill: #e1e1e1 }
+ .terminal-3867713494-r3 { fill: #c5c8c6 }
+ .terminal-3867713494-r4 { fill: #008000 }
+ .terminal-3867713494-r5 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- LogApp
+ MyApp
-
-
-
- Hello, World!
- What's up?
- FOO
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ╔═══╗
+ ║ foo ║
+ ╚═══╝
+
+
+ ┌────────────────────────────┐
+ │ │
+ │ │
+ ┌────────────────────────────┐ │ │
+ │ │ │ │
+ │ │ │ ╔═══╗ │
+ │ │ │ ║ bar ║ │
+ │ ╔═══╗ │ │ ╚═══╝ │
+ │ ║ bar ║ │ │ │
+ │ ╚═══╝ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ └────────────────────────────┘ └────────────────────────────┘
+
+
+
+
@@ -24458,7 +27023,7 @@
'''
# ---
-# name: test_log_write_lines
+# name: test_markdown_component_classes_reloading
'''
@@ -24481,141 +27046,150 @@
font-weight: 700;
}
- .terminal-100291914-matrix {
+ .terminal-635127795-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-100291914-title {
+ .terminal-635127795-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-100291914-r1 { fill: #e1e1e1 }
- .terminal-100291914-r2 { fill: #c5c8c6 }
- .terminal-100291914-r3 { fill: #14191f }
- .terminal-100291914-r4 { fill: #23568b }
+ .terminal-635127795-r1 { fill: #e1e1e1 }
+ .terminal-635127795-r2 { fill: #c5c8c6 }
+ .terminal-635127795-r3 { fill: #4ebf71;font-weight: bold }
+ .terminal-635127795-r4 { fill: #e2e3e3 }
+ .terminal-635127795-r5 { fill: #e2e3e3;font-weight: bold }
+ .terminal-635127795-r6 { fill: #939393;font-weight: bold }
+ .terminal-635127795-r7 { fill: #e1e1e1;font-weight: bold }
+ .terminal-635127795-r8 { fill: #e1e1e1;font-style: italic; }
+ .terminal-635127795-r9 { fill: #e1e1e1;text-decoration: line-through; }
+ .terminal-635127795-r10 { fill: #d2d2d2 }
+ .terminal-635127795-r11 { fill: #82aaff }
+ .terminal-635127795-r12 { fill: #89ddff }
+ .terminal-635127795-r13 { fill: #c3e88d }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- LogApp
+ MyApp
-
-
-
- I must not fear. And when it has go Hello, World Fear is the mind-k
- Fear is the mind-k Where the fear has Fear is the little
- Fear is the little I must not fear. I will face my fea
- I will face my fea ▁▁ Fear is the mind-k I will permit it t
- I will permit it t Fear is the little And when it has go
- And when it has go I will face my fea Where the fear has
- Where the fear has I will permit it t
- I must not fear. And when it has go
- Fear is the mind-k Where the fear has
- Fear is the little I must not fear.
- I will face my fea Fear is the mind-k
- I will permit it t Fear is the little
- And when it has go I will face my fea
- Where the fear has I will permit it t
- I must not fear. And when it has go
- Fear is the mind-k Where the fear has
- Fear is the little I must not fear.
- I will face my fea Fear is the mind-k
- I will permit it t Fear is the little
- And when it has go I will face my fea ▇▇
- Where the fear has I will permit it t
- I must not fear. And when it has go
- Fear is the mind-k Where the fear has
- ▊ ▊ ▊
+
+
+
+
+
+ This is a header
+
+
+ col1 col2
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+ value 1 value 2
+
+ Here's some code: from itertools import product . Bold text Emphasized text
+ strikethrough
+
+
+ print ( "Hello, world!" )
+
+
+ That was some code.
+
+
+
+
+
+
+
'''
# ---
-# name: test_markdown_component_classes_reloading
+# name: test_markdown_dark_theme_override
'''
@@ -24638,145 +27212,138 @@
font-weight: 700;
}
- .terminal-762794446-matrix {
+ .terminal-1842878139-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-762794446-title {
+ .terminal-1842878139-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-762794446-r1 { fill: #e1e1e1 }
- .terminal-762794446-r2 { fill: #121212 }
- .terminal-762794446-r3 { fill: #c5c8c6 }
- .terminal-762794446-r4 { fill: #0053aa }
- .terminal-762794446-r5 { fill: #dde8f3;font-weight: bold }
- .terminal-762794446-r6 { fill: #e2e3e3 }
- .terminal-762794446-r7 { fill: #24292f }
- .terminal-762794446-r8 { fill: #e2e3e3;font-weight: bold }
- .terminal-762794446-r9 { fill: #939393;font-weight: bold }
- .terminal-762794446-r10 { fill: #e1e1e1;font-weight: bold }
- .terminal-762794446-r11 { fill: #e1e1e1;font-style: italic; }
- .terminal-762794446-r12 { fill: #e1e1e1;text-decoration: line-through; }
- .terminal-762794446-r13 { fill: #d2d2d2 }
- .terminal-762794446-r14 { fill: #82aaff }
- .terminal-762794446-r15 { fill: #89ddff }
- .terminal-762794446-r16 { fill: #c3e88d }
+ .terminal-1842878139-r1 { fill: #e1e1e1 }
+ .terminal-1842878139-r2 { fill: #c5c8c6 }
+ .terminal-1842878139-r3 { fill: #4ebf71;font-weight: bold }
+ .terminal-1842878139-r4 { fill: #d2d2d2 }
+ .terminal-1842878139-r5 { fill: #859900 }
+ .terminal-1842878139-r6 { fill: #839496 }
+ .terminal-1842878139-r7 { fill: #268bd2 }
+ .terminal-1842878139-r8 { fill: #34535b;font-style: italic; }
+ .terminal-1842878139-r9 { fill: #2aa198 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MyApp
+ MarkdownThemeSwitchertApp
-
-
-
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▎ ▊
- ▎ This is a header ▊
- ▎ ▊
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
-
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▎ ▊
- ▎ col1 col2 ▊
- ▎ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ▊
- ▎ value 1 value 2 ▊
- ▎ ▊
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
-
- Here's some code: from itertools import product . Bold text Emphasized
- text strikethrough
-
-
- print ( " Hello, world! " )
-
-
- That was some code.
-
+
+
+
+
+
+ This is a H1
+
+
+ def main ():
+ │ print ( "Hello world!" )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -24784,7 +27351,7 @@
'''
# ---
-# name: test_markdown_dark_theme_override
+# name: test_markdown_example
'''
@@ -24807,140 +27374,136 @@
font-weight: 700;
}
- .terminal-2700004228-matrix {
+ .terminal-3628679305-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2700004228-title {
+ .terminal-3628679305-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2700004228-r1 { fill: #e1e1e1 }
- .terminal-2700004228-r2 { fill: #121212 }
- .terminal-2700004228-r3 { fill: #c5c8c6 }
- .terminal-2700004228-r4 { fill: #0053aa }
- .terminal-2700004228-r5 { fill: #dde8f3;font-weight: bold }
- .terminal-2700004228-r6 { fill: #d2d2d2 }
- .terminal-2700004228-r7 { fill: #859900 }
- .terminal-2700004228-r8 { fill: #839496 }
- .terminal-2700004228-r9 { fill: #268bd2 }
- .terminal-2700004228-r10 { fill: #34535b;font-style: italic; }
- .terminal-2700004228-r11 { fill: #2aa198 }
+ .terminal-3628679305-r1 { fill: #e1e1e1 }
+ .terminal-3628679305-r2 { fill: #c5c8c6 }
+ .terminal-3628679305-r3 { fill: #4ebf71;font-weight: bold }
+ .terminal-3628679305-r4 { fill: #939393;font-weight: bold }
+ .terminal-3628679305-r5 { fill: #4ebf71;text-decoration: underline; }
+ .terminal-3628679305-r6 { fill: #e1e1e1;font-style: italic; }
+ .terminal-3628679305-r7 { fill: #e1e1e1;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MarkdownThemeSwitchertApp
+ MarkdownExampleApp
-
-
-
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▎ ▊
- ▎ This is a H1 ▊
- ▎ ▊
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
-
-
- def main ( ) :
- │ print ( " Hello world! " )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ Markdown Document
+
+ This is an example of Textual's Markdown widget.
+
+
+ Features
+
+ Markdown syntax and extensions are supported.
+
+ ● Typography emphasis , strong , inline code etc.
+ ● Headers
+ ● Lists (bullet and ordered)
+ ● Syntax highlighted code blocks
+ ● Tables!
+
+
+
+
+
+
+
@@ -24948,7 +27511,7 @@
'''
# ---
-# name: test_markdown_example
+# name: test_markdown_light_theme_override
'''
@@ -24971,140 +27534,138 @@
font-weight: 700;
}
- .terminal-454878977-matrix {
+ .terminal-2710843744-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-454878977-title {
+ .terminal-2710843744-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-454878977-r1 { fill: #e1e1e1 }
- .terminal-454878977-r2 { fill: #121212 }
- .terminal-454878977-r3 { fill: #c5c8c6 }
- .terminal-454878977-r4 { fill: #0053aa }
- .terminal-454878977-r5 { fill: #dde8f3;font-weight: bold }
- .terminal-454878977-r6 { fill: #939393;font-weight: bold }
- .terminal-454878977-r7 { fill: #24292f }
- .terminal-454878977-r8 { fill: #e2e3e3;font-weight: bold }
- .terminal-454878977-r9 { fill: #4ebf71;font-weight: bold }
- .terminal-454878977-r10 { fill: #e1e1e1;font-style: italic; }
- .terminal-454878977-r11 { fill: #e1e1e1;font-weight: bold }
+ .terminal-2710843744-r1 { fill: #1f1f1f }
+ .terminal-2710843744-r2 { fill: #c5c8c6 }
+ .terminal-2710843744-r3 { fill: #004578;font-weight: bold }
+ .terminal-2710843744-r4 { fill: #d2d2d2 }
+ .terminal-2710843744-r5 { fill: #859900 }
+ .terminal-2710843744-r6 { fill: #657b83 }
+ .terminal-2710843744-r7 { fill: #268bd2 }
+ .terminal-2710843744-r8 { fill: #bdc3bb;font-style: italic; }
+ .terminal-2710843744-r9 { fill: #2aa198 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MarkdownExampleApp
+ MarkdownThemeSwitchertApp
-
-
-
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▎ ▊
- ▎ Markdown Document ▊
- ▎ ▊
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
- This is an example of Textual's Markdown widget.
-
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▎ ▊
- ▎ Features ▊
- ▎ ▊
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
- Markdown syntax and extensions are supported.
-
- ● Typography emphasis , strong , inline code etc.
- ● Headers
- ● Lists (bullet and ordered)
- ● Syntax highlighted code blocks
- ● Tables!
-
-
-
-
+
+
+
+
+
+ This is a H1
+
+
+ def main ():
+ │ print ( "Hello world!" )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -25112,7 +27673,7 @@
'''
# ---
-# name: test_markdown_light_theme_override
+# name: test_markdown_space_squashing
'''
@@ -25135,148 +27696,152 @@
font-weight: 700;
}
- .terminal-3405842733-matrix {
+ .terminal-1634757789-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3405842733-title {
+ .terminal-1634757789-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3405842733-r1 { fill: #1f1f1f }
- .terminal-3405842733-r2 { fill: #efefef }
- .terminal-3405842733-r3 { fill: #c5c8c6 }
- .terminal-3405842733-r4 { fill: #0053aa }
- .terminal-3405842733-r5 { fill: #dde8f3;font-weight: bold }
- .terminal-3405842733-r6 { fill: #d2d2d2 }
- .terminal-3405842733-r7 { fill: #859900 }
- .terminal-3405842733-r8 { fill: #657b83 }
- .terminal-3405842733-r9 { fill: #268bd2 }
- .terminal-3405842733-r10 { fill: #bdc3bb;font-style: italic; }
- .terminal-3405842733-r11 { fill: #2aa198 }
+ .terminal-1634757789-r1 { fill: #ff0000 }
+ .terminal-1634757789-r2 { fill: #e1e1e1 }
+ .terminal-1634757789-r3 { fill: #c5c8c6 }
+ .terminal-1634757789-r4 { fill: #e1e1e1;text-decoration: underline; }
+ .terminal-1634757789-r5 { fill: #e1e1e1;font-style: italic; }
+ .terminal-1634757789-r6 { fill: #e1e1e1;font-weight: bold }
+ .terminal-1634757789-r7 { fill: #e1e1e1;text-decoration: line-through; }
+ .terminal-1634757789-r8 { fill: #d2d2d2 }
+ .terminal-1634757789-r9 { fill: #546e7a;font-style: italic; }
+ .terminal-1634757789-r10 { fill: #bb80b3 }
+ .terminal-1634757789-r11 { fill: #eeffff }
+ .terminal-1634757789-r12 { fill: #ffcb6b }
+ .terminal-1634757789-r13 { fill: #89ddff }
+ .terminal-1634757789-r14 { fill: #41565f;font-style: italic; }
+ .terminal-1634757789-r15 { fill: #f78c6c }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MarkdownThemeSwitchertApp
+ MarkdownSpaceApp
-
-
-
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▎ ▊
- ▎ This is a H1 ▊
- ▎ ▊
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
-
-
- def main ( ) :
- │ print ( " Hello world! " )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ │ X X │ X X │ X X X X X X
+ │ │ │
+ │ X X │ X X │ X X X X X X
+ │ │ │
+ │ X X │ │ X X X X X X
+ │ │ │
+ │ X X │ │ X X X X X X
+ │ │ │
+ ┌───────────────────────────────────────────────────────────────────────────────
+ │
+ │
+ │ # Two spaces: see?
+ │ class Foo :
+ │ │ '''This is a doc string.'''
+ │ │ some_code ( 1 , 2 , 3 , 4 )
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
'''
# ---
-# name: test_markdown_space_squashing
+# name: test_markdown_theme_switching
'''
@@ -25299,152 +27864,147 @@
font-weight: 700;
}
- .terminal-2455550894-matrix {
+ .terminal-1160849185-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2455550894-title {
+ .terminal-1160849185-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2455550894-r1 { fill: #ff0000 }
- .terminal-2455550894-r2 { fill: #e1e1e1 }
- .terminal-2455550894-r3 { fill: #c5c8c6 }
- .terminal-2455550894-r4 { fill: #e1e1e1;text-decoration: underline; }
- .terminal-2455550894-r5 { fill: #e1e1e1;font-style: italic; }
- .terminal-2455550894-r6 { fill: #e1e1e1;font-weight: bold }
- .terminal-2455550894-r7 { fill: #e1e1e1;text-decoration: line-through; }
- .terminal-2455550894-r8 { fill: #d2d2d2 }
- .terminal-2455550894-r9 { fill: #546e7a;font-style: italic; }
- .terminal-2455550894-r10 { fill: #bb80b3 }
- .terminal-2455550894-r11 { fill: #eeffff }
- .terminal-2455550894-r12 { fill: #ffcb6b }
- .terminal-2455550894-r13 { fill: #89ddff }
- .terminal-2455550894-r14 { fill: #41565f;font-style: italic; }
- .terminal-2455550894-r15 { fill: #f78c6c }
+ .terminal-1160849185-r1 { fill: #1f1f1f }
+ .terminal-1160849185-r2 { fill: #c5c8c6 }
+ .terminal-1160849185-r3 { fill: #004578;font-weight: bold }
+ .terminal-1160849185-r4 { fill: #d2d2d2 }
+ .terminal-1160849185-r5 { fill: #008000;font-weight: bold }
+ .terminal-1160849185-r6 { fill: #000000 }
+ .terminal-1160849185-r7 { fill: #0000ff }
+ .terminal-1160849185-r8 { fill: #87adad;font-style: italic; }
+ .terminal-1160849185-r9 { fill: #008000 }
+ .terminal-1160849185-r10 { fill: #ba2121 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MarkdownSpaceApp
+ MarkdownThemeSwitchertApp
-
-
-
- │ X X │ X X │ X X X X X X
- │ │ │
- │ X X │ X X │ X X X X X X
- │ │ │
- │ X X │ │ X X X X X X
- │ │ │
- │ X X │ │ X X X X X X
- │ │ │
- ┌ ───────────────────────────────────────────────────────────────────────────────
- │
- │
- │ # Two spaces: see?
- │ class Foo :
- │ │ '''This is a doc string.'''
- │ │ some_code ( 1 , 2 , 3 , 4 )
- │
- │
- │
- │
- │
- │
- │
- │
- │
+
+
+
+
+
+ This is a H1
+
+
+ def main ():
+ │ print ( "Hello world!" )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
'''
# ---
-# name: test_markdown_theme_switching
+# name: test_markdown_viewer_example
'''
@@ -25467,149 +28027,149 @@
font-weight: 700;
}
- .terminal-2184772309-matrix {
+ .terminal-1516567938-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2184772309-title {
+ .terminal-1516567938-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2184772309-r1 { fill: #1f1f1f }
- .terminal-2184772309-r2 { fill: #efefef }
- .terminal-2184772309-r3 { fill: #c5c8c6 }
- .terminal-2184772309-r4 { fill: #0053aa }
- .terminal-2184772309-r5 { fill: #dde8f3;font-weight: bold }
- .terminal-2184772309-r6 { fill: #d2d2d2 }
- .terminal-2184772309-r7 { fill: #008000;font-weight: bold }
- .terminal-2184772309-r8 { fill: #000000 }
- .terminal-2184772309-r9 { fill: #0000ff }
- .terminal-2184772309-r10 { fill: #87adad;font-style: italic; }
- .terminal-2184772309-r11 { fill: #008000 }
- .terminal-2184772309-r12 { fill: #ba2121 }
+ .terminal-1516567938-r1 { fill: #c5c8c6 }
+ .terminal-1516567938-r2 { fill: #24292f }
+ .terminal-1516567938-r3 { fill: #e1e1e1 }
+ .terminal-1516567938-r4 { fill: #e2e3e3 }
+ .terminal-1516567938-r5 { fill: #96989b }
+ .terminal-1516567938-r6 { fill: #008139 }
+ .terminal-1516567938-r7 { fill: #4ebf71;font-weight: bold }
+ .terminal-1516567938-r8 { fill: #939393;font-weight: bold }
+ .terminal-1516567938-r9 { fill: #4ebf71;text-decoration: underline; }
+ .terminal-1516567938-r10 { fill: #14191f }
+ .terminal-1516567938-r11 { fill: #e1e1e1;font-style: italic; }
+ .terminal-1516567938-r12 { fill: #e1e1e1;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MarkdownThemeSwitchertApp
+ MarkdownExampleApp
-
-
-
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▎ ▊
- ▎ This is a H1 ▊
- ▎ ▊
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
-
-
- def main ( ) :
- │ print ( " Hello world! " )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ▊
+ ▼ Ⅰ Markdown Viewer ▊
+ ├── Ⅱ Features ▊ Markdown Viewer
+ ├── Ⅱ Tables ▊
+ └── Ⅱ Code Blocks ▊ This is an example of Textual's MarkdownViewer
+ ▊ widget.
+ ▊
+ ▊
+ ▊ Features
+ ▊
+ ▊ Markdown syntax and extensions are supported.
+ ▊ ▇▇
+ ▊ ● Typography emphasis , strong , inline code etc.
+ ▊ ● Headers
+ ▊ ● Lists (bullet and ordered)
+ ▊ ● Syntax highlighted code blocks
+ ▊ ● Tables!
+ ▊
+ ▊
+ ▊ Tables
+ ▊
+ ▊ Tables are displayed in a DataTable widget.
+ ▊
+ ▊
'''
# ---
-# name: test_markdown_viewer_example
+# name: test_max_height_100
'''
@@ -25632,152 +28192,142 @@
font-weight: 700;
}
- .terminal-1185109701-matrix {
+ .terminal-2780811796-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1185109701-title {
+ .terminal-2780811796-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1185109701-r1 { fill: #c5c8c6 }
- .terminal-1185109701-r2 { fill: #24292f }
- .terminal-1185109701-r3 { fill: #e1e1e1 }
- .terminal-1185109701-r4 { fill: #121212 }
- .terminal-1185109701-r5 { fill: #e2e3e3 }
- .terminal-1185109701-r6 { fill: #96989b }
- .terminal-1185109701-r7 { fill: #0053aa }
- .terminal-1185109701-r8 { fill: #008139 }
- .terminal-1185109701-r9 { fill: #dde8f3;font-weight: bold }
- .terminal-1185109701-r10 { fill: #939393;font-weight: bold }
- .terminal-1185109701-r11 { fill: #14191f }
- .terminal-1185109701-r12 { fill: #e2e3e3;font-weight: bold }
- .terminal-1185109701-r13 { fill: #4ebf71;font-weight: bold }
- .terminal-1185109701-r14 { fill: #e1e1e1;font-style: italic; }
- .terminal-1185109701-r15 { fill: #e1e1e1;font-weight: bold }
+ .terminal-2780811796-r1 { fill: #dde6ed;font-weight: bold }
+ .terminal-2780811796-r2 { fill: #e1e1e1 }
+ .terminal-2780811796-r3 { fill: #c5c8c6 }
+ .terminal-2780811796-r4 { fill: #211505 }
+ .terminal-2780811796-r5 { fill: #14191f }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MarkdownExampleApp
+ HappyDataTableFunApp
-
-
-
- ▊ ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▼ Ⅰ Markdown Viewer ▊ ▎ ▊
- ├── Ⅱ Features ▊ ▎ Markdown Viewer ▊
- ├── Ⅱ Tables ▊ ▎ ▊
- └── Ⅱ Code Blocks ▊ ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
- ▊ This is an example of Textual's MarkdownViewer
- ▊ widget.
- ▊
- ▊ ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁ ▅▅
- ▊ ▎ ▊
- ▊ ▎ Features ▊
- ▊ ▎ ▊
- ▊ ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
- ▊ Markdown syntax and extensions are supported.
- ▊
- ▊ ● Typography emphasis , strong , inline code
- ▊ etc.
- ▊ ● Headers
- ▊ ● Lists (bullet and ordered)
- ▊ ● Syntax highlighted code blocks
- ▊ ● Tables!
- ▊
- ▊ ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▊ ▎ ▊
+
+
+
+ Column 0 Column 1 Column 2 Column 3 Column 4 Column 5 Column 6 Column
+ 0 0 0 0 0 0 0 0
+ 0 1 2 3 4 5 6 7
+ 0 2 4 6 8 10 12 14
+ 0 3 6 9 12 15 18 21
+ 0 4 8 12 16 20 24 28 ▆▆
+ 0 5 10 15 20 25 30 35
+ 0 6 12 18 24 30 36 42
+ 0 7 14 21 28 35 42 49
+ 0 8 16 24 32 40 48 56
+ 0 9 18 27 36 45 54 63
+ 0 10 20 30 40 50 60 70
+ 0 11 22 33 44 55 66 77
+ 0 12 24 36 48 60 72 84
+ 0 13 26 39 52 65 78 91
+ 0 14 28 42 56 70 84 98
+ 0 15 30 45 60 75 90 105
+ 0 16 32 48 64 80 96 112
+ 0 17 34 51 68 85 102 119
+ 0 18 36 54 72 90 108 126
+ 0 19 38 57 76 95 114 133
+ 0 20 40 60 80 100 120 140
+ 0 21 42 63 84 105 126 147
+
'''
# ---
-# name: test_max_height_100
+# name: test_missing_vertical_scroll
'''
@@ -25800,142 +28350,144 @@
font-weight: 700;
}
- .terminal-3027843796-matrix {
+ .terminal-3836203720-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3027843796-title {
+ .terminal-3836203720-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3027843796-r1 { fill: #dde6ed;font-weight: bold }
- .terminal-3027843796-r2 { fill: #e1e1e1 }
- .terminal-3027843796-r3 { fill: #c5c8c6 }
- .terminal-3027843796-r4 { fill: #211505 }
- .terminal-3027843796-r5 { fill: #14191f }
+ .terminal-3836203720-r1 { fill: #1e1e1e }
+ .terminal-3836203720-r2 { fill: #0178d4 }
+ .terminal-3836203720-r3 { fill: #c5c8c6 }
+ .terminal-3836203720-r4 { fill: #ddedf9;font-weight: bold }
+ .terminal-3836203720-r5 { fill: #e2e2e2 }
+ .terminal-3836203720-r6 { fill: #e2e2e2;font-weight: bold }
+ .terminal-3836203720-r7 { fill: #14191f }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- HappyDataTableFunApp
+ MissingScrollbarApp
-
-
-
- Column 0 Column 1 Column 2 Column 3 Column 4 Column 5 Column 6 Column
- 0 0 0 0 0 0 0 0
- 0 1 2 3 4 5 6 7
- 0 2 4 6 8 10 12 14
- 0 3 6 9 12 15 18 21
- 0 4 8 12 16 20 24 28 ▆▆
- 0 5 10 15 20 25 30 35
- 0 6 12 18 24 30 36 42
- 0 7 14 21 28 35 42 49
- 0 8 16 24 32 40 48 56
- 0 9 18 27 36 45 54 63
- 0 10 20 30 40 50 60 70
- 0 11 22 33 44 55 66 77
- 0 12 24 36 48 60 72 84
- 0 13 26 39 52 65 78 91
- 0 14 28 42 56 70 84 98
- 0 15 30 45 60 75 90 105
- 0 16 32 48 64 80 96 112
- 0 17 34 51 68 85 102 119
- 0 18 36 54 72 90 108 126
- 0 19 38 57 76 95 114 133
- 0 20 40 60 80 100 120 140
- 0 21 42 63 84 105 126 147
-
+
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ 0 ▎ ▊ 0 ▎▊ 0 ▎
+ ▊ 1 ▎ ▊ 1 ▎▊ 1 ▎
+ ▊ 2 ▄▄ ▎ ▊ 2 ▄▄ ▎▊ 2 ▄▄ ▎
+ ▊ 3 ▎ ▊ 3 ▎▊ 3 ▎
+ ▊ 4 ▎ ▊ 4 ▎▊ 4 ▎
+ ▊ 5 ▎ ▊ 5 ▎▊ 5 ▎
+ ▊ 6 ▎ ▊ 6 ▎▊ 6 ▎
+ ▊ 7 ▎ ▊ 7 ▎▊ 7 ▎
+ ▊ 8 ▎ ▊ 8 ▎▊ 8 ▎
+ ▊ 9 ▎ ▊ 9 ▎▊ 9 ▎
+ ▊ 10 ▎ ▊ 10 ▎▊ 10 ▎
+ ▊ 11 ▎ ▊ 11 ▎▊ 11 ▎
+ ▊ 12 ▎ ▊ 12 ▎▊ 12 ▎
+ ▊ 13 ▎ ▊ 13 ▎▊ 13 ▎
+ ▊ 14 ▎ ▊ 14 ▎▊ 14 ▎
+ ▊ 15 ▎ ▊ 15 ▎▊ 15 ▎
+ ▊ 16 ▎ ▊ 16 ▎▊ 16 ▎
+ ▊ 17 ▎ ▊ 17 ▎▊ 17 ▎
+ ▊ 18 ▎ ▊ 18 ▎▊ 18 ▎
+ ▊ 19 ▎ ▊ 19 ▎▊ 19 ▎
+ ▊ 20 ▎ ▊ 20 ▎▊ 20 ▎
+ ▊ 21 ▎ ▊ 21 ▎▊ 21 ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
'''
# ---
-# name: test_missing_vertical_scroll
+# name: test_modal_dialog_bindings
'''
@@ -25958,144 +28510,143 @@
font-weight: 700;
}
- .terminal-3017029652-matrix {
+ .terminal-878467025-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3017029652-title {
+ .terminal-878467025-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3017029652-r1 { fill: #1e1e1e }
- .terminal-3017029652-r2 { fill: #0178d4 }
- .terminal-3017029652-r3 { fill: #c5c8c6 }
- .terminal-3017029652-r4 { fill: #ddedf9;font-weight: bold }
- .terminal-3017029652-r5 { fill: #e2e2e2 }
- .terminal-3017029652-r6 { fill: #e2e2e2;font-weight: bold }
- .terminal-3017029652-r7 { fill: #14191f }
+ .terminal-878467025-r1 { fill: #c5c8c6 }
+ .terminal-878467025-r2 { fill: #e3e3e3 }
+ .terminal-878467025-r3 { fill: #e1e1e1 }
+ .terminal-878467025-r4 { fill: #fea62b;font-weight: bold }
+ .terminal-878467025-r5 { fill: #a7a9ab }
+ .terminal-878467025-r6 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MissingScrollbarApp
+ ModalApp
-
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ 0 ▎ ▊ 0 ▎ ▊ 0 ▎
- ▊ 1 ▎ ▊ 1 ▎ ▊ 1 ▎
- ▊ 2 ▄▄ ▎ ▊ 2 ▄▄ ▎ ▊ 2 ▄▄ ▎
- ▊ 3 ▎ ▊ 3 ▎ ▊ 3 ▎
- ▊ 4 ▎ ▊ 4 ▎ ▊ 4 ▎
- ▊ 5 ▎ ▊ 5 ▎ ▊ 5 ▎
- ▊ 6 ▎ ▊ 6 ▎ ▊ 6 ▎
- ▊ 7 ▎ ▊ 7 ▎ ▊ 7 ▎
- ▊ 8 ▎ ▊ 8 ▎ ▊ 8 ▎
- ▊ 9 ▎ ▊ 9 ▎ ▊ 9 ▎
- ▊ 10 ▎ ▊ 10 ▎ ▊ 10 ▎
- ▊ 11 ▎ ▊ 11 ▎ ▊ 11 ▎
- ▊ 12 ▎ ▊ 12 ▎ ▊ 12 ▎
- ▊ 13 ▎ ▊ 13 ▎ ▊ 13 ▎
- ▊ 14 ▎ ▊ 14 ▎ ▊ 14 ▎
- ▊ 15 ▎ ▊ 15 ▎ ▊ 15 ▎
- ▊ 16 ▎ ▊ 16 ▎ ▊ 16 ▎
- ▊ 17 ▎ ▊ 17 ▎ ▊ 17 ▎
- ▊ 18 ▎ ▊ 18 ▎ ▊ 18 ▎
- ▊ 19 ▎ ▊ 19 ▎ ▊ 19 ▎
- ▊ 20 ▎ ▊ 20 ▎ ▊ 20 ▎
- ▊ 21 ▎ ▊ 21 ▎ ▊ 21 ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+ ⭘ ModalApp
+ Hello
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ⏎ Open Dialog
'''
# ---
-# name: test_modal_dialog_bindings
+# name: test_modal_dialog_bindings_input
'''
@@ -26118,142 +28669,148 @@
font-weight: 700;
}
- .terminal-543315859-matrix {
+ .terminal-2859158718-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-543315859-title {
+ .terminal-2859158718-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-2859158718-r1 { fill: #e0e0e0 }
+ .terminal-2859158718-r2 { fill: #656565 }
+ .terminal-2859158718-r3 { fill: #c5c8c6 }
+ .terminal-2859158718-r4 { fill: #121212 }
+ .terminal-2859158718-r5 { fill: #e1e1e1 }
+ .terminal-2859158718-r6 { fill: #454a50 }
+ .terminal-2859158718-r7 { fill: #646464 }
+ .terminal-2859158718-r8 { fill: #24292f;font-weight: bold }
+ .terminal-2859158718-r9 { fill: #000000 }
+ .terminal-2859158718-r10 { fill: #704d1c;font-weight: bold }
+ .terminal-2859158718-r11 { fill: #4d4e4f }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ModalApp
+ ModalApp
-
-
-
- ⭘ ModalApp
- Hello
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ⏎ Open Dialog
+
+
+
+ Dialog ModalApp
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ hi! ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ OK
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ⏎ Open Dialog
'''
# ---
-# name: test_modal_dialog_bindings_input
+# name: test_mount_style_fix
'''
@@ -26276,148 +28833,141 @@
font-weight: 700;
}
- .terminal-2766044148-matrix {
+ .terminal-2240675037-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2766044148-title {
+ .terminal-2240675037-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2766044148-r1 { fill: #e0e0e0 }
- .terminal-2766044148-r2 { fill: #656565 }
- .terminal-2766044148-r3 { fill: #c5c8c6 }
- .terminal-2766044148-r4 { fill: #121212 }
- .terminal-2766044148-r5 { fill: #e1e1e1 }
- .terminal-2766044148-r6 { fill: #454a50 }
- .terminal-2766044148-r7 { fill: #646464 }
- .terminal-2766044148-r8 { fill: #24292f;font-weight: bold }
- .terminal-2766044148-r9 { fill: #000000 }
- .terminal-2766044148-r10 { fill: #63676c;font-weight: bold }
- .terminal-2766044148-r11 { fill: #63696e }
+ .terminal-2240675037-r1 { fill: #e1e1e1 }
+ .terminal-2240675037-r2 { fill: #c5c8c6 }
+ .terminal-2240675037-r3 { fill: #00ff00 }
+ .terminal-2240675037-r4 { fill: #ffdddd }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ModalApp
+ BrokenClassesApp
-
-
-
- Dialog ModalApp
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ hi! ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- OK
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ⏎ Open Dialog
+
+
+
+
+
+
+
+
+
+ ┌──────────────────────────────────────┐
+ │ This should have a red background │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ └──────────────────────────────────────┘
+
+
+
+
+
+
'''
# ---
-# name: test_mount_style_fix
+# name: test_multi_keys
'''
@@ -26440,134 +28990,135 @@
font-weight: 700;
}
- .terminal-1136068071-matrix {
+ .terminal-3226482548-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1136068071-title {
+ .terminal-3226482548-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1136068071-r1 { fill: #e1e1e1 }
- .terminal-1136068071-r2 { fill: #c5c8c6 }
- .terminal-1136068071-r3 { fill: #00ff00 }
- .terminal-1136068071-r4 { fill: #ffdddd }
+ .terminal-3226482548-r1 { fill: #e1e1e1 }
+ .terminal-3226482548-r2 { fill: #c5c8c6 }
+ .terminal-3226482548-r3 { fill: #fea62b;font-weight: bold }
+ .terminal-3226482548-r4 { fill: #a7a9ab }
+ .terminal-3226482548-r5 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- BrokenClassesApp
+ MApp
-
-
-
-
-
-
-
-
-
- ┌ ────────────────────────────────────── ┐
- │ This should have a red background │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- └ ────────────────────────────────────── ┘
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ o Options
@@ -26754,136 +29305,136 @@
font-weight: 700;
}
- .terminal-3211563364-matrix {
+ .terminal-700215523-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3211563364-title {
+ .terminal-700215523-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3211563364-r1 { fill: #90ee90 }
- .terminal-3211563364-r2 { fill: #c5c8c6 }
- .terminal-3211563364-r3 { fill: #add8e6 }
- .terminal-3211563364-r4 { fill: #ddeedd }
- .terminal-3211563364-r5 { fill: #808080 }
- .terminal-3211563364-r6 { fill: #dddddd }
- .terminal-3211563364-r7 { fill: #ffdddd }
+ .terminal-700215523-r1 { fill: #90ee90 }
+ .terminal-700215523-r2 { fill: #c5c8c6 }
+ .terminal-700215523-r3 { fill: #add8e6 }
+ .terminal-700215523-r4 { fill: #ddeedd }
+ .terminal-700215523-r5 { fill: #808080 }
+ .terminal-700215523-r6 { fill: #dddddd }
+ .terminal-700215523-r7 { fill: #ffdddd }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- NestedAutoApp
+ NestedAutoApp
-
-
-
- ┏ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┓
- ┃ ┏ ━━━━━━━━━━━━━━━ ┓ ┃
- ┃ ┃ ┏ ━━━━━━━━━━━━━ ┓ ┃ ┃
- ┃ ┃ ┃ JUST ONE LINE ┃ ┃ ┃
- ┃ ┃ ┗ ━━━━━━━━━━━━━ ┛ ┃ ┃
- ┃ ┗ ━━━━━━━━━━━━━━━ ┛ ┃
- ┗ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┛
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ ┏━━━━━━━━━━━━━━━┓ ┃
+ ┃ ┃ ┏━━━━━━━━━━━━━┓ ┃ ┃
+ ┃ ┃ ┃ JUST ONE LINE ┃ ┃ ┃
+ ┃ ┃ ┗━━━━━━━━━━━━━┛ ┃ ┃
+ ┃ ┗━━━━━━━━━━━━━━━┛ ┃
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -26914,134 +29465,134 @@
font-weight: 700;
}
- .terminal-3973651935-matrix {
+ .terminal-2204042077-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3973651935-title {
+ .terminal-2204042077-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3973651935-r1 { fill: #ffffff }
- .terminal-3973651935-r2 { fill: #c5c8c6 }
- .terminal-3973651935-r3 { fill: #ffff00 }
- .terminal-3973651935-r4 { fill: #002121 }
+ .terminal-2204042077-r1 { fill: #ffffff }
+ .terminal-2204042077-r2 { fill: #c5c8c6 }
+ .terminal-2204042077-r3 { fill: #ffff00 }
+ .terminal-2204042077-r4 { fill: #002121 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AutoApp
+ AutoApp
-
-
-
- ┌ ────────────────────────────────────────────────────────────────────────────── ┐
- │ ┌ ──────────────────────────────────────────────────────────────────────────── ┐ │
- │ │ Hello │ │
- │ │ World! │ │
- │ │ foo │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ └ ──────────────────────────────────────────────────────────────────────────── ┘ │
- └ ────────────────────────────────────────────────────────────────────────────── ┘
+
+
+
+ ┌──────────────────────────────────────────────────────────────────────────────┐
+ │ ┌────────────────────────────────────────────────────────────────────────────┐ │
+ │ │ Hello │ │
+ │ │ World! │ │
+ │ │ foo │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ └────────────────────────────────────────────────────────────────────────────┘ │
+ └──────────────────────────────────────────────────────────────────────────────┘
@@ -27071,135 +29622,135 @@
font-weight: 700;
}
- .terminal-2955864965-matrix {
+ .terminal-788257423-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2955864965-title {
+ .terminal-788257423-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2955864965-r1 { fill: #00ff00 }
- .terminal-2955864965-r2 { fill: #008000 }
- .terminal-2955864965-r3 { fill: #c5c8c6 }
- .terminal-2955864965-r4 { fill: #e4e1e1 }
- .terminal-2955864965-r5 { fill: #e0e4e0 }
+ .terminal-788257423-r1 { fill: #00ff00 }
+ .terminal-788257423-r2 { fill: #008000 }
+ .terminal-788257423-r3 { fill: #c5c8c6 }
+ .terminal-788257423-r4 { fill: #e4e1e1 }
+ .terminal-788257423-r5 { fill: #e0e4e0 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- NestedPseudoClassesApp
+ NestedPseudoClassesApp
-
-
-
- ╭ ────────────────────────────────────── ╮
- This isn't using nested CSS │ This is using nested CSS │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- │ │
- ╰ ────────────────────────────────────── ╯
+
+
+
+ ╭──────────────────────────────────────╮
+ This isn't using nested CSS │ This is using nested CSS │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ ╰──────────────────────────────────────╯
@@ -27229,135 +29780,135 @@
font-weight: 700;
}
- .terminal-2734338184-matrix {
+ .terminal-475433983-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2734338184-title {
+ .terminal-475433983-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2734338184-r1 { fill: #e1e1e1 }
- .terminal-2734338184-r2 { fill: #c5c8c6 }
- .terminal-2734338184-r3 { fill: #56c278 }
- .terminal-2734338184-r4 { fill: #1d1d1d }
- .terminal-2734338184-r5 { fill: #e3e4e4 }
- .terminal-2734338184-r6 { fill: #e3e4e4;text-decoration: underline; }
+ .terminal-475433983-r1 { fill: #e1e1e1 }
+ .terminal-475433983-r2 { fill: #c5c8c6 }
+ .terminal-475433983-r3 { fill: #56c278 }
+ .terminal-475433983-r4 { fill: #1d1d1d }
+ .terminal-475433983-r5 { fill: #e3e4e4 }
+ .terminal-475433983-r6 { fill: #e3e4e4;text-decoration: underline; }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- NotifyWithInlineLinkApp
+ NotifyWithInlineLinkApp
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ▌ ▐
- ▌ Click here for the bell sound. ▐
- ▌ ▐
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ▌ ▐
+ ▌ Click here for the bell sound. ▐
+ ▌ ▐
@@ -27388,135 +29939,135 @@
font-weight: 700;
}
- .terminal-500610332-matrix {
+ .terminal-2367525011-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-500610332-title {
+ .terminal-2367525011-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-500610332-r1 { fill: #e1e1e1 }
- .terminal-500610332-r2 { fill: #c5c8c6 }
- .terminal-500610332-r3 { fill: #56c278 }
- .terminal-500610332-r4 { fill: #1d1d1d }
- .terminal-500610332-r5 { fill: #e3e4e4 }
- .terminal-500610332-r6 { fill: #ddedf9;font-weight: bold }
+ .terminal-2367525011-r1 { fill: #e1e1e1 }
+ .terminal-2367525011-r2 { fill: #c5c8c6 }
+ .terminal-2367525011-r3 { fill: #56c278 }
+ .terminal-2367525011-r4 { fill: #1d1d1d }
+ .terminal-2367525011-r5 { fill: #e3e4e4 }
+ .terminal-2367525011-r6 { fill: #ddedf9;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- NotifyWithInlineLinkApp
+ NotifyWithInlineLinkApp
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ▌ ▐
- ▌ Click here for the bell sound. ▐
- ▌ ▐
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ▌ ▐
+ ▌ Click here for the bell sound. ▐
+ ▌ ▐
@@ -27547,139 +30098,139 @@
font-weight: 700;
}
- .terminal-3535066299-matrix {
+ .terminal-3920474212-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3535066299-title {
+ .terminal-3920474212-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3535066299-r1 { fill: #e1e1e1 }
- .terminal-3535066299-r2 { fill: #c5c8c6 }
- .terminal-3535066299-r3 { fill: #56c278 }
- .terminal-3535066299-r4 { fill: #1d1d1d }
- .terminal-3535066299-r5 { fill: #e3e4e4 }
- .terminal-3535066299-r6 { fill: #feaa35 }
- .terminal-3535066299-r7 { fill: #e89719;font-weight: bold }
- .terminal-3535066299-r8 { fill: #e3e4e4;font-weight: bold }
- .terminal-3535066299-r9 { fill: #e3e4e4;font-weight: bold;font-style: italic; }
- .terminal-3535066299-r10 { fill: #bc4563 }
+ .terminal-3920474212-r1 { fill: #e1e1e1 }
+ .terminal-3920474212-r2 { fill: #c5c8c6 }
+ .terminal-3920474212-r3 { fill: #56c278 }
+ .terminal-3920474212-r4 { fill: #1d1d1d }
+ .terminal-3920474212-r5 { fill: #e3e4e4 }
+ .terminal-3920474212-r6 { fill: #feaa35 }
+ .terminal-3920474212-r7 { fill: #e89719;font-weight: bold }
+ .terminal-3920474212-r8 { fill: #e3e4e4;font-weight: bold }
+ .terminal-3920474212-r9 { fill: #e3e4e4;font-weight: bold;font-style: italic; }
+ .terminal-3920474212-r10 { fill: #bc4563 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ToastApp
+ ToastApp
-
-
-
-
-
-
- ▌ ▐
- ▌ It's an older code, sir, but it ▐
- ▌ checks out. ▐
- ▌ ▐
-
- ▌ ▐
- ▌ Possible trap detected ▐
- ▌ Now witness the firepower of this ▐
- ▌ fully ARMED and OPERATIONAL battle ▐
- ▌ station! ▐
- ▌ ▐
-
- ▌ ▐
- ▌ It's a trap! ▐
- ▌ ▐
-
- ▌ ▐
- ▌ It's against my programming to ▐
- ▌ impersonate a deity. ▐
- ▌ ▐
+
+
+
+
+
+
+ ▌ ▐
+ ▌ It's an older code, sir, but it ▐
+ ▌ checks out. ▐
+ ▌ ▐
+
+ ▌ ▐
+ ▌ Possible trap detected ▐
+ ▌ Now witness the firepower of this ▐
+ ▌ fully ARMED and OPERATIONAL battle ▐
+ ▌ station! ▐
+ ▌ ▐
+
+ ▌ ▐
+ ▌ It's a trap! ▐
+ ▌ ▐
+
+ ▌ ▐
+ ▌ It's against my programming to ▐
+ ▌ impersonate a deity. ▐
+ ▌ ▐
@@ -27710,117 +30261,117 @@
font-weight: 700;
}
- .terminal-4012933681-matrix {
+ .terminal-2138961308-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-4012933681-title {
+ .terminal-2138961308-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-4012933681-r1 { fill: #c5c8c6 }
- .terminal-4012933681-r2 { fill: #56c278 }
- .terminal-4012933681-r3 { fill: #1d1d1d }
- .terminal-4012933681-r4 { fill: #e3e4e4 }
+ .terminal-2138961308-r1 { fill: #c5c8c6 }
+ .terminal-2138961308-r2 { fill: #56c278 }
+ .terminal-2138961308-r3 { fill: #1d1d1d }
+ .terminal-2138961308-r4 { fill: #e3e4e4 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- LoadingOverlayApp
+ LoadingOverlayApp
-
-
-
-
-
-
-
-
-
- ▌ ▐
- ▌ This is a big notification. ▐
- ▌ This is a big notification. ▐
- ▌ This is a big notification. ▐
- ▌ This is a big notification. ▐
- ▌ This is a big notification. ▐
- ▌ This is a big notification. ▐
- ▌ This is a big notification. ▐
- ▌ This is a big notification. ▐
- ▌ This is a big notification. ▐
- ▌ This is a big notification. ▐
- ▌ ▐
- ▌ ▐
+
+
+
+
+
+
+
+
+
+ ▌ ▐
+ ▌ This is a big notification. ▐
+ ▌ This is a big notification. ▐
+ ▌ This is a big notification. ▐
+ ▌ This is a big notification. ▐
+ ▌ This is a big notification. ▐
+ ▌ This is a big notification. ▐
+ ▌ This is a big notification. ▐
+ ▌ This is a big notification. ▐
+ ▌ This is a big notification. ▐
+ ▌ This is a big notification. ▐
+ ▌ ▐
+ ▌ ▐
@@ -27851,134 +30402,134 @@
font-weight: 700;
}
- .terminal-1373062254-matrix {
+ .terminal-3370984838-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1373062254-title {
+ .terminal-3370984838-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1373062254-r1 { fill: #e1e1e1 }
- .terminal-1373062254-r2 { fill: #56c278 }
- .terminal-1373062254-r3 { fill: #c5c8c6 }
- .terminal-1373062254-r4 { fill: #1d1d1d }
- .terminal-1373062254-r5 { fill: #e3e4e4 }
+ .terminal-3370984838-r1 { fill: #e1e1e1 }
+ .terminal-3370984838-r2 { fill: #56c278 }
+ .terminal-3370984838-r3 { fill: #c5c8c6 }
+ .terminal-3370984838-r4 { fill: #1d1d1d }
+ .terminal-3370984838-r5 { fill: #e3e4e4 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- NotifyThroughModesApp
+ NotifyThroughModesApp
-
-
-
- This is a mode screen ▌ ▐
- ▌ 4 ▐
- ▌ ▐
-
- ▌ ▐
- ▌ 5 ▐
- ▌ ▐
-
- ▌ ▐
- ▌ 6 ▐
- ▌ ▐
-
- ▌ ▐
- ▌ 7 ▐
- ▌ ▐
-
- ▌ ▐
- ▌ 8 ▐
- ▌ ▐
-
- ▌ ▐
- ▌ 9 ▐
- ▌ ▐
+
+
+
+ This is a mode screen ▌ ▐
+ ▌ 4 ▐
+ ▌ ▐
+
+ ▌ ▐
+ ▌ 5 ▐
+ ▌ ▐
+
+ ▌ ▐
+ ▌ 6 ▐
+ ▌ ▐
+
+ ▌ ▐
+ ▌ 7 ▐
+ ▌ ▐
+
+ ▌ ▐
+ ▌ 8 ▐
+ ▌ ▐
+
+ ▌ ▐
+ ▌ 9 ▐
+ ▌ ▐
@@ -28009,134 +30560,134 @@
font-weight: 700;
}
- .terminal-3060613351-matrix {
+ .terminal-843325951-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3060613351-title {
+ .terminal-843325951-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3060613351-r1 { fill: #e1e1e1 }
- .terminal-3060613351-r2 { fill: #56c278 }
- .terminal-3060613351-r3 { fill: #c5c8c6 }
- .terminal-3060613351-r4 { fill: #1d1d1d }
- .terminal-3060613351-r5 { fill: #e3e4e4 }
+ .terminal-843325951-r1 { fill: #e1e1e1 }
+ .terminal-843325951-r2 { fill: #56c278 }
+ .terminal-843325951-r3 { fill: #c5c8c6 }
+ .terminal-843325951-r4 { fill: #1d1d1d }
+ .terminal-843325951-r5 { fill: #e3e4e4 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- NotifyDownScreensApp
+ NotifyDownScreensApp
-
-
-
- Screen 10 ▌ ▐
- ▌ 4 ▐
- ▌ ▐
-
- ▌ ▐
- ▌ 5 ▐
- ▌ ▐
-
- ▌ ▐
- ▌ 6 ▐
- ▌ ▐
-
- ▌ ▐
- ▌ 7 ▐
- ▌ ▐
-
- ▌ ▐
- ▌ 8 ▐
- ▌ ▐
-
- ▌ ▐
- ▌ 9 ▐
- ▌ ▐
+
+
+
+ Screen 10 ▌ ▐
+ ▌ 4 ▐
+ ▌ ▐
+
+ ▌ ▐
+ ▌ 5 ▐
+ ▌ ▐
+
+ ▌ ▐
+ ▌ 6 ▐
+ ▌ ▐
+
+ ▌ ▐
+ ▌ 7 ▐
+ ▌ ▐
+
+ ▌ ▐
+ ▌ 8 ▐
+ ▌ ▐
+
+ ▌ ▐
+ ▌ 9 ▐
+ ▌ ▐
@@ -28167,133 +30718,133 @@
font-weight: 700;
}
- .terminal-4150241775-matrix {
+ .terminal-1996000257-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-4150241775-title {
+ .terminal-1996000257-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-4150241775-r1 { fill: #e1e1e1 }
- .terminal-4150241775-r2 { fill: #c5c8c6 }
- .terminal-4150241775-r3 { fill: #ffffff }
- .terminal-4150241775-r4 { fill: #ddddef }
+ .terminal-1996000257-r1 { fill: #e1e1e1 }
+ .terminal-1996000257-r2 { fill: #c5c8c6 }
+ .terminal-1996000257-r3 { fill: #ffffff }
+ .terminal-1996000257-r4 { fill: #ddddef }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- OffsetsApp
+ OffsetsApp
-
-
-
-
-
-
-
-
- ┌ ────────────── ┐
- │ FOO │
- │ BAR │
- │ BAZ │
- └ ────────────── ┘
-
-
-
-
-
- ┌ ────────────── ┐
- │ FOO │
- │ BAR │
- │ BAZ │
- └ ────────────── ┘
-
-
-
+
+
+
+
+
+
+
+
+ ┌──────────────┐
+ │ FOO │
+ │ BAR │
+ │ BAZ │
+ └──────────────┘
+
+
+
+
+
+ ┌──────────────┐
+ │ FOO │
+ │ BAR │
+ │ BAZ │
+ └──────────────┘
+
+
+
@@ -28324,138 +30875,138 @@
font-weight: 700;
}
- .terminal-43804987-matrix {
+ .terminal-3880461415-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-43804987-title {
+ .terminal-3880461415-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-43804987-r1 { fill: #1e1e1e }
- .terminal-43804987-r2 { fill: #0178d4 }
- .terminal-43804987-r3 { fill: #c5c8c6 }
- .terminal-43804987-r4 { fill: #ddedf9;font-weight: bold }
- .terminal-43804987-r5 { fill: #e2e2e2;font-weight: bold }
- .terminal-43804987-r6 { fill: #e2e2e2 }
- .terminal-43804987-r7 { fill: #434343 }
- .terminal-43804987-r8 { fill: #f4005f }
- .terminal-43804987-r9 { fill: #e1e1e1 }
+ .terminal-3880461415-r1 { fill: #1e1e1e }
+ .terminal-3880461415-r2 { fill: #0178d4 }
+ .terminal-3880461415-r3 { fill: #c5c8c6 }
+ .terminal-3880461415-r4 { fill: #ddedf9;font-weight: bold }
+ .terminal-3880461415-r5 { fill: #e2e2e2;font-weight: bold }
+ .terminal-3880461415-r6 { fill: #e2e2e2 }
+ .terminal-3880461415-r7 { fill: #434343 }
+ .terminal-3880461415-r8 { fill: #f4005f }
+ .terminal-3880461415-r9 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- OptionListApp
+ OptionListApp
-
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ One ▎ ▊ One ▎ ▊ One ▎
- ▊ Two ▎ ▊ Two ▎ ▊ Two ▎
- ▊ ────────────────────── ▎ ▊ ─────────────────────── ▎ ▊ ─────────────────────── ▎
- ▊ Three ▎ ▊ Three ▎ ▊ Three ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ One ▎ ▊ One ▎▊ One ▎
+ ▊ Two ▎ ▊ Two ▎▊ Two ▎
+ ▊ ────────────────────── ▎ ▊ ─────────────────────── ▎▊ ─────────────────────── ▎
+ ▊ Three ▎ ▊ Three ▎▊ Three ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -28486,140 +31037,140 @@
font-weight: 700;
}
- .terminal-2682133191-matrix {
+ .terminal-3287598191-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2682133191-title {
+ .terminal-3287598191-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2682133191-r1 { fill: #c5c8c6 }
- .terminal-2682133191-r2 { fill: #e3e3e3 }
- .terminal-2682133191-r3 { fill: #e1e1e1 }
- .terminal-2682133191-r4 { fill: #1e1e1e }
- .terminal-2682133191-r5 { fill: #0178d4 }
- .terminal-2682133191-r6 { fill: #ddedf9;font-weight: bold }
- .terminal-2682133191-r7 { fill: #e2e2e2 }
- .terminal-2682133191-r8 { fill: #434343 }
- .terminal-2682133191-r9 { fill: #787878 }
- .terminal-2682133191-r10 { fill: #14191f }
- .terminal-2682133191-r11 { fill: #ddedf9 }
+ .terminal-3287598191-r1 { fill: #c5c8c6 }
+ .terminal-3287598191-r2 { fill: #e3e3e3 }
+ .terminal-3287598191-r3 { fill: #e1e1e1 }
+ .terminal-3287598191-r4 { fill: #1e1e1e }
+ .terminal-3287598191-r5 { fill: #0178d4 }
+ .terminal-3287598191-r6 { fill: #ddedf9;font-weight: bold }
+ .terminal-3287598191-r7 { fill: #e2e2e2 }
+ .terminal-3287598191-r8 { fill: #434343 }
+ .terminal-3287598191-r9 { fill: #787878 }
+ .terminal-3287598191-r10 { fill: #14191f }
+ .terminal-3287598191-r11 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- OptionListApp
+ OptionListApp
-
-
-
- ⭘ OptionListApp
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ Aerilon ▎
- ▊ Aquaria ▎
- ▊ ────────────────────────────────────────────────── ▎
- ▊ Canceron ▎
- ▊ Caprica ▎
- ▊ ────────────────────────────────────────────────── ▎
- ▊ Gemenon ▎
- ▊ ────────────────────────────────────────────────── ▎
- ▊ Leonis ▎
- ▊ Libran ▎
- ▊ ────────────────────────────────────────────────── ▎
- ▊ Picon ▁▁ ▎
- ▊ ────────────────────────────────────────────────── ▎
- ▊ Sagittaron ▎
- ▊ Scorpia ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
+
+
+
+ ⭘ OptionListApp
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ Aerilon ▎
+ ▊ Aquaria ▎
+ ▊ ────────────────────────────────────────────────── ▎
+ ▊ Canceron ▎
+ ▊ Caprica ▎
+ ▊ ────────────────────────────────────────────────── ▎
+ ▊ Gemenon ▎
+ ▊ ────────────────────────────────────────────────── ▎
+ ▊ Leonis ▎
+ ▊ Libran ▎
+ ▊ ────────────────────────────────────────────────── ▎
+ ▊ Picon ▁▁ ▎
+ ▊ ────────────────────────────────────────────────── ▎
+ ▊ Sagittaron ▎
+ ▊ Scorpia ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
@@ -28650,137 +31201,137 @@
font-weight: 700;
}
- .terminal-1891202557-matrix {
+ .terminal-4064027832-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1891202557-title {
+ .terminal-4064027832-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1891202557-r1 { fill: #c5c8c6 }
- .terminal-1891202557-r2 { fill: #e3e3e3 }
- .terminal-1891202557-r3 { fill: #1e1e1e }
- .terminal-1891202557-r4 { fill: #0178d4 }
- .terminal-1891202557-r5 { fill: #ddedf9;font-weight: bold }
- .terminal-1891202557-r6 { fill: #e2e2e2 }
- .terminal-1891202557-r7 { fill: #e1e1e1 }
- .terminal-1891202557-r8 { fill: #ddedf9 }
+ .terminal-4064027832-r1 { fill: #c5c8c6 }
+ .terminal-4064027832-r2 { fill: #e3e3e3 }
+ .terminal-4064027832-r3 { fill: #1e1e1e }
+ .terminal-4064027832-r4 { fill: #0178d4 }
+ .terminal-4064027832-r5 { fill: #ddedf9;font-weight: bold }
+ .terminal-4064027832-r6 { fill: #e2e2e2 }
+ .terminal-4064027832-r7 { fill: #e1e1e1 }
+ .terminal-4064027832-r8 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- OptionListApp
+ OptionListApp
-
-
-
- ⭘ OptionListApp
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ 1. Another single line ▎
- ▊ 2. Two ▎
- ▊ lines ▎
- ▊ 3. Three ▎
- ▊ lines ▎
- ▊ of text ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ⭘ OptionListApp
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ 1. Another single line ▎
+ ▊ 2. Two ▎
+ ▊ lines ▎
+ ▊ 3. Three ▎
+ ▊ lines ▎
+ ▊ of text ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -28811,137 +31362,137 @@
font-weight: 700;
}
- .terminal-2188746417-matrix {
+ .terminal-3844865806-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2188746417-title {
+ .terminal-3844865806-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2188746417-r1 { fill: #c5c8c6 }
- .terminal-2188746417-r2 { fill: #e3e3e3 }
- .terminal-2188746417-r3 { fill: #1e1e1e }
- .terminal-2188746417-r4 { fill: #0178d4 }
- .terminal-2188746417-r5 { fill: #ddedf9;font-weight: bold }
- .terminal-2188746417-r6 { fill: #e2e2e2 }
- .terminal-2188746417-r7 { fill: #e1e1e1 }
- .terminal-2188746417-r8 { fill: #ddedf9 }
+ .terminal-3844865806-r1 { fill: #c5c8c6 }
+ .terminal-3844865806-r2 { fill: #e3e3e3 }
+ .terminal-3844865806-r3 { fill: #1e1e1e }
+ .terminal-3844865806-r4 { fill: #0178d4 }
+ .terminal-3844865806-r5 { fill: #ddedf9;font-weight: bold }
+ .terminal-3844865806-r6 { fill: #e2e2e2 }
+ .terminal-3844865806-r7 { fill: #e1e1e1 }
+ .terminal-3844865806-r8 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- OptionListApp
+ OptionListApp
-
-
-
- ⭘ OptionListApp
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ 1. Two ▎
- ▊ lines ▎
- ▊ 2. Two ▎
- ▊ lines ▎
- ▊ 3. Three ▎
- ▊ lines ▎
- ▊ of text ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ⭘ OptionListApp
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ 1. Two ▎
+ ▊ lines ▎
+ ▊ 2. Two ▎
+ ▊ lines ▎
+ ▊ 3. Three ▎
+ ▊ lines ▎
+ ▊ of text ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -28972,137 +31523,137 @@
font-weight: 700;
}
- .terminal-2667681921-matrix {
+ .terminal-570883420-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2667681921-title {
+ .terminal-570883420-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2667681921-r1 { fill: #c5c8c6 }
- .terminal-2667681921-r2 { fill: #e3e3e3 }
- .terminal-2667681921-r3 { fill: #1e1e1e }
- .terminal-2667681921-r4 { fill: #0178d4 }
- .terminal-2667681921-r5 { fill: #ddedf9;font-weight: bold }
- .terminal-2667681921-r6 { fill: #e2e2e2 }
- .terminal-2667681921-r7 { fill: #e1e1e1 }
- .terminal-2667681921-r8 { fill: #ddedf9 }
+ .terminal-570883420-r1 { fill: #c5c8c6 }
+ .terminal-570883420-r2 { fill: #e3e3e3 }
+ .terminal-570883420-r3 { fill: #1e1e1e }
+ .terminal-570883420-r4 { fill: #0178d4 }
+ .terminal-570883420-r5 { fill: #ddedf9;font-weight: bold }
+ .terminal-570883420-r6 { fill: #e2e2e2 }
+ .terminal-570883420-r7 { fill: #e1e1e1 }
+ .terminal-570883420-r8 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- OptionListApp
+ OptionListApp
-
-
-
- ⭘ OptionListApp
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ 1. Single line ▎
- ▊ 1. Three ▎
- ▊ lines ▎
- ▊ of text ▎
- ▊ 3. Three ▎
- ▊ lines ▎
- ▊ of text ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ⭘ OptionListApp
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ 1. Single line ▎
+ ▊ 1. Three ▎
+ ▊ lines ▎
+ ▊ of text ▎
+ ▊ 3. Three ▎
+ ▊ lines ▎
+ ▊ of text ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -29133,136 +31684,301 @@
font-weight: 700;
}
- .terminal-1990786949-matrix {
+ .terminal-1141874057-matrix {
+ font-family: Fira Code, monospace;
+ font-size: 20px;
+ line-height: 24.4px;
+ font-variant-east-asian: full-width;
+ }
+
+ .terminal-1141874057-title {
+ font-size: 18px;
+ font-weight: bold;
+ font-family: arial;
+ }
+
+ .terminal-1141874057-r1 { fill: #1e1e1e }
+ .terminal-1141874057-r2 { fill: #0178d4 }
+ .terminal-1141874057-r3 { fill: #c5c8c6 }
+ .terminal-1141874057-r4 { fill: #e2e2e2 }
+ .terminal-1141874057-r5 { fill: #23568b }
+ .terminal-1141874057-r6 { fill: #ddedf9;font-weight: bold }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LongOptionListApp
+
+
+
+
+
+
+
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ This is option #78 ▎
+ ▊ This is option #79 ▎
+ ▊ This is option #80 ▎
+ ▊ This is option #81 ▎
+ ▊ This is option #82 ▎
+ ▊ This is option #83 ▎
+ ▊ This is option #84 ▎
+ ▊ This is option #85 ▎
+ ▊ This is option #86 ▎
+ ▊ This is option #87 ▎
+ ▊ This is option #88 ▎
+ ▊ This is option #89 ▎
+ ▊ This is option #90 ▎
+ ▊ This is option #91 ▎
+ ▊ This is option #92 ▎
+ ▊ This is option #93 ▎
+ ▊ This is option #94 ▎
+ ▊ This is option #95 ▇▇ ▎
+ ▊ This is option #96 ▎
+ ▊ This is option #97 ▎
+ ▊ This is option #98 ▎
+ ▊ This is option #99 ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+ '''
+# ---
+# name: test_option_list_scrolling_with_multiline_options
+ '''
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- LongOptionListApp
+ OptionListApp
-
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ This is option #78 ▎
- ▊ This is option #79 ▎
- ▊ This is option #80 ▎
- ▊ This is option #81 ▎
- ▊ This is option #82 ▎
- ▊ This is option #83 ▎
- ▊ This is option #84 ▎
- ▊ This is option #85 ▎
- ▊ This is option #86 ▎
- ▊ This is option #87 ▎
- ▊ This is option #88 ▎
- ▊ This is option #89 ▎
- ▊ This is option #90 ▎
- ▊ This is option #91 ▎
- ▊ This is option #92 ▎
- ▊ This is option #93 ▎
- ▊ This is option #94 ▎
- ▊ This is option #95 ▇▇ ▎
- ▊ This is option #96 ▎
- ▊ This is option #97 ▎
- ▊ This is option #98 ▎
- ▊ This is option #99 ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+ ⭘ OptionListApp
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ ┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩ ▎
+ ▊ │ Dionysus │ 450 Million │ Celeste │ ▎
+ ▊ └───────────────┴───────────────┴────────────────┘ ▎
+ ▊ Data for Tauron ▎
+ ▊ ┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ ▎
+ ▊ ┃ Patron God ┃ Population ┃ Capital City ┃ ▎
+ ▊ ┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩ ▎
+ ▊ │ Ares │ 2.5 Billion │ Hypatia │ ▎
+ ▊ └───────────────┴───────────────┴────────────────┘ ▎
+ ▊ Data for Virgon ▎
+ ▊ ┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ ▎
+ ▊ ┃ Patron God ┃ Population ┃ Capital City ┃ ▁▁ ▎
+ ▊ ┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩ ▎
+ ▊ │ Hestia │ 4.3 Billion │ Boskirk │ ▎
+ ▊ └───────────────┴───────────────┴────────────────┘ ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
@@ -29292,137 +32008,137 @@
font-weight: 700;
}
- .terminal-534841697-matrix {
+ .terminal-3590136409-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-534841697-title {
+ .terminal-3590136409-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-534841697-r1 { fill: #c5c8c6 }
- .terminal-534841697-r2 { fill: #e3e3e3 }
- .terminal-534841697-r3 { fill: #e1e1e1 }
- .terminal-534841697-r4 { fill: #1e1e1e }
- .terminal-534841697-r5 { fill: #0178d4 }
- .terminal-534841697-r6 { fill: #ddedf9;font-weight: bold }
- .terminal-534841697-r7 { fill: #e2e2e2 }
- .terminal-534841697-r8 { fill: #ddedf9 }
+ .terminal-3590136409-r1 { fill: #c5c8c6 }
+ .terminal-3590136409-r2 { fill: #e3e3e3 }
+ .terminal-3590136409-r3 { fill: #e1e1e1 }
+ .terminal-3590136409-r4 { fill: #1e1e1e }
+ .terminal-3590136409-r5 { fill: #0178d4 }
+ .terminal-3590136409-r6 { fill: #ddedf9;font-weight: bold }
+ .terminal-3590136409-r7 { fill: #e2e2e2 }
+ .terminal-3590136409-r8 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- 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 ▎
+ ▊ ▎
+ ▊ ▎
+ ▊ ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
@@ -29453,141 +32169,141 @@
font-weight: 700;
}
- .terminal-1755547624-matrix {
+ .terminal-4277961929-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1755547624-title {
+ .terminal-4277961929-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1755547624-r1 { fill: #c5c8c6 }
- .terminal-1755547624-r2 { fill: #e3e3e3 }
- .terminal-1755547624-r3 { fill: #e1e1e1 }
- .terminal-1755547624-r4 { fill: #1e1e1e }
- .terminal-1755547624-r5 { fill: #0178d4 }
- .terminal-1755547624-r6 { fill: #ddedf9;font-weight: bold;font-style: italic; }
- .terminal-1755547624-r7 { fill: #e2e2e2 }
- .terminal-1755547624-r8 { fill: #ddedf9;font-weight: bold }
- .terminal-1755547624-r9 { fill: #14191f }
- .terminal-1755547624-r10 { fill: #e2e2e2;font-style: italic; }
- .terminal-1755547624-r11 { fill: #e2e2e2;font-weight: bold }
- .terminal-1755547624-r12 { fill: #ddedf9 }
+ .terminal-4277961929-r1 { fill: #c5c8c6 }
+ .terminal-4277961929-r2 { fill: #e3e3e3 }
+ .terminal-4277961929-r3 { fill: #e1e1e1 }
+ .terminal-4277961929-r4 { fill: #1e1e1e }
+ .terminal-4277961929-r5 { fill: #0178d4 }
+ .terminal-4277961929-r6 { fill: #ddedf9;font-weight: bold;font-style: italic; }
+ .terminal-4277961929-r7 { fill: #e2e2e2 }
+ .terminal-4277961929-r8 { fill: #ddedf9;font-weight: bold }
+ .terminal-4277961929-r9 { fill: #14191f }
+ .terminal-4277961929-r10 { fill: #e2e2e2;font-style: italic; }
+ .terminal-4277961929-r11 { fill: #e2e2e2;font-weight: bold }
+ .terminal-4277961929-r12 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- 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 ▎
- ▊ ┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ ▎
- ▊ ┃ Patron God ┃ Population ┃ Capital City ┃ ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
+
+
+
+ ⭘ 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 ▎
+ ▊ ┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ ▎
+ ▊ ┃ Patron God ┃ Population ┃ Capital City ┃ ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
@@ -29618,136 +32334,137 @@
font-weight: 700;
}
- .terminal-159476969-matrix {
+ .terminal-749580480-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-159476969-title {
+ .terminal-749580480-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-159476969-r1 { fill: #ffff00 }
- .terminal-159476969-r2 { fill: #e3e3e3 }
- .terminal-159476969-r3 { fill: #c5c8c6 }
- .terminal-159476969-r4 { fill: #e1e1e1 }
- .terminal-159476969-r5 { fill: #dde8f3;font-weight: bold }
- .terminal-159476969-r6 { fill: #ddedf9 }
+ .terminal-749580480-r1 { fill: #ffff00 }
+ .terminal-749580480-r2 { fill: #e3e3e3 }
+ .terminal-749580480-r3 { fill: #c5c8c6 }
+ .terminal-749580480-r4 { fill: #e1e1e1 }
+ .terminal-749580480-r5 { fill: #fea62b;font-weight: bold }
+ .terminal-749580480-r6 { fill: #a7a9ab }
+ .terminal-749580480-r7 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- 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
@@ -29777,136 +32494,137 @@
font-weight: 700;
}
- .terminal-4133316721-matrix {
+ .terminal-888187976-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-4133316721-title {
+ .terminal-888187976-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-4133316721-r1 { fill: #ffff00 }
- .terminal-4133316721-r2 { fill: #e3e3e3 }
- .terminal-4133316721-r3 { fill: #c5c8c6 }
- .terminal-4133316721-r4 { fill: #ddeedd }
- .terminal-4133316721-r5 { fill: #dde8f3;font-weight: bold }
- .terminal-4133316721-r6 { fill: #ddedf9 }
+ .terminal-888187976-r1 { fill: #ffff00 }
+ .terminal-888187976-r2 { fill: #e3e3e3 }
+ .terminal-888187976-r3 { fill: #c5c8c6 }
+ .terminal-888187976-r4 { fill: #ddeedd }
+ .terminal-888187976-r5 { fill: #fea62b;font-weight: bold }
+ .terminal-888187976-r6 { fill: #a7a9ab }
+ .terminal-888187976-r7 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- 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
@@ -30191,142 +32909,142 @@
font-weight: 700;
}
- .terminal-3476127971-matrix {
+ .terminal-4273831210-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3476127971-title {
+ .terminal-4273831210-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3476127971-r1 { fill: #c5c8c6 }
- .terminal-3476127971-r2 { fill: #eae3e5 }
- .terminal-3476127971-r3 { fill: #e8e0e7 }
- .terminal-3476127971-r4 { fill: #efe9e4 }
- .terminal-3476127971-r5 { fill: #ede6e6 }
- .terminal-3476127971-r6 { fill: #efeedf }
- .terminal-3476127971-r7 { fill: #e9eee5 }
- .terminal-3476127971-r8 { fill: #e3e6eb }
- .terminal-3476127971-r9 { fill: #dfe9ed;font-weight: bold }
- .terminal-3476127971-r10 { fill: #e6e3e9;font-weight: bold }
- .terminal-3476127971-r11 { fill: #e4eee8 }
- .terminal-3476127971-r12 { fill: #e2edeb;font-weight: bold }
- .terminal-3476127971-r13 { fill: #dfebed }
+ .terminal-4273831210-r1 { fill: #c5c8c6 }
+ .terminal-4273831210-r2 { fill: #eae3e5 }
+ .terminal-4273831210-r3 { fill: #e8e0e7 }
+ .terminal-4273831210-r4 { fill: #efe9e4 }
+ .terminal-4273831210-r5 { fill: #ede6e6 }
+ .terminal-4273831210-r6 { fill: #efeedf }
+ .terminal-4273831210-r7 { fill: #e9eee5 }
+ .terminal-4273831210-r8 { fill: #e3e6eb }
+ .terminal-4273831210-r9 { fill: #dfe9ed;font-weight: bold }
+ .terminal-4273831210-r10 { fill: #e6e3e9;font-weight: bold }
+ .terminal-4273831210-r11 { fill: #e4eee8 }
+ .terminal-4273831210-r12 { fill: #e2edeb;font-weight: bold }
+ .terminal-4273831210-r13 { fill: #dfebed }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- PlaceholderApp
+ PlaceholderApp
-
-
-
-
- Placeholder p2 here!
- This is a custom label for p1.
- #p4
- #p3 #p5 Placeholde
- r
-
- Lorem ipsum dolor sit
- 26 x 6 amet, 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. Sed consectetur 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 #p5 Placeholde
+ r
+
+ Lorem ipsum dolor sit
+ 26 x 6 amet, 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. Sed consectetur 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
@@ -30385,39 +33103,194 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MyApp
+
+
+
+
+
+
+
+
+
+ [ 'This is a string that has some chars' ]
+
+ This should be 1 cell away from ^
+
+
+
+
+
+
+
+
+ '''
+# ---
+# name: test_print_capture
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
- MyApp
+ CaptureApp
-
-
-
- [ 'This is a string that has some chars' ]
-
- This should be 1 cell away from ^
-
-
-
+
+
+
+ RichLog
+ This will be captured!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -30425,7 +33298,7 @@
'''
# ---
-# name: test_print_capture
+# name: test_programmatic_disable_button
'''
@@ -30448,132 +33321,138 @@
font-weight: 700;
}
- .terminal-3935013562-matrix {
+ .terminal-2109104343-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3935013562-title {
+ .terminal-2109104343-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3935013562-r1 { fill: #e1e1e1 }
- .terminal-3935013562-r2 { fill: #c5c8c6 }
+ .terminal-2109104343-r1 { fill: #e1e1e1 }
+ .terminal-2109104343-r2 { fill: #c5c8c6 }
+ .terminal-2109104343-r3 { fill: #303336 }
+ .terminal-2109104343-r4 { fill: #a7a7a7;font-weight: bold }
+ .terminal-2109104343-r5 { fill: #0f0f0f }
+ .terminal-2109104343-r6 { fill: #fea62b;font-weight: bold }
+ .terminal-2109104343-r7 { fill: #a7a9ab }
+ .terminal-2109104343-r8 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- CaptureApp
+ ExampleApp
-
-
-
- RichLog
- This will be captured!
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ Hover the button then hit space
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Disabled
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+
+
+
+
+
+
+
+
+
+ SPACE Toggle Button
@@ -30603,132 +33482,132 @@
font-weight: 700;
}
- .terminal-4222066429-matrix {
+ .terminal-2268888456-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-4222066429-title {
+ .terminal-2268888456-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-4222066429-r1 { fill: #ffdddd }
- .terminal-4222066429-r2 { fill: #c5c8c6 }
- .terminal-4222066429-r3 { fill: #e1e1e1 }
+ .terminal-2268888456-r1 { fill: #ffdddd }
+ .terminal-2268888456-r2 { fill: #c5c8c6 }
+ .terminal-2268888456-r3 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ProgrammaticScrollbarGutterChange
+ ProgrammaticScrollbarGutterChange
-
-
-
- one two
-
-
-
-
-
-
-
-
-
-
-
- three four
-
-
-
-
-
-
-
-
-
-
+
+
+
+ one two
+
+
+
+
+
+
+
+
+
+
+
+ three four
+
+
+
+
+
+
+
+
+
+
@@ -30759,135 +33638,136 @@
font-weight: 700;
}
- .terminal-2592118671-matrix {
+ .terminal-3159068456-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2592118671-title {
+ .terminal-3159068456-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2592118671-r1 { fill: #e1e1e1 }
- .terminal-2592118671-r2 { fill: #c5c8c6 }
- .terminal-2592118671-r3 { fill: #4ebf71 }
- .terminal-2592118671-r4 { fill: #dde8f3;font-weight: bold }
- .terminal-2592118671-r5 { fill: #ddedf9 }
+ .terminal-3159068456-r1 { fill: #e1e1e1 }
+ .terminal-3159068456-r2 { fill: #c5c8c6 }
+ .terminal-3159068456-r3 { fill: #4ebf71 }
+ .terminal-3159068456-r4 { fill: #fea62b;font-weight: bold }
+ .terminal-3159068456-r5 { fill: #a7a9ab }
+ .terminal-3159068456-r6 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- IndeterminateProgressBar
+ IndeterminateProgressBar
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% --:--:--
-
-
-
-
-
-
-
-
-
-
-
- S Start
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% --:--:--
+
+
+
+
+
+
+
+
+
+
+
+ s Start
@@ -30917,137 +33797,138 @@
font-weight: 700;
}
- .terminal-3036192677-matrix {
+ .terminal-3030506209-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3036192677-title {
+ .terminal-3030506209-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3036192677-r1 { fill: #e1e1e1 }
- .terminal-3036192677-r2 { fill: #c5c8c6 }
- .terminal-3036192677-r3 { fill: #b93c5b }
- .terminal-3036192677-r4 { fill: #1e1e1e }
- .terminal-3036192677-r5 { fill: #e1e1e1;text-decoration: underline; }
- .terminal-3036192677-r6 { fill: #dde8f3;font-weight: bold }
- .terminal-3036192677-r7 { fill: #ddedf9 }
+ .terminal-3030506209-r1 { fill: #e1e1e1 }
+ .terminal-3030506209-r2 { fill: #c5c8c6 }
+ .terminal-3030506209-r3 { fill: #b93c5b }
+ .terminal-3030506209-r4 { fill: #1e1e1e }
+ .terminal-3030506209-r5 { fill: #e1e1e1;text-decoration: underline; }
+ .terminal-3030506209-r6 { fill: #fea62b;font-weight: bold }
+ .terminal-3030506209-r7 { fill: #a7a9ab }
+ .terminal-3030506209-r8 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- StyledProgressBar
+ StyledProgressBar
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% --:--:--
-
-
-
-
-
-
-
-
-
-
-
- S Start
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% --:--:--
+
+
+
+
+
+
+
+
+
+
+
+ s Start
@@ -31077,136 +33958,137 @@
font-weight: 700;
}
- .terminal-2114723073-matrix {
+ .terminal-3658011159-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2114723073-title {
+ .terminal-3658011159-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2114723073-r1 { fill: #e1e1e1 }
- .terminal-2114723073-r2 { fill: #c5c8c6 }
- .terminal-2114723073-r3 { fill: #fea62b }
- .terminal-2114723073-r4 { fill: #323232 }
- .terminal-2114723073-r5 { fill: #dde8f3;font-weight: bold }
- .terminal-2114723073-r6 { fill: #ddedf9 }
+ .terminal-3658011159-r1 { fill: #e1e1e1 }
+ .terminal-3658011159-r2 { fill: #c5c8c6 }
+ .terminal-3658011159-r3 { fill: #fea62b }
+ .terminal-3658011159-r4 { fill: #323232 }
+ .terminal-3658011159-r5 { fill: #fea62b;font-weight: bold }
+ .terminal-3658011159-r6 { fill: #a7a9ab }
+ .terminal-3658011159-r7 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- IndeterminateProgressBar
+ IndeterminateProgressBar
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ━━━━━━━━━━━━ ╺ ━━━━━━━━━━━━━━━━━━━ 39% 00:00:07
-
-
-
-
-
-
-
-
-
-
-
- S Start
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ━━━━━━━━━━━━ ╺━━━━━━━━━━━━━━━━━━━ 39% 00:00:07
+
+
+
+
+
+
+
+
+
+
+
+ s Start
@@ -31236,138 +34118,139 @@
font-weight: 700;
}
- .terminal-1351164996-matrix {
+ .terminal-2213813497-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1351164996-title {
+ .terminal-2213813497-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1351164996-r1 { fill: #e1e1e1 }
- .terminal-1351164996-r2 { fill: #c5c8c6 }
- .terminal-1351164996-r3 { fill: #004578 }
- .terminal-1351164996-r4 { fill: #152939 }
- .terminal-1351164996-r5 { fill: #1e1e1e }
- .terminal-1351164996-r6 { fill: #e1e1e1;text-decoration: underline; }
- .terminal-1351164996-r7 { fill: #dde8f3;font-weight: bold }
- .terminal-1351164996-r8 { fill: #ddedf9 }
+ .terminal-2213813497-r1 { fill: #e1e1e1 }
+ .terminal-2213813497-r2 { fill: #c5c8c6 }
+ .terminal-2213813497-r3 { fill: #004578 }
+ .terminal-2213813497-r4 { fill: #152939 }
+ .terminal-2213813497-r5 { fill: #1e1e1e }
+ .terminal-2213813497-r6 { fill: #e1e1e1;text-decoration: underline; }
+ .terminal-2213813497-r7 { fill: #fea62b;font-weight: bold }
+ .terminal-2213813497-r8 { fill: #a7a9ab }
+ .terminal-2213813497-r9 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- StyledProgressBar
+ StyledProgressBar
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ━━━━━━━━━━━━ ╺ ━━━━━━━━━━━━━━━━━━━ 39% 00:00:07
-
-
-
-
-
-
-
-
-
-
-
- S Start
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ━━━━━━━━━━━━ ╺━━━━━━━━━━━━━━━━━━━ 39% 00:00:07
+
+
+
+
+
+
+
+
+
+
+
+ s Start
@@ -31397,136 +34280,137 @@
font-weight: 700;
}
- .terminal-852664727-matrix {
+ .terminal-2628588616-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-852664727-title {
+ .terminal-2628588616-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-852664727-r1 { fill: #e1e1e1 }
- .terminal-852664727-r2 { fill: #c5c8c6 }
- .terminal-852664727-r3 { fill: #323232 }
- .terminal-852664727-r4 { fill: #b93c5b }
- .terminal-852664727-r5 { fill: #dde8f3;font-weight: bold }
- .terminal-852664727-r6 { fill: #ddedf9 }
+ .terminal-2628588616-r1 { fill: #e1e1e1 }
+ .terminal-2628588616-r2 { fill: #c5c8c6 }
+ .terminal-2628588616-r3 { fill: #323232 }
+ .terminal-2628588616-r4 { fill: #b93c5b }
+ .terminal-2628588616-r5 { fill: #fea62b;font-weight: bold }
+ .terminal-2628588616-r6 { fill: #a7a9ab }
+ .terminal-2628588616-r7 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- IndeterminateProgressBar
+ IndeterminateProgressBar
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ━ ╸ ━━━━━━━━ ╺ ━━━━━━━━━━━━━━━━━━━━━ --% --:--:--
-
-
-
-
-
-
-
-
-
-
-
- S Start
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ━╸ ━━━━━━━━ ╺━━━━━━━━━━━━━━━━━━━━━ --% --:--:--
+
+
+
+
+
+
+
+
+
+
+
+ s Start
@@ -31556,138 +34440,139 @@
font-weight: 700;
}
- .terminal-1175498223-matrix {
+ .terminal-1415143893-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1175498223-title {
+ .terminal-1415143893-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1175498223-r1 { fill: #e1e1e1 }
- .terminal-1175498223-r2 { fill: #c5c8c6 }
- .terminal-1175498223-r3 { fill: #fea62b }
- .terminal-1175498223-r4 { fill: #004578 }
- .terminal-1175498223-r5 { fill: #1e1e1e }
- .terminal-1175498223-r6 { fill: #e1e1e1;text-decoration: underline; }
- .terminal-1175498223-r7 { fill: #dde8f3;font-weight: bold }
- .terminal-1175498223-r8 { fill: #ddedf9 }
+ .terminal-1415143893-r1 { fill: #e1e1e1 }
+ .terminal-1415143893-r2 { fill: #c5c8c6 }
+ .terminal-1415143893-r3 { fill: #fea62b }
+ .terminal-1415143893-r4 { fill: #004578 }
+ .terminal-1415143893-r5 { fill: #1e1e1e }
+ .terminal-1415143893-r6 { fill: #e1e1e1;text-decoration: underline; }
+ .terminal-1415143893-r7 { fill: #fea62b;font-weight: bold }
+ .terminal-1415143893-r8 { fill: #a7a9ab }
+ .terminal-1415143893-r9 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- StyledProgressBar
+ StyledProgressBar
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ━ ╸ ━━━━━━━━ ╺ ━━━━━━━━━━━━━━━━━━━━━ --% --:--:--
-
-
-
-
-
-
-
-
-
-
-
- S Start
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ━╸ ━━━━━━━━ ╺━━━━━━━━━━━━━━━━━━━━━ --% --:--:--
+
+
+
+
+
+
+
+
+
+
+
+ s Start
@@ -31717,135 +34602,135 @@
font-weight: 700;
}
- .terminal-1103805314-matrix {
+ .terminal-1240162166-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1103805314-title {
+ .terminal-1240162166-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1103805314-r1 { fill: #c5c8c6 }
- .terminal-1103805314-r2 { fill: #e1e1e1 }
- .terminal-1103805314-r3 { fill: #737373 }
- .terminal-1103805314-r4 { fill: #e1e1e1;font-weight: bold }
- .terminal-1103805314-r5 { fill: #474747 }
- .terminal-1103805314-r6 { fill: #0178d4 }
+ .terminal-1240162166-r1 { fill: #c5c8c6 }
+ .terminal-1240162166-r2 { fill: #e1e1e1 }
+ .terminal-1240162166-r3 { fill: #737373 }
+ .terminal-1240162166-r4 { fill: #e1e1e1;font-weight: bold }
+ .terminal-1240162166-r5 { fill: #474747 }
+ .terminal-1240162166-r6 { fill: #0178d4 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- QuicklyChangeTabsApp
+ QuicklyChangeTabsApp
-
-
-
-
- one two three
- ━━━━━━━━━━━━━ ╸ ━━━━━ ╺ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
- three
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ one two three
+ ━━━━━━━━━━━━━╸ ━━━━━ ╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ three
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -32203,135 +35088,135 @@
font-weight: 700;
}
- .terminal-1786282230-matrix {
+ .terminal-1943930343-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1786282230-title {
+ .terminal-1943930343-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1786282230-r1 { fill: #ff0000 }
- .terminal-1786282230-r2 { fill: #c5c8c6 }
- .terminal-1786282230-r3 { fill: #e1e1e1;font-weight: bold }
- .terminal-1786282230-r4 { fill: #e1e1e1 }
- .terminal-1786282230-r5 { fill: #fea62b }
- .terminal-1786282230-r6 { fill: #323232 }
+ .terminal-1943930343-r1 { fill: #ff0000 }
+ .terminal-1943930343-r2 { fill: #c5c8c6 }
+ .terminal-1943930343-r3 { fill: #e1e1e1;font-weight: bold }
+ .terminal-1943930343-r4 { fill: #e1e1e1 }
+ .terminal-1943930343-r5 { fill: #fea62b }
+ .terminal-1943930343-r6 { fill: #323232 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- RecomposeApp
+ RecomposeApp
-
-
-
- ┌ ───────── ┐ ┌ ───────── ┐ ┌ ────────── ┐ ┌ ───────── ┐ ┌ ────────── ┐ ┌ ───────── ┐ ┌ ────────── ┐
- │ ┓ ┏━┓ │ │ ┓ ┓ │ │ ┓ ╺━┓ │ │ ┓ ╺━┓ │ │ ┓ ╻ ╻ │ │ ┓ ┏━╸ │ │ ┓ ┏━╸ │
- │ ┃ ┃ ┃ │ │ ┃ ┃ │ │ ┃ ┏━┛ │ │ ┃ ━┫ │ │ ┃ ┗━┫ │ │ ┃ ┗━┓ │ │ ┃ ┣━┓ │
- │ ╺┻╸┗━┛ │ │ ╺┻╸╺┻╸ │ │ ╺┻╸┗━╸ │ │ ╺┻╸╺━┛ │ │ ╺┻╸ ╹ │ │ ╺┻╸╺━┛ │ │ ╺┻╸┗━┛ │
- └ ───────── ┘ └ ───────── ┘ └ ────────── ┘ └ ───────── ┘ └ ────────── ┘ └ ───────── ┘ └ ────────── ┘
-
-
-
-
-
-
-
- ━━━━━━━━━━━━━━━━ ╺ ━━━━━━━━━━━━━━━ 50%
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ┌─────────┐┌─────────┐┌──────────┐┌─────────┐┌──────────┐┌─────────┐┌──────────┐
+ │ ┓ ┏━┓ ││ ┓ ┓ ││ ┓ ╺━┓ ││ ┓ ╺━┓ ││ ┓ ╻ ╻ ││ ┓ ┏━╸ ││ ┓ ┏━╸ │
+ │ ┃ ┃ ┃ ││ ┃ ┃ ││ ┃ ┏━┛ ││ ┃ ━┫ ││ ┃ ┗━┫ ││ ┃ ┗━┓ ││ ┃ ┣━┓ │
+ │ ╺┻╸┗━┛ ││ ╺┻╸╺┻╸ ││ ╺┻╸┗━╸ ││ ╺┻╸╺━┛ ││ ╺┻╸ ╹ ││ ╺┻╸╺━┛ ││ ╺┻╸┗━┛ │
+ └─────────┘└─────────┘└──────────┘└─────────┘└──────────┘└─────────┘└──────────┘
+
+
+
+
+
+
+
+ ━━━━━━━━━━━━━━━━ ╺━━━━━━━━━━━━━━━ 50%
+
+
+
+
+
+
+
+
+
+
@@ -32362,137 +35247,138 @@
font-weight: 700;
}
- .terminal-1855273214-matrix {
+ .terminal-2357839144-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1855273214-title {
+ .terminal-2357839144-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1855273214-r1 { fill: #c5c8c6 }
- .terminal-1855273214-r2 { fill: #e3e3e3 }
- .terminal-1855273214-r3 { fill: #008000 }
- .terminal-1855273214-r4 { fill: #ffff00 }
- .terminal-1855273214-r5 { fill: #e1e1e1 }
- .terminal-1855273214-r6 { fill: #dde8f3;font-weight: bold }
- .terminal-1855273214-r7 { fill: #ddedf9 }
+ .terminal-2357839144-r1 { fill: #c5c8c6 }
+ .terminal-2357839144-r2 { fill: #e3e3e3 }
+ .terminal-2357839144-r3 { fill: #008000 }
+ .terminal-2357839144-r4 { fill: #ffff00 }
+ .terminal-2357839144-r5 { fill: #e1e1e1 }
+ .terminal-2357839144-r6 { fill: #fea62b;font-weight: bold }
+ .terminal-2357839144-r7 { fill: #a7a9ab }
+ .terminal-2357839144-r8 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- VerticalRemoveApp
+ VerticalRemoveApp
-
-
-
- ⭘ VerticalRemoveApp
- ╭ ────────────────────────────────────────────────────────────────────────────── ╮
- │ ╭ ──────────────────── ╮ │
- │ │ This is a test label │ │
- │ ╰ ──────────────────── ╯ │
- ╰ ────────────────────────────────────────────────────────────────────────────── ╯
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- A Add D Delete
+
+
+
+ ⭘ VerticalRemoveApp
+ ╭──────────────────────────────────────────────────────────────────────────────╮
+ │ ╭────────────────────╮ │
+ │ │This is a test label│ │
+ │ ╰────────────────────╯ │
+ ╰──────────────────────────────────────────────────────────────────────────────╯
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ a Add d Delete
@@ -32522,131 +35408,131 @@
font-weight: 700;
}
- .terminal-707514202-matrix {
+ .terminal-2800944241-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-707514202-title {
+ .terminal-2800944241-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-707514202-r1 { fill: #e1e1e1 }
- .terminal-707514202-r2 { fill: #c5c8c6 }
+ .terminal-2800944241-r1 { fill: #e1e1e1 }
+ .terminal-2800944241-r2 { fill: #c5c8c6 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- RichLogLines
+ RichLogLines
-
-
-
- Key press #3
- Key press #4
- Key press #5
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Key press #3
+ Key press #4
+ Key press #5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -32677,131 +35563,131 @@
font-weight: 700;
}
- .terminal-1498692038-matrix {
+ .terminal-1910524980-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1498692038-title {
+ .terminal-1910524980-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1498692038-r1 { fill: #e1e1e1 }
- .terminal-1498692038-r2 { fill: #c5c8c6 }
+ .terminal-1910524980-r1 { fill: #e1e1e1 }
+ .terminal-1910524980-r2 { fill: #c5c8c6 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- RichLogScrollApp
+ RichLogScrollApp
-
-
-
- Line 0 Line 10 Line 0
- Line 1 Line 11 Line 1
- Line 2 Line 12 Line 2
- Line 3 Line 13 Line 3
- Line 4 Line 14 Line 4
- Line 5 Line 15 Line 5
- Line 6 Line 16 Line 6
- Line 7 Line 17 Line 7
- Line 8 Line 18 Line 8
- Line 9 Line 19 Line 9
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Line 0 Line 10 Line 0
+ Line 1 Line 11 Line 1
+ Line 2 Line 12 Line 2
+ Line 3 Line 13 Line 3
+ Line 4 Line 14 Line 4
+ Line 5 Line 15 Line 5
+ Line 6 Line 16 Line 6
+ Line 7 Line 17 Line 7
+ Line 8 Line 18 Line 8
+ Line 9 Line 19 Line 9
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -32987,132 +35873,132 @@
font-weight: 700;
}
- .terminal-2818358681-matrix {
+ .terminal-2750102054-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2818358681-title {
+ .terminal-2750102054-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2818358681-r1 { fill: #e1e1e1 }
- .terminal-2818358681-r2 { fill: #c5c8c6 }
- .terminal-2818358681-r3 { fill: #004578 }
+ .terminal-2750102054-r1 { fill: #e1e1e1 }
+ .terminal-2750102054-r2 { fill: #c5c8c6 }
+ .terminal-2750102054-r3 { fill: #004578 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- HorizontalRulesApp
+ HorizontalRulesApp
-
-
-
- solid (default)
-
- ────────────────────────────────────────────────────────────────
-
- heavy
-
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
- thick
-
- ████████████████████████████████████████████████████████████████
-
- dashed
-
- ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍
-
- double
-
- ════════════════════════════════════════════════════════════════
-
- ascii
-
- ----------------------------------------------------------------
+
+
+
+ solid (default)
+
+ ────────────────────────────────────────────────────────────────
+
+ heavy
+
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ thick
+
+ ████████████████████████████████████████████████████████████████
+
+ dashed
+
+ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍
+
+ double
+
+ ════════════════════════════════════════════════════════════════
+
+ ascii
+
+ ----------------------------------------------------------------
@@ -33143,132 +36029,132 @@
font-weight: 700;
}
- .terminal-30569631-matrix {
+ .terminal-870531946-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-30569631-title {
+ .terminal-870531946-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-30569631-r1 { fill: #e1e1e1 }
- .terminal-30569631-r2 { fill: #c5c8c6 }
- .terminal-30569631-r3 { fill: #004578 }
+ .terminal-870531946-r1 { fill: #e1e1e1 }
+ .terminal-870531946-r2 { fill: #c5c8c6 }
+ .terminal-870531946-r3 { fill: #004578 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- VerticalRulesApp
+ VerticalRulesApp
-
-
-
-
-
- solid │ heavy ┃ thick █ dashed ╏ double ║ ascii |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
- │ ┃ █ ╏ ║ |
-
-
+
+
+
+
+
+ solid │ heavy ┃ thick █ dashed ╏ double ║ ascii |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+ │ ┃ █ ╏ ║ |
+
+
@@ -33276,6 +36162,162 @@
'''
# ---
+# name: test_rules
+ '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ RuleApp
+
+
+
+
+
+
+
+
+
+
+ --------------------------------------------------------------------------------
+
+
+
+ ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍
+
+ ════════════════════════════════════════════════════════════════════════════════
+
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+
+ | ╏ ║ ┃ │ █
+ | ╏ ║ ┃ │ █
+ | ╏ ║ ┃ │ █
+ | ╏ ║ ┃ │ █
+ | ╏ ║ ┃ │ █
+ | ╏ ║ ┃ │ █
+ | ╏ ║ ┃ │ █
+ | ╏ ║ ┃ │ █
+ | ╏ ║ ┃ │ █
+ | ╏ ║ ┃ │ █
+ | ╏ ║ ┃ │ █
+ | ╏ ║ ┃ │ █
+
+
+
+
+ '''
+# ---
# name: test_scoped_css
'''
@@ -33299,133 +36341,133 @@
font-weight: 700;
}
- .terminal-1271303658-matrix {
+ .terminal-1207765677-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1271303658-title {
+ .terminal-1207765677-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1271303658-r1 { fill: #ff00ff }
- .terminal-1271303658-r2 { fill: #c5c8c6 }
- .terminal-1271303658-r3 { fill: #008000 }
- .terminal-1271303658-r4 { fill: #e1e1e1 }
+ .terminal-1207765677-r1 { fill: #ff00ff }
+ .terminal-1207765677-r2 { fill: #c5c8c6 }
+ .terminal-1207765677-r3 { fill: #008000 }
+ .terminal-1207765677-r4 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MyApp
+ MyApp
-
-
-
- ┌ ────────────────────────────────────────────────────────────────────────────── ┐
- │ ┌ ─── ┐ │
- │ │ foo │ │
- │ └ ─── ┘ │
- │ ┌ ─── ┐ │
- │ │ bar │ │
- │ └ ─── ┘ │
- └ ────────────────────────────────────────────────────────────────────────────── ┘
- ┌ ────────────────────────────────────────────────────────────────────────────── ┐
- │ ┌ ─── ┐ │
- │ │ foo │ │
- │ └ ─── ┘ │
- │ ┌ ─── ┐ │
- │ │ bar │ │
- │ └ ─── ┘ │
- └ ────────────────────────────────────────────────────────────────────────────── ┘
- I should not be styled
-
-
-
-
-
-
+
+
+
+ ┌──────────────────────────────────────────────────────────────────────────────┐
+ │ ┌───┐ │
+ │ │ foo │ │
+ │ └───┘ │
+ │ ┌───┐ │
+ │ │ bar │ │
+ │ └───┘ │
+ └──────────────────────────────────────────────────────────────────────────────┘
+ ┌──────────────────────────────────────────────────────────────────────────────┐
+ │ ┌───┐ │
+ │ │ foo │ │
+ │ └───┘ │
+ │ ┌───┐ │
+ │ │ bar │ │
+ │ └───┘ │
+ └──────────────────────────────────────────────────────────────────────────────┘
+ I should not be styled
+
+
+
+
+
+
@@ -33456,135 +36498,136 @@
font-weight: 700;
}
- .terminal-1316892474-matrix {
+ .terminal-300775983-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1316892474-title {
+ .terminal-300775983-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-300775983-r1 { fill: #c5c8c6 }
+ .terminal-300775983-r2 { fill: #e3e3e3 }
+ .terminal-300775983-r3 { fill: #e1e1e1 }
+ .terminal-300775983-r4 { fill: #fea62b;font-weight: bold }
+ .terminal-300775983-r5 { fill: #a7a9ab }
+ .terminal-300775983-r6 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ModalApp
+ ModalApp
-
-
-
- ⭘ ModalApp
- B
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- A Push screen A
+
+
+
+ ⭘ ModalApp
+ B
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ a Push screen A
@@ -33614,142 +36657,142 @@
font-weight: 700;
}
- .terminal-2006637091-matrix {
+ .terminal-2408879539-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2006637091-title {
+ .terminal-2408879539-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2006637091-r1 { fill: #1e1e1e }
- .terminal-2006637091-r2 { fill: #c5c8c6 }
- .terminal-2006637091-r3 { fill: #434343 }
- .terminal-2006637091-r4 { fill: #262626;font-weight: bold }
- .terminal-2006637091-r5 { fill: #e2e2e2 }
- .terminal-2006637091-r6 { fill: #e1e1e1 }
- .terminal-2006637091-r7 { fill: #23568b }
- .terminal-2006637091-r8 { fill: #14191f }
- .terminal-2006637091-r9 { fill: #ddedf9 }
+ .terminal-2408879539-r1 { fill: #1e1e1e }
+ .terminal-2408879539-r2 { fill: #e1e1e1 }
+ .terminal-2408879539-r3 { fill: #c5c8c6 }
+ .terminal-2408879539-r4 { fill: #434343 }
+ .terminal-2408879539-r5 { fill: #262626;font-weight: bold }
+ .terminal-2408879539-r6 { fill: #e2e2e2 }
+ .terminal-2408879539-r7 { fill: #23568b }
+ .terminal-2408879539-r8 { fill: #14191f }
+ .terminal-2408879539-r9 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- ScrollOffByOne
+ ScrollOffByOne
-
-
-
- ▊ ▐ X ▌ 43 ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- ▊ ▐ X ▌ 44 ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- ▊ ▐ X ▌ 45 ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- ▊ ▐ X ▌ 46 ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎ ▃▃
- ▊ ▔▔▔▔▔▔▔▔ ▎
- ▊ ▐ X ▌ 47 ▎ ▂▂
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- ▊ ▐ X ▌ 48 ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- ▊ ▐ X ▌ 49 ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- ▊ ▐ X ▌ 50 ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
+
+
+
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ ▊ ▐ X ▌ 43 ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ ▊ ▐ X ▌ 44 ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ ▊ ▐ X ▌ 45 ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ ▊ ▐ X ▌ 46 ▎ ▄▄
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎ ▃▃
+ ▊ ▐ X ▌ 47 ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ ▊ ▐ X ▌ 48 ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ ▊ ▐ X ▌ 49 ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ ▊ ▐ X ▌ 50 ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
@@ -33780,144 +36823,300 @@
font-weight: 700;
}
- .terminal-1340160965-matrix {
+ .terminal-3177497111-matrix {
+ font-family: Fira Code, monospace;
+ font-size: 20px;
+ line-height: 24.4px;
+ font-variant-east-asian: full-width;
+ }
+
+ .terminal-3177497111-title {
+ font-size: 18px;
+ font-weight: bold;
+ font-family: arial;
+ }
+
+ .terminal-3177497111-r1 { fill: #e1e1e1 }
+ .terminal-3177497111-r2 { fill: #c5c8c6 }
+ .terminal-3177497111-r3 { fill: #004578 }
+ .terminal-3177497111-r4 { fill: #23568b }
+ .terminal-3177497111-r5 { fill: #fea62b }
+ .terminal-3177497111-r6 { fill: #f4005f }
+ .terminal-3177497111-r7 { fill: #14191f }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MyApp
+
+
+
+
+
+
+
+
+
+ SPAM
+ ╭────────────────────────────────────────────────────────────────────────────╮
+ │ SPAM │
+ │ SPAM │
+ │ SPAM │
+ │ SPAM │
+ │ SPAM │
+ │ SPAM │
+ │ SPAM │
+ │ SPAM │ ▁▁
+ │ ╭────────────────────────────────────────────────────────────────────────╮ │
+ │ │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ >>bullseye<< @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │ │
+ │ │ │ │
+ │ │ │ ▄▄ │
+ │ │ │ │ ▄▄
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ ╰────────────────────────────────────────────────────────────────────────────╯
+ SPAM
+ SPAM
+
+
+
+
+ '''
+# ---
+# name: test_scroll_visible
+ '''
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MyApp
+ MyApp
-
-
-
- SPAM
- SPAM
- SPAM
- ╭ ──────────────────────────────────────────────────────────────────────────── ╮
- │ SPAM │
- │ SPAM │
- │ SPAM │
- │ SPAM │
- │ SPAM │
- │ SPAM │ ▄▄
- │ SPAM │
- │ SPAM │
- │ ╭ ──────────────────────────────────────────────────────────────────────── ╮ │
- │ │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ >>bullseye<< @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │ │
- │ │ │ │ ▇▇
- │ │ │ ▄▄ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- ╰ ──────────────────────────────────────────────────────────────────────────── ╯
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ | ▆▆
+ |
+ |
+ |
+ |
+ SHOULD BE VISIBLE
'''
# ---
-# name: test_scroll_visible
+# name: test_scroll_visible_with_margin
'''
@@ -33940,133 +37139,137 @@
font-weight: 700;
}
- .terminal-150767416-matrix {
+ .terminal-1023786546-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-150767416-title {
+ .terminal-1023786546-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-150767416-r1 { fill: #e1e1e1 }
- .terminal-150767416-r2 { fill: #c5c8c6 }
- .terminal-150767416-r3 { fill: #23568b }
+ .terminal-1023786546-r1 { fill: #ff0000 }
+ .terminal-1023786546-r2 { fill: #454a50 }
+ .terminal-1023786546-r3 { fill: #e1e1e1 }
+ .terminal-1023786546-r4 { fill: #c5c8c6 }
+ .terminal-1023786546-r5 { fill: #e2e3e3;font-weight: bold }
+ .terminal-1023786546-r6 { fill: #000000 }
+ .terminal-1023786546-r7 { fill: #23568b }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- MyApp
+ ScrollVisibleMargin
-
-
-
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- | ▆▆
- |
- |
- |
- |
- SHOULD BE VISIBLE
+
+
+
+ │ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ │
+ │ Hello, world! (19) │
+ │ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ │
+ │ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ │
+ │ Hello, world! (20) │
+ │ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ │
+ │ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ │
+ │ Hello, world! (21) │
+ │ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ │
+ │ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ │ ▅▅
+ │ Hello, world! (22) │
+ │ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ │
+ │ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ │
+ │ Hello, world! (23) │
+ │ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ │
+ │ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ │
+ │ Hello, world! (24) │
+ │ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ │
+ │ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ │
+ │ Hello, world! (25) │
+ │ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ │
+ │ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ │
+ │ Hello, world! (26) │
+ │ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ │
@@ -34096,134 +37299,134 @@
font-weight: 700;
}
- .terminal-4204360114-matrix {
+ .terminal-323125957-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-4204360114-title {
+ .terminal-323125957-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-4204360114-r1 { fill: #c5c8c6 }
- .terminal-4204360114-r2 { fill: #e3e3e3 }
- .terminal-4204360114-r3 { fill: #ff0000 }
- .terminal-4204360114-r4 { fill: #dde2e8 }
- .terminal-4204360114-r5 { fill: #ddedf9 }
+ .terminal-323125957-r1 { fill: #c5c8c6 }
+ .terminal-323125957-r2 { fill: #e3e3e3 }
+ .terminal-323125957-r3 { fill: #ff0000 }
+ .terminal-323125957-r4 { fill: #dde2e8 }
+ .terminal-323125957-r5 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- 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 │
+ ╰──────────────────────────────────────────────────────────────────────────────╯
@@ -34254,136 +37457,136 @@
font-weight: 700;
}
- .terminal-1161182100-matrix {
+ .terminal-1692763632-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1161182100-title {
+ .terminal-1692763632-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1161182100-r1 { fill: #c5c8c6 }
- .terminal-1161182100-r2 { fill: #e3e3e3 }
- .terminal-1161182100-r3 { fill: #e1e1e1 }
- .terminal-1161182100-r4 { fill: #1e1e1e }
- .terminal-1161182100-r5 { fill: #0178d4 }
- .terminal-1161182100-r6 { fill: #787878 }
- .terminal-1161182100-r7 { fill: #a8a8a8 }
+ .terminal-1692763632-r1 { fill: #c5c8c6 }
+ .terminal-1692763632-r2 { fill: #e3e3e3 }
+ .terminal-1692763632-r3 { fill: #e1e1e1 }
+ .terminal-1692763632-r4 { fill: #1e1e1e }
+ .terminal-1692763632-r5 { fill: #0178d4 }
+ .terminal-1692763632-r6 { fill: #787878 }
+ .terminal-1692763632-r7 { fill: #a8a8a8 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- SelectApp
+ SelectApp
-
-
-
- ⭘ SelectApp
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ Select ▼ ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ⭘ SelectApp
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ Select ▼ ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -34414,140 +37617,140 @@
font-weight: 700;
}
- .terminal-2035490498-matrix {
+ .terminal-1789191797-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2035490498-title {
+ .terminal-1789191797-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2035490498-r1 { fill: #c5c8c6 }
- .terminal-2035490498-r2 { fill: #e3e3e3 }
- .terminal-2035490498-r3 { fill: #e1e1e1 }
- .terminal-2035490498-r4 { fill: #1e1e1e }
- .terminal-2035490498-r5 { fill: #0178d4 }
- .terminal-2035490498-r6 { fill: #787878 }
- .terminal-2035490498-r7 { fill: #a8a8a8 }
- .terminal-2035490498-r8 { fill: #121212 }
- .terminal-2035490498-r9 { fill: #ddedf9;font-weight: bold }
- .terminal-2035490498-r10 { fill: #85beea;font-weight: bold }
- .terminal-2035490498-r11 { fill: #e2e3e3 }
+ .terminal-1789191797-r1 { fill: #c5c8c6 }
+ .terminal-1789191797-r2 { fill: #e3e3e3 }
+ .terminal-1789191797-r3 { fill: #e1e1e1 }
+ .terminal-1789191797-r4 { fill: #1e1e1e }
+ .terminal-1789191797-r5 { fill: #0178d4 }
+ .terminal-1789191797-r6 { fill: #787878 }
+ .terminal-1789191797-r7 { fill: #a8a8a8 }
+ .terminal-1789191797-r8 { fill: #121212 }
+ .terminal-1789191797-r9 { fill: #ddedf9;font-weight: bold }
+ .terminal-1789191797-r10 { fill: #85beea;font-weight: bold }
+ .terminal-1789191797-r11 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- SelectApp
+ 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. ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
+
+
+
+ ⭘ 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. ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
@@ -34578,136 +37781,136 @@
font-weight: 700;
}
- .terminal-4010426174-matrix {
+ .terminal-1609533850-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-4010426174-title {
+ .terminal-1609533850-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-4010426174-r1 { fill: #c5c8c6 }
- .terminal-4010426174-r2 { fill: #e3e3e3 }
- .terminal-4010426174-r3 { fill: #e1e1e1 }
- .terminal-4010426174-r4 { fill: #1e1e1e }
- .terminal-4010426174-r5 { fill: #0178d4 }
- .terminal-4010426174-r6 { fill: #e2e2e2 }
- .terminal-4010426174-r7 { fill: #a8a8a8 }
+ .terminal-1609533850-r1 { fill: #c5c8c6 }
+ .terminal-1609533850-r2 { fill: #e3e3e3 }
+ .terminal-1609533850-r3 { fill: #e1e1e1 }
+ .terminal-1609533850-r4 { fill: #1e1e1e }
+ .terminal-1609533850-r5 { fill: #0178d4 }
+ .terminal-1609533850-r6 { fill: #e2e2e2 }
+ .terminal-1609533850-r7 { fill: #a8a8a8 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- SelectApp
+ SelectApp
-
-
-
- ⭘ I must not fear.
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ I must not fear. ▼ ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ⭘ I must not fear.
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ I must not fear. ▼ ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -34738,140 +37941,140 @@
font-weight: 700;
}
- .terminal-2035490498-matrix {
+ .terminal-1789191797-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2035490498-title {
+ .terminal-1789191797-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2035490498-r1 { fill: #c5c8c6 }
- .terminal-2035490498-r2 { fill: #e3e3e3 }
- .terminal-2035490498-r3 { fill: #e1e1e1 }
- .terminal-2035490498-r4 { fill: #1e1e1e }
- .terminal-2035490498-r5 { fill: #0178d4 }
- .terminal-2035490498-r6 { fill: #787878 }
- .terminal-2035490498-r7 { fill: #a8a8a8 }
- .terminal-2035490498-r8 { fill: #121212 }
- .terminal-2035490498-r9 { fill: #ddedf9;font-weight: bold }
- .terminal-2035490498-r10 { fill: #85beea;font-weight: bold }
- .terminal-2035490498-r11 { fill: #e2e3e3 }
+ .terminal-1789191797-r1 { fill: #c5c8c6 }
+ .terminal-1789191797-r2 { fill: #e3e3e3 }
+ .terminal-1789191797-r3 { fill: #e1e1e1 }
+ .terminal-1789191797-r4 { fill: #1e1e1e }
+ .terminal-1789191797-r5 { fill: #0178d4 }
+ .terminal-1789191797-r6 { fill: #787878 }
+ .terminal-1789191797-r7 { fill: #a8a8a8 }
+ .terminal-1789191797-r8 { fill: #121212 }
+ .terminal-1789191797-r9 { fill: #ddedf9;font-weight: bold }
+ .terminal-1789191797-r10 { fill: #85beea;font-weight: bold }
+ .terminal-1789191797-r11 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- SelectApp
+ 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. ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
+
+
+
+ ⭘ 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. ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
@@ -34902,136 +38105,136 @@
font-weight: 700;
}
- .terminal-4010426174-matrix {
+ .terminal-1609533850-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-4010426174-title {
+ .terminal-1609533850-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-4010426174-r1 { fill: #c5c8c6 }
- .terminal-4010426174-r2 { fill: #e3e3e3 }
- .terminal-4010426174-r3 { fill: #e1e1e1 }
- .terminal-4010426174-r4 { fill: #1e1e1e }
- .terminal-4010426174-r5 { fill: #0178d4 }
- .terminal-4010426174-r6 { fill: #e2e2e2 }
- .terminal-4010426174-r7 { fill: #a8a8a8 }
+ .terminal-1609533850-r1 { fill: #c5c8c6 }
+ .terminal-1609533850-r2 { fill: #e3e3e3 }
+ .terminal-1609533850-r3 { fill: #e1e1e1 }
+ .terminal-1609533850-r4 { fill: #1e1e1e }
+ .terminal-1609533850-r5 { fill: #0178d4 }
+ .terminal-1609533850-r6 { fill: #e2e2e2 }
+ .terminal-1609533850-r7 { fill: #a8a8a8 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- SelectApp
+ SelectApp
-
-
-
- ⭘ I must not fear.
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ I must not fear. ▼ ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ⭘ I must not fear.
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ I must not fear. ▼ ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -35062,139 +38265,139 @@
font-weight: 700;
}
- .terminal-330554958-matrix {
+ .terminal-1173659784-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-330554958-title {
+ .terminal-1173659784-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-330554958-r1 { fill: #1e1e1e }
- .terminal-330554958-r2 { fill: #0178d4 }
- .terminal-330554958-r3 { fill: #c5c8c6 }
- .terminal-330554958-r4 { fill: #787878 }
- .terminal-330554958-r5 { fill: #a8a8a8 }
- .terminal-330554958-r6 { fill: #121212 }
- .terminal-330554958-r7 { fill: #ddedf9;font-weight: bold }
- .terminal-330554958-r8 { fill: #85beea;font-weight: bold }
- .terminal-330554958-r9 { fill: #e2e3e3 }
- .terminal-330554958-r10 { fill: #e1e1e1 }
+ .terminal-1173659784-r1 { fill: #1e1e1e }
+ .terminal-1173659784-r2 { fill: #0178d4 }
+ .terminal-1173659784-r3 { fill: #c5c8c6 }
+ .terminal-1173659784-r4 { fill: #787878 }
+ .terminal-1173659784-r5 { fill: #a8a8a8 }
+ .terminal-1173659784-r6 { fill: #121212 }
+ .terminal-1173659784-r7 { fill: #ddedf9;font-weight: bold }
+ .terminal-1173659784-r8 { fill: #85beea;font-weight: bold }
+ .terminal-1173659784-r9 { fill: #e2e3e3 }
+ .terminal-1173659784-r10 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- SelectRebuildApp
+ SelectRebuildApp
-
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ Select ▲ ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ Select ▎
- ▊ This ▎
- ▊ Should ▎
- ▊ Be ▎
- ▊ What ▎
- ▊ Goes ▎
- ▊ Into ▎
- ▊ The ▎
- ▊ Snapshit ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
-
+
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ Select ▲ ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ Select ▎
+ ▊ This ▎
+ ▊ Should ▎
+ ▊ Be ▎
+ ▊ What ▎
+ ▊ Goes ▎
+ ▊ Into ▎
+ ▊ The ▎
+ ▊ Snapshit ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
+
@@ -35225,136 +38428,136 @@
font-weight: 700;
}
- .terminal-202000048-matrix {
+ .terminal-3673133324-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-202000048-title {
+ .terminal-3673133324-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-202000048-r1 { fill: #c5c8c6 }
- .terminal-202000048-r2 { fill: #e3e3e3 }
- .terminal-202000048-r3 { fill: #e1e1e1 }
- .terminal-202000048-r4 { fill: #1e1e1e }
- .terminal-202000048-r5 { fill: #0178d4 }
- .terminal-202000048-r6 { fill: #e2e2e2 }
- .terminal-202000048-r7 { fill: #a8a8a8 }
+ .terminal-3673133324-r1 { fill: #c5c8c6 }
+ .terminal-3673133324-r2 { fill: #e3e3e3 }
+ .terminal-3673133324-r3 { fill: #e1e1e1 }
+ .terminal-3673133324-r4 { fill: #1e1e1e }
+ .terminal-3673133324-r5 { fill: #0178d4 }
+ .terminal-3673133324-r6 { fill: #e2e2e2 }
+ .terminal-3673133324-r7 { fill: #a8a8a8 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- SelectApp
+ SelectApp
-
-
-
- ⭘ Twinkle, twinkle, little star,
-
-
- ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
- ▊ Twinkle, twinkle, little star, ▼ ▎
- ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ⭘ Twinkle, twinkle, little star,
+
+
+ ▊ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▎
+ ▊ Twinkle, twinkle, little star, ▼ ▎
+ ▊ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▎
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -35385,141 +38588,141 @@
font-weight: 700;
}
- .terminal-1590895671-matrix {
+ .terminal-4188177788-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1590895671-title {
+ .terminal-4188177788-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1590895671-r1 { fill: #c5c8c6 }
- .terminal-1590895671-r2 { fill: #e3e3e3 }
- .terminal-1590895671-r3 { fill: #e1e1e1 }
- .terminal-1590895671-r4 { fill: #0178d4 }
- .terminal-1590895671-r5 { fill: #e1e1e1;font-weight: bold }
- .terminal-1590895671-r6 { fill: #575757 }
- .terminal-1590895671-r7 { fill: #4ebf71;font-weight: bold }
- .terminal-1590895671-r8 { fill: #ddedf9;font-weight: bold }
- .terminal-1590895671-r9 { fill: #98e024 }
- .terminal-1590895671-r10 { fill: #262626;font-weight: bold }
- .terminal-1590895671-r11 { fill: #e2e2e2 }
- .terminal-1590895671-r12 { fill: #ddedf9 }
+ .terminal-4188177788-r1 { fill: #c5c8c6 }
+ .terminal-4188177788-r2 { fill: #e3e3e3 }
+ .terminal-4188177788-r3 { fill: #e1e1e1 }
+ .terminal-4188177788-r4 { fill: #0178d4 }
+ .terminal-4188177788-r5 { fill: #e1e1e1;font-weight: bold }
+ .terminal-4188177788-r6 { fill: #575757 }
+ .terminal-4188177788-r7 { fill: #4ebf71;font-weight: bold }
+ .terminal-4188177788-r8 { fill: #ddedf9;font-weight: bold }
+ .terminal-4188177788-r9 { fill: #98e024 }
+ .terminal-4188177788-r10 { fill: #262626;font-weight: bold }
+ .terminal-4188177788-r11 { fill: #e2e2e2 }
+ .terminal-4188177788-r12 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- SelectionListApp
+ SelectionListApp
-
-
-
- ⭘ SelectionListApp
-
-
- ┌ ─ Shall we play some games? ── ┐ ┌ ─ Selected games ───────────── ┐
- │ │ │ [ │
- │ ▐ X ▌ Falken's Maze │ │ 'secret_back_door' , │
- │ ▐ X ▌ Black Jack │ │ 'a_nice_game_of_chess' , │
- │ ▐ X ▌ Gin Rummy │ │ 'fighter_combat' │
- │ ▐ X ▌ Hearts │ │ ] │
- │ ▐ X ▌ Bridge │ └ ────────────────────────────── ┘
- │ ▐ X ▌ Checkers │
- │ ▐ X ▌ Chess │
- │ ▐ X ▌ Poker │
- │ ▐ X ▌ Fighter Combat │
- │ │
- └ ────────────────────────────── ┘
-
-
-
-
-
-
-
+
+
+
+ ⭘ SelectionListApp
+
+
+ ┌─ Shall we play some games? ──┐ ┌─ Selected games ─────────────┐
+ │ │ │ [ │
+ │ ▐ X ▌ Falken's Maze │ │ 'secret_back_door' , │
+ │ ▐ X ▌ Black Jack │ │ 'a_nice_game_of_chess' , │
+ │ ▐ X ▌ Gin Rummy │ │ 'fighter_combat' │
+ │ ▐ X ▌ Hearts │ │ ] │
+ │ ▐ X ▌ Bridge │ └──────────────────────────────┘
+ │ ▐ X ▌ Checkers │
+ │ ▐ X ▌ Chess │
+ │ ▐ X ▌ Poker │
+ │ ▐ X ▌ Fighter Combat │
+ │ │
+ └──────────────────────────────┘
+
+
+
+
+
+
+
@@ -35550,139 +38753,139 @@
font-weight: 700;
}
- .terminal-2928221602-matrix {
+ .terminal-2007146543-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2928221602-title {
+ .terminal-2007146543-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2928221602-r1 { fill: #c5c8c6 }
- .terminal-2928221602-r2 { fill: #e3e3e3 }
- .terminal-2928221602-r3 { fill: #e1e1e1 }
- .terminal-2928221602-r4 { fill: #0178d4 }
- .terminal-2928221602-r5 { fill: #575757 }
- .terminal-2928221602-r6 { fill: #4ebf71;font-weight: bold }
- .terminal-2928221602-r7 { fill: #ddedf9;font-weight: bold }
- .terminal-2928221602-r8 { fill: #262626;font-weight: bold }
- .terminal-2928221602-r9 { fill: #e2e2e2 }
- .terminal-2928221602-r10 { fill: #ddedf9 }
+ .terminal-2007146543-r1 { fill: #c5c8c6 }
+ .terminal-2007146543-r2 { fill: #e3e3e3 }
+ .terminal-2007146543-r3 { fill: #e1e1e1 }
+ .terminal-2007146543-r4 { fill: #0178d4 }
+ .terminal-2007146543-r5 { fill: #575757 }
+ .terminal-2007146543-r6 { fill: #4ebf71;font-weight: bold }
+ .terminal-2007146543-r7 { fill: #ddedf9;font-weight: bold }
+ .terminal-2007146543-r8 { fill: #262626;font-weight: bold }
+ .terminal-2007146543-r9 { fill: #e2e2e2 }
+ .terminal-2007146543-r10 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- SelectionListApp
+ SelectionListApp
-
-
-
- ⭘ SelectionListApp
-
-
- ┌ ─ Shall we play some games? ────────────────────────────────── ┐
- │ │
- │ ▐ X ▌ Falken's Maze │
- │ ▐ X ▌ Black Jack │
- │ ▐ X ▌ Gin Rummy │
- │ ▐ X ▌ Hearts │
- │ ▐ X ▌ Bridge │
- │ ▐ X ▌ Checkers │
- │ ▐ X ▌ Chess │
- │ ▐ X ▌ Poker │
- │ ▐ X ▌ Fighter Combat │
- │ │
- │ │
- │ │
- │ │
- │ │
- └ ────────────────────────────────────────────────────────────── ┘
-
-
-
+
+
+
+ ⭘ SelectionListApp
+
+
+ ┌─ Shall we play some games? ──────────────────────────────────┐
+ │ │
+ │ ▐ X ▌ Falken's Maze │
+ │ ▐ X ▌ Black Jack │
+ │ ▐ X ▌ Gin Rummy │
+ │ ▐ X ▌ Hearts │
+ │ ▐ X ▌ Bridge │
+ │ ▐ X ▌ Checkers │
+ │ ▐ X ▌ Chess │
+ │ ▐ X ▌ Poker │
+ │ ▐ X ▌ Fighter Combat │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ └──────────────────────────────────────────────────────────────┘
+
+
+
@@ -35713,139 +38916,139 @@
font-weight: 700;
}
- .terminal-2928221602-matrix {
+ .terminal-2007146543-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2928221602-title {
+ .terminal-2007146543-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2928221602-r1 { fill: #c5c8c6 }
- .terminal-2928221602-r2 { fill: #e3e3e3 }
- .terminal-2928221602-r3 { fill: #e1e1e1 }
- .terminal-2928221602-r4 { fill: #0178d4 }
- .terminal-2928221602-r5 { fill: #575757 }
- .terminal-2928221602-r6 { fill: #4ebf71;font-weight: bold }
- .terminal-2928221602-r7 { fill: #ddedf9;font-weight: bold }
- .terminal-2928221602-r8 { fill: #262626;font-weight: bold }
- .terminal-2928221602-r9 { fill: #e2e2e2 }
- .terminal-2928221602-r10 { fill: #ddedf9 }
+ .terminal-2007146543-r1 { fill: #c5c8c6 }
+ .terminal-2007146543-r2 { fill: #e3e3e3 }
+ .terminal-2007146543-r3 { fill: #e1e1e1 }
+ .terminal-2007146543-r4 { fill: #0178d4 }
+ .terminal-2007146543-r5 { fill: #575757 }
+ .terminal-2007146543-r6 { fill: #4ebf71;font-weight: bold }
+ .terminal-2007146543-r7 { fill: #ddedf9;font-weight: bold }
+ .terminal-2007146543-r8 { fill: #262626;font-weight: bold }
+ .terminal-2007146543-r9 { fill: #e2e2e2 }
+ .terminal-2007146543-r10 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- SelectionListApp
+ SelectionListApp
-
-
-
- ⭘ SelectionListApp
-
-
- ┌ ─ Shall we play some games? ────────────────────────────────── ┐
- │ │
- │ ▐ X ▌ Falken's Maze │
- │ ▐ X ▌ Black Jack │
- │ ▐ X ▌ Gin Rummy │
- │ ▐ X ▌ Hearts │
- │ ▐ X ▌ Bridge │
- │ ▐ X ▌ Checkers │
- │ ▐ X ▌ Chess │
- │ ▐ X ▌ Poker │
- │ ▐ X ▌ Fighter Combat │
- │ │
- │ │
- │ │
- │ │
- │ │
- └ ────────────────────────────────────────────────────────────── ┘
-
-
-
+
+
+
+ ⭘ SelectionListApp
+
+
+ ┌─ Shall we play some games? ──────────────────────────────────┐
+ │ │
+ │ ▐ X ▌ Falken's Maze │
+ │ ▐ X ▌ Black Jack │
+ │ ▐ X ▌ Gin Rummy │
+ │ ▐ X ▌ Hearts │
+ │ ▐ X ▌ Bridge │
+ │ ▐ X ▌ Checkers │
+ │ ▐ X ▌ Chess │
+ │ ▐ X ▌ Poker │
+ │ ▐ X ▌ Fighter Combat │
+ │ │
+ │ │
+ │ │
+ │ │
+ │ │
+ └──────────────────────────────────────────────────────────────┘
+
+
+
@@ -35876,137 +39079,137 @@
font-weight: 700;
}
- .terminal-4088188211-matrix {
+ .terminal-1104434961-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-4088188211-title {
+ .terminal-1104434961-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-4088188211-r1 { fill: #008000 }
- .terminal-4088188211-r2 { fill: #c5c8c6 }
- .terminal-4088188211-r3 { fill: #e1e1e1 }
+ .terminal-1104434961-r1 { fill: #008000 }
+ .terminal-1104434961-r2 { fill: #c5c8c6 }
+ .terminal-1104434961-r3 { fill: #e1e1e1 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- SortApp
+ SortApp
-
-
-
- ┌ ──────────────────────── ┐ ┌ ───────────────────────── ┐ ┌ ───────────────────────── ┐
- │ 5 │ │ 1 │ │ 5 │
- │ │ └ ───────────────────────── ┘ │ │
- │ │ ┌ ───────────────────────── ┐ │ │
- │ │ │ 2 │ │ │
- │ │ │ │ │ │
- └ ──────────────────────── ┘ └ ───────────────────────── ┘ └ ───────────────────────── ┘
- ┌ ──────────────────────── ┐ ┌ ───────────────────────── ┐ ┌ ───────────────────────── ┐
- │ 1 │ │ 3 │ │ 4 │
- └ ──────────────────────── ┘ │ │ │ │
- ┌ ──────────────────────── ┐ │ │ │ │
- │ 3 │ └ ───────────────────────── ┘ │ │
- │ │ ┌ ───────────────────────── ┐ └ ───────────────────────── ┘
- │ │ │ 4 │ ┌ ───────────────────────── ┐
- └ ──────────────────────── ┘ │ │ │ 3 │
- ┌ ──────────────────────── ┐ │ │ │ │
- │ 2 │ │ │ │ │
- │ │ └ ───────────────────────── ┘ └ ───────────────────────── ┘
- └ ──────────────────────── ┘ ┌ ───────────────────────── ┐ ┌ ───────────────────────── ┐
- ┌ ──────────────────────── ┐ │ 5 │ │ 2 │
- │ 4 │ │ │ │ │
- │ │ │ │ └ ───────────────────────── ┘
- │ │ │ │ ┌ ───────────────────────── ┐
- │ │ │ │ │ 1 │
- └ ──────────────────────── ┘ └ ───────────────────────── ┘ └ ───────────────────────── ┘
+
+
+
+ ┌────────────────────────┐┌─────────────────────────┐┌─────────────────────────┐
+ │ 5 ││ 1 ││ 5 │
+ │ │└─────────────────────────┘│ │
+ │ │┌─────────────────────────┐│ │
+ │ ││ 2 ││ │
+ │ ││ ││ │
+ └────────────────────────┘└─────────────────────────┘└─────────────────────────┘
+ ┌────────────────────────┐┌─────────────────────────┐┌─────────────────────────┐
+ │ 1 ││ 3 ││ 4 │
+ └────────────────────────┘│ ││ │
+ ┌────────────────────────┐│ ││ │
+ │ 3 │└─────────────────────────┘│ │
+ │ │┌─────────────────────────┐└─────────────────────────┘
+ │ ││ 4 │┌─────────────────────────┐
+ └────────────────────────┘│ ││ 3 │
+ ┌────────────────────────┐│ ││ │
+ │ 2 ││ ││ │
+ │ │└─────────────────────────┘└─────────────────────────┘
+ └────────────────────────┘┌─────────────────────────┐┌─────────────────────────┐
+ ┌────────────────────────┐│ 5 ││ 2 │
+ │ 4 ││ ││ │
+ │ ││ │└─────────────────────────┘
+ │ ││ │┌─────────────────────────┐
+ │ ││ ││ 1 │
+ └────────────────────────┘└─────────────────────────┘└─────────────────────────┘
@@ -36036,682 +39239,682 @@
font-weight: 700;
}
- .terminal-2491064415-matrix {
+ .terminal-1281792983-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2491064415-title {
- font-size: 18px;
- font-weight: bold;
- font-family: arial;
- }
-
- .terminal-2491064415-r1 { fill: #e1e1e1 }
- .terminal-2491064415-r2 { fill: #c5c8c6 }
- .terminal-2491064415-r3 { fill: #fea62b }
- .terminal-2491064415-r4 { fill: #eea831 }
- .terminal-2491064415-r5 { fill: #d0ac3c }
- .terminal-2491064415-r6 { fill: #c2ae42 }
- .terminal-2491064415-r7 { fill: #b4b048 }
- .terminal-2491064415-r8 { fill: #9ab452 }
- .terminal-2491064415-r9 { fill: #8db557 }
- .terminal-2491064415-r10 { fill: #78b860 }
- .terminal-2491064415-r11 { fill: #6eba63 }
- .terminal-2491064415-r12 { fill: #66bb67 }
- .terminal-2491064415-r13 { fill: #59bd6c }
- .terminal-2491064415-r14 { fill: #54be6e }
- .terminal-2491064415-r15 { fill: #4ebe70 }
- .terminal-2491064415-r16 { fill: #50be70 }
- .terminal-2491064415-r17 { fill: #57bd6d }
- .terminal-2491064415-r18 { fill: #5cbc6b }
- .terminal-2491064415-r19 { fill: #63bb68 }
- .terminal-2491064415-r20 { fill: #74b961 }
- .terminal-2491064415-r21 { fill: #7eb85d }
- .terminal-2491064415-r22 { fill: #94b454 }
- .terminal-2491064415-r23 { fill: #a1b34f }
- .terminal-2491064415-r24 { fill: #aeb14a }
- .terminal-2491064415-r25 { fill: #caad3f }
- .terminal-2491064415-r26 { fill: #d9ab39 }
- .terminal-2491064415-r27 { fill: #f7a62d }
- .terminal-2491064415-r28 { fill: #f5a72e }
- .terminal-2491064415-r29 { fill: #d7ab3a }
- .terminal-2491064415-r30 { fill: #c8ad40 }
- .terminal-2491064415-r31 { fill: #baaf45 }
- .terminal-2491064415-r32 { fill: #9fb350 }
- .terminal-2491064415-r33 { fill: #93b555 }
- .terminal-2491064415-r34 { fill: #7cb85e }
- .terminal-2491064415-r35 { fill: #72b962 }
- .terminal-2491064415-r36 { fill: #6abb65 }
- .terminal-2491064415-r37 { fill: #5bbd6b }
- .terminal-2491064415-r38 { fill: #56bd6d }
- .terminal-2491064415-r39 { fill: #4fbe70 }
- .terminal-2491064415-r40 { fill: #55bd6e }
- .terminal-2491064415-r41 { fill: #5abd6c }
- .terminal-2491064415-r42 { fill: #60bc69 }
- .terminal-2491064415-r43 { fill: #70ba63 }
- .terminal-2491064415-r44 { fill: #79b85f }
- .terminal-2491064415-r45 { fill: #8fb556 }
- .terminal-2491064415-r46 { fill: #9bb352 }
- .terminal-2491064415-r47 { fill: #a8b24c }
- .terminal-2491064415-r48 { fill: #c4ae41 }
- .terminal-2491064415-r49 { fill: #d3ac3c }
- .terminal-2491064415-r50 { fill: #f1a730 }
- .terminal-2491064415-r51 { fill: #fba62b }
- .terminal-2491064415-r52 { fill: #ddaa37 }
- .terminal-2491064415-r53 { fill: #ceac3d }
- .terminal-2491064415-r54 { fill: #c0ae43 }
- .terminal-2491064415-r55 { fill: #a5b24e }
- .terminal-2491064415-r56 { fill: #98b453 }
- .terminal-2491064415-r57 { fill: #81b75c }
- .terminal-2491064415-r58 { fill: #76b960 }
- .terminal-2491064415-r59 { fill: #6dba64 }
- .terminal-2491064415-r60 { fill: #5ebc6a }
- .terminal-2491064415-r61 { fill: #58bd6c }
- .terminal-2491064415-r62 { fill: #50be6f }
- .terminal-2491064415-r63 { fill: #4ebf71 }
- .terminal-2491064415-r64 { fill: #53be6e }
- .terminal-2491064415-r65 { fill: #58bd6d }
- .terminal-2491064415-r66 { fill: #5dbc6a }
- .terminal-2491064415-r67 { fill: #6cba64 }
- .terminal-2491064415-r68 { fill: #75b961 }
- .terminal-2491064415-r69 { fill: #8ab658 }
- .terminal-2491064415-r70 { fill: #96b454 }
- .terminal-2491064415-r71 { fill: #a3b24f }
- .terminal-2491064415-r72 { fill: #beaf44 }
- .terminal-2491064415-r73 { fill: #ccac3e }
- .terminal-2491064415-r74 { fill: #7bb85f }
- .terminal-2491064415-r75 { fill: #89b659 }
- .terminal-2491064415-r76 { fill: #97b453 }
- .terminal-2491064415-r77 { fill: #b1b049 }
- .terminal-2491064415-r78 { fill: #d3ac3b }
- .terminal-2491064415-r79 { fill: #ddaa38 }
- .terminal-2491064415-r80 { fill: #e5a934 }
- .terminal-2491064415-r81 { fill: #f2a72f }
- .terminal-2491064415-r82 { fill: #fda62b }
- .terminal-2491064415-r83 { fill: #f4a72e }
- .terminal-2491064415-r84 { fill: #efa830 }
- .terminal-2491064415-r85 { fill: #e8a933 }
- .terminal-2491064415-r86 { fill: #cdac3e }
- .terminal-2491064415-r87 { fill: #b7b047 }
- .terminal-2491064415-r88 { fill: #aab14c }
- .terminal-2491064415-r89 { fill: #9db351 }
- .terminal-2491064415-r90 { fill: #83b75b }
- .terminal-2491064415-r91 { fill: #91b556 }
- .terminal-2491064415-r92 { fill: #acb14b }
- .terminal-2491064415-r93 { fill: #b8af46 }
- .terminal-2491064415-r94 { fill: #cfac3d }
- .terminal-2491064415-r95 { fill: #e1a936 }
- .terminal-2491064415-r96 { fill: #f0a730 }
- .terminal-2491064415-r97 { fill: #fca62b }
- .terminal-2491064415-r98 { fill: #f6a72d }
- .terminal-2491064415-r99 { fill: #f1a72f }
- .terminal-2491064415-r100 { fill: #eba832 }
- .terminal-2491064415-r101 { fill: #dbaa38 }
- .terminal-2491064415-r102 { fill: #d2ac3c }
- .terminal-2491064415-r103 { fill: #bcaf45 }
- .terminal-2491064415-r104 { fill: #b0b149 }
- .terminal-2491064415-r105 { fill: #87b65a }
- .terminal-2491064415-r106 { fill: #78b85f }
- .terminal-2491064415-r107 { fill: #5abd6b }
- .terminal-2491064415-r108 { fill: #6eba64 }
- .terminal-2491064415-r109 { fill: #7db85e }
- .terminal-2491064415-r110 { fill: #8bb658 }
- .terminal-2491064415-r111 { fill: #a6b24d }
- .terminal-2491064415-r112 { fill: #b3b048 }
- .terminal-2491064415-r113 { fill: #d5ab3b }
- .terminal-2491064415-r114 { fill: #deaa37 }
- .terminal-2491064415-r115 { fill: #eda831 }
- .terminal-2491064415-r116 { fill: #f3a72f }
- .terminal-2491064415-r117 { fill: #fba62c }
- .terminal-2491064415-r118 { fill: #f8a62d }
- .terminal-2491064415-r119 { fill: #f3a72e }
- .terminal-2491064415-r120 { fill: #dfaa37 }
- .terminal-2491064415-r121 { fill: #d6ab3a }
- .terminal-2491064415-r122 { fill: #c1ae43 }
- .terminal-2491064415-r123 { fill: #b5b047 }
- .terminal-2491064415-r124 { fill: #7fb85d }
- .terminal-2491064415-r125 { fill: #f89c2f }
- .terminal-2491064415-r126 { fill: #ec8a37 }
- .terminal-2491064415-r127 { fill: #e6823b }
- .terminal-2491064415-r128 { fill: #e1793f }
- .terminal-2491064415-r129 { fill: #d66946 }
- .terminal-2491064415-r130 { fill: #d26249 }
- .terminal-2491064415-r131 { fill: #c9554f }
- .terminal-2491064415-r132 { fill: #c54f52 }
- .terminal-2491064415-r133 { fill: #c24a54 }
- .terminal-2491064415-r134 { fill: #bd4257 }
- .terminal-2491064415-r135 { fill: #bb4059 }
- .terminal-2491064415-r136 { fill: #b93c5a }
- .terminal-2491064415-r137 { fill: #b93d5a }
- .terminal-2491064415-r138 { fill: #bc4158 }
- .terminal-2491064415-r139 { fill: #be4456 }
- .terminal-2491064415-r140 { fill: #c14855 }
- .terminal-2491064415-r141 { fill: #c75350 }
- .terminal-2491064415-r142 { fill: #cb584d }
- .terminal-2491064415-r143 { fill: #d46647 }
- .terminal-2491064415-r144 { fill: #d96e44 }
- .terminal-2491064415-r145 { fill: #de7640 }
- .terminal-2491064415-r146 { fill: #e98738 }
- .terminal-2491064415-r147 { fill: #ef8f34 }
- .terminal-2491064415-r148 { fill: #fba22c }
- .terminal-2491064415-r149 { fill: #faa02d }
- .terminal-2491064415-r150 { fill: #ee8e35 }
- .terminal-2491064415-r151 { fill: #e98539 }
- .terminal-2491064415-r152 { fill: #e37d3d }
- .terminal-2491064415-r153 { fill: #d86d44 }
- .terminal-2491064415-r154 { fill: #d46548 }
- .terminal-2491064415-r155 { fill: #cb584e }
- .terminal-2491064415-r156 { fill: #c75250 }
- .terminal-2491064415-r157 { fill: #c44c53 }
- .terminal-2491064415-r158 { fill: #be4457 }
- .terminal-2491064415-r159 { fill: #bd4357 }
- .terminal-2491064415-r160 { fill: #c04755 }
- .terminal-2491064415-r161 { fill: #c65051 }
- .terminal-2491064415-r162 { fill: #ca564f }
- .terminal-2491064415-r163 { fill: #d26349 }
- .terminal-2491064415-r164 { fill: #d76a45 }
- .terminal-2491064415-r165 { fill: #dc7242 }
- .terminal-2491064415-r166 { fill: #e7833a }
- .terminal-2491064415-r167 { fill: #ed8c36 }
- .terminal-2491064415-r168 { fill: #f89e2e }
- .terminal-2491064415-r169 { fill: #fda42b }
- .terminal-2491064415-r170 { fill: #f19233 }
- .terminal-2491064415-r171 { fill: #eb8937 }
- .terminal-2491064415-r172 { fill: #e5803b }
- .terminal-2491064415-r173 { fill: #db7043 }
- .terminal-2491064415-r174 { fill: #d66846 }
- .terminal-2491064415-r175 { fill: #cd5a4d }
- .terminal-2491064415-r176 { fill: #c9544f }
- .terminal-2491064415-r177 { fill: #bf4556 }
- .terminal-2491064415-r178 { fill: #bd4258 }
- .terminal-2491064415-r179 { fill: #ba3d5a }
- .terminal-2491064415-r180 { fill: #b93c5b }
- .terminal-2491064415-r181 { fill: #bb3f59 }
- .terminal-2491064415-r182 { fill: #bc4258 }
- .terminal-2491064415-r183 { fill: #c44e52 }
- .terminal-2491064415-r184 { fill: #c85350 }
- .terminal-2491064415-r185 { fill: #d0604a }
- .terminal-2491064415-r186 { fill: #d56747 }
- .terminal-2491064415-r187 { fill: #da6f43 }
- .terminal-2491064415-r188 { fill: #e57f3c }
- .terminal-2491064415-r189 { fill: #ea8838 }
- .terminal-2491064415-r190 { fill: #be4556 }
- .terminal-2491064415-r191 { fill: #ca574e }
- .terminal-2491064415-r192 { fill: #d05f4a }
- .terminal-2491064415-r193 { fill: #d56846 }
- .terminal-2491064415-r194 { fill: #e0783f }
- .terminal-2491064415-r195 { fill: #e47f3c }
- .terminal-2491064415-r196 { fill: #f49731 }
- .terminal-2491064415-r197 { fill: #f99f2e }
- .terminal-2491064415-r198 { fill: #fba12c }
- .terminal-2491064415-r199 { fill: #fda52b }
- .terminal-2491064415-r200 { fill: #f89d2f }
- .terminal-2491064415-r201 { fill: #f59930 }
- .terminal-2491064415-r202 { fill: #ef8e35 }
- .terminal-2491064415-r203 { fill: #eb8938 }
- .terminal-2491064415-r204 { fill: #e27b3e }
- .terminal-2491064415-r205 { fill: #dd7341 }
- .terminal-2491064415-r206 { fill: #d86b45 }
- .terminal-2491064415-r207 { fill: #c75251 }
- .terminal-2491064415-r208 { fill: #cd5c4c }
- .terminal-2491064415-r209 { fill: #d36448 }
- .terminal-2491064415-r210 { fill: #de7441 }
- .terminal-2491064415-r211 { fill: #e27c3d }
- .terminal-2491064415-r212 { fill: #ef8f35 }
- .terminal-2491064415-r213 { fill: #f29532 }
- .terminal-2491064415-r214 { fill: #f89d2e }
- .terminal-2491064415-r215 { fill: #f99e2e }
- .terminal-2491064415-r216 { fill: #f69a30 }
- .terminal-2491064415-r217 { fill: #f09134 }
- .terminal-2491064415-r218 { fill: #ec8b36 }
- .terminal-2491064415-r219 { fill: #e47e3c }
- .terminal-2491064415-r220 { fill: #df7740 }
- .terminal-2491064415-r221 { fill: #cf5e4b }
- .terminal-2491064415-r222 { fill: #be4357 }
- .terminal-2491064415-r223 { fill: #d1614a }
- .terminal-2491064415-r224 { fill: #db7142 }
- .terminal-2491064415-r225 { fill: #e0793f }
- .terminal-2491064415-r226 { fill: #ed8d36 }
- .terminal-2491064415-r227 { fill: #f79c2f }
- .terminal-2491064415-r228 { fill: #f99f2d }
- .terminal-2491064415-r229 { fill: #fca42b }
- .terminal-2491064415-r230 { fill: #fa9f2d }
- .terminal-2491064415-r231 { fill: #f29333 }
- .terminal-2491064415-r232 { fill: #e6813b }
- .terminal-2491064415-r233 { fill: #e17a3e }
- .terminal-2491064415-r234 { fill: #d16249 }
- .terminal-2491064415-r235 { fill: #cc594d }
- .terminal-2491064415-r236 { fill: #153954 }
- .terminal-2491064415-r237 { fill: #133e5f }
- .terminal-2491064415-r238 { fill: #0f4974 }
- .terminal-2491064415-r239 { fill: #0e4e7f }
- .terminal-2491064415-r240 { fill: #0c5389 }
- .terminal-2491064415-r241 { fill: #095c9c }
- .terminal-2491064415-r242 { fill: #0861a5 }
- .terminal-2491064415-r243 { fill: #0568b5 }
- .terminal-2491064415-r244 { fill: #046cbc }
- .terminal-2491064415-r245 { fill: #036fc2 }
- .terminal-2491064415-r246 { fill: #0273cb }
- .terminal-2491064415-r247 { fill: #0175cf }
- .terminal-2491064415-r248 { fill: #0177d3 }
- .terminal-2491064415-r249 { fill: #0177d2 }
- .terminal-2491064415-r250 { fill: #0274cd }
- .terminal-2491064415-r251 { fill: #0272c9 }
- .terminal-2491064415-r252 { fill: #0370c4 }
- .terminal-2491064415-r253 { fill: #056ab8 }
- .terminal-2491064415-r254 { fill: #0666b0 }
- .terminal-2491064415-r255 { fill: #095ea0 }
- .terminal-2491064415-r256 { fill: #0a5a97 }
- .terminal-2491064415-r257 { fill: #0b558d }
- .terminal-2491064415-r258 { fill: #0f4b79 }
- .terminal-2491064415-r259 { fill: #10466e }
- .terminal-2491064415-r260 { fill: #143b58 }
- .terminal-2491064415-r261 { fill: #143c5a }
- .terminal-2491064415-r262 { fill: #104670 }
- .terminal-2491064415-r263 { fill: #0e4c7a }
- .terminal-2491064415-r264 { fill: #0d5185 }
- .terminal-2491064415-r265 { fill: #0a5a98 }
- .terminal-2491064415-r266 { fill: #085fa1 }
- .terminal-2491064415-r267 { fill: #0667b2 }
- .terminal-2491064415-r268 { fill: #056ab9 }
- .terminal-2491064415-r269 { fill: #046dbf }
- .terminal-2491064415-r270 { fill: #0273c9 }
- .terminal-2491064415-r271 { fill: #0174cd }
- .terminal-2491064415-r272 { fill: #0175ce }
- .terminal-2491064415-r273 { fill: #0371c6 }
- .terminal-2491064415-r274 { fill: #046bbb }
- .terminal-2491064415-r275 { fill: #0568b4 }
- .terminal-2491064415-r276 { fill: #0860a4 }
- .terminal-2491064415-r277 { fill: #095c9b }
- .terminal-2491064415-r278 { fill: #0b5791 }
- .terminal-2491064415-r279 { fill: #0e4d7d }
- .terminal-2491064415-r280 { fill: #104873 }
- .terminal-2491064415-r281 { fill: #133d5d }
- .terminal-2491064415-r282 { fill: #143955 }
- .terminal-2491064415-r283 { fill: #11446b }
- .terminal-2491064415-r284 { fill: #0f4976 }
- .terminal-2491064415-r285 { fill: #0e4f80 }
- .terminal-2491064415-r286 { fill: #0a5894 }
- .terminal-2491064415-r287 { fill: #095d9d }
- .terminal-2491064415-r288 { fill: #0665ae }
- .terminal-2491064415-r289 { fill: #0569b6 }
- .terminal-2491064415-r290 { fill: #0272c7 }
- .terminal-2491064415-r291 { fill: #0274cc }
- .terminal-2491064415-r292 { fill: #0177d1 }
- .terminal-2491064415-r293 { fill: #0178d4 }
- .terminal-2491064415-r294 { fill: #0176cf }
- .terminal-2491064415-r295 { fill: #0272c8 }
- .terminal-2491064415-r296 { fill: #046dbd }
- .terminal-2491064415-r297 { fill: #0569b7 }
- .terminal-2491064415-r298 { fill: #0762a7 }
- .terminal-2491064415-r299 { fill: #095e9f }
- .terminal-2491064415-r300 { fill: #0a5996 }
- .terminal-2491064415-r301 { fill: #0d4f82 }
- .terminal-2491064415-r302 { fill: #0f4a77 }
- .terminal-2491064415-r303 { fill: #0667b3 }
- .terminal-2491064415-r304 { fill: #0762a8 }
- .terminal-2491064415-r305 { fill: #095d9e }
- .terminal-2491064415-r306 { fill: #0c548b }
- .terminal-2491064415-r307 { fill: #104872 }
- .terminal-2491064415-r308 { fill: #124165 }
- .terminal-2491064415-r309 { fill: #133d5c }
- .terminal-2491064415-r310 { fill: #143954 }
- .terminal-2491064415-r311 { fill: #133c5a }
- .terminal-2491064415-r312 { fill: #133e5e }
- .terminal-2491064415-r313 { fill: #124063 }
- .terminal-2491064415-r314 { fill: #10466f }
- .terminal-2491064415-r315 { fill: #0c5287 }
- .terminal-2491064415-r316 { fill: #0b5690 }
- .terminal-2491064415-r317 { fill: #0a5b9a }
- .terminal-2491064415-r318 { fill: #056ab7 }
- .terminal-2491064415-r319 { fill: #0764ad }
- .terminal-2491064415-r320 { fill: #085fa2 }
- .terminal-2491064415-r321 { fill: #0b568f }
- .terminal-2491064415-r322 { fill: #0d5186 }
- .terminal-2491064415-r323 { fill: #0f4975 }
- .terminal-2491064415-r324 { fill: #114368 }
- .terminal-2491064415-r325 { fill: #133d5e }
- .terminal-2491064415-r326 { fill: #143b59 }
- .terminal-2491064415-r327 { fill: #123f61 }
- .terminal-2491064415-r328 { fill: #11456c }
- .terminal-2491064415-r329 { fill: #0d5083 }
- .terminal-2491064415-r330 { fill: #0c548c }
- .terminal-2491064415-r331 { fill: #0763aa }
- .terminal-2491064415-r332 { fill: #0273ca }
- .terminal-2491064415-r333 { fill: #0667b1 }
- .terminal-2491064415-r334 { fill: #0761a7 }
- .terminal-2491064415-r335 { fill: #0b5893 }
- .terminal-2491064415-r336 { fill: #0c538a }
- .terminal-2491064415-r337 { fill: #104771 }
- .terminal-2491064415-r338 { fill: #133e60 }
- .terminal-2491064415-r339 { fill: #133c5b }
- .terminal-2491064415-r340 { fill: #143956 }
- .terminal-2491064415-r341 { fill: #143a58 }
- .terminal-2491064415-r342 { fill: #11436a }
- .terminal-2491064415-r343 { fill: #104770 }
- .terminal-2491064415-r344 { fill: #0e4e80 }
- .terminal-2491064415-r345 { fill: #0c5288 }
- .terminal-2491064415-r346 { fill: #4c2730 }
- .terminal-2491064415-r347 { fill: #552833 }
- .terminal-2491064415-r348 { fill: #672c3b }
- .terminal-2491064415-r349 { fill: #702e3e }
- .terminal-2491064415-r350 { fill: #792f41 }
- .terminal-2491064415-r351 { fill: #893248 }
- .terminal-2491064415-r352 { fill: #91344b }
- .terminal-2491064415-r353 { fill: #9e3650 }
- .terminal-2491064415-r354 { fill: #a43852 }
- .terminal-2491064415-r355 { fill: #a93954 }
- .terminal-2491064415-r356 { fill: #b13a58 }
- .terminal-2491064415-r357 { fill: #b43b59 }
- .terminal-2491064415-r358 { fill: #b83b5a }
- .terminal-2491064415-r359 { fill: #b73b5a }
- .terminal-2491064415-r360 { fill: #b33a58 }
- .terminal-2491064415-r361 { fill: #af3a57 }
- .terminal-2491064415-r362 { fill: #ab3955 }
- .terminal-2491064415-r363 { fill: #a13751 }
- .terminal-2491064415-r364 { fill: #9b364f }
- .terminal-2491064415-r365 { fill: #8d3349 }
- .terminal-2491064415-r366 { fill: #853246 }
- .terminal-2491064415-r367 { fill: #7d3043 }
- .terminal-2491064415-r368 { fill: #6b2d3c }
- .terminal-2491064415-r369 { fill: #622b38 }
- .terminal-2491064415-r370 { fill: #502731 }
- .terminal-2491064415-r371 { fill: #512832 }
- .terminal-2491064415-r372 { fill: #632b39 }
- .terminal-2491064415-r373 { fill: #6d2d3d }
- .terminal-2491064415-r374 { fill: #752f40 }
- .terminal-2491064415-r375 { fill: #863247 }
- .terminal-2491064415-r376 { fill: #8e334a }
- .terminal-2491064415-r377 { fill: #9c364f }
- .terminal-2491064415-r378 { fill: #a23751 }
- .terminal-2491064415-r379 { fill: #a73854 }
- .terminal-2491064415-r380 { fill: #b03a57 }
- .terminal-2491064415-r381 { fill: #b13a57 }
- .terminal-2491064415-r382 { fill: #ad3956 }
- .terminal-2491064415-r383 { fill: #a33752 }
- .terminal-2491064415-r384 { fill: #9d3650 }
- .terminal-2491064415-r385 { fill: #90344a }
- .terminal-2491064415-r386 { fill: #883247 }
- .terminal-2491064415-r387 { fill: #803144 }
- .terminal-2491064415-r388 { fill: #6f2d3e }
- .terminal-2491064415-r389 { fill: #662c3a }
- .terminal-2491064415-r390 { fill: #542833 }
- .terminal-2491064415-r391 { fill: #4d2730 }
- .terminal-2491064415-r392 { fill: #602a37 }
- .terminal-2491064415-r393 { fill: #692c3b }
- .terminal-2491064415-r394 { fill: #722e3f }
- .terminal-2491064415-r395 { fill: #833145 }
- .terminal-2491064415-r396 { fill: #8a3348 }
- .terminal-2491064415-r397 { fill: #99354e }
- .terminal-2491064415-r398 { fill: #9f3751 }
- .terminal-2491064415-r399 { fill: #a53853 }
- .terminal-2491064415-r400 { fill: #ae3a56 }
- .terminal-2491064415-r401 { fill: #b23a58 }
- .terminal-2491064415-r402 { fill: #b53b59 }
- .terminal-2491064415-r403 { fill: #a63853 }
- .terminal-2491064415-r404 { fill: #a03751 }
- .terminal-2491064415-r405 { fill: #93344c }
- .terminal-2491064415-r406 { fill: #8c3349 }
- .terminal-2491064415-r407 { fill: #843146 }
- .terminal-2491064415-r408 { fill: #732e3f }
- .terminal-2491064415-r409 { fill: #6a2c3c }
- .terminal-2491064415-r410 { fill: #9d364f }
- .terminal-2491064415-r411 { fill: #94344c }
- .terminal-2491064415-r412 { fill: #8b3349 }
- .terminal-2491064415-r413 { fill: #7b3042 }
- .terminal-2491064415-r414 { fill: #602a38 }
- .terminal-2491064415-r415 { fill: #5b2936 }
- .terminal-2491064415-r416 { fill: #532832 }
- .terminal-2491064415-r417 { fill: #592935 }
- .terminal-2491064415-r418 { fill: #772f41 }
- .terminal-2491064415-r419 { fill: #7f3044 }
- .terminal-2491064415-r420 { fill: #873247 }
- .terminal-2491064415-r421 { fill: #a23752 }
- .terminal-2491064415-r422 { fill: #97354d }
- .terminal-2491064415-r423 { fill: #8f334a }
- .terminal-2491064415-r424 { fill: #7e3043 }
- .terminal-2491064415-r425 { fill: #762f40 }
- .terminal-2491064415-r426 { fill: #682c3b }
- .terminal-2491064415-r427 { fill: #622b39 }
- .terminal-2491064415-r428 { fill: #5d2a36 }
- .terminal-2491064415-r429 { fill: #532833 }
- .terminal-2491064415-r430 { fill: #572934 }
- .terminal-2491064415-r431 { fill: #612b38 }
- .terminal-2491064415-r432 { fill: #672c3a }
- .terminal-2491064415-r433 { fill: #742e40 }
- .terminal-2491064415-r434 { fill: #7c3043 }
- .terminal-2491064415-r435 { fill: #95354c }
- .terminal-2491064415-r436 { fill: #a43853 }
- .terminal-2491064415-r437 { fill: #92344b }
- .terminal-2491064415-r438 { fill: #813145 }
- .terminal-2491064415-r439 { fill: #7a2f42 }
- .terminal-2491064415-r440 { fill: #652b39 }
- .terminal-2491064415-r441 { fill: #5f2a37 }
- .terminal-2491064415-r442 { fill: #562834 }
- .terminal-2491064415-r443 { fill: #522832 }
- .terminal-2491064415-r444 { fill: #4f2731 }
- .terminal-2491064415-r445 { fill: #5e2a37 }
- .terminal-2491064415-r446 { fill: #642b39 }
- .terminal-2491064415-r447 { fill: #712e3e }
- .terminal-2491064415-r448 { fill: #782f41 }
- .terminal-2491064415-r449 { fill: #9a364e }
- .terminal-2491064415-r450 { fill: #2c4e36 }
- .terminal-2491064415-r451 { fill: #2e573b }
- .terminal-2491064415-r452 { fill: #346a45 }
- .terminal-2491064415-r453 { fill: #377449 }
- .terminal-2491064415-r454 { fill: #3a7d4e }
- .terminal-2491064415-r455 { fill: #3f8e57 }
- .terminal-2491064415-r456 { fill: #41955b }
- .terminal-2491064415-r457 { fill: #45a362 }
- .terminal-2491064415-r458 { fill: #47a965 }
- .terminal-2491064415-r459 { fill: #49af68 }
- .terminal-2491064415-r460 { fill: #4bb76d }
- .terminal-2491064415-r461 { fill: #4cba6e }
- .terminal-2491064415-r462 { fill: #4dbe70 }
- .terminal-2491064415-r463 { fill: #4dbd70 }
- .terminal-2491064415-r464 { fill: #4cb96d }
- .terminal-2491064415-r465 { fill: #4bb56c }
- .terminal-2491064415-r466 { fill: #49b169 }
- .terminal-2491064415-r467 { fill: #46a664 }
- .terminal-2491064415-r468 { fill: #44a060 }
- .terminal-2491064415-r469 { fill: #409159 }
- .terminal-2491064415-r470 { fill: #3d8955 }
- .terminal-2491064415-r471 { fill: #3b8050 }
- .terminal-2491064415-r472 { fill: #356e47 }
- .terminal-2491064415-r473 { fill: #336542 }
- .terminal-2491064415-r474 { fill: #2d5238 }
- .terminal-2491064415-r475 { fill: #2d5338 }
- .terminal-2491064415-r476 { fill: #336642 }
- .terminal-2491064415-r477 { fill: #367047 }
- .terminal-2491064415-r478 { fill: #39794c }
- .terminal-2491064415-r479 { fill: #3e8a55 }
- .terminal-2491064415-r480 { fill: #409259 }
- .terminal-2491064415-r481 { fill: #44a161 }
- .terminal-2491064415-r482 { fill: #46a764 }
- .terminal-2491064415-r483 { fill: #48ac67 }
- .terminal-2491064415-r484 { fill: #4bb66c }
- .terminal-2491064415-r485 { fill: #4cb96e }
- .terminal-2491064415-r486 { fill: #4bb76c }
- .terminal-2491064415-r487 { fill: #4ab36a }
- .terminal-2491064415-r488 { fill: #45a262 }
- .terminal-2491064415-r489 { fill: #41945a }
- .terminal-2491064415-r490 { fill: #3e8c56 }
- .terminal-2491064415-r491 { fill: #3c8452 }
- .terminal-2491064415-r492 { fill: #377249 }
- .terminal-2491064415-r493 { fill: #346944 }
- .terminal-2491064415-r494 { fill: #2e563a }
- .terminal-2491064415-r495 { fill: #2c4f36 }
- .terminal-2491064415-r496 { fill: #326240 }
- .terminal-2491064415-r497 { fill: #356c45 }
- .terminal-2491064415-r498 { fill: #37754a }
- .terminal-2491064415-r499 { fill: #3d8753 }
- .terminal-2491064415-r500 { fill: #3f8f58 }
- .terminal-2491064415-r501 { fill: #449e5f }
- .terminal-2491064415-r502 { fill: #46a463 }
- .terminal-2491064415-r503 { fill: #47aa66 }
- .terminal-2491064415-r504 { fill: #4ab46b }
- .terminal-2491064415-r505 { fill: #4bb86d }
- .terminal-2491064415-r506 { fill: #4cbb6f }
- .terminal-2491064415-r507 { fill: #4cb86d }
- .terminal-2491064415-r508 { fill: #48ab66 }
- .terminal-2491064415-r509 { fill: #46a563 }
- .terminal-2491064415-r510 { fill: #42985c }
- .terminal-2491064415-r511 { fill: #3f9058 }
- .terminal-2491064415-r512 { fill: #3d8854 }
- .terminal-2491064415-r513 { fill: #38764b }
- .terminal-2491064415-r514 { fill: #356d46 }
- .terminal-2491064415-r515 { fill: #4bb56b }
- .terminal-2491064415-r516 { fill: #45a261 }
- .terminal-2491064415-r517 { fill: #42985d }
- .terminal-2491064415-r518 { fill: #3a7e4f }
- .terminal-2491064415-r519 { fill: #38774b }
- .terminal-2491064415-r520 { fill: #326341 }
- .terminal-2491064415-r521 { fill: #305d3e }
- .terminal-2491064415-r522 { fill: #2e5539 }
- .terminal-2491064415-r523 { fill: #2d5339 }
- .terminal-2491064415-r524 { fill: #2e573a }
- .terminal-2491064415-r525 { fill: #305b3d }
- .terminal-2491064415-r526 { fill: #356c46 }
- .terminal-2491064415-r527 { fill: #397b4d }
- .terminal-2491064415-r528 { fill: #3c8351 }
- .terminal-2491064415-r529 { fill: #439c5f }
- .terminal-2491064415-r530 { fill: #40935a }
- .terminal-2491064415-r531 { fill: #3b8251 }
- .terminal-2491064415-r532 { fill: #397a4d }
- .terminal-2491064415-r533 { fill: #356b45 }
- .terminal-2491064415-r534 { fill: #31603f }
- .terminal-2491064415-r535 { fill: #2e553a }
- .terminal-2491064415-r536 { fill: #2f593c }
- .terminal-2491064415-r537 { fill: #346a44 }
- .terminal-2491064415-r538 { fill: #38784c }
- .terminal-2491064415-r539 { fill: #429a5d }
- .terminal-2491064415-r540 { fill: #44a061 }
- .terminal-2491064415-r541 { fill: #42975c }
- .terminal-2491064415-r542 { fill: #3c8553 }
- .terminal-2491064415-r543 { fill: #336843 }
- .terminal-2491064415-r544 { fill: #2f583b }
- .terminal-2491064415-r545 { fill: #2e5439 }
- .terminal-2491064415-r546 { fill: #2d5137 }
- .terminal-2491064415-r547 { fill: #2d5439 }
- .terminal-2491064415-r548 { fill: #316140 }
- .terminal-2491064415-r549 { fill: #336743 }
- .terminal-2491064415-r550 { fill: #37744a }
- .terminal-2491064415-r551 { fill: #3a7c4e }
- .terminal-2491064415-r552 { fill: #41965b }
- .terminal-2491064415-r553 { fill: #449f60 }
-
-
-
-
+ .terminal-1281792983-title {
+ font-size: 18px;
+ font-weight: bold;
+ font-family: arial;
+ }
+
+ .terminal-1281792983-r1 { fill: #e1e1e1 }
+ .terminal-1281792983-r2 { fill: #c5c8c6 }
+ .terminal-1281792983-r3 { fill: #fea62b }
+ .terminal-1281792983-r4 { fill: #eea831 }
+ .terminal-1281792983-r5 { fill: #d0ac3c }
+ .terminal-1281792983-r6 { fill: #c2ae42 }
+ .terminal-1281792983-r7 { fill: #b4b048 }
+ .terminal-1281792983-r8 { fill: #9ab452 }
+ .terminal-1281792983-r9 { fill: #8db557 }
+ .terminal-1281792983-r10 { fill: #78b860 }
+ .terminal-1281792983-r11 { fill: #6eba63 }
+ .terminal-1281792983-r12 { fill: #66bb67 }
+ .terminal-1281792983-r13 { fill: #59bd6c }
+ .terminal-1281792983-r14 { fill: #54be6e }
+ .terminal-1281792983-r15 { fill: #4ebe70 }
+ .terminal-1281792983-r16 { fill: #50be70 }
+ .terminal-1281792983-r17 { fill: #57bd6d }
+ .terminal-1281792983-r18 { fill: #5cbc6b }
+ .terminal-1281792983-r19 { fill: #63bb68 }
+ .terminal-1281792983-r20 { fill: #74b961 }
+ .terminal-1281792983-r21 { fill: #7eb85d }
+ .terminal-1281792983-r22 { fill: #94b454 }
+ .terminal-1281792983-r23 { fill: #a1b34f }
+ .terminal-1281792983-r24 { fill: #aeb14a }
+ .terminal-1281792983-r25 { fill: #caad3f }
+ .terminal-1281792983-r26 { fill: #d9ab39 }
+ .terminal-1281792983-r27 { fill: #f7a62d }
+ .terminal-1281792983-r28 { fill: #f5a72e }
+ .terminal-1281792983-r29 { fill: #d7ab3a }
+ .terminal-1281792983-r30 { fill: #c8ad40 }
+ .terminal-1281792983-r31 { fill: #baaf45 }
+ .terminal-1281792983-r32 { fill: #9fb350 }
+ .terminal-1281792983-r33 { fill: #93b555 }
+ .terminal-1281792983-r34 { fill: #7cb85e }
+ .terminal-1281792983-r35 { fill: #72b962 }
+ .terminal-1281792983-r36 { fill: #6abb65 }
+ .terminal-1281792983-r37 { fill: #5bbd6b }
+ .terminal-1281792983-r38 { fill: #56bd6d }
+ .terminal-1281792983-r39 { fill: #4fbe70 }
+ .terminal-1281792983-r40 { fill: #55bd6e }
+ .terminal-1281792983-r41 { fill: #5abd6c }
+ .terminal-1281792983-r42 { fill: #60bc69 }
+ .terminal-1281792983-r43 { fill: #70ba63 }
+ .terminal-1281792983-r44 { fill: #79b85f }
+ .terminal-1281792983-r45 { fill: #8fb556 }
+ .terminal-1281792983-r46 { fill: #9bb352 }
+ .terminal-1281792983-r47 { fill: #a8b24c }
+ .terminal-1281792983-r48 { fill: #c4ae41 }
+ .terminal-1281792983-r49 { fill: #d3ac3c }
+ .terminal-1281792983-r50 { fill: #f1a730 }
+ .terminal-1281792983-r51 { fill: #fba62b }
+ .terminal-1281792983-r52 { fill: #ddaa37 }
+ .terminal-1281792983-r53 { fill: #ceac3d }
+ .terminal-1281792983-r54 { fill: #c0ae43 }
+ .terminal-1281792983-r55 { fill: #a5b24e }
+ .terminal-1281792983-r56 { fill: #98b453 }
+ .terminal-1281792983-r57 { fill: #81b75c }
+ .terminal-1281792983-r58 { fill: #76b960 }
+ .terminal-1281792983-r59 { fill: #6dba64 }
+ .terminal-1281792983-r60 { fill: #5ebc6a }
+ .terminal-1281792983-r61 { fill: #58bd6c }
+ .terminal-1281792983-r62 { fill: #50be6f }
+ .terminal-1281792983-r63 { fill: #4ebf71 }
+ .terminal-1281792983-r64 { fill: #53be6e }
+ .terminal-1281792983-r65 { fill: #58bd6d }
+ .terminal-1281792983-r66 { fill: #5dbc6a }
+ .terminal-1281792983-r67 { fill: #6cba64 }
+ .terminal-1281792983-r68 { fill: #75b961 }
+ .terminal-1281792983-r69 { fill: #8ab658 }
+ .terminal-1281792983-r70 { fill: #96b454 }
+ .terminal-1281792983-r71 { fill: #a3b24f }
+ .terminal-1281792983-r72 { fill: #beaf44 }
+ .terminal-1281792983-r73 { fill: #ccac3e }
+ .terminal-1281792983-r74 { fill: #7bb85f }
+ .terminal-1281792983-r75 { fill: #89b659 }
+ .terminal-1281792983-r76 { fill: #97b453 }
+ .terminal-1281792983-r77 { fill: #b1b049 }
+ .terminal-1281792983-r78 { fill: #d3ac3b }
+ .terminal-1281792983-r79 { fill: #ddaa38 }
+ .terminal-1281792983-r80 { fill: #e5a934 }
+ .terminal-1281792983-r81 { fill: #f2a72f }
+ .terminal-1281792983-r82 { fill: #fda62b }
+ .terminal-1281792983-r83 { fill: #f4a72e }
+ .terminal-1281792983-r84 { fill: #efa830 }
+ .terminal-1281792983-r85 { fill: #e8a933 }
+ .terminal-1281792983-r86 { fill: #cdac3e }
+ .terminal-1281792983-r87 { fill: #b7b047 }
+ .terminal-1281792983-r88 { fill: #aab14c }
+ .terminal-1281792983-r89 { fill: #9db351 }
+ .terminal-1281792983-r90 { fill: #83b75b }
+ .terminal-1281792983-r91 { fill: #91b556 }
+ .terminal-1281792983-r92 { fill: #acb14b }
+ .terminal-1281792983-r93 { fill: #b8af46 }
+ .terminal-1281792983-r94 { fill: #cfac3d }
+ .terminal-1281792983-r95 { fill: #e1a936 }
+ .terminal-1281792983-r96 { fill: #f0a730 }
+ .terminal-1281792983-r97 { fill: #fca62b }
+ .terminal-1281792983-r98 { fill: #f6a72d }
+ .terminal-1281792983-r99 { fill: #f1a72f }
+ .terminal-1281792983-r100 { fill: #eba832 }
+ .terminal-1281792983-r101 { fill: #dbaa38 }
+ .terminal-1281792983-r102 { fill: #d2ac3c }
+ .terminal-1281792983-r103 { fill: #bcaf45 }
+ .terminal-1281792983-r104 { fill: #b0b149 }
+ .terminal-1281792983-r105 { fill: #87b65a }
+ .terminal-1281792983-r106 { fill: #78b85f }
+ .terminal-1281792983-r107 { fill: #5abd6b }
+ .terminal-1281792983-r108 { fill: #6eba64 }
+ .terminal-1281792983-r109 { fill: #7db85e }
+ .terminal-1281792983-r110 { fill: #8bb658 }
+ .terminal-1281792983-r111 { fill: #a6b24d }
+ .terminal-1281792983-r112 { fill: #b3b048 }
+ .terminal-1281792983-r113 { fill: #d5ab3b }
+ .terminal-1281792983-r114 { fill: #deaa37 }
+ .terminal-1281792983-r115 { fill: #eda831 }
+ .terminal-1281792983-r116 { fill: #f3a72f }
+ .terminal-1281792983-r117 { fill: #fba62c }
+ .terminal-1281792983-r118 { fill: #f8a62d }
+ .terminal-1281792983-r119 { fill: #f3a72e }
+ .terminal-1281792983-r120 { fill: #dfaa37 }
+ .terminal-1281792983-r121 { fill: #d6ab3a }
+ .terminal-1281792983-r122 { fill: #c1ae43 }
+ .terminal-1281792983-r123 { fill: #b5b047 }
+ .terminal-1281792983-r124 { fill: #7fb85d }
+ .terminal-1281792983-r125 { fill: #f89c2f }
+ .terminal-1281792983-r126 { fill: #ec8a37 }
+ .terminal-1281792983-r127 { fill: #e6823b }
+ .terminal-1281792983-r128 { fill: #e1793f }
+ .terminal-1281792983-r129 { fill: #d66946 }
+ .terminal-1281792983-r130 { fill: #d26249 }
+ .terminal-1281792983-r131 { fill: #c9554f }
+ .terminal-1281792983-r132 { fill: #c54f52 }
+ .terminal-1281792983-r133 { fill: #c24a54 }
+ .terminal-1281792983-r134 { fill: #bd4257 }
+ .terminal-1281792983-r135 { fill: #bb4059 }
+ .terminal-1281792983-r136 { fill: #b93c5a }
+ .terminal-1281792983-r137 { fill: #b93d5a }
+ .terminal-1281792983-r138 { fill: #bc4158 }
+ .terminal-1281792983-r139 { fill: #be4456 }
+ .terminal-1281792983-r140 { fill: #c14855 }
+ .terminal-1281792983-r141 { fill: #c75350 }
+ .terminal-1281792983-r142 { fill: #cb584d }
+ .terminal-1281792983-r143 { fill: #d46647 }
+ .terminal-1281792983-r144 { fill: #d96e44 }
+ .terminal-1281792983-r145 { fill: #de7640 }
+ .terminal-1281792983-r146 { fill: #e98738 }
+ .terminal-1281792983-r147 { fill: #ef8f34 }
+ .terminal-1281792983-r148 { fill: #fba22c }
+ .terminal-1281792983-r149 { fill: #faa02d }
+ .terminal-1281792983-r150 { fill: #ee8e35 }
+ .terminal-1281792983-r151 { fill: #e98539 }
+ .terminal-1281792983-r152 { fill: #e37d3d }
+ .terminal-1281792983-r153 { fill: #d86d44 }
+ .terminal-1281792983-r154 { fill: #d46548 }
+ .terminal-1281792983-r155 { fill: #cb584e }
+ .terminal-1281792983-r156 { fill: #c75250 }
+ .terminal-1281792983-r157 { fill: #c44c53 }
+ .terminal-1281792983-r158 { fill: #be4457 }
+ .terminal-1281792983-r159 { fill: #bd4357 }
+ .terminal-1281792983-r160 { fill: #c04755 }
+ .terminal-1281792983-r161 { fill: #c65051 }
+ .terminal-1281792983-r162 { fill: #ca564f }
+ .terminal-1281792983-r163 { fill: #d26349 }
+ .terminal-1281792983-r164 { fill: #d76a45 }
+ .terminal-1281792983-r165 { fill: #dc7242 }
+ .terminal-1281792983-r166 { fill: #e7833a }
+ .terminal-1281792983-r167 { fill: #ed8c36 }
+ .terminal-1281792983-r168 { fill: #f89e2e }
+ .terminal-1281792983-r169 { fill: #fda42b }
+ .terminal-1281792983-r170 { fill: #f19233 }
+ .terminal-1281792983-r171 { fill: #eb8937 }
+ .terminal-1281792983-r172 { fill: #e5803b }
+ .terminal-1281792983-r173 { fill: #db7043 }
+ .terminal-1281792983-r174 { fill: #d66846 }
+ .terminal-1281792983-r175 { fill: #cd5a4d }
+ .terminal-1281792983-r176 { fill: #c9544f }
+ .terminal-1281792983-r177 { fill: #bf4556 }
+ .terminal-1281792983-r178 { fill: #bd4258 }
+ .terminal-1281792983-r179 { fill: #ba3d5a }
+ .terminal-1281792983-r180 { fill: #b93c5b }
+ .terminal-1281792983-r181 { fill: #bb3f59 }
+ .terminal-1281792983-r182 { fill: #bc4258 }
+ .terminal-1281792983-r183 { fill: #c44e52 }
+ .terminal-1281792983-r184 { fill: #c85350 }
+ .terminal-1281792983-r185 { fill: #d0604a }
+ .terminal-1281792983-r186 { fill: #d56747 }
+ .terminal-1281792983-r187 { fill: #da6f43 }
+ .terminal-1281792983-r188 { fill: #e57f3c }
+ .terminal-1281792983-r189 { fill: #ea8838 }
+ .terminal-1281792983-r190 { fill: #be4556 }
+ .terminal-1281792983-r191 { fill: #ca574e }
+ .terminal-1281792983-r192 { fill: #d05f4a }
+ .terminal-1281792983-r193 { fill: #d56846 }
+ .terminal-1281792983-r194 { fill: #e0783f }
+ .terminal-1281792983-r195 { fill: #e47f3c }
+ .terminal-1281792983-r196 { fill: #f49731 }
+ .terminal-1281792983-r197 { fill: #f99f2e }
+ .terminal-1281792983-r198 { fill: #fba12c }
+ .terminal-1281792983-r199 { fill: #fda52b }
+ .terminal-1281792983-r200 { fill: #f89d2f }
+ .terminal-1281792983-r201 { fill: #f59930 }
+ .terminal-1281792983-r202 { fill: #ef8e35 }
+ .terminal-1281792983-r203 { fill: #eb8938 }
+ .terminal-1281792983-r204 { fill: #e27b3e }
+ .terminal-1281792983-r205 { fill: #dd7341 }
+ .terminal-1281792983-r206 { fill: #d86b45 }
+ .terminal-1281792983-r207 { fill: #c75251 }
+ .terminal-1281792983-r208 { fill: #cd5c4c }
+ .terminal-1281792983-r209 { fill: #d36448 }
+ .terminal-1281792983-r210 { fill: #de7441 }
+ .terminal-1281792983-r211 { fill: #e27c3d }
+ .terminal-1281792983-r212 { fill: #ef8f35 }
+ .terminal-1281792983-r213 { fill: #f29532 }
+ .terminal-1281792983-r214 { fill: #f89d2e }
+ .terminal-1281792983-r215 { fill: #f99e2e }
+ .terminal-1281792983-r216 { fill: #f69a30 }
+ .terminal-1281792983-r217 { fill: #f09134 }
+ .terminal-1281792983-r218 { fill: #ec8b36 }
+ .terminal-1281792983-r219 { fill: #e47e3c }
+ .terminal-1281792983-r220 { fill: #df7740 }
+ .terminal-1281792983-r221 { fill: #cf5e4b }
+ .terminal-1281792983-r222 { fill: #be4357 }
+ .terminal-1281792983-r223 { fill: #d1614a }
+ .terminal-1281792983-r224 { fill: #db7142 }
+ .terminal-1281792983-r225 { fill: #e0793f }
+ .terminal-1281792983-r226 { fill: #ed8d36 }
+ .terminal-1281792983-r227 { fill: #f79c2f }
+ .terminal-1281792983-r228 { fill: #f99f2d }
+ .terminal-1281792983-r229 { fill: #fca42b }
+ .terminal-1281792983-r230 { fill: #fa9f2d }
+ .terminal-1281792983-r231 { fill: #f29333 }
+ .terminal-1281792983-r232 { fill: #e6813b }
+ .terminal-1281792983-r233 { fill: #e17a3e }
+ .terminal-1281792983-r234 { fill: #d16249 }
+ .terminal-1281792983-r235 { fill: #cc594d }
+ .terminal-1281792983-r236 { fill: #153954 }
+ .terminal-1281792983-r237 { fill: #133e5f }
+ .terminal-1281792983-r238 { fill: #0f4974 }
+ .terminal-1281792983-r239 { fill: #0e4e7f }
+ .terminal-1281792983-r240 { fill: #0c5389 }
+ .terminal-1281792983-r241 { fill: #095c9c }
+ .terminal-1281792983-r242 { fill: #0861a5 }
+ .terminal-1281792983-r243 { fill: #0568b5 }
+ .terminal-1281792983-r244 { fill: #046cbc }
+ .terminal-1281792983-r245 { fill: #036fc2 }
+ .terminal-1281792983-r246 { fill: #0273cb }
+ .terminal-1281792983-r247 { fill: #0175cf }
+ .terminal-1281792983-r248 { fill: #0177d3 }
+ .terminal-1281792983-r249 { fill: #0177d2 }
+ .terminal-1281792983-r250 { fill: #0274cd }
+ .terminal-1281792983-r251 { fill: #0272c9 }
+ .terminal-1281792983-r252 { fill: #0370c4 }
+ .terminal-1281792983-r253 { fill: #056ab8 }
+ .terminal-1281792983-r254 { fill: #0666b0 }
+ .terminal-1281792983-r255 { fill: #095ea0 }
+ .terminal-1281792983-r256 { fill: #0a5a97 }
+ .terminal-1281792983-r257 { fill: #0b558d }
+ .terminal-1281792983-r258 { fill: #0f4b79 }
+ .terminal-1281792983-r259 { fill: #10466e }
+ .terminal-1281792983-r260 { fill: #143b58 }
+ .terminal-1281792983-r261 { fill: #143c5a }
+ .terminal-1281792983-r262 { fill: #104670 }
+ .terminal-1281792983-r263 { fill: #0e4c7a }
+ .terminal-1281792983-r264 { fill: #0d5185 }
+ .terminal-1281792983-r265 { fill: #0a5a98 }
+ .terminal-1281792983-r266 { fill: #085fa1 }
+ .terminal-1281792983-r267 { fill: #0667b2 }
+ .terminal-1281792983-r268 { fill: #056ab9 }
+ .terminal-1281792983-r269 { fill: #046dbf }
+ .terminal-1281792983-r270 { fill: #0273c9 }
+ .terminal-1281792983-r271 { fill: #0174cd }
+ .terminal-1281792983-r272 { fill: #0175ce }
+ .terminal-1281792983-r273 { fill: #0371c6 }
+ .terminal-1281792983-r274 { fill: #046bbb }
+ .terminal-1281792983-r275 { fill: #0568b4 }
+ .terminal-1281792983-r276 { fill: #0860a4 }
+ .terminal-1281792983-r277 { fill: #095c9b }
+ .terminal-1281792983-r278 { fill: #0b5791 }
+ .terminal-1281792983-r279 { fill: #0e4d7d }
+ .terminal-1281792983-r280 { fill: #104873 }
+ .terminal-1281792983-r281 { fill: #133d5d }
+ .terminal-1281792983-r282 { fill: #143955 }
+ .terminal-1281792983-r283 { fill: #11446b }
+ .terminal-1281792983-r284 { fill: #0f4976 }
+ .terminal-1281792983-r285 { fill: #0e4f80 }
+ .terminal-1281792983-r286 { fill: #0a5894 }
+ .terminal-1281792983-r287 { fill: #095d9d }
+ .terminal-1281792983-r288 { fill: #0665ae }
+ .terminal-1281792983-r289 { fill: #0569b6 }
+ .terminal-1281792983-r290 { fill: #0272c7 }
+ .terminal-1281792983-r291 { fill: #0274cc }
+ .terminal-1281792983-r292 { fill: #0177d1 }
+ .terminal-1281792983-r293 { fill: #0178d4 }
+ .terminal-1281792983-r294 { fill: #0176cf }
+ .terminal-1281792983-r295 { fill: #0272c8 }
+ .terminal-1281792983-r296 { fill: #046dbd }
+ .terminal-1281792983-r297 { fill: #0569b7 }
+ .terminal-1281792983-r298 { fill: #0762a7 }
+ .terminal-1281792983-r299 { fill: #095e9f }
+ .terminal-1281792983-r300 { fill: #0a5996 }
+ .terminal-1281792983-r301 { fill: #0d4f82 }
+ .terminal-1281792983-r302 { fill: #0f4a77 }
+ .terminal-1281792983-r303 { fill: #0667b3 }
+ .terminal-1281792983-r304 { fill: #0762a8 }
+ .terminal-1281792983-r305 { fill: #095d9e }
+ .terminal-1281792983-r306 { fill: #0c548b }
+ .terminal-1281792983-r307 { fill: #104872 }
+ .terminal-1281792983-r308 { fill: #124165 }
+ .terminal-1281792983-r309 { fill: #133d5c }
+ .terminal-1281792983-r310 { fill: #143954 }
+ .terminal-1281792983-r311 { fill: #133c5a }
+ .terminal-1281792983-r312 { fill: #133e5e }
+ .terminal-1281792983-r313 { fill: #124063 }
+ .terminal-1281792983-r314 { fill: #10466f }
+ .terminal-1281792983-r315 { fill: #0c5287 }
+ .terminal-1281792983-r316 { fill: #0b5690 }
+ .terminal-1281792983-r317 { fill: #0a5b9a }
+ .terminal-1281792983-r318 { fill: #056ab7 }
+ .terminal-1281792983-r319 { fill: #0764ad }
+ .terminal-1281792983-r320 { fill: #085fa2 }
+ .terminal-1281792983-r321 { fill: #0b568f }
+ .terminal-1281792983-r322 { fill: #0d5186 }
+ .terminal-1281792983-r323 { fill: #0f4975 }
+ .terminal-1281792983-r324 { fill: #114368 }
+ .terminal-1281792983-r325 { fill: #133d5e }
+ .terminal-1281792983-r326 { fill: #143b59 }
+ .terminal-1281792983-r327 { fill: #123f61 }
+ .terminal-1281792983-r328 { fill: #11456c }
+ .terminal-1281792983-r329 { fill: #0d5083 }
+ .terminal-1281792983-r330 { fill: #0c548c }
+ .terminal-1281792983-r331 { fill: #0763aa }
+ .terminal-1281792983-r332 { fill: #0273ca }
+ .terminal-1281792983-r333 { fill: #0667b1 }
+ .terminal-1281792983-r334 { fill: #0761a7 }
+ .terminal-1281792983-r335 { fill: #0b5893 }
+ .terminal-1281792983-r336 { fill: #0c538a }
+ .terminal-1281792983-r337 { fill: #104771 }
+ .terminal-1281792983-r338 { fill: #133e60 }
+ .terminal-1281792983-r339 { fill: #133c5b }
+ .terminal-1281792983-r340 { fill: #143956 }
+ .terminal-1281792983-r341 { fill: #143a58 }
+ .terminal-1281792983-r342 { fill: #11436a }
+ .terminal-1281792983-r343 { fill: #104770 }
+ .terminal-1281792983-r344 { fill: #0e4e80 }
+ .terminal-1281792983-r345 { fill: #0c5288 }
+ .terminal-1281792983-r346 { fill: #4c2730 }
+ .terminal-1281792983-r347 { fill: #552833 }
+ .terminal-1281792983-r348 { fill: #672c3b }
+ .terminal-1281792983-r349 { fill: #702e3e }
+ .terminal-1281792983-r350 { fill: #792f41 }
+ .terminal-1281792983-r351 { fill: #893248 }
+ .terminal-1281792983-r352 { fill: #91344b }
+ .terminal-1281792983-r353 { fill: #9e3650 }
+ .terminal-1281792983-r354 { fill: #a43852 }
+ .terminal-1281792983-r355 { fill: #a93954 }
+ .terminal-1281792983-r356 { fill: #b13a58 }
+ .terminal-1281792983-r357 { fill: #b43b59 }
+ .terminal-1281792983-r358 { fill: #b83b5a }
+ .terminal-1281792983-r359 { fill: #b73b5a }
+ .terminal-1281792983-r360 { fill: #b33a58 }
+ .terminal-1281792983-r361 { fill: #af3a57 }
+ .terminal-1281792983-r362 { fill: #ab3955 }
+ .terminal-1281792983-r363 { fill: #a13751 }
+ .terminal-1281792983-r364 { fill: #9b364f }
+ .terminal-1281792983-r365 { fill: #8d3349 }
+ .terminal-1281792983-r366 { fill: #853246 }
+ .terminal-1281792983-r367 { fill: #7d3043 }
+ .terminal-1281792983-r368 { fill: #6b2d3c }
+ .terminal-1281792983-r369 { fill: #622b38 }
+ .terminal-1281792983-r370 { fill: #502731 }
+ .terminal-1281792983-r371 { fill: #512832 }
+ .terminal-1281792983-r372 { fill: #632b39 }
+ .terminal-1281792983-r373 { fill: #6d2d3d }
+ .terminal-1281792983-r374 { fill: #752f40 }
+ .terminal-1281792983-r375 { fill: #863247 }
+ .terminal-1281792983-r376 { fill: #8e334a }
+ .terminal-1281792983-r377 { fill: #9c364f }
+ .terminal-1281792983-r378 { fill: #a23751 }
+ .terminal-1281792983-r379 { fill: #a73854 }
+ .terminal-1281792983-r380 { fill: #b03a57 }
+ .terminal-1281792983-r381 { fill: #b13a57 }
+ .terminal-1281792983-r382 { fill: #ad3956 }
+ .terminal-1281792983-r383 { fill: #a33752 }
+ .terminal-1281792983-r384 { fill: #9d3650 }
+ .terminal-1281792983-r385 { fill: #90344a }
+ .terminal-1281792983-r386 { fill: #883247 }
+ .terminal-1281792983-r387 { fill: #803144 }
+ .terminal-1281792983-r388 { fill: #6f2d3e }
+ .terminal-1281792983-r389 { fill: #662c3a }
+ .terminal-1281792983-r390 { fill: #542833 }
+ .terminal-1281792983-r391 { fill: #4d2730 }
+ .terminal-1281792983-r392 { fill: #602a37 }
+ .terminal-1281792983-r393 { fill: #692c3b }
+ .terminal-1281792983-r394 { fill: #722e3f }
+ .terminal-1281792983-r395 { fill: #833145 }
+ .terminal-1281792983-r396 { fill: #8a3348 }
+ .terminal-1281792983-r397 { fill: #99354e }
+ .terminal-1281792983-r398 { fill: #9f3751 }
+ .terminal-1281792983-r399 { fill: #a53853 }
+ .terminal-1281792983-r400 { fill: #ae3a56 }
+ .terminal-1281792983-r401 { fill: #b23a58 }
+ .terminal-1281792983-r402 { fill: #b53b59 }
+ .terminal-1281792983-r403 { fill: #a63853 }
+ .terminal-1281792983-r404 { fill: #a03751 }
+ .terminal-1281792983-r405 { fill: #93344c }
+ .terminal-1281792983-r406 { fill: #8c3349 }
+ .terminal-1281792983-r407 { fill: #843146 }
+ .terminal-1281792983-r408 { fill: #732e3f }
+ .terminal-1281792983-r409 { fill: #6a2c3c }
+ .terminal-1281792983-r410 { fill: #9d364f }
+ .terminal-1281792983-r411 { fill: #94344c }
+ .terminal-1281792983-r412 { fill: #8b3349 }
+ .terminal-1281792983-r413 { fill: #7b3042 }
+ .terminal-1281792983-r414 { fill: #602a38 }
+ .terminal-1281792983-r415 { fill: #5b2936 }
+ .terminal-1281792983-r416 { fill: #532832 }
+ .terminal-1281792983-r417 { fill: #592935 }
+ .terminal-1281792983-r418 { fill: #772f41 }
+ .terminal-1281792983-r419 { fill: #7f3044 }
+ .terminal-1281792983-r420 { fill: #873247 }
+ .terminal-1281792983-r421 { fill: #a23752 }
+ .terminal-1281792983-r422 { fill: #97354d }
+ .terminal-1281792983-r423 { fill: #8f334a }
+ .terminal-1281792983-r424 { fill: #7e3043 }
+ .terminal-1281792983-r425 { fill: #762f40 }
+ .terminal-1281792983-r426 { fill: #682c3b }
+ .terminal-1281792983-r427 { fill: #622b39 }
+ .terminal-1281792983-r428 { fill: #5d2a36 }
+ .terminal-1281792983-r429 { fill: #532833 }
+ .terminal-1281792983-r430 { fill: #572934 }
+ .terminal-1281792983-r431 { fill: #612b38 }
+ .terminal-1281792983-r432 { fill: #672c3a }
+ .terminal-1281792983-r433 { fill: #742e40 }
+ .terminal-1281792983-r434 { fill: #7c3043 }
+ .terminal-1281792983-r435 { fill: #95354c }
+ .terminal-1281792983-r436 { fill: #a43853 }
+ .terminal-1281792983-r437 { fill: #92344b }
+ .terminal-1281792983-r438 { fill: #813145 }
+ .terminal-1281792983-r439 { fill: #7a2f42 }
+ .terminal-1281792983-r440 { fill: #652b39 }
+ .terminal-1281792983-r441 { fill: #5f2a37 }
+ .terminal-1281792983-r442 { fill: #562834 }
+ .terminal-1281792983-r443 { fill: #522832 }
+ .terminal-1281792983-r444 { fill: #4f2731 }
+ .terminal-1281792983-r445 { fill: #5e2a37 }
+ .terminal-1281792983-r446 { fill: #642b39 }
+ .terminal-1281792983-r447 { fill: #712e3e }
+ .terminal-1281792983-r448 { fill: #782f41 }
+ .terminal-1281792983-r449 { fill: #9a364e }
+ .terminal-1281792983-r450 { fill: #2c4e36 }
+ .terminal-1281792983-r451 { fill: #2e573b }
+ .terminal-1281792983-r452 { fill: #346a45 }
+ .terminal-1281792983-r453 { fill: #377449 }
+ .terminal-1281792983-r454 { fill: #3a7d4e }
+ .terminal-1281792983-r455 { fill: #3f8e57 }
+ .terminal-1281792983-r456 { fill: #41955b }
+ .terminal-1281792983-r457 { fill: #45a362 }
+ .terminal-1281792983-r458 { fill: #47a965 }
+ .terminal-1281792983-r459 { fill: #49af68 }
+ .terminal-1281792983-r460 { fill: #4bb76d }
+ .terminal-1281792983-r461 { fill: #4cba6e }
+ .terminal-1281792983-r462 { fill: #4dbe70 }
+ .terminal-1281792983-r463 { fill: #4dbd70 }
+ .terminal-1281792983-r464 { fill: #4cb96d }
+ .terminal-1281792983-r465 { fill: #4bb56c }
+ .terminal-1281792983-r466 { fill: #49b169 }
+ .terminal-1281792983-r467 { fill: #46a664 }
+ .terminal-1281792983-r468 { fill: #44a060 }
+ .terminal-1281792983-r469 { fill: #409159 }
+ .terminal-1281792983-r470 { fill: #3d8955 }
+ .terminal-1281792983-r471 { fill: #3b8050 }
+ .terminal-1281792983-r472 { fill: #356e47 }
+ .terminal-1281792983-r473 { fill: #336542 }
+ .terminal-1281792983-r474 { fill: #2d5238 }
+ .terminal-1281792983-r475 { fill: #2d5338 }
+ .terminal-1281792983-r476 { fill: #336642 }
+ .terminal-1281792983-r477 { fill: #367047 }
+ .terminal-1281792983-r478 { fill: #39794c }
+ .terminal-1281792983-r479 { fill: #3e8a55 }
+ .terminal-1281792983-r480 { fill: #409259 }
+ .terminal-1281792983-r481 { fill: #44a161 }
+ .terminal-1281792983-r482 { fill: #46a764 }
+ .terminal-1281792983-r483 { fill: #48ac67 }
+ .terminal-1281792983-r484 { fill: #4bb66c }
+ .terminal-1281792983-r485 { fill: #4cb96e }
+ .terminal-1281792983-r486 { fill: #4bb76c }
+ .terminal-1281792983-r487 { fill: #4ab36a }
+ .terminal-1281792983-r488 { fill: #45a262 }
+ .terminal-1281792983-r489 { fill: #41945a }
+ .terminal-1281792983-r490 { fill: #3e8c56 }
+ .terminal-1281792983-r491 { fill: #3c8452 }
+ .terminal-1281792983-r492 { fill: #377249 }
+ .terminal-1281792983-r493 { fill: #346944 }
+ .terminal-1281792983-r494 { fill: #2e563a }
+ .terminal-1281792983-r495 { fill: #2c4f36 }
+ .terminal-1281792983-r496 { fill: #326240 }
+ .terminal-1281792983-r497 { fill: #356c45 }
+ .terminal-1281792983-r498 { fill: #37754a }
+ .terminal-1281792983-r499 { fill: #3d8753 }
+ .terminal-1281792983-r500 { fill: #3f8f58 }
+ .terminal-1281792983-r501 { fill: #449e5f }
+ .terminal-1281792983-r502 { fill: #46a463 }
+ .terminal-1281792983-r503 { fill: #47aa66 }
+ .terminal-1281792983-r504 { fill: #4ab46b }
+ .terminal-1281792983-r505 { fill: #4bb86d }
+ .terminal-1281792983-r506 { fill: #4cbb6f }
+ .terminal-1281792983-r507 { fill: #4cb86d }
+ .terminal-1281792983-r508 { fill: #48ab66 }
+ .terminal-1281792983-r509 { fill: #46a563 }
+ .terminal-1281792983-r510 { fill: #42985c }
+ .terminal-1281792983-r511 { fill: #3f9058 }
+ .terminal-1281792983-r512 { fill: #3d8854 }
+ .terminal-1281792983-r513 { fill: #38764b }
+ .terminal-1281792983-r514 { fill: #356d46 }
+ .terminal-1281792983-r515 { fill: #4bb56b }
+ .terminal-1281792983-r516 { fill: #45a261 }
+ .terminal-1281792983-r517 { fill: #42985d }
+ .terminal-1281792983-r518 { fill: #3a7e4f }
+ .terminal-1281792983-r519 { fill: #38774b }
+ .terminal-1281792983-r520 { fill: #326341 }
+ .terminal-1281792983-r521 { fill: #305d3e }
+ .terminal-1281792983-r522 { fill: #2e5539 }
+ .terminal-1281792983-r523 { fill: #2d5339 }
+ .terminal-1281792983-r524 { fill: #2e573a }
+ .terminal-1281792983-r525 { fill: #305b3d }
+ .terminal-1281792983-r526 { fill: #356c46 }
+ .terminal-1281792983-r527 { fill: #397b4d }
+ .terminal-1281792983-r528 { fill: #3c8351 }
+ .terminal-1281792983-r529 { fill: #439c5f }
+ .terminal-1281792983-r530 { fill: #40935a }
+ .terminal-1281792983-r531 { fill: #3b8251 }
+ .terminal-1281792983-r532 { fill: #397a4d }
+ .terminal-1281792983-r533 { fill: #356b45 }
+ .terminal-1281792983-r534 { fill: #31603f }
+ .terminal-1281792983-r535 { fill: #2e553a }
+ .terminal-1281792983-r536 { fill: #2f593c }
+ .terminal-1281792983-r537 { fill: #346a44 }
+ .terminal-1281792983-r538 { fill: #38784c }
+ .terminal-1281792983-r539 { fill: #429a5d }
+ .terminal-1281792983-r540 { fill: #44a061 }
+ .terminal-1281792983-r541 { fill: #42975c }
+ .terminal-1281792983-r542 { fill: #3c8553 }
+ .terminal-1281792983-r543 { fill: #336843 }
+ .terminal-1281792983-r544 { fill: #2f583b }
+ .terminal-1281792983-r545 { fill: #2e5439 }
+ .terminal-1281792983-r546 { fill: #2d5137 }
+ .terminal-1281792983-r547 { fill: #2d5439 }
+ .terminal-1281792983-r548 { fill: #316140 }
+ .terminal-1281792983-r549 { fill: #336743 }
+ .terminal-1281792983-r550 { fill: #37744a }
+ .terminal-1281792983-r551 { fill: #3a7c4e }
+ .terminal-1281792983-r552 { fill: #41965b }
+ .terminal-1281792983-r553 { fill: #449f60 }
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- SparklineColorsApp
+ SparklineColorsApp
-
-
-
-
- ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
-
- ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
-
- ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
-
- ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
-
- ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
-
- ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
-
- ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
-
- ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
-
- ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
-
- ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
-
-
-
+
+
+
+
+ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
+
+ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
+
+ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
+
+ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
+
+ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
+
+ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
+
+ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
+
+ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇█▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
+
+ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇ █ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
+
+ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▃ ▃ ▄ ▅ ▆ ▆ ▆ ▇ ▇ ▇ ▇▇▇ ▇ ▇ ▇ ▆ ▆ ▅ ▄ ▄ ▃ ▂ ▁ ▁ ▂ ▂ ▃ ▄ ▅ ▅ ▆ ▆ ▇ ▇ ▇ ▇█▇ ▇ ▇ ▇ ▆ ▆ ▅ ▅ ▄ ▃ ▂
+
+
+
@@ -36742,217 +39945,217 @@
font-weight: 700;
}
- .terminal-3805192064-matrix {
+ .terminal-2944033235-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3805192064-title {
+ .terminal-2944033235-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3805192064-r1 { fill: #e1e1e1 }
- .terminal-3805192064-r2 { fill: #c5c8c6 }
- .terminal-3805192064-r3 { fill: #104670 }
- .terminal-3805192064-r4 { fill: #0c548b }
- .terminal-3805192064-r5 { fill: #104772 }
- .terminal-3805192064-r6 { fill: #0a5996 }
- .terminal-3805192064-r7 { fill: #0d5083 }
- .terminal-3805192064-r8 { fill: #0d5186 }
- .terminal-3805192064-r9 { fill: #0569b6 }
- .terminal-3805192064-r10 { fill: #0762a7 }
- .terminal-3805192064-r11 { fill: #0e4d7d }
- .terminal-3805192064-r12 { fill: #104872 }
- .terminal-3805192064-r13 { fill: #0f4a77 }
- .terminal-3805192064-r14 { fill: #0b5791 }
- .terminal-3805192064-r15 { fill: #0274cc }
- .terminal-3805192064-r16 { fill: #0d5084 }
- .terminal-3805192064-r17 { fill: #0371c6 }
- .terminal-3805192064-r18 { fill: #085fa1 }
- .terminal-3805192064-r19 { fill: #0a5b99 }
- .terminal-3805192064-r20 { fill: #0c538a }
- .terminal-3805192064-r21 { fill: #0a5a97 }
- .terminal-3805192064-r22 { fill: #0c5288 }
- .terminal-3805192064-r23 { fill: #11456d }
- .terminal-3805192064-r24 { fill: #0d4f81 }
- .terminal-3805192064-r25 { fill: #0d5185 }
- .terminal-3805192064-r26 { fill: #0b568f }
- .terminal-3805192064-r27 { fill: #0178d4 }
- .terminal-3805192064-r28 { fill: #0668b3 }
- .terminal-3805192064-r29 { fill: #0f4a76 }
- .terminal-3805192064-r30 { fill: #0f4b78 }
- .terminal-3805192064-r31 { fill: #0763aa }
- .terminal-3805192064-r32 { fill: #0b5690 }
- .terminal-3805192064-r33 { fill: #0e4c7c }
- .terminal-3805192064-r34 { fill: #0175cf }
- .terminal-3805192064-r35 { fill: #0e4e80 }
- .terminal-3805192064-r36 { fill: #0c5388 }
- .terminal-3805192064-r37 { fill: #0c5287 }
- .terminal-3805192064-r38 { fill: #0a5894 }
- .terminal-3805192064-r39 { fill: #0b558d }
- .terminal-3805192064-r40 { fill: #056ab8 }
- .terminal-3805192064-r41 { fill: #0e4c7b }
- .terminal-3805192064-r42 { fill: #0762a8 }
- .terminal-3805192064-r43 { fill: #0665ad }
- .terminal-3805192064-r44 { fill: #0e4d7c }
- .terminal-3805192064-r45 { fill: #0c548c }
- .terminal-3805192064-r46 { fill: #0e4e7f }
- .terminal-3805192064-r47 { fill: #0f4b7a }
- .terminal-3805192064-r48 { fill: #0667b2 }
- .terminal-3805192064-r49 { fill: #11446c }
- .terminal-3805192064-r50 { fill: #0f4975 }
- .terminal-3805192064-r51 { fill: #0568b4 }
- .terminal-3805192064-r52 { fill: #0f4976 }
- .terminal-3805192064-r53 { fill: #085fa2 }
- .terminal-3805192064-r54 { fill: #0a5a98 }
- .terminal-3805192064-r55 { fill: #124164 }
- .terminal-3805192064-r56 { fill: #0d5287 }
- .terminal-3805192064-r57 { fill: #133e5e }
- .terminal-3805192064-r58 { fill: #10466f }
- .terminal-3805192064-r59 { fill: #124266 }
- .terminal-3805192064-r60 { fill: #123f61 }
- .terminal-3805192064-r61 { fill: #124063 }
- .terminal-3805192064-r62 { fill: #114267 }
- .terminal-3805192064-r63 { fill: #114369 }
- .terminal-3805192064-r64 { fill: #124062 }
- .terminal-3805192064-r65 { fill: #133e5f }
- .terminal-3805192064-r66 { fill: #124165 }
- .terminal-3805192064-r67 { fill: #124166 }
- .terminal-3805192064-r68 { fill: #10456d }
- .terminal-3805192064-r69 { fill: #123f62 }
- .terminal-3805192064-r70 { fill: #114368 }
- .terminal-3805192064-r71 { fill: #11446a }
- .terminal-3805192064-r72 { fill: #124064 }
- .terminal-3805192064-r73 { fill: #104873 }
- .terminal-3805192064-r74 { fill: #133f60 }
- .terminal-3805192064-r75 { fill: #133d5d }
- .terminal-3805192064-r76 { fill: #11446b }
- .terminal-3805192064-r77 { fill: #11456c }
- .terminal-3805192064-r78 { fill: #123f60 }
- .terminal-3805192064-r79 { fill: #11436a }
- .terminal-3805192064-r80 { fill: #133d5c }
- .terminal-3805192064-r81 { fill: #143954 }
- .terminal-3805192064-r82 { fill: #143b58 }
- .terminal-3805192064-r83 { fill: #143a56 }
- .terminal-3805192064-r84 { fill: #143955 }
- .terminal-3805192064-r85 { fill: #153954 }
- .terminal-3805192064-r86 { fill: #143a57 }
- .terminal-3805192064-r87 { fill: #143956 }
- .terminal-3805192064-r88 { fill: #143c5a }
-
-
-
-
+ .terminal-2944033235-r1 { fill: #e1e1e1 }
+ .terminal-2944033235-r2 { fill: #c5c8c6 }
+ .terminal-2944033235-r3 { fill: #104670 }
+ .terminal-2944033235-r4 { fill: #0c548b }
+ .terminal-2944033235-r5 { fill: #104772 }
+ .terminal-2944033235-r6 { fill: #0a5996 }
+ .terminal-2944033235-r7 { fill: #0d5083 }
+ .terminal-2944033235-r8 { fill: #0d5186 }
+ .terminal-2944033235-r9 { fill: #0569b6 }
+ .terminal-2944033235-r10 { fill: #0762a7 }
+ .terminal-2944033235-r11 { fill: #0e4d7d }
+ .terminal-2944033235-r12 { fill: #104872 }
+ .terminal-2944033235-r13 { fill: #0f4a77 }
+ .terminal-2944033235-r14 { fill: #0b5791 }
+ .terminal-2944033235-r15 { fill: #0274cc }
+ .terminal-2944033235-r16 { fill: #0d5084 }
+ .terminal-2944033235-r17 { fill: #0371c6 }
+ .terminal-2944033235-r18 { fill: #085fa1 }
+ .terminal-2944033235-r19 { fill: #0a5b99 }
+ .terminal-2944033235-r20 { fill: #0c538a }
+ .terminal-2944033235-r21 { fill: #0a5a97 }
+ .terminal-2944033235-r22 { fill: #0c5288 }
+ .terminal-2944033235-r23 { fill: #11456d }
+ .terminal-2944033235-r24 { fill: #0d4f81 }
+ .terminal-2944033235-r25 { fill: #0d5185 }
+ .terminal-2944033235-r26 { fill: #0b568f }
+ .terminal-2944033235-r27 { fill: #0178d4 }
+ .terminal-2944033235-r28 { fill: #0668b3 }
+ .terminal-2944033235-r29 { fill: #0f4a76 }
+ .terminal-2944033235-r30 { fill: #0f4b78 }
+ .terminal-2944033235-r31 { fill: #0763aa }
+ .terminal-2944033235-r32 { fill: #0b5690 }
+ .terminal-2944033235-r33 { fill: #0e4c7c }
+ .terminal-2944033235-r34 { fill: #0175cf }
+ .terminal-2944033235-r35 { fill: #0e4e80 }
+ .terminal-2944033235-r36 { fill: #0c5388 }
+ .terminal-2944033235-r37 { fill: #0c5287 }
+ .terminal-2944033235-r38 { fill: #0a5894 }
+ .terminal-2944033235-r39 { fill: #0b558d }
+ .terminal-2944033235-r40 { fill: #056ab8 }
+ .terminal-2944033235-r41 { fill: #0e4c7b }
+ .terminal-2944033235-r42 { fill: #0762a8 }
+ .terminal-2944033235-r43 { fill: #0665ad }
+ .terminal-2944033235-r44 { fill: #0e4d7c }
+ .terminal-2944033235-r45 { fill: #0c548c }
+ .terminal-2944033235-r46 { fill: #0e4e7f }
+ .terminal-2944033235-r47 { fill: #0f4b7a }
+ .terminal-2944033235-r48 { fill: #0667b2 }
+ .terminal-2944033235-r49 { fill: #11446c }
+ .terminal-2944033235-r50 { fill: #0f4975 }
+ .terminal-2944033235-r51 { fill: #0568b4 }
+ .terminal-2944033235-r52 { fill: #0f4976 }
+ .terminal-2944033235-r53 { fill: #085fa2 }
+ .terminal-2944033235-r54 { fill: #0a5a98 }
+ .terminal-2944033235-r55 { fill: #124164 }
+ .terminal-2944033235-r56 { fill: #0d5287 }
+ .terminal-2944033235-r57 { fill: #133e5e }
+ .terminal-2944033235-r58 { fill: #10466f }
+ .terminal-2944033235-r59 { fill: #124266 }
+ .terminal-2944033235-r60 { fill: #123f61 }
+ .terminal-2944033235-r61 { fill: #124063 }
+ .terminal-2944033235-r62 { fill: #114267 }
+ .terminal-2944033235-r63 { fill: #114369 }
+ .terminal-2944033235-r64 { fill: #124062 }
+ .terminal-2944033235-r65 { fill: #133e5f }
+ .terminal-2944033235-r66 { fill: #124165 }
+ .terminal-2944033235-r67 { fill: #124166 }
+ .terminal-2944033235-r68 { fill: #10456d }
+ .terminal-2944033235-r69 { fill: #123f62 }
+ .terminal-2944033235-r70 { fill: #114368 }
+ .terminal-2944033235-r71 { fill: #11446a }
+ .terminal-2944033235-r72 { fill: #124064 }
+ .terminal-2944033235-r73 { fill: #104873 }
+ .terminal-2944033235-r74 { fill: #133f60 }
+ .terminal-2944033235-r75 { fill: #133d5d }
+ .terminal-2944033235-r76 { fill: #11446b }
+ .terminal-2944033235-r77 { fill: #11456c }
+ .terminal-2944033235-r78 { fill: #123f60 }
+ .terminal-2944033235-r79 { fill: #11436a }
+ .terminal-2944033235-r80 { fill: #133d5c }
+ .terminal-2944033235-r81 { fill: #143954 }
+ .terminal-2944033235-r82 { fill: #143b58 }
+ .terminal-2944033235-r83 { fill: #143a56 }
+ .terminal-2944033235-r84 { fill: #143955 }
+ .terminal-2944033235-r85 { fill: #153954 }
+ .terminal-2944033235-r86 { fill: #143a57 }
+ .terminal-2944033235-r87 { fill: #143956 }
+ .terminal-2944033235-r88 { fill: #143c5a }
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- SparklineSummaryFunctionApp
+ SparklineSummaryFunctionApp
-
-
-
-
-
- ▂ ▄ ▂ ▄ ▃ ▃ ▆ ▅ ▃ ▂ ▃ ▂ ▃ ▂ ▄ ▇ ▃ ▃ ▇ ▅ ▄ ▃ ▄ ▄ ▃ ▂ ▃ ▂ ▃ ▄ ▄ █ ▆ ▂ ▃ ▃ ▅ ▃ ▃ ▄ ▃ ▇ ▃ ▃ ▃ ▄ ▄ ▆ ▃ ▃ ▅ ▂ ▅ ▃ ▄ ▃ ▃ ▄ ▃ ▅ ▆ ▂ ▂ ▃ ▆ ▂ ▃ ▄ ▅ ▄ ▃ ▄ ▄ ▁ ▃ ▂
-
-
- ▁ ▂ ▁ ▂ ▁ ▁ ▂ ▂ ▁ ▁ ▁ ▁ ▂ ▁ ▁ ▂ ▁ ▁ ▂ ▂ ▂ ▂ ▂ ▂ ▁ ▁ ▁ ▁ ▁ ▂ ▁ ▂ ▂ ▁ ▁ ▁ ▂ ▁ ▁ ▂ ▁ ▂ ▁ ▁ ▁ ▁ ▂ ▂ ▁ ▁ ▁ ▁ ▂ ▁ ▁ ▂ ▁ ▂ ▁ ▁ ▂ ▁ ▁ ▁ ▁ ▁ ▁ ▂ ▂ ▂ ▁ ▂ ▁ ▁ ▁ ▁
-
-
- ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ ▂ ▄ ▂ ▄ ▃ ▃ ▆ ▅ ▃ ▂ ▃ ▂ ▃ ▂ ▄ ▇ ▃ ▃ ▇ ▅ ▄ ▃ ▄ ▄ ▃ ▂ ▃ ▂ ▃ ▄ ▄ █ ▆ ▂ ▃ ▃ ▅ ▃ ▃ ▄ ▃ ▇ ▃ ▃ ▃ ▄ ▄ ▆ ▃ ▃ ▅ ▂ ▅ ▃ ▄ ▃ ▃ ▄ ▃ ▅ ▆ ▂ ▂ ▃ ▆ ▂ ▃ ▄ ▅ ▄ ▃ ▄ ▄ ▁ ▃ ▂
+
+
+ ▁ ▂ ▁ ▂ ▁ ▁ ▂ ▂ ▁ ▁ ▁ ▁ ▂ ▁ ▁ ▂ ▁ ▁ ▂ ▂ ▂ ▂ ▂▂ ▁ ▁ ▁ ▁ ▁ ▂ ▁ ▂ ▂ ▁ ▁ ▁ ▂ ▁ ▁ ▂ ▁ ▂ ▁ ▁▁ ▁ ▂ ▂ ▁ ▁ ▁ ▁ ▂ ▁ ▁ ▂ ▁ ▂ ▁ ▁ ▂ ▁ ▁ ▁ ▁ ▁ ▁ ▂ ▂ ▂ ▁ ▂ ▁ ▁ ▁ ▁
+
+
+ ▁ ▁ ▁ ▁ ▁ ▁ ▁▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁▁▁ ▁ ▁▁ ▁▁ ▁ ▁▁▁ ▁▁ ▁ ▁ ▁ ▁▁▁ ▁ ▁ ▁ ▁▁ ▁ ▁ ▁ ▁ ▁▁ ▁▁ ▁▁ ▁ ▁ ▁▁ ▁ ▁ ▁ ▁ ▁▁▁▁▁ ▁ ▁ ▁ ▁ ▁ ▁▁ ▁▁▁ ▁
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -36983,136 +40186,136 @@
font-weight: 700;
}
- .terminal-3011216141-matrix {
+ .terminal-2056257650-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3011216141-title {
+ .terminal-2056257650-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3011216141-r1 { fill: #e1e1e1 }
- .terminal-3011216141-r2 { fill: #c5c8c6 }
- .terminal-3011216141-r3 { fill: #e1e1e1;font-weight: bold }
- .terminal-3011216141-r4 { fill: #1e1e1e }
- .terminal-3011216141-r5 { fill: #0178d4 }
- .terminal-3011216141-r6 { fill: #e2e2e2 }
- .terminal-3011216141-r7 { fill: #e3e8e8 }
+ .terminal-2056257650-r1 { fill: #e1e1e1 }
+ .terminal-2056257650-r2 { fill: #c5c8c6 }
+ .terminal-2056257650-r3 { fill: #e1e1e1;font-weight: bold }
+ .terminal-2056257650-r4 { fill: #1e1e1e }
+ .terminal-2056257650-r5 { fill: #0178d4 }
+ .terminal-2056257650-r6 { fill: #e2e2e2 }
+ .terminal-2056257650-r7 { fill: #e3e8e8 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- SwitchApp
+ SwitchApp
-
-
-
-
-
-
-
- Example switches
-
-
- ▊ ▔▔▔▔▔▔▔▔ ▎
- off: ▊ ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- on: ▊ ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- focused: ▊ ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
- ▊ ▔▔▔▔▔▔▔▔ ▎
- custom: ▊ ▎
- ▊ ▁▁▁▁▁▁▁▁ ▎
-
-
-
-
+
+
+
+
+
+
+
+ Example switches
+
+
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ off: ▊ ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ on: ▊ ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ focused: ▊ ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+ ▊ ▔▔▔▔▔▔▔▔ ▎
+ custom: ▊ ▎
+ ▊ ▁▁▁▁▁▁▁▁ ▎
+
+
+
+
@@ -37143,135 +40346,135 @@
font-weight: 700;
}
- .terminal-1525284282-matrix {
+ .terminal-4106412953-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1525284282-title {
+ .terminal-4106412953-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1525284282-r1 { fill: #c5c8c6 }
- .terminal-1525284282-r2 { fill: #e1e1e1 }
- .terminal-1525284282-r3 { fill: #e1e1e1;font-weight: bold }
- .terminal-1525284282-r4 { fill: #737373 }
- .terminal-1525284282-r5 { fill: #474747 }
- .terminal-1525284282-r6 { fill: #0178d4 }
+ .terminal-4106412953-r1 { fill: #c5c8c6 }
+ .terminal-4106412953-r2 { fill: #e1e1e1 }
+ .terminal-4106412953-r3 { fill: #e1e1e1;font-weight: bold }
+ .terminal-4106412953-r4 { fill: #737373 }
+ .terminal-4106412953-r5 { fill: #474747 }
+ .terminal-4106412953-r6 { fill: #0178d4 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- TabRenameApp
+ TabRenameApp
-
-
-
-
- This is a much longer label for the tab 0 11 222 3333 44444
- ━ ╸ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ╺ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
- TabPane #test
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ This is a much longer label for the tab 0 11 222 3333 44444
+ ━╸ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ TabPane #test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -37302,141 +40505,141 @@
font-weight: 700;
}
- .terminal-19952654-matrix {
+ .terminal-1980553710-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-19952654-title {
+ .terminal-1980553710-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-19952654-r1 { fill: #c5c8c6 }
- .terminal-19952654-r2 { fill: #e1e1e1 }
- .terminal-19952654-r3 { fill: #737373 }
- .terminal-19952654-r4 { fill: #e1e1e1;font-weight: bold }
- .terminal-19952654-r5 { fill: #474747 }
- .terminal-19952654-r6 { fill: #0178d4 }
- .terminal-19952654-r7 { fill: #121212 }
- .terminal-19952654-r8 { fill: #0053aa }
- .terminal-19952654-r9 { fill: #dde8f3;font-weight: bold }
- .terminal-19952654-r10 { fill: #323232 }
- .terminal-19952654-r11 { fill: #ddedf9 }
+ .terminal-1980553710-r1 { fill: #c5c8c6 }
+ .terminal-1980553710-r2 { fill: #e1e1e1 }
+ .terminal-1980553710-r3 { fill: #737373 }
+ .terminal-1980553710-r4 { fill: #e1e1e1;font-weight: bold }
+ .terminal-1980553710-r5 { fill: #474747 }
+ .terminal-1980553710-r6 { fill: #0178d4 }
+ .terminal-1980553710-r7 { fill: #4ebf71;font-weight: bold }
+ .terminal-1980553710-r8 { fill: #323232 }
+ .terminal-1980553710-r9 { fill: #fea62b;font-weight: bold }
+ .terminal-1980553710-r10 { fill: #a7a9ab }
+ .terminal-1980553710-r11 { fill: #e2e3e3 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- TabbedApp
+ TabbedApp
-
-
-
-
- Leto Jessica Paul
- ━━━━━━━━ ╸ ━━━━━━━ ╺ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
- ▁ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▁
- ▎ ▊
- ▎ Lady Jessica ▊
- ▎ ▊
- ▔ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▔
- Bene Gesserit and concubine of Leto, and mother of Paul and Alia.
-
-
-
- Paul Alia
- ━ ╸ ━━━━ ╺ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
- First child
-
-
-
-
-
-
- L Leto J Jessica P Paul
+
+
+
+
+ Leto Jessica Paul
+ ━━━━━━━━╸ ━━━━━━━ ╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+
+
+ Lady Jessica
+
+ Bene Gesserit and concubine of Leto, and mother of Paul and Alia.
+
+
+
+ Paul Alia
+ ━╸ ━━━━ ╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ First child
+
+
+
+
+
+
+
+ l Leto j Jessica p Paul
@@ -37466,139 +40669,139 @@
font-weight: 700;
}
- .terminal-1634536353-matrix {
+ .terminal-15712074-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1634536353-title {
+ .terminal-15712074-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1634536353-r1 { fill: #c5c8c6 }
- .terminal-1634536353-r2 { fill: #e1e1e1 }
- .terminal-1634536353-r3 { fill: #e1e1e1;font-weight: bold }
- .terminal-1634536353-r4 { fill: #474747 }
- .terminal-1634536353-r5 { fill: #0178d4 }
- .terminal-1634536353-r6 { fill: #454a50 }
- .terminal-1634536353-r7 { fill: #e2e3e3;font-weight: bold }
- .terminal-1634536353-r8 { fill: #000000 }
- .terminal-1634536353-r9 { fill: #737373 }
- .terminal-1634536353-r10 { fill: #323232 }
+ .terminal-15712074-r1 { fill: #c5c8c6 }
+ .terminal-15712074-r2 { fill: #e1e1e1 }
+ .terminal-15712074-r3 { fill: #e1e1e1;font-weight: bold }
+ .terminal-15712074-r4 { fill: #474747 }
+ .terminal-15712074-r5 { fill: #0178d4 }
+ .terminal-15712074-r6 { fill: #454a50 }
+ .terminal-15712074-r7 { fill: #e2e3e3;font-weight: bold }
+ .terminal-15712074-r8 { fill: #000000 }
+ .terminal-15712074-r9 { fill: #737373 }
+ .terminal-15712074-r10 { fill: #323232 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- TabbedContentStyleLeakTestApp
+ TabbedContentStyleLeakTestApp
-
-
-
-
- Leak Test
- ━ ╸ ━━━━━━━━━ ╺ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
- This label should come first
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- This button should come second
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
- These Tabs Should Come Last
- ━ ╸ ━━━━━ ╺ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ Leak Test
+ ━╸ ━━━━━━━━━ ╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ This label should come first
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ This button should come second
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+ These Tabs Should Come Last
+ ━╸ ━━━━━ ╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+
+
+
+
+
+
+
+
+
+
+
@@ -37629,140 +40832,140 @@
font-weight: 700;
}
- .terminal-3976501021-matrix {
+ .terminal-4066879127-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3976501021-title {
+ .terminal-4066879127-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3976501021-r1 { fill: #c5c8c6 }
- .terminal-3976501021-r2 { fill: #e1e1e1 }
- .terminal-3976501021-r3 { fill: #484848;font-weight: bold }
- .terminal-3976501021-r4 { fill: #484848 }
- .terminal-3976501021-r5 { fill: #737373 }
- .terminal-3976501021-r6 { fill: #474747 }
- .terminal-3976501021-r7 { fill: #0178d4 }
- .terminal-3976501021-r8 { fill: #a32327 }
- .terminal-3976501021-r9 { fill: #ffdddd }
- .terminal-3976501021-r10 { fill: #f09d9e;font-weight: bold }
- .terminal-3976501021-r11 { fill: #810000 }
+ .terminal-4066879127-r1 { fill: #c5c8c6 }
+ .terminal-4066879127-r2 { fill: #e1e1e1 }
+ .terminal-4066879127-r3 { fill: #484848;font-weight: bold }
+ .terminal-4066879127-r4 { fill: #484848 }
+ .terminal-4066879127-r5 { fill: #737373 }
+ .terminal-4066879127-r6 { fill: #474747 }
+ .terminal-4066879127-r7 { fill: #0178d4 }
+ .terminal-4066879127-r8 { fill: #a32327 }
+ .terminal-4066879127-r9 { fill: #ffdddd }
+ .terminal-4066879127-r10 { fill: #f09d9e;font-weight: bold }
+ .terminal-4066879127-r11 { fill: #810000 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- FiddleWithTabsApp
+ FiddleWithTabsApp
-
-
-
-
- Tab 1 Tab 2 Tab 4 Tab 5
- ━ ╸ ━━━━━ ╺ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- Button
- ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ Tab 1 Tab 2 Tab 4 Tab 5
+ ━╸ ━━━━━ ╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ Button
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -37793,136 +40996,136 @@
font-weight: 700;
}
- .terminal-2580112047-matrix {
+ .terminal-284173292-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2580112047-title {
+ .terminal-284173292-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2580112047-r1 { fill: #e1e1e1 }
- .terminal-2580112047-r2 { fill: #c5c8c6 }
- .terminal-2580112047-r3 { fill: #e1e1e1;font-weight: bold }
- .terminal-2580112047-r4 { fill: #98e024;font-weight: bold;font-style: italic; }
- .terminal-2580112047-r5 { fill: #f4005f;font-weight: bold }
- .terminal-2580112047-r6 { fill: #e1e1e1;font-style: italic; }
- .terminal-2580112047-r7 { fill: #e1e1e1;text-decoration: underline; }
+ .terminal-284173292-r1 { fill: #e1e1e1 }
+ .terminal-284173292-r2 { fill: #c5c8c6 }
+ .terminal-284173292-r3 { fill: #e1e1e1;font-weight: bold }
+ .terminal-284173292-r4 { fill: #98e024;font-weight: bold;font-style: italic; }
+ .terminal-284173292-r5 { fill: #f4005f;font-weight: bold }
+ .terminal-284173292-r6 { fill: #e1e1e1;font-style: italic; }
+ .terminal-284173292-r7 { fill: #e1e1e1;text-decoration: underline; }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- TableStaticApp
+ TableStaticApp
-
-
-
- ┏━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━┓
- ┃ Foo ┃ Bar ┃ baz ┃
- ┡━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━┩
- │ Hello World! │ Italic │ Underline │
- └──────────────┴────────┴───────────┘
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ┏━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━┓
+ ┃ Foo ┃ Bar ┃ baz ┃
+ ┡━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━┩
+ │ Hello World! │ Italic │ Underline │
+ └──────────────┴────────┴───────────┘
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -37953,136 +41156,136 @@
font-weight: 700;
}
- .terminal-3233270805-matrix {
+ .terminal-3291459360-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3233270805-title {
+ .terminal-3291459360-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3233270805-r1 { fill: #c5c8c6 }
- .terminal-3233270805-r2 { fill: #e1e1e1 }
- .terminal-3233270805-r3 { fill: #737373 }
- .terminal-3233270805-r4 { fill: #e1e1e1;font-weight: bold }
- .terminal-3233270805-r5 { fill: #474747 }
- .terminal-3233270805-r6 { fill: #0178d4 }
- .terminal-3233270805-r7 { fill: #0000ff }
+ .terminal-3291459360-r1 { fill: #c5c8c6 }
+ .terminal-3291459360-r2 { fill: #e1e1e1 }
+ .terminal-3291459360-r3 { fill: #737373 }
+ .terminal-3291459360-r4 { fill: #e1e1e1;font-weight: bold }
+ .terminal-3291459360-r5 { fill: #474747 }
+ .terminal-3291459360-r6 { fill: #0178d4 }
+ .terminal-3291459360-r7 { fill: #0000ff }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+