Python TUI framework with mouse support, modular widget system, customizable and rapid terminal markup language and more!
The previous InputField implementation was highly restrained by the lacking (and slow) break_line
implementation present at the time. Since that was recently improved a lot, the manually placed restrictions didn't make much sense anymore. There is now support for multiline text editing, syntax highlighters and more!
Refactor InputField
(6cd72d677ba139d7a31ae59020e508ae1edadc36)
Start caching Token.sequence
to improve performance (9638e9513796b51a64a58134b39b245397732854)
Start lazy-evaluating terminal resolution (4a7c25baa6097bf216833ca74712713577acb8d9)
Some terminals don't support this feature, so we used to have a very short (0.01) second timeout for an emulator to respond. This was way too short in some situations, leaving the response to be printed to STDOUT instead of being captured. The bigger problem was that there is basically no code in the wild that actually uses this feature: it was implemented for the still-upcoming image support, but no other systems really use it. Because of this, it was pretty useless to query it on every startup.
Use SVG export prefix as the class of the generated text
elements (7c977f15fdb43649aa02f7e924290dbfdcc61e09, #67)
Previously, the text
style selector selected every SVG in the document. This was problematic, as it messed up non-PTG SVGs displayed alongside our own.
Fix Inspector
not resizing to custom global terminals (0bb071711586ad8ee129fd85df14ea9eae3b06bb)
Add break_line
fill
argument (1426edc9627577764d4e286a3a65f8b6593afb46)
This is currently unused by the library at large, but it allows padding out every yielded line to the target width using the given character. Can be pretty useful.
Add chrome
SVG argument (182258517ff0e7d459b9c7a42a9413ffeed629fc)
Here is a simple file I wrote using
sandbox/input_field.py
, a file created fromexamples/boilerplate.py
in barely any code. Try it yourself!
Web compatibility strikes again. While Chrome supports alignment-baseline
for SVG text
tags, Firefox only supports dominant-baseline
. Then, Safari comes in as a unicorn proclaiming (very quietly) that it supports neither.
All hail dy
, I guess!
This release mainly targets my new mkdocs
plugin, termage, which allows you to insert SVGs of Python program output, generated at build time. Its documentation website is still waiting for DNS propagation, but it uses the plugin as well. You can expect a revamp of the documentation using mkdocs
and termage
very soon!
For that system to work, WindowManager
needed to gain the capability of running in headless mode, so that it can render an output and quit without interaction.
\n
being escaped when highlighting python (48a365a24f9b837108a1cf946bcc86af3b84cda0)WindowManager.autorun
class attribute (6ea6044afcb7e11603fd2c5ed00ffe0ef2a7e699)The aforementioned documentation website:
StandardColor
HEX and RGB being indexed from the wrong pool (66384acbb838df6f4c262b03fe595df8fbeb6fd5)ttype=POSITION
tokens (2f7a5616f0909499d1f391eed4f75b77bc667a82)ttype=POSITION
tokens acting unpredictably when multiple were present in a string (c2c227f074c762a6607b4b5be7ba6d4da969c68e)WindowManager.autorun
attribute (ab0fd94871bd5f03246ef24db8f6d39a7f4d41b4)Implement usage of SVG tags when exporting (589e65003daab3cb374589b6eaa1fa6176272834)
Previously, we were using foreignObject
SVG tags to render inner content. This worked, but only in browsers, and looked ever so slightly different between each of them. We now use the proper SVG tags, and thus our exports are now real images not linked to browsers!
You can also now export screenshots from a WindowManager
application, and they look the same as they did in your terminal. This is allowed by the 2 position token fixes above. As with the previous version, this uses your terminal's background & foreground colors, as well as its size when generating. Makes it actually look like it's a screenshot of your terminal!
All of the images here are straight-up SVGs dragged into the release note, no conversions or anything necessary.
Thank you to everyone for the 1000 stars the project recently reached. It's an incredible achievement, and I am beyond grateful for the support.
⭐ 🚀
This version was mostly for bugfixing, but it introduces the new __fancy_repl__
protocol into the library.
The purpose of this protocol is for objects to gain control over how they are displayed in a pretty
REPL environment. See the docs for more info.
There is also now support for string format specs for Color
objects. You can now do things like:
markup = f"[{color:tim}]Text"
and I think that's pretty neat.
Introduce __fancy_repr__
protocol (09bcb0ef1b92a5217b12576f7a60e2a62e292c49)
This protocol is somewhat inspired by rich's __rich__
, but tries to be agnostic to the underlying ANSI interface used. It can be used to customize highlighting an object's repr output.
It is already implemented by all the Color
classes, and RegexHighlighter
. More (probably) to come.
Add Color
format specifiers (c529c2c0b805ff9fe5f357023f70e55a3ee8f985)
Add Animation.pause
& Animation.unpause
(bf7ddbdf554c3228ca0860c6adfa900721147e1c)
highlight_python vs strings
(8a9f63a7977facbddb7855ce1f14f33fa1a82929, 9e1042fa85df3123be5a099f299321c65e565206)This version was originally meant to focus on the new layout system, but things got out of hand quickly. In the end, we have the aforementioned feature, alongside a completely reimagined & much more useful CLI, a regex-based syntax highlighting base, major performance improvements and some general QOL things. There unfortunately are some (relatively minor) API changes, which is why this version is 6.0.0 instead of 5.1.0.
Remove Window.allow_fullscreen
, Window.toggle_fullscreen
(114da3a3d769d6abd72a8727ed036b1e947c0a23)
These methods are no longer really relevant in the new layout-based system. They were also handled in a really odd way from the library's side, so it should be one less bug-prone area to figure out. You can now set windows fullscreen using the layout system:
manager.layout.add_slot("Body")
manager.add(window, assign="body")
More on the layout API later.
Add new, customizable window blur styles (114da3a3d769d6abd72a8727ed036b1e947c0a23)
The old style was visually impressive, however also prone to causing bugs that could not be resolved with either stripping functionality or changing the library source. The new version follows traditional and modern TUIs more closely by only changing the color of borders, and is actually customizable. However, since there are new styles for both blurred
and focused
states for both the corner
and border
set of characters, a small helper was also added, Window.set_focus_styles
.
Rename all builtin aliases to follow the domain.item
naming scheme (3a416f5f7d2dbc80328478ea16fddb4e980d1437)
Improve animation stability (c806b975c7815c876eb6845637c9f125c2f26c61, 05b61268479696298873cf616b4696ccab77b8fe)
Start caching real_length
, strip_markup
& strip_ansi
results (a07ce9f606eaa96ee923e97a437edf375b89552d, 532e3ccc66fee81e4e9bfc0df0407ca72e4705c0)
This was altogether a sub-10 line change, but it actually has huge performance implications. It will also be useful for the future, more advanced compositing running at an acceptable speed.
Refactor StyledText
to only tokenize on-demand (b15eba881cb7a83b0c5302ae6855ec949db5a4b6)
Rewrite the pattern used to match markup to improve how escapes are handled (d656b7534d15769c375e9f083adc7896052ba4a1, 5a37470ac072746ee54c2029a01dcaf00832a7d8)
Introduce new, layout based CLI with an RGB colorpicker & inspector (31433362b208c5cc3f4c879004e69c913f361980)
This started off as a way to showcase the new layouts, and wasn't even supposed to be a full rewrite. However, I eventually realized how ridiculously over-engineered the previous CLI was, so I decided to come up with a new system. There is a new app, Inspector
, which basically runs the inspect
function interactively, and can be called from the command line with no interaction. There is also now an RGB color picker, and some bindings to the new highlight_python
function (more on that later).
For some reason my favorite part of this new module is the output of -v
:
Add Collapsible
widget (114da3a3d769d6abd72a8727ed036b1e947c0a23)
Add new highlighters
module with RegexHighlighter
class (7e6cd024739b880aa53ef3b9c2ca9414c15493e9, 6bd3d4a3d7b8be179c4aeb7aa73079a57c29f8b3)
This class can be used to statically highlight any type of text, combining regex with TIM. Since it's fully based around regex it will never be a full highlighter, a'la Pygments, but I feel like that is outside of the scope of the library. See an example of this in the showcase section.
Add is_scroll
, is_primary
& is_secondary
helpers to MouseEvent
(522e4dd939c13824b4c6c6bade69ebb8bfdf51bb)
Add Terminal.no_record
context (b7d4976efc6756548b3903c77b3878f6972b156c)
Add WindowManager.alert
and WindowManager.float
methods (53e9b84c177e24aab3546ab57bfb218274ea06ac)
The alert
function is pretty much the exact same in terms of functionality to the one that came before it, though it's been refactored a bit. The toast
method however is completely new to the library, but it's goal and general look & feel come from the Android feature of the same name. I personally think it looks dope, and you catch a glance at it on ptg
startup!
Add layout assign
parameter to WindowManager.add
(6c95ac0c416290e810ffa9a012e422d3ca2861bc)
Add window manager Layout
class (8250d6d7bebe687b53a0ee3e6f7169f2ffa0883d)
This really is the feature of this update. Essentially, layouts let you define sizes & positions for your windows, which is then continually applied whenever the terminal resizes or windows are removed/added. It also tries to be minimal but expressive in its syntax, and do as many dynamic things as possible.
For example, take the following code:
manager.layout = ptg.Layout()
manager.layout.add_slot("Header", height=5)
manager.layout.add_break()
manager.layout.add_slot("Sidebar", width=0.2)
manager.layout.add_slot("Body")
manager.layout.add_break()
manager.layout.add_slot("Footer", height=3)
It creates a 3-row layout. The first row just contains a header with a height of 5, and spanning the entire terminal in its width. The second row contains a sidebar that is 20% of the terminal's width, and a body that fills whatever remaining space it has. Finally, we have a footer, that is 3 characters height and once again spans the entire terminal.
You may have noticed that the second row had no height definitions whatsoever. This is because the layout can calculate the remaining height for the row based on the slots with static dimensions. In effect, this means that you only need to specify the 'defining dimension' for each slot, and the rest will be calculated by the library. This Dimension
system may in the future be extended into the realm of widgets, and I have plans of first adding the ability to nest layouts, and maybe eventually adding a layout-based container widget.
MarkupLanguage.parse
getting rid of important unsetters (32e30b5e50e5d71f89c2d5a64775820dce805f99)/
not being handled properly in various places (2b15e5174582082999352140faafccbe05fa2670, e3e9e87ccb137a378406139e5d1f57c5decb4ce2)
That's all for now. Next up should be either a full ScrollView
widget that wraps around an inner Container
, a more efficient compositing method, a universal & real-life user tested color palette or a combination of all. Since I'm currently quite busy with university I'm not sure when I'll get around to creating a new release, but it shouldn't be too far off.
As a final note, this release might be the one to push the library over 1000 stars. Thank you to everyone who has ever used PTG, raised an issue or even just checked it out or left a critical HackerNews
comment for it. I never imagined my work to become this popular, and I can't wait to see where it ends up.
Ok, bye
:rocket:
Pretentious title, I know.
This version is essentially a rewrite of the window_manager
module. There is now better task distribution, things are cleaner, and there is one less 1000+ line module in the library.
The biggest change was moving all printing logic into a new module compositor
. At the moment this does basically the same thing as WindowManager
used to, albeit a slight bit more optimized, but in the future implementing a difference-only drawer should be relatively simple.
The animation system also got completely rewritten. I'm still not fully satisfied with some aspects, but this should be the last API-breaking set of changes done to it for the foreseeable future. The TL;DR of this would be something like:
Animation
base classFloatAnimation
that simply transitions its state from 0.0 to 1.0 over the given durationOverall, this release provides much better internal structure and stability, plus some extra features and additions. The upcoming couple of releases will focus more on the feature side of things, but there is at least one similar refactor planned (for the ansi_interface
module), though likely involving less API changes.
Items marked in bold are API breaking changes.
window_manager.py
into 3 files under window_manager
submodule
ScrollableWidget
class (37f8ffae651d7804fac07175901bd3e35aa5baab)Terminal
API (d0209fbf2e59950f220375f857baac35e6d7dee3)widgets/layouts
→ widgets/containers
(e06608d4f2ba88216ca73a6fcbd1ddcde62a54c0)StyleManager.__call__
method that sets the given **kwargs keys and values (2e69cbf27ca235ef958d5ef4d3d62ffda413abd8)Widget.get_change
helper (48d3e5c78237a7998e93055db0a9b5550b374242)As this is an internal refactor based update, there isn't much to showcase. Toggling fullscreen on a Window is now animated, and some other animations are improved.
Here is a random SVG screenshot.
In comparison, my phone's GitHub app tends to render text off the "terminal" panel for some reason, and even the browser tends to render the colored unicode blocks incorrectly, often adding a border around them. Odd!
This release is focused on fixing two bugs, #46 and #42. It also introduces the getch_timeout
function, which is pretty neat, though sadly special functionality is limited to POSIX systems.
getch_timeout
method (49ff24445329e6f3581c1cf34c5265225ef765bd)This release fixes some minor inconveniences that slipped through the last version release.
errno.EINVAL
on stream truncation (f8b1d3f3846dd19fbdad6a064ba1e99e3472e9d3, #45)Container
first lines sometimes having wrong indentations in SVG exports (9c32ac9e298786449f101cad8d2664584304ffa7)Helpers
app not showing any bindings (61dbed9be42220b96c42eb97105ccda0f2071bc4)This version improves the Terminal
class and gives it some extra abilities, which are then used by some new modules.
Anything written to the terminal can now be recorded by it, simply using the terminal.record
context manager. This will then give you a new Recorder
instance, which contains all the data written to the terminal in the context. This can then be exported to static HTML, or even a terminal-mimicking SVG!
The recording and subsequent outputs also respect positional printing, thus your screenshots will be accurate regardless of what weird thing you might be doing. Other than the extra functionality mentioned above, this release was a great stress-test for TIM
and related utilities, and a lot of minor but important improvements have been made during the process.
Helpers
ptg app (ac5687076507dc2fdb7ab6f87a71a48fc00dcd70)input.keys
(5157546c9c9729266b1744321fa2b155f14bf9a4)terminal
Recorder
class, ability to record anything written to the terminal using a context (b8c49e0ec823afbdfc9d0b31758055316abb6657)Color.hex
property (ffbb4567ab65e8d2c9c3c691628419e1925135f1)Color
default fore & background setter and getters, using the terminal’s palette (5b2c4a3ff9ef586a19c709def766d082adaeda8b)LINK
and POSITION
TIM
token types (309e30171084a06a424d6562a3f72585eca8d73b, 401aad8d19da6fb3f70be70f170817726c728a37)exporters
module to generate HTML pages and SVG screenshots from any terminal content (a01a6f8670dc2ecc8e4860b423ffc9a1bba012a4, d6060d5ffe042ae76c2867cf139ecc2fc2e869b7, cd69680cd01e55bed3ac4e0f6dc8b0cfd9f9572a)style
construction argument to Label
(9f7336eb31a8052827beff460ee6d2dd6dc24a45)tim.get\_styled\_plains
method (8c05e69c82925f08c2a6f0b559d52dd8154d3e15)WindowManager.print
works (99bad926f9cd44c3610acbe7dea1d008cce50315)regex.py
(e8befc10b97109edbc1a35b0f1bfdcfe550992bf)These screenshots are all directly coming from PTG, and are not actual native screenshots taken by the operating system.