Skip to content

Release v0.3.3#23

Merged
shoom1 merged 20 commits into
mainfrom
develop
Jun 13, 2026
Merged

Release v0.3.3#23
shoom1 merged 20 commits into
mainfrom
develop

Conversation

@shoom1

@shoom1 shoom1 commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Release v0.4.0

Minor release rolling up PRs #17#22 from develop. Bumps 0.3.2 → 0.4.0.

Why minor (not patch)

  • ThinkingBoxManager.finish_all() return tuples gained a fifth element (max_collapsed_lines) — a public signature change.
  • Ctrl+C at an idle prompt now also calls app.exit() in addition to raising KeyboardInterrupt, a visible behavior change for code driving prompt_async() directly.

Fixed

  • First Ctrl+C at an idle prompt exits instead of leaving a zombie session that echoed but dropped input.
  • DialogManager.show() raises RuntimeError on a concurrent dialog instead of orphaning the first dialog's future.
  • show_settings_dialog(can_cancel=False) no longer leaks the string "close" on Escape (now disabled in that mode).
  • Custom AppInfo.expand_key reflected in the truncation hint.
  • StreamingContent.set_line() raises a clear IndexError for out-of-range negative indices.
  • Deprecated finish_thinking() truncates each box to its own max_lines.
  • clear() goes through the renderer while the app is running.
  • ANSI styling bytes excluded from thinking-box line counting; header sized to terminal width.

Changed

  • No global rich.markdown.Markdown monkey-patch on import (scoped subclass instead).
  • Rich/Pygments helpers extracted to rich_utils (backward-compat re-exports kept).
  • Dead ThinkingBoxControl methods removed; DEFAULT_SPINNER_FRAMES deduped into types.
  • Docs: sync handlers block the event loop; demo sources its version from __version__.

Full detail in CHANGELOG.md.

shoom1 added 20 commits June 12, 2026 13:03
At an idle prompt, prompt_async() always has a live _pending_input
future, so the Ctrl+C binding took the cancel-pending branch: it
cancelled the future (which broke the input loop via KeyboardInterrupt)
but never called app.exit(). The app kept running with a dead input
loop — typed input was echoed and silently dropped, and only a second
Ctrl+C exited.

Pending input no longer counts as in-flight work when deciding whether
to exit: Ctrl+C with no running handler and no active boxes now exits
on the first press, matching the documented behavior. The future is
still cancelled first so direct prompt_async() callers continue to
observe KeyboardInterrupt.

The idle-exit unit test previously set _pending_input = None — a state
that never occurs at a real idle prompt — which is how this slipped
past. It now models the live future, and a new end-to-end regression
test drives run_async() with a stubbed Application to assert the
session actually terminates on the first Ctrl+C.
DialogManager.show() now raises RuntimeError if a dialog is already
open. Previously a second show() overwrote _current_dialog, orphaning
the first dialog's result future — its awaiter hung forever with no
way to resolve. Failing loudly makes the single-dialog contract
explicit.

SettingsDialog with can_cancel=False now disables Escape (_UNSET)
instead of setting escape_result to the string "close", which leaked
through DialogManager.show() as the return value of
show_settings_dialog() — violating its documented dict-or-None
contract (callers iterating result.items() would crash on a str).
With no cancel concept, the Done button is the only way out.
ThinkingBoxControl was always created with its default expand_key
("c-t"), so a custom AppInfo.expand_key changed the actual binding
but the truncation hint still read "ctrl-t to expand". The manager
now takes expand_key and forwards it to every box it creates.
StreamingContent.set_line(-5, ...) on shorter content fell through
normalization still negative, skipped the extend loop, and failed on
the list assignment with an error naming the normalized index. It now
raises immediately with the index the caller passed, and leaves the
content untouched.
finish_all() now reports each box's max_collapsed_lines, and
finish_thinking() uses it instead of the session-wide
max_thinking_height — matching the per-box finish path used by
ThinkingContext.finish(). Boxes created with a custom max_lines were
previously truncated to the wrong limit on the deprecated path.
Display.clear() raw-printed \033[2J\033[H to stdout, bypassing
prompt_toolkit's renderer. With the app running, the renderer's idea
of what is on screen goes stale and the next repaint draws against
the wrong baseline. Screen clearing now lives in
ThinkingPromptSession.clear(): app.renderer.clear() when running,
escape-code fallback when not. Display.clear() handles only its own
state (history buffer + pending output).
get_line_count measured raw len(line), so SGR escape sequences in
ansi-format content inflated the wrapping estimate and the collapsed
box was sized taller than its visible content. Escape sequences are
now stripped before width math.
The header line was rendered at a hardcoded 80 columns, so it never
spanned wider terminals and overflowed narrower ones. The width now
comes from the running app's output size, falling back to 80 outside
a running app.
…obally

Move the Rich/Pygments helpers (_is_rich_renderable, _rich_to_ansi,
_renderable_to_ansi, _markdown_to_ansi, _highlight_code) out of
display.py into rich_utils.py. types.py previously had to import these
from display at function level to dodge a cycle — a low-level module
reaching up into the display layer. display.py re-exports the names
for backward compatibility.

The left-aligned heading style was applied by replacing
Markdown.elements['heading_open'] at import time, which changed
heading rendering for the host application's own Rich usage. It is
now a scoped Markdown subclass used only by our markdown rendering;
rich.markdown.Markdown is left untouched.
…_output

Neither method has a production caller: the session-level binding
(via ThinkingBoxManager) owns expand/collapse, and Display.thinking()
owns console truncation. get_key_bindings also shadowed the
UIControl.get_key_bindings() API with an incompatible signature and
built a fresh KeyBindings object per call. Tests that existed only to
exercise the dead methods are removed with them.
The same frame tuple was duplicated in layout.py and app_info.py. It
now lives in types.py (which imports nothing from the package);
layout re-exports it for backward compatibility and AppInfo uses it
as the field default.
Document that synchronous input handlers run on the event loop and
freeze the UI (including Ctrl+C handling) until they return — in the
README quick start and the on_input/run_async docstrings. Add
Unreleased changelog entries covering the Ctrl+C idle-exit fix, dialog
lifecycle fixes, thinking-box polish, and the rich_utils refactor.
fix: first Ctrl+C at idle prompt exits instead of zombifying the session
fix: dialog reentrancy guard and settings-dialog Escape contract
fix: thinking-box polish — six small correctness fixes
refactor: extract rich_utils; stop monkey-patching Rich's Markdown globally
docs: sync-handler event-loop warning; changelog for pending fixes
The showcase hardcoded version="0.3.0" while the package was already
at 0.3.2, so the welcome banner showed a stale version in recordings.
Sourcing it from thinking_prompt.__version__ keeps it correct across
future bumps.
chore(demo): source showcase version from package __version__
Minor bump from 0.3.2. Highlights (see CHANGELOG for full list):
- Fix: first Ctrl+C at an idle prompt exits instead of zombifying the session
- Fix: DialogManager.show() guards against concurrent dialogs
- Fix: settings dialog (can_cancel=False) no longer leaks "close" on Escape
- Fix: expand_key reflected in the truncation hint; renderer-safe clear();
  ANSI-aware line counting; terminal-width header; per-box finish truncation
- Refactor: rich_utils module; no global Rich Markdown monkey-patch

Minor (not patch) because ThinkingBoxManager.finish_all() gained a fifth
tuple element and idle Ctrl+C now also calls app.exit() for direct
prompt_async() consumers.
@shoom1 shoom1 merged commit 89816c8 into main Jun 13, 2026
8 checks passed
shoom1 added a commit that referenced this pull request Jun 13, 2026
@shoom1 shoom1 changed the title Release v0.4.0 Release v0.3.3 Jun 13, 2026
@shoom1

shoom1 commented Jun 13, 2026

Copy link
Copy Markdown
Owner Author

Re-released as v0.3.3 (patch) instead of 0.4.0 per maintainer request. The v0.4.0 tag and GitHub release were removed; main/develop history was rewritten so the release commit reads v0.3.3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant