Skip to content

fix(tui): recover Windows shell UI from mid-session console blanking#192

Merged
elkaix merged 3 commits into
mainfrom
fix/windows-console-blank-recovery
Jul 2, 2026
Merged

fix(tui): recover Windows shell UI from mid-session console blanking#192
elkaix merged 3 commits into
mainfrom
fix/windows-console-blank-recovery

Conversation

@elkaix

@elkaix elkaix commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Related Issue

No linked issue.

Description

Fixes a Windows-specific bug where the shell UI's transcript and input box
could go blank mid-session and stay blank until the terminal was restarted.

  • Child processes spawned via pythinker_host.LocalHost.exec, the background
    task manager/worker, and the shell ! command runner no longer attach to
    the interactive console on Windows (CREATE_NO_WINDOW combined with
    CREATE_NEW_PROCESS_GROUP), so a child touching the Win32 console API can
    no longer bypass redirected stdio and blank the parent TUI.
  • The Windows creationflags computation is centralized in a shared
    pythinker_host.windows.windows_console_detach_flags() helper instead of
    being duplicated (and drifting) across the four spawn sites.
  • The prompt renderer now forces an absolute repaint on Ctrl+L, after
    terminal resizes, and after failed scrollback handoffs, instead of diffing
    against a possibly stale frame.

Checklist

  • I have added tests that prove my fix is effective or that my feature works.

Summary by CodeRabbit

  • Bug Fixes

    • Improved Windows behavior so spawned child processes and shell “bang” commands detach from the interactive console, reducing chances of blank/corrupted terminal screens.
    • Enhanced prompt rendering recovery after terminal resizes and failed scrollback/output handoffs to ensure the UI repaints cleanly.
  • New Features

    • Added Ctrl+L (c-l) to force a full shell screen repaint for quick recovery from blank or stale displays.
  • Documentation

    • Updated the changelog and keyboard shortcuts help to reflect the new Ctrl+L recovery option.

elkaix added 2 commits July 2, 2026 16:09
The Windows shell UI could go blank mid-session (transcript and input box
gone until terminal restart). Two mechanisms, both fixed:

- Child processes attached to the interactive console: Shell-tool,
  background-task, and shell-escape spawns now pass CREATE_NO_WINDOW so a
  child touching the console via the Win32 console API (cls, Clear-Host,
  SetConsoleMode) cannot corrupt the TUI. Kill semantics are unchanged
  (TerminateProcess; no CTRL_BREAK usage anywhere).

- prompt_toolkit's differential renderer diffing against a stale frame
  after the real screen diverged (ConPTY resize rewrap, terminal replay,
  half-completed teardown erase): the prompt renderer state is now reset
  after terminal geometry changes and failed scrollback handoffs so the
  next redraw is absolute.

Also corrects the sync_output docstring: Windows10_Output delegates
_buffer to its vt100 output, so DEC-2026 marks ARE installed on
VT-capable Windows consoles (safe: WT expires after 100ms, xterm.js 5s).
- Add an explicit Ctrl+L binding (pins prompt_toolkit's default clear-screen)
  with a contained-failure handler, list it in /help shortcuts and
  docs/en/reference/keyboard.md.
- Add spawn-flag tests for the background worker child and the `!` foreground
  shell command (CREATE_NO_WINDOW on Windows), plus hard-repaint tests.
- Reviewed e2e cancellation failure: test_shell_cancel_running_command_kills_
  process_and_recovers passes outside the sandboxed test runner and on CI;
  the local failure was sandbox interference with PTY signal delivery.
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 500969ff-de60-4bb1-bf6a-1c2467b24637

📥 Commits

Reviewing files that changed from the base of the PR and between 7d5ffc8 and f22bc0c.

📒 Files selected for processing (5)
  • packages/pythinker-host/src/pythinker_host/local.py
  • packages/pythinker-host/src/pythinker_host/windows.py
  • src/pythinker_code/background/manager.py
  • src/pythinker_code/background/worker.py
  • src/pythinker_code/ui/shell/__init__.py

📝 Walkthrough

Walkthrough

This PR updates Windows subprocess spawning to detach child processes from the interactive console and adds prompt repaint recovery for Ctrl+L, resize changes, and scrollback handoff failures.

Changes

Windows subprocess console detachment

Layer / File(s) Summary
pythinker-host exec console detach
packages/pythinker-host/src/pythinker_host/windows.py, packages/pythinker-host/src/pythinker_host/local.py, packages/pythinker-host/tests/test_local_host.py
Adds a shared Windows flag helper, uses it in LocalHost.exec, and verifies the subprocess gets CREATE_NO_WINDOW plus CREATE_NEW_PROCESS_GROUP.
Background manager/worker console detach
src/pythinker_code/background/manager.py, src/pythinker_code/background/worker.py, tests/background/test_manager.py, tests/background/test_worker.py
Background worker spawning now uses the shared Windows detach flags, with tests covering both manager and worker spawn options.
Shell bang command console detach
src/pythinker_code/ui/shell/__init__.py, tests/ui_and_conv/test_shell_bang_spawn.py
Foreground shell command spawning now applies Windows-only detach flags through spawn_kwargs, and the async test checks the flag is present.

Prompt renderer hard repaint recovery

Layer / File(s) Summary
Ctrl+L hard repaint binding
src/pythinker_code/ui/shell/prompt.py, src/pythinker_code/ui/shell/slash.py, tests/ui_and_conv/test_prompt_hard_repaint.py
Adds a Ctrl+L binding that calls _hard_repaint, which clears the renderer with error handling; the shortcut is listed in help text and tested.
Resize and scrollback handoff renderer reset
src/pythinker_code/ui/shell/visualize/_interactive.py, tests/ui_and_conv/test_visualize_running_prompt.py
Adds _reset_prompt_renderer(reason) and invokes it on resize recovery and scrollback handoff failure, with tests for the helper and both trigger paths.
Docs and changelog updates
src/pythinker_code/ui/shell/sync_output.py, CHANGELOG.md
Updates the synchronized output docstring and adds the Windows blank-screen fix note to the changelog.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CustomPromptSession
  participant Renderer
  User->>CustomPromptSession: press Ctrl+L
  CustomPromptSession->>Renderer: clear()
  CustomPromptSession->>CustomPromptSession: log debug on clear() failure
Loading
sequenceDiagram
  participant Shell as Shell._run_shell_command
  participant PromptLiveView as _PromptLiveView
  participant Renderer
  Shell->>Shell: set creationflags on Windows
  PromptLiveView->>Renderer: reset() on resize
  PromptLiveView->>Renderer: reset() on scrollback handoff failure
Loading

Possibly related PRs

  • Pythoughts-labs/pythinker-code#162: Also changes the prompt visualization recovery path in _interactive.py, including renderer reset behavior during resize/scrollback handling.

Suggested labels: bug

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title follows conventional commit format and accurately summarizes the Windows TUI blanking fix.
Description check ✅ Passed The description matches the template structure and explains the fix, but the related issue is unlinked and some checklist items are unchecked.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/windows-console-blank-recovery

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.44444% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...kages/pythinker-host/src/pythinker_host/windows.py 88.88% 0 Missing and 1 partial ⚠️
src/pythinker_code/ui/shell/prompt.py 87.50% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/pythinker-host/src/pythinker_host/local.py`:
- Around line 199-210: The Windows creationflags logic is duplicated across
multiple subprocess launch paths and should be centralized. Extract the repeated
`getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", ...) | getattr(subprocess,
"CREATE_NO_WINDOW", ...)` into a shared helper (for example, in
`pythinker_host`) and have `local.py`, `background/manager.py`
(`_launch_worker`), `background/worker.py`, and the UI shell code call that
helper instead. Keep the fallback constant consistent in one place so the
behavior cannot drift between `process_options` setup sites.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b9ad1333-8816-427e-bae6-a6f1d123b0b9

📥 Commits

Reviewing files that changed from the base of the PR and between 29174c3 and 7d5ffc8.

⛔ Files ignored due to path filters (1)
  • docs/en/reference/keyboard.md is excluded by !docs/**
📒 Files selected for processing (15)
  • CHANGELOG.md
  • packages/pythinker-host/src/pythinker_host/local.py
  • packages/pythinker-host/tests/test_local_host.py
  • src/pythinker_code/background/manager.py
  • src/pythinker_code/background/worker.py
  • src/pythinker_code/ui/shell/__init__.py
  • src/pythinker_code/ui/shell/prompt.py
  • src/pythinker_code/ui/shell/slash.py
  • src/pythinker_code/ui/shell/sync_output.py
  • src/pythinker_code/ui/shell/visualize/_interactive.py
  • tests/background/test_manager.py
  • tests/background/test_worker.py
  • tests/ui_and_conv/test_prompt_hard_repaint.py
  • tests/ui_and_conv/test_shell_bang_spawn.py
  • tests/ui_and_conv/test_visualize_running_prompt.py

Comment thread packages/pythinker-host/src/pythinker_host/local.py
@elkaix elkaix changed the title Fix/windows console blank recovery fix(tui): recover Windows shell UI from mid-session console blanking Jul 2, 2026
The CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW computation was
duplicated across four subprocess spawn sites, and had already drifted:
local.py used a 0x00000200 fallback for CREATE_NEW_PROCESS_GROUP while
manager.py/worker.py used 0. Extract a shared
windows_console_detach_flags() helper so the fallback constants and
console-detachment logic can't diverge again.
@elkaix

elkaix commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

All findings addressed in f22bc0c:

  • Duplicated Windows creationflags logic (inline finding) extracted into a shared pythinker_host.windows.windows_console_detach_flags() helper.
  • Title check: renamed the PR title to conventional-commit form.
  • Description check: filled in the Related Issue / Description sections.

Codecov's 1-line patch-coverage note is left as-is — the codecov/patch check passed and it's unrelated to this fix.

@elkaix elkaix merged commit 5665370 into main Jul 2, 2026
52 checks passed
@elkaix elkaix deleted the fix/windows-console-blank-recovery branch July 2, 2026 22:12
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