NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #473 most downloaded on PyPI
Rich toolkit for building command-line applications
Last release 26 days ago
08 Sep 2026
Ships fairly regularly
a new release about every 4 weeks
Some releases are documented
notes for 25 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
2 years old
62 releases · first in 2024
One column per month.
Fix Unicode output on streams using encodings such as ASCII, CP1252, and GBK. Unsupported characters are displayed as ?, while supported characters re
Fix Unicode output on streams using encodings such as ASCII, CP1252, and GBK.
Unsupported characters are displayed as ?, while supported
characters remain unchanged. For example, café 🚀 is displayed as
café ? on a CP1252 stream.
Built-in decorations use ASCII alternatives when needed. Fix cursor positioning, empty Fancy-style input rows, and table sizing when encoding fallbacks are used. Unsupported characters in hyperlink targets are percent-encoded to keep links working.
Input and menu values remain unchanged. JSON output always uses ASCII with JSON escapes, including on UTF-8 streams, and restores the original values when decoded. Add full-suite test coverage for UTF-8, ASCII, CP1252, and GBK in GitHub Actions.
This release was contributed by @patrick91 in #66
This release handles interactive prompts cleanly when no controlling terminal is available.
This release handles interactive prompts cleanly when no controlling terminal is available.
On Unix, failing to open /dev/tty now raises EOFError instead of exposing the
underlying operating-system error. Inputs and menus are marked as cancelled and
the final state is rendered before the error is propagated.
This release was contributed by @patrick91 in #65
This release keeps updated progress titles visible when logs are preserved.
This release keeps updated progress titles visible when logs are preserved.
For progress displays with inline preserved logs, assigning to
progress.title now also updates the displayed message unless
progress.current_message was changed explicitly.
This release was contributed by @patrick91 in #63
This release preserves progress logs automatically in CI and non-interactive output.
This release preserves progress logs automatically in CI and non-interactive output.
In these environments, every progress.log() message appears in the output once
without line breaks inserted at the terminal width. Interactive terminals
continue to use the live progress display.
Pass preserve_progress_logs=True or preserve_progress_logs=False to
RichToolkit to override the behavior globally. Individual progress displays
can override it with preserve_logs.
This release was contributed by @patrick91 in #62
This release lets styles control the automatic context-manager padding printed when entering and exiting RichToolkit.
This release lets styles control the automatic context-manager padding printed
when entering and exiting RichToolkit.
BaseStyle now exposes render_context_enter() and render_context_exit()
hooks. Existing styled output keeps the previous blank padding by default, while
MinimalStyle suppresses both automatic lines so minimal applications do not
emit extra newlines around their content.
This release was contributed by @patrick91 in #61
This release adds JSON output support for CLI-friendly structured results.
This release adds JSON output support for CLI-friendly structured results.
RichToolkit(mode="json") now suppresses human-only rendering and writes JSON-compatible output through output(). Pydantic-style models are serialized through model_dump(mode="json"), dictionaries and lists remain supported, and generators can be passed to output() to stream newline-delimited JSON events.
The release also adds examples showing final JSON output, streaming JSON output, list output, and custom human renderers.
This release was contributed by @patrick91 in #60
This release fixes the terminal color query hanging when the program runs from a background process group.
This release fixes the terminal color query hanging when the program runs from a background process group.
_get_terminal_color used tty.setcbreak, which calls tcsetattr and
generates SIGTTOU when invoked from a background process group (for example
when the program is launched with prog & or run under a job-control shell
that isn't giving it the terminal). The default disposition of SIGTTOU stops
the process, so the program would hang.
It now checks that it owns the terminal's foreground process group before querying, and falls back to the default color otherwise.
This release fixes progress log rendering when messages contain embedded newlines.
This release fixes progress log rendering when messages contain embedded newlines.
Progress.log() now preserves multiline messages in non-inline progress
rendering, while inline progress logs split embedded newlines into separate log
entries. This keeps partial-line logging with end="" working while also
handling output that arrives with newline characters already included.
Cancelled progress rendering now keeps the progress output that was already
visible and always shows Cancelled. on its own final line, avoiding duplicated
titles in framed styles.
This release was contributed by @patrick91 in #58
This release adds support for passing end to RichToolkit.print(), RichToolkit.print_title(), and Progress.log().
This release adds support for passing end to RichToolkit.print(),
RichToolkit.print_title(), and Progress.log().
This makes it possible to print or log partial lines without automatically ending them with a newline:
app.print("Hello, ", end="")
app.print("World!")
with app.progress("Downloading") as progress:
progress.log("Downloaded ", end="")
progress.log("50%")
This release was contributed by @patrick91 in #56
Show cancelled state when progress is interrupted with Ctrl+C.
Show cancelled state when progress is interrupted with Ctrl+C.
Previously, pressing Ctrl+C during a progress operation would exit without any visual indication that the operation was cancelled. Now, the progress displays "Cancelled." (matching how inputs and menus already handle cancellation).
The tagged style also shows the tag blocks in red (using error animation colors) when a progress is cancelled.
This release was contributed by @patrick91 in #55
Fix metadata being lost when passed to Progress. Element.__init__ was called without the metadata argument, causing it to be overwritten to an empty d
Fix metadata being lost when passed to Progress. Element.__init__ was called
without the metadata argument, causing it to be overwritten to an empty dict.
Also removed a redundant self.metadata assignment in Menu.
This release was contributed by @patrick91 in #54
Add metadata support to the progress() method, aligning it with the other RichToolkit methods (print, input, confirm, ask) that already accept and for
Add **metadata support to the progress() method, aligning it with the other
RichToolkit methods (print, input, confirm, ask) that already accept
and forward metadata to the underlying components.
This release was contributed by @patrick91 in #53
This release adds support for passing a value parameter to Input and RichToolkit.input(), which sets the initial editable text of the input field. The
This release adds support for passing a value parameter to Input and RichToolkit.input(), which sets the initial editable text of the input field. The cursor is positioned at the end of the value, so users can immediately continue typing or edit the pre-populated text.
app.input(
"What is your name?",
value="Patrick",
)
This release was contributed by @patrick91 in #52
This release fixes the input component to show the explicit placeholder text when both a placeholder and default value are provided. Previously, the d
This release fixes the input component to show the explicit placeholder text
when both a placeholder and default value are provided. Previously, the
default value would always take priority, hiding the placeholder text.
This release was contributed by @patrick91 in #51
This release hides the placeholder text when an input is cancelled. Previously, the placeholder was shown with strikethrough styling, but now only use
This release hides the placeholder text when an input is cancelled. Previously, the placeholder was shown with strikethrough styling, but now only user-typed text is displayed when cancelling.
This release was contributed by @patrick91 in #50
This release fixes an issue where the cursor would appear in the wrong position when an input's label was long enough to wrap to multiple lines. It al
This release fixes an issue where the cursor would appear in the wrong position when an input's label was long enough to wrap to multiple lines. It also fixes text being cut off at the terminal edge in FancyStyle instead of wrapping correctly.
This release was contributed by @patrick91 in #49
This release refactors our styling a bit, improving the visual distinction between submitted input values and placeholders, and properly handling the
This release refactors our styling a bit, improving the visual distinction between submitted input values and placeholders, and properly handling the cancelled state of inputs.
In addition to that we also merge the custom theme on top of the base theme instead of replacing it, which allows us to preserve base styles like placeholder.cancelled while still allowing users to customize their themes.
This release was contributed by @patrick91 in #48
Fixed a crash that occurred when pressing Ctrl+C to cancel a menu. Previously, the render_menu function would crash with an IndexError when trying to
Fixed a crash that occurred when pressing Ctrl+C to cancel a menu. Previously, the render_menu function would crash with an IndexError when trying to display a cancelled menu because it attempted to access an invalid selection index.
The fix checks if the menu element was cancelled or has an invalid selection index before attempting to access the selected option, and displays "Cancelled." instead.
This also includes comprehensive test coverage for cancelled menu rendering scenarios.
This release was contributed by @patrick91 in #42
This release adds multiple selection support to the Menu component.
This release adds multiple selection support to the Menu component.
When passing multiple=True to RichToolkit.ask() or Menu(), the menu
switches to multi-select mode:
■/□) instead of circles (●/○)selected = app.ask(
"Which languages do you use?",
options=[
{"name": "Python", "value": "python"},
{"name": "JavaScript", "value": "javascript"},
{"name": "Rust", "value": "rust"},
],
multiple=True,
)
# selected is e.g. ["python", "rust"]
Works with filtering (allow_filtering=True) and all existing styles
(tagged, bordered, etc.). Raises ValueError if combined with inline=True.
This release was contributed by @patrick91 in #46
This release fixed the colour detection function, mostly to fix a bug interfering with forked worker processes (e.g. uvicorn with --workers).
This release fixed the colour detection function, mostly to fix a bug
interfering with forked worker processes (e.g. uvicorn with --workers).
The colour query now uses a dedicated file descriptor instead of stdin/stdout, preventing issues with logging and signal handling in multi-process environments.
Fix inline menu option wrapping by using spaces instead of tabs.
Fix inline menu option wrapping by using spaces instead of tabs.
When using inline menus (e.g., app.confirm()), options like "Yes" and "No" were wrapping to separate lines due to tab character separators expanding unpredictably in fixed-width table columns. This release replaces tab separators with two spaces for consistent, predictable spacing.
Before:
● Yes
○ No
After:
● Yes ○ No
Add scrolling support for menus with many options.
Add scrolling support for menus with many options.
When a menu has more options than can fit on the terminal screen, it now automatically scrolls as the user navigates with arrow keys. This prevents the UI from breaking when the terminal is too small to display all options.
Features:
↑ more / ↓ more) show when more options existTaggedStyle and BorderedStylemax_visible parameter for explicit controlExample usage:
from rich_toolkit import RichToolkit
from rich_toolkit.styles.tagged import TaggedStyle
# Auto-scrolling based on terminal height
with RichToolkit(style=TaggedStyle()) as app:
result = app.ask(
"Select a country:",
options=[{"name": country, "value": country} for country in countries],
allow_filtering=True,
)
# Or with explicit max_visible limit
from rich_toolkit.menu import Menu
menu = Menu(
label="Pick an option:",
options=[{"name": f"Option {i}", "value": i} for i in range(50)],
max_visible=10, # Only show 10 options at a time
)
result = menu.ask()
Add Pydantic v1/v2 compatibility for Input validators using a Protocol-based approach.
Add Pydantic v1/v2 compatibility for Input validators using a Protocol-based approach.
The Input component now accepts any object with a validate_python method through the new Validator protocol, making it compatible with both Pydantic v1 and v2.
Usage with Pydantic v2:
from pydantic import TypeAdapter
validator = TypeAdapter(int)
app.input("Enter a number:", validator=validator)
Usage with Pydantic v1:
from pydantic import parse_obj_as
class V1Validator:
def __init__(self, type_):
self.type_ = type_
def validate_python(self, value):
return parse_obj_as(self.type_, value)
validator = V1Validator(int)
app.input("Enter a number:", validator=validator)
Changes:
Validator protocol that accepts any object with a validate_python methodThis release add proper support for CJK characters
This release add proper support for CJK characters
This release increases the paste buffer from 32 to 4096 characters, enabling users to paste longer text into input fields.
This release increases the paste buffer from 32 to 4096 characters, enabling users to paste longer text into input fields.
It also adds full Windows compatibility with proper special key handling and fixes how password fields to always show asterisks.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →