Releases: bczsalba/pytermgui
v7.1.0: Better InputField cursor!
This minor release mostly aims to fix up some issues caused in previous releases, as well as tighten up InputField
cursor behaviour.
Note that the release size of the project became enormous since the move to pyproject.toml
for building. This should be resolved by the next update.
Changelog
Additions
- Re-introduce
InputField
prompt attribute (#90)
Bugfixes
- Fix
ptg --version
(#89) - Start wrapping
InputField
cursor when it goes outside of the given width (634c1b5) - Fix incorrect
KeyboardButton
label generation (#88)
Showcase
Here is a sneak-peak of the upcoming update to the file-definition system, which will add things like selector-based targeting, native markup support and more!
v7.0.0: TIM-v3!
This release brings completely re-written TIM implementation to the library. It is significantly faster (~2x for markup without macros, >10x for markup with macros) than the previous version, and it's a lot easier to maintain and improve upon.
The previous hyperlink syntax ([!link(https://example.org)]Example site[/!link]
) is now deprecated in favour of a new, much simpler one:
[~https://example.org]Example site[/~]
There have also been a couple of changes to SVG exports to make them more accurate and aesthetically pleasing. We also support the inverse
style for them as well!
Changelog
Changes marked in bold are API-breaking.
Additions
- Support CSS color names in TIM code (77d2ca9)
- Support terminal resize events on Windows (@Tired-Fox, #80)
- Add
ignore_any
parameter toWidget.execute_binding
(68866e6)
Bugfixes
- Fix
InputField
handling clicks & drags started outside of it (#72, #75) - Fix
CTRL_C
not killing compositor thread by making it a daemon (#78) - Fix contents of
widget.positioned_line_buffer
being duplicated before and after vertical alignment (#70)
Refactors
- Rewrite of the TIM engine (now at version 3!) (#84)
- Move TIM highlighting from an instance method to a highlighter (635b6f6)
- Move to
pyproject.toml
-based builds (#82) - Stop relying on
visibility=hidden
in SVG exports (d070724)
Removals
- Remove
get_applied_sequences
helper function (a7d41bd)
API updates
Type | Change | Alternative | Comment |
---|---|---|---|
Removal | get_applied_sequences |
A custom tokenization based implementation | This function was no longer used internally, and if someone needed it the new tokenizers are a much more performant and smart way to go about implementing it. |
Removal | MarkupLanguage.prettify_markup |
highlight_tim |
Using regex-based highlighting allows the output markup to be completely identical char-by-char to the source, whereas the previous token based implementation had a tendency to change things around. It's also much less LOC and faster. |
Deprecation | Markup hyperlink syntax !link(https://example.org) |
The new ~https://example.org syntax |
Previously hyperlinks were implemented as macros, but they were very messy under the hood. A first-class syntax for them is vastly superior. |
Refactor | The role of StyledText |
N/A | Previously, StyledText was meant to be barely detectable when used. It was returned in various places, but wanted to not be a thing you thought about often. Now it's on an opt-in basis, and it no longer tries to pass as a str . It's immutable, and is only meant to be generated by MarkupLanguage.group_styles or StyledText.group_styles . You can also now create them from markup, not just ANSI-coded text. |
Showcase
Note that since the above image,
tim-v3
went down to ~180-190 ns per parse
v6.4.0: Semantic mouse handling!
This release brings the ability to use on_{mouse_event}
functions to handle specific events, instead of having to sort manually within handle_mouse
. This is done within Widget
's handle_mouse
method, so it will be implemented by all widgets that call super().handle_mouse
before doing their own handling. It also improves general mouse handling behaviour, and makes it all a bit more predictable.
There is a minor change in return-value semantics as well. Previously, handle_mouse() == True
stood for "I was able to handle this event, do not bubble it up". This wasn't very specific, and there were some special cases where undefined behaviour would occur. It has now been changed to "I want to handle more mouse events, even if they aren't directly targeting me". This essentially allows a widget to declare itself "sticky" for certain mouse events, and "unsticky" when it is no longer needed.
Changelog
Additions
- Add hover highlighting for
Button
(dcf5b0d)
Bugfixes
- Fix Splitters only sending mouse events on the first row of height (ad5cffc)
Refactors
-
Improve mouse input cascade logic (ae97102)
-
Implement semantic mouse handlers (f11f524)
All widgets now have some "implicit" callbacks for each specific mouse event. They are only called when defined, so it is fully backwards compatible. They all follow the syntax of:
on_{event}
Where
event
can be any snakeified mouse action name. For example,LEFT_CLICK
callson_left_click
, andSCROLL_UP
callson_scroll_up
. For most events, there is a lesser layer of specificity allowed. For example, you usually want to handle both scroll directions with the same function. Because of this, we also allow definingon_scroll
to deal with both events. Whenever multiple specificity level handlers are defined for the same action, the most specific will be used. For example, a widget with bothon_drag
andon_left_drag
will invokeon_drag
forRIGHT_DRAG
, andon_left_drag
forLEFT_DRAG
.
Showcase
Here is a little mouse handling demo I created while writing this version. All mouse actions are defined with custom on_{event}
handlers, where both drag events return True, and everything else False. This makes those events stick to the tile that started handling them.
LbPGMBCr5xPDU8oW.mp4
v6.3.0: A better input field
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!
Changelog
Refactors
-
Refactor
InputField
(6cd72d6) -
Start caching
Token.sequence
to improve performance (9638e95) -
Start lazy-evaluating terminal resolution (4a7c25b)
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.
Bugfixes
-
Use SVG export prefix as the class of the generated
text
elements (7c977f1, #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 (0bb0717)
Additions
-
Add
break_line
fill
argument (1426edc)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 (1822585)
Showcase
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!
v6.2.2: SVG fixes
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!
Changelog
Bugfixes
- Fix text being misaligned in both Firefox and Safari
Refactors
- Reduce redundancy in SVG export dimensions
Headless SVGs!
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.
Changelog
Bugfixes
- Fix
\n
being escaped when highlighting python (48a365a)
Refactors
- Add
WindowManager.autorun
class attribute (6ea6044)
Showcase
v6.2.0: Native SVGs!
Changelog
Bugfixes
- Fix
StandardColor
HEX and RGB being indexed from the wrong pool (66384ac) - Fix overly greedy
yeetingoptimization ofttype=POSITION
tokens (2f7a561) - Fix
ttype=POSITION
tokens acting unpredictably when multiple were present in a string (c2c227f)
Additions
- Add
WindowManager.autorun
attribute (ab0fd94)
Refactors
-
Implement usage of SVG tags when exporting (589e650)
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!
Showcase
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.
⭐
🚀
v6.1.0: Fancy repr protocol!
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.
Changelog
Additions
-
Introduce
__fancy_repr__
protocol (09bcb0e)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, andRegexHighlighter
. More (probably) to come. -
Add
Color
format specifiers (c529c2c) -
Add
Animation.pause
&Animation.unpause
(bf7ddbd)
Bugfixes
- Fix scrolled widgets not getting positioned correctly (29a2aeb)
- Fix an issue with 2-bit colors being interpreted as 8-bit ones (632ac1b, #56)
Refactors
Showcase
v6.0.0: Layouts, inspection & highlighting!
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.
Changelog
-
Remove
Window.allow_fullscreen
,Window.toggle_fullscreen
(114da3a)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.
Refactors
-
Add new, customizable window blur styles (114da3a)
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
andfocused
states for both thecorner
andborder
set of characters, a small helper was also added,Window.set_focus_styles
. -
Rename all builtin aliases to follow the
domain.item
naming scheme (3a416f5) -
Start caching
real_length
,strip_markup
&strip_ansi
results (a07ce9f, 532e3cc)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 (b15eba8) -
Rewrite the pattern used to match markup to improve how escapes are handled (d656b75, 5a37470)
-
Introduce new, layout based CLI with an RGB colorpicker & inspector (3143336)
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 theinspect
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 newhighlight_python
function (more on that later).For some reason my favorite part of this new module is the output of
-v
:
Additions
-
Add
Collapsible
widget (114da3a) -
Add new
highlighters
module withRegexHighlighter
class (7e6cd02, 6bd3d4a)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 toMouseEvent
(522e4dd) -
Add
Terminal.no_record
context (b7d4976) -
Add
WindowManager.alert
andWindowManager.float
methods (53e9b84)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. Thetoast
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 onptg
startup! -
Add layout
assign
parameter toWindowManager.add
(6c95ac0) -
Add window manager
Layout
class (8250d6d)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.
Bugfixes
- Fix overly greedy optimization in
MarkupLanguage.parse
getting rid of important unsetters (32e30b5) - Fix
/
not being handled properly in various places (2b15e51, e3e9e87)
Showcase
Closing thoughts
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
🚀
v5.0.0: Compositing a better future!
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:
- A single
Animation
base class - Looping animations
- An exposed
FloatAnimation
that simply transitions its state from 0.0 to 1.0 over the given duration - Better logic, usage of elapsed time instead of independent measurements & framerate calculations
Overall, 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.
Refactors
- Refactor
window_manager.py
into 3 files underwindow_manager
submodule - Refactor the entire animation system (71cf261)
- Move scrolling behaviour into new
ScrollableWidget
class (37f8ffa) - Improve
Terminal
API (d0209fb) - Rename
widgets/layouts
→widgets/containers
(e06608d)
Additions
- Add
StyleManager.__call__
method that sets the given **kwargs keys and values (2e69cbf) - Add (currently unused)
Widget.get_change
helper (48d3e5c)
Showcase
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.
Unrelated note on browser rendering oddities
HTML engines seem to render `foreignObject` SVG-s just slightly differently to eachother. Here is what the above looks like on Chrome for my machine: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!