Commit Graph
988 Commits
Author SHA1 Message Date
roboomp b1b6d99627 fix(tui): backpressured slow loader paints
Measured each animated Loader paint and recursively scheduled the next frame with a 10% duty-cycle target, capped at 200ms to retain responsiveness.

Added a regression covering a slow synchronized-output direct-write path and documented the fix.

Fixes #7290
2026-08-01 17:23:26 +00:00
can1357 a91fa13116 Merge PR #7055: fix(tui): preserve literal code block rows (@GratefulDave) 2026-07-31 19:42:43 +02:00
can1357 f1f7ed7f75 Merge PR #7113: fix(tui): ignore system sleep in loop watchdog (@wolfiesch) 2026-07-31 19:28:43 +02:00
can1357 540e8f5797 Merge PR #7188: fix(coding-agent): preserve scrollback on appearance changes (@Sairen777)
# Conflicts:
#	packages/coding-agent/src/modes/controllers/input-controller.ts
2026-07-31 19:08:16 +02:00
Alex Jaden daa9b99aab fix(coding-agent): preserve scrollback on appearance changes 2026-07-31 10:34:43 +05:00
can1357 652647770e feat(coding-agent): added app.live.toggle keybinding and map display reset to alt+l
- Add the `app.live.toggle` keybinding defaulted to `Ctrl+L` to start or stop live voice mode.
- Remap the default display-reset action (`app.display.reset`) from `Ctrl+L` to `Alt+L`.
- Update the live visualizer to listen for stop keys so the toggle chord terminates active sessions.
2026-07-31 00:20:04 +02:00
Wolfgang Schoenberger 2841e99a95 fix(tui): ignore system sleep in loop watchdog 2026-07-30 14:48:06 -07:00
David Andrews 9502bc2341 fix(tui): preserve literal fenced code rows 2026-07-30 15:41:35 -04:00
David Andrews aa2417c8f6 fix(tui): preserve literal fenced code rows 2026-07-30 15:27:21 -04:00
David Andrews 096ff49fc5 fix(tui): preserve nested code copy 2026-07-30 15:27:21 -04:00
David Andrews 7928353e02 fix(tui): preserve assistant code copy 2026-07-30 15:27:21 -04:00
David Andrews cf069f37c8 fix(tui): preserve literal code block rows 2026-07-30 15:27:21 -04:00
can1357 83294afc0a Merge PR #7013: fix(tui): prevent UUID prefixes rendering color swatches (@roboomp) 2026-07-29 23:09:04 +02:00
roboompandcan1357 4e06b7f1f5 fix(tui): prevented UUID prefixes rendering color swatches
Excluded canonical hash-prefixed UUIDs from prose hex-color matching while preserving standalone 8-digit CSS colors.

Fixes #7002

(cherry picked from commit b423fe6e51bb1d45b88916f885f1b81e1ce54ff0)
2026-07-29 23:09:03 +02:00
can1357 e24babd8e1 Merge PR #6941: fix(tui): stop loader spinner re-wrapping text every tick (@roboomp) 2026-07-29 23:08:40 +02:00
roboompandcan1357 beaafb9955 fix(tui): handled mixed-width spinner frames
Cached one stable layout representative per frame display width so same-width animation retains the fast path while width transitions rewrap before painting. Added coverage for wider custom frames remaining within terminal bounds.

(cherry picked from commit 3aa173a574538d754e68f8d10f7b88d437709884)
2026-07-29 23:08:40 +02:00
roboompandcan1357 b152f40134 fix(tui): stopped loader spinner re-wrapping text every tick
The Loader baked the advancing braille glyph into the underlying Text
via setText, so Text.render's wrap cache (keyed on text) missed every
80ms tick and re-ran wrapTextWithAnsi plus per-line width measurement
over the whole message even though only a 1-cell glyph changed. On a
single-subagent hub wait the direct-write render loop consumed ~48% of
a saturated core.

The wrapped text now carries a stable sentinel glyph (frames[0]); the
spinner advance only bumps the frame index and requests a repaint, and
render() swaps the visible glyph over the cached layout. wrapTextWithAnsi
and stringWidth now run only when the message actually changes.

Fixes #6940

(cherry picked from commit 6ec38053ebc1e33859358df3b66f71fea27532c7)
2026-07-29 23:08:40 +02:00
can1357 7d29a65b1c Merge PR #6923: fix(coding-agent): sync WezTerm theme changes on macOS (@Sairen777) 2026-07-29 23:08:35 +02:00
Alex Jadenandcan1357 028912ef01 fix(tui): suppress appearance probes while stopped
(cherry picked from commit a53b38a8b1b6f0a6d5e45fe46b84857c93e56bd2)
2026-07-29 23:08:34 +02:00
Alex Jadenandcan1357 070ff41f4c fix(coding-agent): sync WezTerm theme changes on macOS
(cherry picked from commit fa70be267daa6f98cf18e2f257041f4e6e68a61d)
2026-07-29 23:08:34 +02:00
can1357 897b0c8d6c Merge PR #6920: fix(tui): prevent Windows pane-close deadlock (@roboomp) 2026-07-29 23:08:31 +02:00
roboompandcan1357 313f3fa1c4 fix(tui): prevented Windows pane-close deadlock
Skipped stdout draining after ProcessTerminal observes a native Windows terminal disconnect, while preserving normal postmortem cleanup and drain behavior for every other shutdown path.

Fixes #6917

(cherry picked from commit b45fc00ab4b5075ccb9650584947dde061beea20)
2026-07-29 23:08:31 +02:00
can1357 c793089797 Merge PR #6856: fix(tui): bound stdout backlog when the pty consumer stalls (@roboomp) 2026-07-29 23:08:23 +02:00
can1357 05e98891f0 Merge PR #6833: fix(tui): wrapped Markdown list and output block layout (@usr-bin-roygbiv) 2026-07-28 10:59:33 +02:00
can1357 6a84575f2a Merge PR #6812: fix(tui): balance keyboard frames on emergency exit (@roboomp)
# Conflicts:
#	packages/tui/src/terminal.ts
2026-07-28 10:59:21 +02:00
usr-bin-roygbiv a28ca5a268 fix(tui): honor padded markdown width budgets 2026-07-28 07:41:59 +00:00
usr_bin_roygbiv f32dd6eff4 Fix wrapped Markdown list and output block layout 2026-07-28 07:06:31 +00:00
roboomp 91bac496cc fix(tui): bound stdout backlog when the pty consumer stalls
ProcessTerminal.#safeWrite wrote every frame with process.stdout.write()
and dropped the backpressure return value. A stalled-but-alive PTY reader
never throws, so the write path had no error to catch: the writable buffer
grew without bound as cosmetic hub-wait spinner and 500ms progress frames
piled up, reaching ~48 GiB RSS and wedging the host in one incident.

Add OutputBacklogGuard to sum the bytes queued since backpressure began and
report when the backlog crosses a 64 MiB cap. #safeWrite then treats a
consumer that will not drain past the cap as a disconnect, taking the same
clean, supervisor-resumable SIGHUP exit a dead terminal already takes
(#5837) instead of unbounded growth.

Fixes #6854
2026-07-28 04:22:52 +00:00
can1357 222e3569bd perf: optimized markdown rendering speed and device doc schemas
- Render device doc parameter schemas as TypeScript types instead of raw JSON schema dumps.
- Optimize marked streaming block rules and add pre-gates to reduce CPU overhead.
- Increase the markdown render cache entry budget from 32 KiB to 256 KiB.
2026-07-27 22:19:51 +02:00
can1357 1888b0999e perf(tui): optimized markdown lexing, editor input, and cursor updates
- Optimized markdown streaming lexing, tokenizers, and render cache limits.
- Improved editor input handling with bulk printable fast paths and iterative chunk processing.
- Added cursor visibility tracking and deduplication to prevent redundant terminal escape sequences.
- Added benchmarks and unit tests covering markdown streaming, incremental lexing, and editor fast paths.
2026-07-27 20:33:39 +02:00
roboomp 384394eea0 fix(tui): balanced keyboard frames on emergency exit
- Popped the alternate-screen keyboard frame before restoring the main screen.
- Reset the main-screen keyboard state in live and blind emergency cleanup.
- Added regression coverage for both restore paths.

Fixes #6810
2026-07-27 14:23:27 +00:00
can1357 39a7525dd0 fix(tui): excluded multiplexers from raw-backspace heuristic
- WT_SESSION leaks into tmux/screen/Zellij panes, so the automatic raw
  0x08 -> ctrl+backspace override deleted a word on plain Backspace.
- The heuristic now requires isWindowsTerminalSession() and not
  isInsideTerminalMultiplexer(); PI_TUI_RAW_BACKSPACE_IS_CTRL=1 stays an
  unconditional opt-in.
- Review follow-up for PR #6784.
2026-07-27 16:02:58 +02:00
can1357 da044f33ef Merge PR #6784: fix(tui): route editor word delete through keybindings registry (@roboomp) 2026-07-27 15:57:45 +02:00
Ivan Dergachev bde5e56695 fix(tui): surfaced raw-mode restore failures on a live terminal
Gates guard 1's suppression on #dead: the disconnected path stays
best-effort, but a failed restore during a normal stop() throws as it
did before the guard, instead of silently leaving stdin in raw mode.

Claude-Session: https://claude.ai/code/session_01LTC1HNAntXEMnTYmuUpGHz
2026-07-27 15:32:10 +04:00
roboomp dda720af41 fix(tui): allowed remote raw backspace opt-in
Windows Terminal identity is commonly lost across SSH and container hops,
leaving only the ambiguous raw 0x08 byte. Added the conservative
PI_TUI_RAW_BACKSPACE_IS_CTRL=1 opt-in so those sessions can map it to
ctrl+backspace without changing the default for terminals where 0x08 means
plain Backspace.

Restored the public isWindowsTerminalSession and matchesRawBackspace exports
and route the parser wrappers through matchesRawBackspace. Documented the
runtime flag and covered local WT, remote opt-in, SSH, and 0x7f behavior.

Fixes #6782
2026-07-27 10:28:00 +00:00
roboomp 6460ba042a fix(tui): route editor word delete through keybindings registry
The multi-line prompt editor matched word/line delete and yank with
hardcoded chords via matchesKey() instead of the keybindings registry,
unlike cursor motion and the single-line Input component. As a result
ctrl+backspace (a declared default of tui.editor.deleteWordBackward)
never fired and keybindings.yml remaps of deleteWordBackward,
deleteWordForward, deleteToLineStart, deleteToLineEnd, yank, and yankPop
were silently ignored in the main prompt.

Route those six actions through kb.matches(). Also re-wire the Windows
Terminal raw 0x08 -> ctrl+backspace disambiguation into the TS
matchesKey/parseKey seam (where WT_SESSION is observable), replacing the
dead matchesRawBackspace helper that no longer had any call sites.

Fixes #6782
2026-07-27 09:54:01 +00:00
Ivan Dergachev 23ec7e725b fix(tui): guarded terminal teardown against a revoked pty on disconnect
A recycled terminal pane revokes the pty. stdin EOFs, the disconnect path
runs, and stop() tries to restore raw mode on an fd that is no longer a
tty - Bun's node:tty shim throws ENOENT. That throw escaped stop() and
#markTerminalDisconnected(), preempting its own process.kill(SIGHUP), so
the process died with an uncaught exception instead of exiting 129.

Restoring raw mode on a dead fd is best-effort, matching what
emergencyTerminalRestore() already does, and the disconnect handler is
now wrapped so no teardown failure can preempt the exit.
2026-07-27 11:00:48 +04:00
can1357 c8cb3a1424 Merge PR #6471: fix(tui): keep full-width overlays above inline images (@oleksoleksoleks) 2026-07-26 15:45:30 +02:00
can1357 6eee23de4b Merge PR #6553: fix(tui): stop Ctrl+O expand truncating the session on ConPTY (@roboomp)
# Conflicts:
#	packages/tui/src/tui.ts
2026-07-26 15:45:30 +02:00
roboomp cc89868cb2 fix(tui): consumed conpty reset intent after updates
Under WSL+tmux, resetDisplay() emitted an in-place update rather than a full
paint, so the one-shot unbounded ConPTY intent was never cleared. A later
/resume or handoff inherited it and replayed a multi-megabyte transcript
without the #2115 bound.

Consume the intent on the next authoritative normal-screen render regardless
of whether it emits an update or full paint. Cover resetDisplay() followed by
a bulk replacement under WSL+tmux.

Fixes #4863
2026-07-24 23:34:21 +00:00
roboomp 432e54dfdc fix(tui): kept conpty resume bound keyed to paint intent
The #4863 fix gated the ConPTY frame-truncation on firstPaint, which also
disabled it for post-startup bulk replacements (/resume, handoff) that enter
the fullPaint path with firstPaint=false — reintroducing the #2115 stall for
those flows.

Key the bound on paint intent instead: only a user-driven resetDisplay()
(Ctrl+O expand, thinking/setting toggles, display reset) opts out via
#unboundedConptyPaintRequested. Bulk transcript-replacement paints — first
paint, /resume, handoff, and resize geometry rebuilds — stay bounded.

Fixes #4863
2026-07-24 23:27:22 +00:00
roboomp 0f385b2435 fix(tui): stopped ctrl+o expand truncating the session on conpty
The ConPTY frame-truncation added for large session resumes (#2115) fired on
every full paint on ConPTY hosts (native Windows + WSL), including the
user-driven resetDisplay() that Ctrl+O (app.tools.expand) triggers. Expanding
to review the whole session dropped every row above the retained tail behind an
"older lines hidden" marker.

Bound only the initial resume paint via a boundConptyPaint flag (= firstPaint);
user-driven redraws (Ctrl+O expand, resize geometry rebuilds) replay the whole
transcript untruncated.

Fixes #4863
2026-07-24 23:18:36 +00:00
roboomp 44586a9324 fix(tui): respected resize-in-place opt-out after detection
Gate the runtime alt-toggle resize latch when PI_TUI_RESIZE_IN_PLACE explicitly forces the fast path off, while preserving multiplexer handling.

Add regression coverage that auto-detects the alt-toggle echo, then verifies a later width resize still borrows the alternate screen and performs the authoritative ED3 rewrap.
2026-07-24 12:30:23 +00:00
roboomp fe4dec9b9b fix(tui): auto-detected alt-toggle size echo to stop overlay-exit flicker
Terminals that re-report their size whenever the alternate screen buffer toggles turned every fullscreen-overlay exit (/settings, models page) and resize into a spurious height-only SIGWINCH. That armed the resize drag/settle behind the overlay and drove a destructive ED3 clear-scrollback full repaint on exit -- visible as a flicker on terminals without DEC 2026 synchronized output -- with the size revert flashing another.

A SIGWINCH while a fullscreen overlay covers the transcript now repaints the overlay in place instead of arming the drag/settle, and the in-place resize path is latched once the height-only alt-toggle echo is observed, so this was previously handled only for Warp and is now auto-detected for any such terminal.

Fixes #6511
2026-07-24 12:23:20 +00:00
can1357 22a2ea1699 refactor(tui): moved Hangul Jamo width onto the TERMINAL capability object
- Replaced resolveHangulCompatibilityJamoWidthFromTerminalIdentity with a
  static hangulJamoWidth field on TerminalInfo (ghostty 2, warp 1,
  platform otherwise); startup now reads TERMINAL.hangulJamoWidth.
2026-07-24 06:53:58 +02:00
Alexander Kirilin 36209a6361 test(tui): pin overlay placeholder graphics mode 2026-07-24 00:25:24 -04:00
Alexander Kirilin 7a392fd0f8 fix(tui): keep full-width overlays above inline images 2026-07-23 23:59:37 -04:00
roboomp 74c4868358 fix(tui): corrected Warp compatibility Jamo width
- Reused terminal identity detection to select Warp narrow and Ghostty wide profiles.
- Covered Warp resolution while preserving platform defaults for other terminals.

Fixes #6461
2026-07-24 03:37:47 +00:00
can1357 ef39fe18b9 Merge PR #6420: fix(tui): re-resolve transient status colors on theme swap (@roboomp) 2026-07-23 22:15:21 +02:00
roboomp 93d032a144 fix(tui): re-resolve transient status colors on theme swap
Status presenters (showWarning/showError/showStatus and extension/tool
error presenters) baked theme.fg() into Text at construction, so a
warning shown while the auto-theme default guess was dark kept the
dark-mode color after async appearance detection switched the active
theme to light — dark-catppuccin Mocha yellow on the light Latte
background (1.12:1 contrast, effectively invisible).

Add Text.setStyleFn(), a foreground styler evaluated at render time, and
route the transient status presenters through it. Because the coding
agent invalidates status components on onThemeChange, a theme swap now
re-shapes them against the live theme instead of replaying the palette
active when they were constructed.

Fixes #6337
2026-07-23 19:20:53 +00:00