Giter Club home page Giter Club logo

textual's Introduction

Textual splash image

Discord

Textual

Textual is a Rapid Application Development framework for Python.

Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser!

๐ŸŽฌ Demonstration

A quick run through of some Textual features.

Screen.Recording.2022-10-22.at.19.00.48.mov

About

Textual adds interactivity to Rich with an API inspired by modern web development.

On modern terminal software (installed by default on most systems), Textual apps can use 16.7 million colors with mouse support and smooth flicker-free animation. A powerful layout engine and re-usable components makes it possible to build apps that rival the desktop and web experience.

Compatibility

Textual runs on Linux, macOS, and Windows. Textual requires Python 3.8 or above.

Installing

Install Textual via pip:

pip install textual

If you plan on developing Textual apps, you should also install the development tools with the following command:

pip install textual-dev

See the docs if you need help getting started.

Demo

Run the following command to see a little of what Textual can do:

python -m textual

Textual demo

Documentation

Head over to the Textual documentation to start building!

Join us on Discord

Join the Textual developers and community on our Discord Server.

Examples

The Textual repository comes with a number of examples you can experiment with or use as a template for your own projects.

๐ŸŽฌ Code browser

This is the code_browser.py example which clocks in at 61 lines (including docstrings and blank lines).

Screen.Recording.2022-10-21.at.12.41.15.mov
๐Ÿ“ท Calculator

This is calculator.py which demonstrates Textual grid layouts.

calculator screenshot

๐ŸŽฌ Stopwatch

This is the Stopwatch example from the tutorial.

Screen.Recording.2022-10-22.at.21.12.22.mov

Reference commands

The textual command has a few sub-commands to preview Textual styles.

๐ŸŽฌ Easing reference

This is the easing reference which demonstrates the easing parameter on animation, with both movement and opacity. You can run it with the following command:

textual easing
Screen.Recording.2022-10-17.at.11.38.13.mov
๐ŸŽฌ Borders reference

This is the borders reference which demonstrates some of the borders styles in Textual. You can run it with the following command:

textual borders
Screen.Recording.2022-10-17.at.11.44.24.mov
๐ŸŽฌ Colors reference

This is a reference for Textual's color design system.

textual colors
Screen.Recording.2022-10-22.at.19.07.20.mov

textual's People

Contributors

aaronst avatar darrenburns avatar davep avatar davetapley avatar davidbrochart avatar dmunozv04 avatar driscollis avatar edrogers avatar gergely-elias avatar ggozad avatar joshbduncan avatar jspv avatar juftin avatar kimvanwyk avatar lllama avatar muongkimhong avatar nekeal avatar nitzan-shaked avatar ofek avatar olivierphi avatar overflowy avatar pawamoy avatar rodrigogiraoserrao avatar tconbeer avatar tomjgooding avatar tusharsadhwani avatar uriyaharpeness avatar valentingregoire avatar willmcgugan avatar zormit avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

textual's Issues

[Feature Request] Tab Widget

Hi Will, awesome work on this library so far!

One widget I commonly use in many different contexts is some sort of tabbed container. I'm willing to open a PR for this myself, but I thought it might be nice to first discuss the API for it.

I'd expect the following usage pattern:

class MyApp(App):
    async def on_mount(self, event: events.Mount) -> None:
        tab1 = Tab("First Tab Label")
        # ... add some widgets to tab1
        await tab1.view.dock(someWidget())
        await tab1.view.dock(someWidget())
        await tab1.view.dock(someWidget())
    
        tab2 = Tab("Second Tab Label")
        # ... add some widgets to the tab
        await tab2.view.dock(someWidget())
        await tab2.view.dock(someWidget())
        await tab2.view.dock(someWidget())
        
        tabs = Tabs([tab1, tab2])
        await self.view.dock(tabs)

And I'd expect the output to look similar to this:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”€โ”€โ”€โ”€โ”
โ”‚ First Tab Label โ”‚ Second Tab Label โ”‚                    โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                         โ”‚
โ”‚         [ Content of selected Tab ]                     โ”‚
โ”†                                                         โ”†
โ”†                                                         โ”†
โ”‚                                                         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”€โ”€โ”€โ”€โ”˜

With the small difference of the selected being highlighted somehow, be it bold or underlined or italic or whatever.

What are your thoughts on something like this?

DivideByZero error in the `python -m textual.app` demo

While playing with the demo I tried scrolling up and down and then I think I hit "B" and got this crash (not sure what the pure steps to reproduce are I'm afraid):

โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Traceback (most recent call last) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ /Users/simon/.local/share/virtualenvs/t-nAAsrU6W/lib/python3.8/site-packages/textual/app.py:93   โ”‚
โ”‚ in process_messages                                                                              โ”‚
โ”‚                                                                                                  โ”‚
โ”‚    90 โ”‚                                                                                          โ”‚
โ”‚    91 โ”‚   async def process_messages(self) -> None:                                              โ”‚
โ”‚    92 โ”‚   โ”‚   try:                                                                               โ”‚
โ”‚ โฑ  93 โ”‚   โ”‚   โ”‚   await self._process_messages()                                                 โ”‚
โ”‚    94 โ”‚   โ”‚   except Exception:                                                                  โ”‚
โ”‚    95 โ”‚   โ”‚   โ”‚   self.console.print_exception(show_locals=True)                                 โ”‚
โ”‚    96                                                                                            โ”‚
โ”‚                                                                                                  โ”‚
โ”‚ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ                                                   โ”‚
โ”‚ โ”‚ self = MyApp(title='Megasoma Application') โ”‚                                                   โ”‚
โ”‚ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ                                                   โ”‚
โ”‚                                                                                                  โ”‚
โ”‚ /Users/simon/.local/share/virtualenvs/t-nAAsrU6W/lib/python3.8/site-packages/textual/app.py:116  โ”‚
โ”‚ in _process_messages                                                                             โ”‚
โ”‚                                                                                                  โ”‚
โ”‚   113 โ”‚   โ”‚   โ”‚   log.exception("error starting application mode")                               โ”‚
โ”‚   114 โ”‚   โ”‚   โ”‚   raise                                                                          โ”‚
โ”‚   115 โ”‚   โ”‚   try:                                                                               โ”‚
โ”‚ โฑ 116 โ”‚   โ”‚   โ”‚   await super().process_messages()                                               โ”‚
โ”‚   117 โ”‚   โ”‚   finally:                                                                           โ”‚
โ”‚   118 โ”‚   โ”‚   โ”‚   try:                                                                           โ”‚
โ”‚   119 โ”‚   โ”‚   โ”‚   โ”‚   if self.children:                                                          โ”‚
โ”‚                                                                                                  โ”‚
โ”‚ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ               โ”‚
โ”‚ โ”‚ __class__ = <class '__main__.App'>                                             โ”‚               โ”‚
โ”‚ โ”‚ close_all = <function App._process_messages.<locals>.close_all at 0x106df0430> โ”‚               โ”‚
โ”‚ โ”‚    driver = <textual._linux_driver.LinuxDriver object at 0x106897cd0>          โ”‚               โ”‚
โ”‚ โ”‚      loop = <_UnixSelectorEventLoop running=True closed=False debug=False>     โ”‚               โ”‚
โ”‚ โ”‚      self = MyApp(title='Megasoma Application')                                โ”‚               โ”‚
โ”‚ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ               โ”‚
โ”‚                                                                                                  โ”‚
โ”‚ /Users/simon/.local/share/virtualenvs/t-nAAsrU6W/lib/python3.8/site-packages/textual/message_pum โ”‚
โ”‚ p.py:172 in process_messages                                                                     โ”‚
โ”‚                                                                                                  โ”‚
โ”‚   169 โ”‚   โ”‚   โ”‚   โ”‚   priority, message = await self.get_message()                               โ”‚
โ”‚   170 โ”‚   โ”‚   โ”‚                                                                                  โ”‚
โ”‚   171 โ”‚   โ”‚   โ”‚   try:                                                                           โ”‚
โ”‚ โฑ 172 โ”‚   โ”‚   โ”‚   โ”‚   await self.dispatch_message(message, priority)                             โ”‚
โ”‚   173 โ”‚   โ”‚   โ”‚   except Exception as error:                                                     โ”‚
โ”‚   174 โ”‚   โ”‚   โ”‚   โ”‚   raise                                                                      โ”‚
โ”‚   175                                                                                            โ”‚
โ”‚                                                                                                  โ”‚
โ”‚ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ             โ”‚
โ”‚ โ”‚ idle_handler = <bound method App.on_idle of MyApp(title='Megasoma Application')> โ”‚             โ”‚
โ”‚ โ”‚      message = Key(key='x')                                                      โ”‚             โ”‚
โ”‚ โ”‚      pending = None                                                              โ”‚             โ”‚
โ”‚ โ”‚     priority = 0                                                                 โ”‚             โ”‚
โ”‚ โ”‚         self = MyApp(title='Megasoma Application')                               โ”‚             โ”‚
โ”‚ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ             โ”‚
โ”‚                                                                                                  โ”‚
โ”‚ /Users/simon/.local/share/virtualenvs/t-nAAsrU6W/lib/python3.8/site-packages/textual/message_pum โ”‚
โ”‚ p.py:186 in dispatch_message                                                                     โ”‚
โ”‚                                                                                                  โ”‚
โ”‚   183 โ”‚   โ”‚   self, message: Message, priority: int = 0                                          โ”‚
โ”‚   184 โ”‚   ) -> bool | None:                                                                      โ”‚
โ”‚   185 โ”‚   โ”‚   if isinstance(message, events.Event):                                              โ”‚
โ”‚ โฑ 186 โ”‚   โ”‚   โ”‚   await self.on_event(message, priority)                                         โ”‚
โ”‚   187 โ”‚   โ”‚   else:                                                                              โ”‚
โ”‚   188 โ”‚   โ”‚   โ”‚   return await self.on_message(message)                                          โ”‚
โ”‚   189 โ”‚   โ”‚   return False                                                                       โ”‚
โ”‚                                                                                                  โ”‚
โ”‚ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ                                               โ”‚
โ”‚ โ”‚  message = Key(key='x')                        โ”‚                                               โ”‚
โ”‚ โ”‚ priority = 0                                   โ”‚                                               โ”‚
โ”‚ โ”‚     self = MyApp(title='Megasoma Application') โ”‚                                               โ”‚
โ”‚ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ                                               โ”‚
โ”‚                                                                                                  โ”‚
โ”‚ /Users/simon/.local/share/virtualenvs/t-nAAsrU6W/lib/python3.8/site-packages/textual/app.py:157  โ”‚
โ”‚ in on_event                                                                                      โ”‚
โ”‚                                                                                                  โ”‚
โ”‚   154 โ”‚   โ”‚   if isinstance(event, events.Key):                                                  โ”‚
โ”‚   155 โ”‚   โ”‚   โ”‚   key_action = self.KEYS.get(event.key, None)                                    โ”‚
โ”‚   156 โ”‚   โ”‚   โ”‚   if key_action is not None:                                                     โ”‚
โ”‚ โฑ 157 โ”‚   โ”‚   โ”‚   โ”‚   await self.action(key_action)                                              โ”‚
โ”‚   158 โ”‚   โ”‚   โ”‚   โ”‚   return                                                                     โ”‚
โ”‚   159 โ”‚   โ”‚                                                                                      โ”‚
โ”‚   160 โ”‚   โ”‚   if isinstance(event, events.InputEvent):                                           โ”‚
โ”‚                                                                                                  โ”‚
โ”‚ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ                                             โ”‚
โ”‚ โ”‚  __class__ = <class '__main__.App'>              โ”‚                                             โ”‚
โ”‚ โ”‚      event = Key(key='x')                        โ”‚                                             โ”‚
โ”‚ โ”‚ key_action = 'bang'                              โ”‚                                             โ”‚
โ”‚ โ”‚   priority = 0                                   โ”‚                                             โ”‚
โ”‚ โ”‚       self = MyApp(title='Megasoma Application') โ”‚                                             โ”‚
โ”‚ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ                                             โ”‚
โ”‚                                                                                                  โ”‚
โ”‚ /Users/simon/.local/share/virtualenvs/t-nAAsrU6W/lib/python3.8/site-packages/textual/app.py:183  โ”‚
โ”‚ in action                                                                                        โ”‚
โ”‚                                                                                                  โ”‚
โ”‚   180 โ”‚   โ”‚   โ”‚   action_name = action                                                           โ”‚
โ”‚   181 โ”‚   โ”‚                                                                                      โ”‚
โ”‚   182 โ”‚   โ”‚   log.debug("ACTION %r %r", destination, action_name)                                โ”‚
โ”‚ โฑ 183 โ”‚   โ”‚   await self.dispatch_action(destination, action_name, params)                       โ”‚
โ”‚   184 โ”‚                                                                                          โ”‚
โ”‚   185 โ”‚   async def dispatch_action(                                                             โ”‚
โ”‚   186 โ”‚   โ”‚   self, destination: str, action_name: str, params: Any                              โ”‚
โ”‚                                                                                                  โ”‚
โ”‚ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ                                            โ”‚
โ”‚ โ”‚      action = 'bang'                              โ”‚                                            โ”‚
โ”‚ โ”‚ action_name = 'bang'                              โ”‚                                            โ”‚
โ”‚ โ”‚ destination = 'app'                               โ”‚                                            โ”‚
โ”‚ โ”‚      params = ()                                  โ”‚                                            โ”‚
โ”‚ โ”‚        self = MyApp(title='Megasoma Application') โ”‚                                            โ”‚
โ”‚ โ”‚      target = 'bang'                              โ”‚                                            โ”‚
โ”‚ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ                                            โ”‚
โ”‚                                                                                                  โ”‚
โ”‚ /Users/simon/.local/share/virtualenvs/t-nAAsrU6W/lib/python3.8/site-packages/textual/app.py:193  โ”‚
โ”‚ in dispatch_action                                                                               โ”‚
โ”‚                                                                                                  โ”‚
โ”‚   190 โ”‚   โ”‚   โ”‚   method_name = f"action_{action_name}"                                          โ”‚
โ”‚   191 โ”‚   โ”‚   โ”‚   method = getattr(action_target, method_name, None)                             โ”‚
โ”‚   192 โ”‚   โ”‚   โ”‚   if method is not None:                                                         โ”‚
โ”‚ โฑ 193 โ”‚   โ”‚   โ”‚   โ”‚   await method(*params)                                                      โ”‚
โ”‚   194 โ”‚                                                                                          โ”‚
โ”‚   195 โ”‚   async def on_shutdown_request(self, event: events.ShutdownRequest) -> None:            โ”‚
โ”‚   196 โ”‚   โ”‚   log.debug("shutdown request")                                                      โ”‚
โ”‚                                                                                                  โ”‚
โ”‚ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ        โ”‚
โ”‚ โ”‚   action_name = 'bang'                                                                โ”‚        โ”‚
โ”‚ โ”‚ action_target = MyApp(title='Megasoma Application')                                   โ”‚        โ”‚
โ”‚ โ”‚   destination = 'app'                                                                 โ”‚        โ”‚
โ”‚ โ”‚        method = <bound method App.action_bang of MyApp(title='Megasoma Application')> โ”‚        โ”‚
โ”‚ โ”‚   method_name = 'action_bang'                                                         โ”‚        โ”‚
โ”‚ โ”‚        params = ()                                                                    โ”‚        โ”‚
โ”‚ โ”‚          self = MyApp(title='Megasoma Application')                                   โ”‚        โ”‚
โ”‚ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ        โ”‚
โ”‚                                                                                                  โ”‚
โ”‚ /Users/simon/.local/share/virtualenvs/t-nAAsrU6W/lib/python3.8/site-packages/textual/app.py:221  โ”‚
โ”‚ in action_bang                                                                                   โ”‚
โ”‚                                                                                                  โ”‚
โ”‚   218 โ”‚   โ”‚   await self.close_messages()                                                        โ”‚
โ”‚   219 โ”‚                                                                                          โ”‚
โ”‚   220 โ”‚   async def action_bang(self) -> None:                                                   โ”‚
โ”‚ โฑ 221 โ”‚   โ”‚   1 / 0                                                                              โ”‚
โ”‚   222                                                                                            โ”‚
โ”‚   223                                                                                            โ”‚
โ”‚   224 if __name__ == "__main__":                                                                 โ”‚
โ”‚                                                                                                  โ”‚
โ”‚ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ                                                   โ”‚
โ”‚ โ”‚ self = MyApp(title='Megasoma Application') โ”‚                                                   โ”‚
โ”‚ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ                                                   โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
ZeroDivisionError: division by zero

just learning....simple.py gives errors

ran this to install
pip install Textual

$ ./test.py
Traceback (most recent call last):
File "./test.py", line 6, in
from textual import events
File "/home/~~~~~/.local/lib/python3.8/site-packages/textual/events.py", line 7, in
from .message import Message
File "/home/~~~~~/.local/lib/python3.8/site-packages/textual/message.py", line 7, in
from ._types import MessageTarget
File "/home/~~~~~/.local/lib/python3.8/site-packages/textual/_types.py", line 31, in
Lines = list[list[Segment]]
TypeError: 'type' object is not subscriptable

am I wrong right from the get go?

Adding a View to a ScrollView

from textual.app import App
from rich.text import Text
from textual.widgets import ScrollView, Static
from textual.views import GridView

class MyApp(App):

    async def on_mount(self):

        grid_view = GridView()
        grid_view.grid.add_column('col1')
        grid_view.grid.add_column('col2')
        for i in range(0, 20):
            grid_view.grid.add_row(f'row{i}', max_size=1)
            grid_view.grid.place(Static(Text(text=f'r{i}-c1')), Static(Text(text=f'r{i}-c2')))

        scroll = ScrollView(grid_view, auto_width=False)

        await self.view.dock(scroll, edge="top")


app = MyApp()
app.run(log="textual.log")

It looks like nesting Views inside of other Views is not supported (based on the implementation of the reflow in Layout).

This leads to the GridView having a width and height always equal to 0 so nothing is ever rendered.

Docking grid_view in the above example instead of scroll works fine.

To be added by @willmcgugan

Mouse codes are printed to the terminal on exit

Sometimes mouse codes are printed to the terminal on exit. This tends to occur if you are moving the mouse around then exit quickly.

I suspect this is because the app exists prior to all mouse events in stdin being consumed.

Find a way to a) reliably reproduce this error and b) fix it.

I suspect this will require disabling input then posting a null event and waiting for that event to be processed, prior to exiting.

Query on Textual

In regards to textual, will you be able to utilise the curses module inside of a widget?

`NoneType in await` and many more problems with example code

Latest update broke something ๐Ÿ˜…

work machine: 3.7.x, pip install rich, textual, pyfiglet
home machine: 3.7.10, 3.9.6, pip install rich, textual, pyfiglet

Downloaded yesterday, everything works fine (work machine, don't have access at the moment, will update this tomorrow), downloaded again tonight on personal machine and I cannot get example code to run. constant expects 1 argument but 2 were given across almost all examples, including the base Beeper app, and the background color switcher spits out object NoneType can't be used in 'await' expression. I know rich is still working, because the output is colored:
image

I pulled the code and built myself with poetry install and used that for the examples (I think I did it correctly) and still no-go.

[Feature Request] Pass custom parameters to `App`

(I know textual is still in active development and that this might be in the works, but just throwing it up here to help keep track)

There's no way to pass in and store values inside a class that inherits from App, to store application-level information. For instance, the following code does not work:

class MyApp(App):
    def __init__(self, custom_arg, *args, **kwargs):
        self.custom_arg = custom_arg
        super().__init__(*args, **kwargs)
    async def on_mount(self):
        print(self.custom_arg)
MyApp(5).run()

The cause of this seems the be that in the run() function, a new instance of MyApp is created, which is the instance on which on_mount() is actually caused. This is a bit annoying and requires using global variables to store the parameters we want, which is not ideal.

Hey

Textual is currently in active development. I will try to keep the sample functional, but I make no guarantee anything will run right now.

Unless this is a blatant bug, you might want to open a discussion for now.

Dynamically adding placeholders

I was trying to add e.g. a new row to a layout after it was created in on_startup(), but it looks like it has no effect (I tried refresh() and require_layout()).
Just asking as I know it might be early for that. Keep up the great work anyway!

Reading from stdin

Hi.

I'm trying to make a textual app that deals with data from stdin. However, if I pipe something to a textual app, it gets consumed as keystrokes. When I consume stdin first (via sys.stdin.read(), for example), the app does not process keystrokes (rather, the typed keys get superimposed onto it).

Is there a solution for this?

Polish geometry.py

geometry.py is fairly unique in that it has stabilized quickly. It is also fairly easy to comprehend on its own.

There are a few missing docstrings (args and return types) etc. There are probably also a few edge cases that could used tests for. Finally, there may be optimizations that could be done. If you want to do optimization, I would expect a serious attempt at profiling, but bear in mind that I don't want to sacrifice readability.

Horizontal scrolling

(hope you don't mind me opening an issue to ask a question, I've tried hard to get to the bottom of this myself)

I'm trying to get horizontal scrolling working with textual and a rich table, but no luck

Below is what I have so far.

It seems like the table is refusing to exceed the width of the terminal, thus the horizontal scroll bar of ScrollView is not coming into play.

Vertical scrolling is working great.

What am I doing wrong?

(ref: https://twitter.com/samuel_colvin/status/1426289617632960515)

from rich.table import Table

from textual import events
from textual.app import App
from textual.widgets import Header, Footer, ScrollView


class MyApp(App):
    """An example of a very simple Textual App"""

    async def on_load(self, event: events.Load) -> None:
        await self.bind("q", "quit", "Quit")

    async def on_mount(self, event: events.Mount) -> None:

        self.body = body = ScrollView()
        body.virtual_size.width = 300

        await self.view.dock(body)

        async def add_content():
            table = Table(title="Demo", width=300, min_width=300)

            for i in range(40):
                table.add_column(f'Col {i + 1}', style='magenta')

            for i in range(40):
                table.add_row(*[f'cell {i},{j}' for j in range(40)])

            await body.update(table)

        await self.call_later(add_content)


MyApp.run(title="Simple App", log="textual.log")

Versions:

python 3.9.5
textual==0.1.10
rich==10.7.0
OS: Ubuntu 21.04 with standard terminal

render() called too often with multiple widgets

And MWE with RenderCounter widget that refresh()es every 2 seconds. The first widget starts out fine (although weirdly it's rendered 2 to 3 times when first displayed). The others are rendered twice every refresh.

from textual.app import App
from textual.widget import Widget

from rich.panel import Panel
from rich import box


class RenderCounter(Widget):
    def on_mount(self):
        self.k = [0]
        self.set_interval(2.0, self.refresh)

    def render(self) -> Panel:
        self.k.append(self.k[-1] + 1)
        return Panel(
            " ".join([str(item) for item in self.k]),
            box=box.SQUARE,
        )


class CounterApp(App):
    async def on_mount(self) -> None:
        await self.view.dock(RenderCounter(), size=10)
        await self.view.dock(RenderCounter(), size=10)
        await self.view.dock(RenderCounter(), size=10)
        await self.view.dock(RenderCounter(), size=10)
        await self.view.dock(RenderCounter(), size=10)


CounterApp.run()

screenshot

Prompt (or equivalent) implementation example?

So far I'm loving working with this library. I see a lot of promise!

I'm working on creating a very simple app right now, using the simple.py example as a starting point, but I needed to capture input from the user. I tried creating a new Widget using a rich Prompt object and putting it in the sidebar, but when it runs the prompt just shows up at the bottom with nothing else, and I am unable to interact with it at all (actually have to kill the whole terminal, but that's probably just due to poor error handling on my part). Is there something I'm not doing right for this to work, or is this just a yet-to-be-implemented functionality ? Full simple test class:

class TestInput(Widget):
    def render(self) -> Prompt:
        test_val = Prompt.ask('Enter Val')
        return test_val

Thanks for any assistance!

Polish parser

The parser class parses stdin to extract mouse codes etc. This code could use docstrings and some tests. It could also use a few tests to check edge cases. I don't know of any bugs, but you may come across some when testing.

error when running examples

i installed textual with pip and i tried to run the first example in the readme with python3 filename.py but i get the error TypeError: on_key() takes 1 positional argument but 2 were given when i press a key. got a similiar error with another example. sorry if im being dumb : p

Implement Windows Driver

Trying to run the textual.app example on Windows 10 with Python 3.9.5:

โžœ python -m textual.app
Traceback (most recent call last):
 File "C:\Program Files\Python39\lib\runpy.py", line 197, in _run_module_as_main
   return _run_code(code, main_globals, None,
 File "C:\Program Files\Python39\lib\runpy.py", line 87, in _run_code
   exec(code, run_globals)
 File "C:\CodeProjects\Python\Manim\manimvenv\lib\site-packages\textual\app.py", line 19, in <module>
   from .driver import Driver
 File "C:\CodeProjects\Python\Manim\manimvenv\lib\site-packages\textual\driver.py", line 8, in <module>
   import curses
 File "C:\Program Files\Python39\lib\curses\__init__.py", line 13, in <module>
   from _curses import *
ModuleNotFoundError: No module named '_curses'

on_key actually is in the base class

The README says: Event handlers in Textual are defined by convention, not by inheritance (so you won't find an on_key method in the base class)..

However, App.on_key actually is there.

AttributeError: module 'textual.events' has no attribute 'Startup'

I get this error when testing examples

~/textual/examples $ python simple.py                   Traceback (most recent call last):                        File "/data/data/com.termux/files/home/textual/examples/simple.py", line 9, in <module>                           class MyApp(App):                                     File "/data/data/com.termux/files/home/textual/examples/simple.py", line 16, in MyApp                             async def on_startup(self, event: events.Startup) -> None:             
AttributeError: module 'textual.events' has no attribute 'Startup'                                     

Live console widget

It would be nice to have a widget that behaves like a console.
At first, just able to print and auto-scroll, and scroll bar, with auto-refresh.
console.print("another brick in the wall")

Maybe reuse Rich's Live View / Console?

calculatory.py does not run if using poetry

Hi @willmcgugan,

I was just playing around with this amazing python module and realized I cannot run the calculator.py example after cloning the repo and running the example with poetry.

I get the following error

Please install pyfiglet to run this example
Traceback (most recent call last):
  File "#############/textual/examples/calculator.py", line 22, in <module>
    from pyfiglet import Figlet
ModuleNotFoundError: No module named 'pyfiglet'

Is if pyfiglet perhaps missing in the dependencies?
It's the first time using Poetry so I dont know how to find out things properly :)

by the way, amazing work!

esda

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

struct disciplina{
char nome[20];
};

struct Estudante{
char nome[20];
char apelido[20];
};
struct pautaNo{
disciplina d;
int ano;
double nota;
Estudante est;
pautaNo* proximo;
};

pautaNo * ps=(pautaNo*)malloc(sizeof(pautaNo)*5);

int tamanho=0;

void colocarProduto(pautaNo p){
ps[tamanho]=p;
tamanho=tamanho+1;
}

void listar(){
for(int i=0; i<tamanho; i++){
printf("Dados do estudante:\n");
printf("Nome: %s \n",ps[i].est.nome);
printf("Apelido: %s \n",ps[i].d.nome);
printf("Disciplina: %s \n",ps[i].est.nome);

	printf("Ano: %f \n",ps[i].ano);
	printf("Nota: %f \n",ps[i].nota); 
	
	printf("\n");
}

}
void pesquisar(char n[]){
for(int i=0; i<tamanho; i++){
if(ps[i].est.nome==n){
printf("Encontrado o nome: %s \n",ps[i].est.nome);
}

}

}

void organizar(){

printf("\n lista organiza da ");
for(int i=0; i<tamanho; i++){
	
	for(int j=0; j<tamanho; j++){
		
	 if(ps[j].nota>ps[j+1].nota){
	 	pautaNo temp=ps[j+1];
	 	ps[j+1]=ps[j];
	 	ps[j]=temp;
	 }
} 
}

}

int main(){

disciplina d;
strcpy(d.nome,"ESDA");

Estudante e1;
strcpy(e1.nome,"Dias");
strcpy(e1.apelido,"Mayato");
pautaNo p1;
p1.ano=2021;
p1.nota=15;
p1.est=e1;
p1.d=d;


Estudante e2;
strcpy(e2.nome,"Dias 2");
strcpy(e2.apelido,"Mayato 1");
pautaNo p2;
p2.ano=2021;
p2.nota=15;
p2.est=e2;
p2.d=d;

Estudante e3;
strcpy(e3.nome,"Mudengue");
strcpy(e3.apelido,"Mayatoo");
pautaNo p3;
p3.ano=2021;
p3.nota=13;
p3.est=e3;
p3.d=d;

Estudante e4;
strcpy(e4.nome,"Mudengue 2");
strcpy(e4.apelido,"Mayato 1");
pautaNo p4;
p4.ano=2021;
p4.nota=10;
p4.est=e4;
p4.d=d;


colocarProduto(p1);
colocarProduto(p2);
colocarProduto(p3);
colocarProduto(p4);

listar();
pesquisar("Dias");
organizar();
listar();

return 0;

}

Scrolling and hitting Q at the same time in textual.app hung the script entirely

See attached video - if I hit Q while scrolling up and down with the trackpad I've occasionally managed to cause the terminal app to hang - such that it no longer responds to keyboard commands or scrolling, plus hitting Ctrl+C or even Ctrl+Z fails to exit it.

I recorded a video here with software that shows the keys I'm pressing:

textual-bug.mov

Best way to implement interactive widgets

I'm implementing an interactive rich table, and wondering about the best way to do it.

I'm currently doing something like:

from rich import box
from rich.align import Align
from rich.console import RenderableType
from rich.panel import Panel
from rich.table import Table
import rich.repr

from textual.widget import Reactive, Widget


def public(opt): return not opt.startswith('_')


@rich.repr.auto(angular=False)
class ChoiceTableWidget(Widget, can_focus=True):

    selected: Reactive[int] = Reactive(0)

    def __rich_repr__(self) -> rich.repr.RichReprResult:
        yield Align.center(self.table, vertical="middle")

    def __init__(self, *args, title="Menu", choices=[], **kwargs):
        self.title = title
        self.table = Table()
        self.choices = choices
        self.length = len(choices)
        self.table.box = box.SIMPLE

        for key in filter(public, choices[0]):
            self.table.add_column(key)

        for item in choices:
            self.table.add_row(*[str(b) for a, b in item.items() if public(a)])

        for row in self.table.rows:
            row.style = 'dim'

        self.table.rows[self.selected].style = 'bold'
        super().__init__(*args, **kwargs)

    def render(self) -> RenderableType:
        return Panel(self.table, title=self.title)

    async def move(self, pos):
        if self.length > self.selected + pos >= 0:
            self.table.rows[self.selected].style = 'dim'
            self.selected += pos
            self.table.rows[self.selected].style = 'bold'

    async def on_down(self):
        await self.move(1)

    async def on_up(self):
        await self.move(-1)

    async def on_enter(self, event):
        return await self.choices[self.selected]['_callback'](self)

But I'm still wondering about the best way to bind keys only to a specific widget (whenver it has the focus), and how to navigate views, being able to replace a view with another, and do something like history.back()

Text Input Widget

To be added by @willmcgugan

Sorry if this is already a thing, or goes against the general philosophy of the project.

I have a need for an input widget a user could type text into. My use case is to search through a long list of items such as a directory tree.

One of the bigger hurdles here would be keys that are already bound to action. There doesn't seem to be a way to "unbind" a key, or "unbind" all keys temporarally when the correct input panel is selected.

Thoughts?

asyncio event loop isn't closed on exit

When run with Python 3.8.5 on Ubuntu 20.04 LTS, the event loop isn't closed when the example is quit by pressing q.

Following error is output and the program exists:

/usr/lib/python3.8/asyncio/base_events:654: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=False>

The terminal is then printing raw bytes (?) of the mouse movement anytime you move the cursor over the window.

image

I'm not sure if it's the Python version or some difference between Linux and OSX.

(In order to run the example I had to patch src/textual/_parser.py, because the type hint list[str] isn't supported in 3.8. I just replaced it with list.)

Method to run code in a thread

I'd like to add a convenient method to execute in job in a thread.

It would probably be added to message pump (used by app / widgets).

It should allow a method to be called with arbitrary parameters, and should return an awaitable.

await self.call_threaded(my_code, "foo", "bar")

I can see this being used for jobs such as reading from disk (which at the moment blocks the vent loop).

Some considerations: a thread pool may be appropriate. We don't want this to be unbounded and potentially launch an unlimited number of threads. It should probably work with both async and sync functions, which will require a little inspection. If you want to tackle this, please discuss your ideas here first.

0.1.9 build failing with Poetry.

Going to admit this is my first time using Poetry so I'm not too familiar with it yet, but it appears to not like the Rich package source that's provided. No idea why. Going to try a manual install, if that fails, I'll try the regular Rich repo and hope nothings broken :)

$ poetry install
Installing dependencies from lock file

Package operations: 1 install, 0 updates, 0 removals

  โ€ข Installing rich (10.6.0 8ae1d4a): Failed

  CalledProcessError

  Command '['git', 'clone', '--recurse-submodules', '[email protected]:willmcgugan/rich', '/home/hyoon/.local/share/virtualenvs/textual-4a5vNUb8/src/rich']' returned non-zero exit status 128.

  at ~/.local/lib/python3.6/site-packages/poetry/utils/_compat.py:218 in run
      214โ”‚                 raise
      215โ”‚             retcode = process.poll()
      216โ”‚             if check and retcode:
      217โ”‚                 raise CalledProcessError(
    โ†’ 218โ”‚                     retcode, process.args, output=stdout, stderr=stderr
      219โ”‚                 )
      220โ”‚         finally:
      221โ”‚             # None because our context manager __exit__ does not use them.
      222โ”‚             process.__exit__(None, None, None)
=== test session starts ===
platform linux (ubuntu 18) -- Python 3.7.11, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /home/hyoon/.local/share/virtualenvs/textual-4a5vNUb8/bin/python
cachedir: .pytest_cache
...
Coverage.py warning: Module textual was never imported. (module-not-imported)

(because of the build fail)

Will report back,.

AttributeError: 'Window' object has no attribute '_disabled_messages'

I try to run simple.py, and i get following errors: (Platform: Mac OS 11.4 with python 3.8.1)

Task exception was never retrieved
future: <Task finished name='Task-5' coro=<MessagePump.process_messages() done, defined at /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/textual/message_pump.py:114> exception=AttributeError("'Window' object has no attribute '_closed'")>
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/textual/message_pump.py", line 116, in process_messages
while not self._closed:
AttributeError: 'Window' object has no attribute '_closed'
Traceback (most recent call last):
File "simple.py", line 24, in
app.run()
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/textual/app.py", line 54, in run
asyncio.run(run_app())
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/runners.py", line 43, in run
return loop.run_until_complete(main)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py", line 612, in run_until_complete
return future.result()
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/textual/app.py", line 52, in run_app
await app.process_messages()
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/textual/app.py", line 73, in process_messages
await super().process_messages()
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/textual/message_pump.py", line 124, in process_messages
await self.dispatch_message(message, priority)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/textual/message_pump.py", line 132, in dispatch_message
await self.on_event(message, priority)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/textual/message_pump.py", line 141, in on_event
await dispatch_function(event)
File "simple.py", line 18, in on_startup
await self.view.mount_all(
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/textual/view.py", line 53, in mount_all
await self.mount(widget, slot=slot)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/textual/view.py", line 108, in mount
await self.app.add(widget)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/textual/app.py", line 84, in add
await child.post_message(events.Created(sender=self))
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/textual/widget.py", line 149, in post_message
if not self.check_message_enabled(message):
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/textual/message_pump.py", line 61, in check_message_enabled
return type(message) not in self._disabled_messages
AttributeError: 'Window' object has no attribute '_disabled_messages'

Typing error with Reactive

PyCharm gives me typing error with Reactive, see screenshot:

image

I'm unsure why this is the case given that mypy doesn't yield such error.

Side question: now that I have found myself a project to use Textual while fully understanding that this is WIP, I'll likely come across many little things like this one you may already be aware of. Should I keep opening issues? Or should I use some other, less formal avenue to keep the noise low and only open ticket when you request it? I'm thinking of Twitter DMs, or possibly some discord server and/or DM. Let me know what works best for you at this stage.

No way to stop quit calculator demo gracefully

Looks like currently there's no other way to stop calculator demo then just to kill from another terminal. It's not the end of the world but still.. it would be nice to have a more friendly way of stopping it.

Unclosed event loop with easing example

Every time I close the easing example. I get the following error:

/usr/lib/python3.9/asyncio/base_events.py:681: ResourceWarning: unclosed event loop <_UnixSelectorEventLoop running=False closed=False debug=False>
  _warn(f"unclosed event loop {self!r}", ResourceWarning, source=self)
ResourceWarning: Enable tracemalloc to get the object allocation traceback

I am thinking there are still messages processing at shutdown that are keeping the loop running, but I have not been able to track down the root cause yet.

Possibly a similar issue to #82?

OS: Manjaro Linux 21.1.4

Switching to v0.1.11 Caused a Crash

I upgraded to v0.1.11 and trying to run an app using v0.1.10 immediately crashed (no other changes were introduced, just the version bump:

โฏ python main.py
Traceback (most recent call last):
  File "/Users/josephlyons/Programming/Python/termifind/main.py", line 34, in <module>
    main()
  File "/Users/josephlyons/Programming/Python/termifind/main.py", line 30, in main
    TermiFindApplication.run(log="termifind.log")
  File "/Users/josephlyons/Programming/Python/termifind/virtual_environment/lib/python3.10/site-packages/textual/app.py", line 187, in run
    asyncio.run(run_app())
  File "/Users/josephlyons/.pyenv/versions/3.10.0b3/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/Users/josephlyons/.pyenv/versions/3.10.0b3/lib/python3.10/asyncio/base_events.py", line 641, in run_until_complete
    return future.result()
  File "/Users/josephlyons/Programming/Python/termifind/virtual_environment/lib/python3.10/site-packages/textual/app.py", line 184, in run_app
    app = cls(console=console, screen=screen, driver_class=driver, **kwargs)
  File "/Users/josephlyons/Programming/Python/termifind/main.py", line 14, in __init__
    self.ui: UI = UI(self.path_container)
  File "/Users/josephlyons/Programming/Python/termifind/src/ui/ui.py", line 22, in __init__
    self.previous_directory_container_scroll_view: TermiFindScrollView = self.__get_scroll_view(
  File "/Users/josephlyons/Programming/Python/termifind/src/ui/ui.py", line 38, in __get_scroll_view
    item_buttons_view: TermiFindButtonsView = self.__get_item_buttons_view(directory_container, should_style_text)
  File "/Users/josephlyons/Programming/Python/termifind/src/ui/ui.py", line 66, in __get_item_buttons_view
    termifind_buttons_view.add(TermiFindButton(item_name_text))
  File "/Users/josephlyons/Programming/Python/termifind/src/ui/custom_views.py", line 10, in add
    self.layout.add(button)
  File "/Users/josephlyons/Programming/Python/termifind/virtual_environment/lib/python3.10/site-packages/textual/layouts/vertical.py", line 27, in add
    self._max_widget_width = max(widget.app.measure(widget), self._max_widget_width)
  File "/Users/josephlyons/Programming/Python/termifind/virtual_environment/lib/python3.10/site-packages/textual/message_pump.py", line 58, in app
    return active_app.get()
LookupError: <ContextVar name='active_app' at 0x1031a4f90>
sys:1: ResourceWarning: unclosed file <_io.TextIOWrapper name='termifind.log' mode='wt' encoding='UTF-8'>

I will try to post a minimal working piece of code that causes the crash, just wanted to get this logged before I forgot about it.

Repo & Branch I'm working on is here: https://github.com/JosephTLyons/termifind-python/tree/Continue-Adapting-Code-To-Use-Textual

pypi lists textual as compatible with 3.7 but it needs at least 3.8

I can't get the traceback because it destroys my terminal but in layout.py here:

    @classmethod
    def _assemble_chops(
        cls, chops: list[dict[int, list[Segment] | None]]
    ) -> Iterable[list[Segment]]:

        for bucket in chops:
            yield sum(
                (segments for _, segments in sorted(bucket.items()) if segments),
                start=[],
            )

sum() can't take start as a keyword argument before 3.8

Problems with rendering `RenderGroup` in `Scrollview` when elements have padding

Textual Version

0.1.10

Minimal repro:

from rich.markdown import Markdown
from rich.padding import Padding
from rich.console import RenderGroup

from textual.app import App
from textual.widgets import ScrollView


class MyApp(App):
    async def on_mount(self, event):
        body = ScrollView()
        await self.view.dock(body, edge="right")
        await body.update(RenderGroup(Padding(Markdown("hello"), 1),
                                      Markdown("world")))

MyApp.run(title="Simple App", log="textual.log")

Expected Result:

hello

world

Actual Result:

hello

It looks like when one of the renderables in a RenderGroup are wrapped in Padding, Textual doesn't display any of the renderables after that one.

result = App.run()

To be added by @willmcgugan

  1. Thanks for such a great library
  2. We are using your library! See the result here: fluidattacks/makes#725
  3. This is not an outstanding feature that must be added
  4. But It would be very useful in some cases

The App class is normally used as a whole application: App.run()

It would be useful if we could return data from it on exit so we can do result = App.run().
This allows for having many 'scenes' (Apps) in our application, for instance for asking user for a value and then do some work, then show other 'scene' (other App), etc

Clock README example crashed when reducing the terminal height

While trying the clock example below, I noticed it would systematically crash when I reduce the height of the terminal:

from datetime import datetime

from rich.align import Align

from textual.app import App
from textual.widget import Widget


class Clock(Widget):
    async def on_mount(self, event):
        self.set_interval(1, callback=self.refresh)

    def render(self) -> Align:
        time = datetime.now().strftime("%X")
        return Align.center(time, vertical="middle")

class ClockApp(App):
    async def on_mount(self, event):
        await self.view.dock(Clock())


ClockApp.run()

Environment:
macOS Big Sur
Python 3.9.6
Textual 0.1.9
Terminal: Apple Terminal 2.11 and iTerm2 3.4.8 (the crash happens in both terminal apps)

Error:

(venv) hhip@ab-mpb-two:~/src/baxi $ python -m baxi.baxi
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Traceback (most recent call last) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ /Users/hhip/src/baxi/venv/lib/python3.9/site-packages/textual/app.py:333 in refresh                                                                                             โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚   330 โ”‚   โ”‚   โ”‚   try:                                                                        โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ                    โ”‚
โ”‚   331 โ”‚   โ”‚   โ”‚   โ”‚   if sync_available:                                                      โ”‚        console = <console width=179 ColorSystem.EIGHT_BIT> โ”‚                    โ”‚
โ”‚   332 โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   console.file.write("\x1bP=1s\x1b\\")                                โ”‚         layout = False                                     โ”‚                    โ”‚
โ”‚ โฑ 333 โ”‚   โ”‚   โ”‚   โ”‚   console.print(Screen(Control.home(), self.view, Control.home()))        โ”‚        repaint = True                                      โ”‚                    โ”‚
โ”‚   334 โ”‚   โ”‚   โ”‚   โ”‚   if sync_available:                                                      โ”‚           self = ClockApp(title='Textual Application')     โ”‚                    โ”‚
โ”‚   335 โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   console.file.write("\x1bP=2s\x1b\\")                                โ”‚ sync_available = False                                     โ”‚                    โ”‚
โ”‚   336 โ”‚   โ”‚   โ”‚   โ”‚   console.file.flush()                                                    โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ                    โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚ /Users/hhip/src/baxi/venv/lib/python3.9/site-packages/rich/console.py:1594 in print                                                                                             โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚   1591 โ”‚   โ”‚   โ”‚   render = self.render                                                                                                                                         โ”‚
โ”‚   1592 โ”‚   โ”‚   โ”‚   if style is None:                                                                                                                                            โ”‚
โ”‚   1593 โ”‚   โ”‚   โ”‚   โ”‚   for renderable in renderables:                                                                                                                           โ”‚
โ”‚ โฑ 1594 โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   extend(render(renderable, render_options))                                                                                                           โ”‚
โ”‚   1595 โ”‚   โ”‚   โ”‚   else:                                                                                                                                                        โ”‚
โ”‚   1596 โ”‚   โ”‚   โ”‚   โ”‚   for renderable in renderables:                                                                                                                           โ”‚
โ”‚   1597 โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   extend(                                                                                                                                              โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ                                                                                 โ”‚
โ”‚ โ”‚           crop = True                                                                       โ”‚                                                                                 โ”‚
โ”‚ โ”‚          emoji = None                                                                       โ”‚                                                                                 โ”‚
โ”‚ โ”‚            end = '\n'                                                                       โ”‚                                                                                 โ”‚
โ”‚ โ”‚         extend = <built-in method extend of list object at 0x10b06a740>                     โ”‚                                                                                 โ”‚
โ”‚ โ”‚         height = None                                                                       โ”‚                                                                                 โ”‚
โ”‚ โ”‚      highlight = None                                                                       โ”‚                                                                                 โ”‚
โ”‚ โ”‚        justify = None                                                                       โ”‚                                                                                 โ”‚
โ”‚ โ”‚         markup = None                                                                       โ”‚                                                                                 โ”‚
โ”‚ โ”‚ new_line_start = False                                                                      โ”‚                                                                                 โ”‚
โ”‚ โ”‚   new_segments = []                                                                         โ”‚                                                                                 โ”‚
โ”‚ โ”‚        no_wrap = None                                                                       โ”‚                                                                                 โ”‚
โ”‚ โ”‚        objects = (<rich.screen.Screen object at 0x10b03e0d0>,)                              โ”‚                                                                                 โ”‚
โ”‚ โ”‚       overflow = None                                                                       โ”‚                                                                                 โ”‚
โ”‚ โ”‚         render = <bound method Console.render of <console width=179 ColorSystem.EIGHT_BIT>> โ”‚                                                                                 โ”‚
โ”‚ โ”‚ render_options = ConsoleOptions(                                                            โ”‚                                                                                 โ”‚
โ”‚ โ”‚                  โ”‚   size=ConsoleDimensions(width=179, height=71),                          โ”‚                                                                                 โ”‚
โ”‚ โ”‚                  โ”‚   legacy_windows=False,                                                  โ”‚                                                                                 โ”‚
โ”‚ โ”‚                  โ”‚   min_width=1,                                                           โ”‚                                                                                 โ”‚
โ”‚ โ”‚                  โ”‚   max_width=179,                                                         โ”‚                                                                                 โ”‚
โ”‚ โ”‚                  โ”‚   is_terminal=True,                                                      โ”‚                                                                                 โ”‚
โ”‚ โ”‚                  โ”‚   encoding='utf-8',                                                      โ”‚                                                                                 โ”‚
โ”‚ โ”‚                  โ”‚   max_height=71,                                                         โ”‚                                                                                 โ”‚
โ”‚ โ”‚                  โ”‚   justify=None,                                                          โ”‚                                                                                 โ”‚
โ”‚ โ”‚                  โ”‚   overflow=None,                                                         โ”‚                                                                                 โ”‚
โ”‚ โ”‚                  โ”‚   no_wrap=None,                                                          โ”‚                                                                                 โ”‚
โ”‚ โ”‚                  โ”‚   highlight=None,                                                        โ”‚                                                                                 โ”‚
โ”‚ โ”‚                  โ”‚   markup=None,                                                           โ”‚                                                                                 โ”‚
โ”‚ โ”‚                  โ”‚   height=None                                                            โ”‚                                                                                 โ”‚
โ”‚ โ”‚                  )                                                                          โ”‚                                                                                 โ”‚
โ”‚ โ”‚     renderable = <rich.screen.Screen object at 0x10b03e0d0>                                 โ”‚                                                                                 โ”‚
โ”‚ โ”‚    renderables = [<rich.screen.Screen object at 0x10b03e0d0>]                               โ”‚                                                                                 โ”‚
โ”‚ โ”‚           self = <console width=179 ColorSystem.EIGHT_BIT>                                  โ”‚                                                                                 โ”‚
โ”‚ โ”‚            sep = ' '                                                                        โ”‚                                                                                 โ”‚
โ”‚ โ”‚      soft_wrap = False                                                                      โ”‚                                                                                 โ”‚
โ”‚ โ”‚          style = None                                                                       โ”‚                                                                                 โ”‚
โ”‚ โ”‚          width = None                                                                       โ”‚                                                                                 โ”‚
โ”‚ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ                                                                                 โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚ /Users/hhip/src/baxi/venv/lib/python3.9/site-packages/rich/console.py:1226 in render                                                                                            โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚   1223 โ”‚   โ”‚   โ”‚   โ”‚   f"object {render_iterable!r} is not renderable"                         โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ  โ”‚
โ”‚   1224 โ”‚   โ”‚   โ”‚   )                                                                           โ”‚        _options = ConsoleOptions(                                           โ”‚  โ”‚
โ”‚   1225 โ”‚   โ”‚   _Segment = Segment                                                              โ”‚                   โ”‚   size=ConsoleDimensions(width=179, height=71),         โ”‚  โ”‚
โ”‚ โฑ 1226 โ”‚   โ”‚   for render_output in iter_render:                                               โ”‚                   โ”‚   legacy_windows=False,                                 โ”‚  โ”‚
โ”‚   1227 โ”‚   โ”‚   โ”‚   if isinstance(render_output, _Segment):                                     โ”‚                   โ”‚   min_width=1,                                          โ”‚  โ”‚
โ”‚   1228 โ”‚   โ”‚   โ”‚   โ”‚   yield render_output                                                     โ”‚                   โ”‚   max_width=179,                                        โ”‚  โ”‚
โ”‚   1229 โ”‚   โ”‚   โ”‚   else:                                                                       โ”‚                   โ”‚   is_terminal=True,                                     โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   encoding='utf-8',                                     โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   max_height=71,                                        โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   justify=None,                                         โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   overflow=None,                                        โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   no_wrap=None,                                         โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   highlight=None,                                       โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   markup=None,                                          โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   height=None                                           โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   )                                                         โ”‚  โ”‚
โ”‚                                                                                                โ”‚        _Segment = <class 'rich.segment.Segment'>                            โ”‚  โ”‚
โ”‚                                                                                                โ”‚     iter_render = <generator object Screen.__rich_console__ at 0x10aff7e40> โ”‚  โ”‚
โ”‚                                                                                                โ”‚         options = ConsoleOptions(                                           โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   size=ConsoleDimensions(width=179, height=71),         โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   legacy_windows=False,                                 โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   min_width=1,                                          โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   max_width=179,                                        โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   is_terminal=True,                                     โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   encoding='utf-8',                                     โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   max_height=71,                                        โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   justify=None,                                         โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   overflow=None,                                        โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   no_wrap=None,                                         โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   highlight=None,                                       โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   markup=None,                                          โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   height=None                                           โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   )                                                         โ”‚  โ”‚
โ”‚                                                                                                โ”‚ render_iterable = <generator object Screen.__rich_console__ at 0x10aff7e40> โ”‚  โ”‚
โ”‚                                                                                                โ”‚      renderable = <rich.screen.Screen object at 0x10b03e0d0>                โ”‚  โ”‚
โ”‚                                                                                                โ”‚            self = <console width=179 ColorSystem.EIGHT_BIT>                 โ”‚  โ”‚
โ”‚                                                                                                โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ  โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚ /Users/hhip/src/baxi/venv/lib/python3.9/site-packages/rich/screen.py:47 in __rich_console__                                                                                     โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚   44 โ”‚   โ”‚   width, height = options.size                                                    โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ             โ”‚
โ”‚   45 โ”‚   โ”‚   style = console.get_style(self.style) if self.style else None                   โ”‚        console = <console width=179 ColorSystem.EIGHT_BIT>         โ”‚             โ”‚
โ”‚   46 โ”‚   โ”‚   render_options = options.update(width=width, height=height)                     โ”‚         height = 71                                                โ”‚             โ”‚
โ”‚ โฑ 47 โ”‚   โ”‚   lines = console.render_lines(                                                   โ”‚        options = ConsoleOptions(                                   โ”‚             โ”‚
โ”‚   48 โ”‚   โ”‚   โ”‚   self.renderable or "", render_options, style=style, pad=True                โ”‚                  โ”‚   size=ConsoleDimensions(width=179, height=71), โ”‚             โ”‚
โ”‚   49 โ”‚   โ”‚   )                                                                               โ”‚                  โ”‚   legacy_windows=False,                         โ”‚             โ”‚
โ”‚   50 โ”‚   โ”‚   lines = Segment.set_shape(lines, width, height, style=style)                    โ”‚                  โ”‚   min_width=1,                                  โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   max_width=179,                                โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   is_terminal=True,                             โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   encoding='utf-8',                             โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   max_height=71,                                โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   justify=None,                                 โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   overflow=None,                                โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   no_wrap=None,                                 โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   highlight=None,                               โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   markup=None,                                  โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   height=None                                   โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  )                                                 โ”‚             โ”‚
โ”‚                                                                                              โ”‚ render_options = ConsoleOptions(                                   โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   size=ConsoleDimensions(width=179, height=71), โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   legacy_windows=False,                         โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   min_width=179,                                โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   max_width=179,                                โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   is_terminal=True,                             โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   encoding='utf-8',                             โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   max_height=71,                                โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   justify=None,                                 โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   overflow=None,                                โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   no_wrap=None,                                 โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   highlight=None,                               โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   markup=None,                                  โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  โ”‚   height=71                                     โ”‚             โ”‚
โ”‚                                                                                              โ”‚                  )                                                 โ”‚             โ”‚
โ”‚                                                                                              โ”‚           self = <rich.screen.Screen object at 0x10b03e0d0>        โ”‚             โ”‚
โ”‚                                                                                              โ”‚          style = None                                              โ”‚             โ”‚
โ”‚                                                                                              โ”‚          width = 179                                               โ”‚             โ”‚
โ”‚                                                                                              โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ             โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚ /Users/hhip/src/baxi/venv/lib/python3.9/site-packages/rich/console.py:1261 in render_lines                                                                                      โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚   1258 โ”‚   โ”‚   โ”‚   _rendered = self.render(renderable, render_options)                         โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ           โ”‚
โ”‚   1259 โ”‚   โ”‚   โ”‚   if style:                                                                   โ”‚      _rendered = <generator object Console.render at 0x10aff7eb0>  โ”‚           โ”‚
โ”‚   1260 โ”‚   โ”‚   โ”‚   โ”‚   _rendered = Segment.apply_style(_rendered, style)                       โ”‚      new_lines = False                                             โ”‚           โ”‚
โ”‚ โฑ 1261 โ”‚   โ”‚   โ”‚   lines = list(                                                               โ”‚        options = ConsoleOptions(                                   โ”‚           โ”‚
โ”‚   1262 โ”‚   โ”‚   โ”‚   โ”‚   islice(                                                                 โ”‚                  โ”‚   size=ConsoleDimensions(width=179, height=71), โ”‚           โ”‚
โ”‚   1263 โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   Segment.split_and_crop_lines(                                       โ”‚                  โ”‚   legacy_windows=False,                         โ”‚           โ”‚
โ”‚   1264 โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   _rendered,                                                      โ”‚                  โ”‚   min_width=179,                                โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   max_width=179,                                โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   is_terminal=True,                             โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   encoding='utf-8',                             โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   max_height=71,                                โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   justify=None,                                 โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   overflow=None,                                โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   no_wrap=None,                                 โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   highlight=None,                               โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   markup=None,                                  โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   height=71                                     โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  )                                                 โ”‚           โ”‚
โ”‚                                                                                                โ”‚            pad = True                                              โ”‚           โ”‚
โ”‚                                                                                                โ”‚ render_options = ConsoleOptions(                                   โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   size=ConsoleDimensions(width=179, height=71), โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   legacy_windows=False,                         โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   min_width=179,                                โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   max_width=179,                                โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   is_terminal=True,                             โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   encoding='utf-8',                             โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   max_height=71,                                โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   justify=None,                                 โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   overflow=None,                                โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   no_wrap=None,                                 โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   highlight=None,                               โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   markup=None,                                  โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  โ”‚   height=71                                     โ”‚           โ”‚
โ”‚                                                                                                โ”‚                  )                                                 โ”‚           โ”‚
โ”‚                                                                                                โ”‚     renderable = <rich.console.Group object at 0x10b03e4c0>        โ”‚           โ”‚
โ”‚                                                                                                โ”‚           self = <console width=179 ColorSystem.EIGHT_BIT>         โ”‚           โ”‚
โ”‚                                                                                                โ”‚          style = None                                              โ”‚           โ”‚
โ”‚                                                                                                โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ           โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚ /Users/hhip/src/baxi/venv/lib/python3.9/site-packages/rich/segment.py:269 in split_and_crop_lines                                                                               โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚   266 โ”‚   โ”‚   adjust_line_length = cls.adjust_line_length                                                                                                                       โ”‚
โ”‚   267 โ”‚   โ”‚   new_line_segment = cls("\n")                                                                                                                                      โ”‚
โ”‚   268 โ”‚   โ”‚                                                                                                                                                                     โ”‚
โ”‚ โฑ 269 โ”‚   โ”‚   for segment in segments:                                                                                                                                          โ”‚
โ”‚   270 โ”‚   โ”‚   โ”‚   if "\n" in segment.text and not segment.control:                                                                                                              โ”‚
โ”‚   271 โ”‚   โ”‚   โ”‚   โ”‚   text, style, _ = segment                                                                                                                                  โ”‚
โ”‚   272 โ”‚   โ”‚   โ”‚   โ”‚   while text:                                                                                                                                               โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ                                                                            โ”‚
โ”‚ โ”‚ adjust_line_length = <bound method Segment.adjust_line_length of <class 'rich.segment.Segment'>> โ”‚                                                                            โ”‚
โ”‚ โ”‚             append = <built-in method append of list object at 0x10b04bdc0>                      โ”‚                                                                            โ”‚
โ”‚ โ”‚                cls = <class 'rich.segment.Segment'>                                              โ”‚                                                                            โ”‚
โ”‚ โ”‚  include_new_lines = False                                                                       โ”‚                                                                            โ”‚
โ”‚ โ”‚             length = 179                                                                         โ”‚                                                                            โ”‚
โ”‚ โ”‚               line = [Segment('\x1b[H', None, [(<ControlType.HOME: 3>,)])]                       โ”‚                                                                            โ”‚
โ”‚ โ”‚   new_line_segment = Segment('\n',)                                                              โ”‚                                                                            โ”‚
โ”‚ โ”‚                pad = True                                                                        โ”‚                                                                            โ”‚
โ”‚ โ”‚            segment = Segment('\x1b[H', None, [(<ControlType.HOME: 3>,)])                         โ”‚                                                                            โ”‚
โ”‚ โ”‚           segments = <generator object Console.render at 0x10aff7eb0>                            โ”‚                                                                            โ”‚
โ”‚ โ”‚              style = None                                                                        โ”‚                                                                            โ”‚
โ”‚ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ                                                                            โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚ /Users/hhip/src/baxi/venv/lib/python3.9/site-packages/rich/console.py:1230 in render                                                                                            โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚   1227 โ”‚   โ”‚   โ”‚   if isinstance(render_output, _Segment):                                     โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ   โ”‚
โ”‚   1228 โ”‚   โ”‚   โ”‚   โ”‚   yield render_output                                                     โ”‚        _options = ConsoleOptions(                                          โ”‚   โ”‚
โ”‚   1229 โ”‚   โ”‚   โ”‚   else:                                                                       โ”‚                   โ”‚   size=ConsoleDimensions(width=179, height=71),        โ”‚   โ”‚
โ”‚ โฑ 1230 โ”‚   โ”‚   โ”‚   โ”‚   yield from self.render(render_output, _options)                         โ”‚                   โ”‚   legacy_windows=False,                                โ”‚   โ”‚
โ”‚   1231 โ”‚                                                                                       โ”‚                   โ”‚   min_width=179,                                       โ”‚   โ”‚
โ”‚   1232 โ”‚   def render_lines(                                                                   โ”‚                   โ”‚   max_width=179,                                       โ”‚   โ”‚
โ”‚   1233 โ”‚   โ”‚   self,                                                                           โ”‚                   โ”‚   is_terminal=True,                                    โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   encoding='utf-8',                                    โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   max_height=71,                                       โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   justify=None,                                        โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   overflow=None,                                       โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   no_wrap=None,                                        โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   highlight=None,                                      โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   markup=None,                                         โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   height=71                                            โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   )                                                        โ”‚   โ”‚
โ”‚                                                                                                โ”‚        _Segment = <class 'rich.segment.Segment'>                           โ”‚   โ”‚
โ”‚                                                                                                โ”‚     iter_render = <generator object Group.__rich_console__ at 0x10aff7d60> โ”‚   โ”‚
โ”‚                                                                                                โ”‚         options = ConsoleOptions(                                          โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   size=ConsoleDimensions(width=179, height=71),        โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   legacy_windows=False,                                โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   min_width=179,                                       โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   max_width=179,                                       โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   is_terminal=True,                                    โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   encoding='utf-8',                                    โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   max_height=71,                                       โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   justify=None,                                        โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   overflow=None,                                       โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   no_wrap=None,                                        โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   highlight=None,                                      โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   markup=None,                                         โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   height=71                                            โ”‚   โ”‚
โ”‚                                                                                                โ”‚                   )                                                        โ”‚   โ”‚
โ”‚                                                                                                โ”‚ render_iterable = <generator object Group.__rich_console__ at 0x10aff7d60> โ”‚   โ”‚
โ”‚                                                                                                โ”‚   render_output = DockView(name='DockView#1')                              โ”‚   โ”‚
โ”‚                                                                                                โ”‚      renderable = <rich.console.Group object at 0x10b03e4c0>               โ”‚   โ”‚
โ”‚                                                                                                โ”‚            self = <console width=179 ColorSystem.EIGHT_BIT>                โ”‚   โ”‚
โ”‚                                                                                                โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ   โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚ /Users/hhip/src/baxi/venv/lib/python3.9/site-packages/rich/console.py:1226 in render                                                                                            โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚   1223 โ”‚   โ”‚   โ”‚   โ”‚   f"object {render_iterable!r} is not renderable"                         โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ  โ”‚
โ”‚   1224 โ”‚   โ”‚   โ”‚   )                                                                           โ”‚        _options = ConsoleOptions(                                           โ”‚  โ”‚
โ”‚   1225 โ”‚   โ”‚   _Segment = Segment                                                              โ”‚                   โ”‚   size=ConsoleDimensions(width=179, height=71),         โ”‚  โ”‚
โ”‚ โฑ 1226 โ”‚   โ”‚   for render_output in iter_render:                                               โ”‚                   โ”‚   legacy_windows=False,                                 โ”‚  โ”‚
โ”‚   1227 โ”‚   โ”‚   โ”‚   if isinstance(render_output, _Segment):                                     โ”‚                   โ”‚   min_width=179,                                        โ”‚  โ”‚
โ”‚   1228 โ”‚   โ”‚   โ”‚   โ”‚   yield render_output                                                     โ”‚                   โ”‚   max_width=179,                                        โ”‚  โ”‚
โ”‚   1229 โ”‚   โ”‚   โ”‚   else:                                                                       โ”‚                   โ”‚   is_terminal=True,                                     โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   encoding='utf-8',                                     โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   max_height=71,                                        โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   justify=None,                                         โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   overflow=None,                                        โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   no_wrap=None,                                         โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   highlight=None,                                       โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   markup=None,                                          โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   height=71                                             โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   )                                                         โ”‚  โ”‚
โ”‚                                                                                                โ”‚        _Segment = <class 'rich.segment.Segment'>                            โ”‚  โ”‚
โ”‚                                                                                                โ”‚     iter_render = <generator object Layout.__rich_console__ at 0x10aff7dd0> โ”‚  โ”‚
โ”‚                                                                                                โ”‚         options = ConsoleOptions(                                           โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   size=ConsoleDimensions(width=179, height=71),         โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   legacy_windows=False,                                 โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   min_width=179,                                        โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   max_width=179,                                        โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   is_terminal=True,                                     โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   encoding='utf-8',                                     โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   max_height=71,                                        โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   justify=None,                                         โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   overflow=None,                                        โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   no_wrap=None,                                         โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   highlight=None,                                       โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   markup=None,                                          โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   โ”‚   height=71                                             โ”‚  โ”‚
โ”‚                                                                                                โ”‚                   )                                                         โ”‚  โ”‚
โ”‚                                                                                                โ”‚ render_iterable = <generator object Layout.__rich_console__ at 0x10aff7dd0> โ”‚  โ”‚
โ”‚                                                                                                โ”‚      renderable = <textual.layouts.dock.DockLayout object at 0x10b024250>   โ”‚  โ”‚
โ”‚                                                                                                โ”‚            self = <console width=179 ColorSystem.EIGHT_BIT>                 โ”‚  โ”‚
โ”‚                                                                                                โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ  โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚ /Users/hhip/src/baxi/venv/lib/python3.9/site-packages/textual/layout.py:376 in __rich_console__                                                                                 โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚   373 โ”‚   def __rich_console__(                                                               โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ             โ”‚
โ”‚   374 โ”‚   โ”‚   self, console: Console, options: ConsoleOptions                                 โ”‚ console = <console width=179 ColorSystem.EIGHT_BIT>               โ”‚             โ”‚
โ”‚   375 โ”‚   ) -> RenderResult:                                                                  โ”‚ options = ConsoleOptions(                                         โ”‚             โ”‚
โ”‚ โฑ 376 โ”‚   โ”‚   yield self.render(console)                                                      โ”‚           โ”‚   size=ConsoleDimensions(width=179, height=71),       โ”‚             โ”‚
โ”‚   377 โ”‚                                                                                       โ”‚           โ”‚   legacy_windows=False,                               โ”‚             โ”‚
โ”‚   378 โ”‚   def update_widget(self, console: Console, widget: Widget) -> LayoutUpdate | None:   โ”‚           โ”‚   min_width=179,                                      โ”‚             โ”‚
โ”‚   379 โ”‚   โ”‚   if widget not in self.regions:                                                  โ”‚           โ”‚   max_width=179,                                      โ”‚             โ”‚
โ”‚                                                                                               โ”‚           โ”‚   is_terminal=True,                                   โ”‚             โ”‚
โ”‚                                                                                               โ”‚           โ”‚   encoding='utf-8',                                   โ”‚             โ”‚
โ”‚                                                                                               โ”‚           โ”‚   max_height=71,                                      โ”‚             โ”‚
โ”‚                                                                                               โ”‚           โ”‚   justify=None,                                       โ”‚             โ”‚
โ”‚                                                                                               โ”‚           โ”‚   overflow=None,                                      โ”‚             โ”‚
โ”‚                                                                                               โ”‚           โ”‚   no_wrap=None,                                       โ”‚             โ”‚
โ”‚                                                                                               โ”‚           โ”‚   highlight=None,                                     โ”‚             โ”‚
โ”‚                                                                                               โ”‚           โ”‚   markup=None,                                        โ”‚             โ”‚
โ”‚                                                                                               โ”‚           โ”‚   height=71                                           โ”‚             โ”‚
โ”‚                                                                                               โ”‚           )                                                       โ”‚             โ”‚
โ”‚                                                                                               โ”‚    self = <textual.layouts.dock.DockLayout object at 0x10b024250> โ”‚             โ”‚
โ”‚                                                                                               โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ             โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚ /Users/hhip/src/baxi/venv/lib/python3.9/site-packages/textual/layout.py:342 in render                                                                                           โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚   339 โ”‚   โ”‚   โ”‚   โ”‚   # last_cut = clamp(render_region.x + render_region.width, clip_x, clip_x                                                                                  โ”‚
โ”‚   340 โ”‚   โ”‚   โ”‚   โ”‚   first_cut = render_region.x                                                                                                                               โ”‚
โ”‚   341 โ”‚   โ”‚   โ”‚   โ”‚   last_cut = render_region.x_max                                                                                                                            โ”‚
โ”‚ โฑ 342 โ”‚   โ”‚   โ”‚   โ”‚   final_cuts = [cut for cut in cuts[y] if (last_cut >= cut >= first_cut)]                                                                                   โ”‚
โ”‚   343 โ”‚   โ”‚   โ”‚   โ”‚   # final_cuts = cuts[y]                                                                                                                                    โ”‚
โ”‚   344 โ”‚   โ”‚   โ”‚   โ”‚                                                                                                                                                             โ”‚
โ”‚   345 โ”‚   โ”‚   โ”‚   โ”‚   # log(final_cuts, render_region.x_extents)                                                                                                                โ”‚
โ”‚                                                                                                                                                                                 โ”‚
โ”‚ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ locals โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ                                  โ”‚
โ”‚ โ”‚          _Segment = <class 'rich.segment.Segment'>                                                                                         โ”‚                                  โ”‚
โ”‚ โ”‚ background_render = [                                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   Segment(                                                                                                       โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   '                                                                                '+99,                     โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   Style(color=Color('black', ColorType.STANDARD, number=0))                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   )                                                                                                              โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   ],                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   Segment(                                                                                                       โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   '                                                                                '+99,                     โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   Style(color=Color('black', ColorType.STANDARD, number=0))                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   )                                                                                                              โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   ],                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   Segment(                                                                                                       โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   '                                                                                '+99,                     โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   Style(color=Color('black', ColorType.STANDARD, number=0))                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   )                                                                                                              โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   ],                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   Segment(                                                                                                       โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   '                                                                                '+99,                     โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   Style(color=Color('black', ColorType.STANDARD, number=0))                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   )                                                                                                              โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   ],                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   Segment(                                                                                                       โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   '                                                                                '+99,                     โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   Style(color=Color('black', ColorType.STANDARD, number=0))                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   )                                                                                                              โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   ],                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   Segment(                                                                                                       โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   '                                                                                '+99,                     โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   Style(color=Color('black', ColorType.STANDARD, number=0))                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   )                                                                                                              โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   ],                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   Segment(                                                                                                       โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   '                                                                                '+99,                     โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   Style(color=Color('black', ColorType.STANDARD, number=0))                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   )                                                                                                              โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   ],                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   Segment(                                                                                                       โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   '                                                                                '+99,                     โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   Style(color=Color('black', ColorType.STANDARD, number=0))                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   )                                                                                                              โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   ],                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   Segment(                                                                                                       โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   '                                                                                '+99,                     โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   Style(color=Color('black', ColorType.STANDARD, number=0))                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   )                                                                                                              โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   ],                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   Segment(                                                                                                       โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   '                                                                                '+99,                     โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   Style(color=Color('black', ColorType.STANDARD, number=0))                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   )                                                                                                              โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   ],                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   ... +61                                                                                                            โ”‚                                  โ”‚
โ”‚ โ”‚                     ]                                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚  background_style = Style(color=Color('black', ColorType.STANDARD, number=0))                                                              โ”‚                                  โ”‚
โ”‚ โ”‚             chops = [                                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   {                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   0: [Segment('                                                                                '+99,)],          โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   179: None                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   },                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   {                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   0: [Segment('                                                                                '+99,)],          โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   179: None                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   },                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   {                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   0: [Segment('                                                                                '+99,)],          โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   179: None                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   },                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   {                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   0: [Segment('                                                                                '+99,)],          โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   179: None                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   },                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   {                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   0: [Segment('                                                                                '+99,)],          โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   179: None                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   },                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   {                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   0: [Segment('                                                                                '+99,)],          โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   179: None                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   },                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   {                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   0: [Segment('                                                                                '+99,)],          โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   179: None                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   },                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   {                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   0: [Segment('                                                                                '+99,)],          โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   179: None                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   },                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   {                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   0: [Segment('                                                                                '+99,)],          โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   179: None                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   },                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   {                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   0: [Segment('                                                                                '+99,)],          โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   179: None                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   },                                                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   ... +61                                                                                                            โ”‚                                  โ”‚
โ”‚ โ”‚                     ]                                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚              clip = Region(x=0, y=0, width=179, height=71)                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚            clip_y = 0                                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚           clip_y2 = 71                                                                                                                     โ”‚                                  โ”‚
โ”‚ โ”‚           console = <console width=179 ColorSystem.EIGHT_BIT>                                                                              โ”‚                                  โ”‚
โ”‚ โ”‚              crop = None                                                                                                                   โ”‚                                  โ”‚
โ”‚ โ”‚       crop_region = Region(x=0, y=0, width=179, height=71)                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚               cut = 0                                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚      cut_segments = [[Segment('                                                                                '+99,)]]                    โ”‚                                  โ”‚
โ”‚ โ”‚              cuts = [[0, 179], [0, 179], [0, 179], [0, 179], [0, 179], [0, 179], [0, 179], [0, 179], [0, 179], [0, 179], ... +61]          โ”‚                                  โ”‚
โ”‚ โ”‚            divide = <bound method Segment.divide of <class 'rich.segment.Segment'>>                                                        โ”‚                                  โ”‚
โ”‚ โ”‚        final_cuts = [0, 179]                                                                                                               โ”‚                                  โ”‚
โ”‚ โ”‚         first_cut = 0                                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚            height = 71                                                                                                                     โ”‚                                  โ”‚
โ”‚ โ”‚          last_cut = 179                                                                                                                    โ”‚                                  โ”‚
โ”‚ โ”‚              line = [Segment('                                                                                '+99,)]                      โ”‚                                  โ”‚
โ”‚ โ”‚             lines = [                                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [Segment('                                                                                '+99,)],                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [Segment('                                                                                '+99,)],                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [Segment('                                                                                '+99,)],                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [Segment('                                                                                '+99,)],                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [Segment('                                                                                '+99,)],                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [Segment('                                                                                '+99,)],                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [Segment('                                                                                '+99,)],                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [Segment('                                                                                '+99,)],                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [Segment('                                                                                '+99,)],                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   [Segment('                                                                                '+99,)],                 โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   ... +62                                                                                                            โ”‚                                  โ”‚
โ”‚ โ”‚                     ]                                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚            region = Region(x=0, y=0, width=179, height=71)                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚     render_region = Region(x=0, y=0, width=179, height=71)                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚           renders = [                                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   (                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   Region(x=0, y=0, width=179, height=71),                                                                        โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   Region(x=0, y=0, width=179, height=71),                                                                        โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   [                                                                                                              โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   [Segment('                                                                                '+99,)],         โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   [Segment('                                                                                '+99,)],         โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   [Segment('                                                                                '+99,)],         โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   [Segment('                                                                                '+99,)],         โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   [Segment('                                                                                '+99,)],         โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   [Segment('                                                                                '+99,)],         โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   [Segment('                                                                                '+99,)],         โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   [Segment('                                                                                '+99,)],         โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   [Segment('                                                                                '+99,)],         โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   [Segment('                                                                                '+99,)],         โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   โ”‚   ... +62                                                                                                    โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   โ”‚   ]                                                                                                              โ”‚                                  โ”‚
โ”‚ โ”‚                     โ”‚   )                                                                                                                  โ”‚                                  โ”‚
โ”‚ โ”‚                     ]                                                                                                                      โ”‚                                  โ”‚
โ”‚ โ”‚            screen = Region(x=0, y=0, width=179, height=71)                                                                                 โ”‚                                  โ”‚
โ”‚ โ”‚          segments = [Segment('                                                                                '+99,)]                      โ”‚                                  โ”‚
โ”‚ โ”‚              self = <textual.layouts.dock.DockLayout object at 0x10b024250>                                                                โ”‚                                  โ”‚
โ”‚ โ”‚             width = 179                                                                                                                    โ”‚                                  โ”‚
โ”‚ โ”‚                 y = 71                                                                                                                     โ”‚                                  โ”‚
โ”‚ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ                                  โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
IndexError: list index out of range
(venv) hhip@ab-mpb-two:~/src/baxi $ 

Add more easing functions

There are a number of 'easing functions' used by the animation system. At the top of _animator.py you will see the following:

EASING = {
    "none": lambda x: 1.0,
    "round": lambda x: 0.0 if x < 0.5 else 1.0,
    "linear": lambda x: x,
    "in_cubic": lambda x: x * x * x,
    "in_out_cubic": lambda x: 4 * x * x * x if x < 0.5 else 1 - pow(-2 * x + 2, 3) / 2,
    "out_cubic": lambda x: 1 - pow(1 - x, 3),
}

These function have been copied from https://easings.net/ and translated from Javascript to Python. I would like to add the remaining functions to the EASING dict above.

See the animation.py example as a way of testing the above functions.

Running shell apps from textual

Any hints on execute shell app, like vim, from key event method?

Now using os.system or subprocess or asyncio.create_subprocess_shell, when I close vim, textual app stops, I even cannot close it with Ctrl-c

Textual In Browser?

Hi Will,

I love the TUI aesthetic and wanted to make a personal website for myself based on something like xterm.js.

I saw your AMA on Reddit about textual, and since it has more in common with JS frameworks, I was wondering if there was a way to display the TUI's in a browser?

Thanks for your time and for your open source work.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.