Commit Graph
581 Commits
Author SHA1 Message Date
roboomp f7dfd43844 fix(tui): preserved scrollback during overflow growth
Stopped foreground streaming from treating an unknown native viewport as permission for destructive history rebuilds, so offscreen growth stays incremental after the viewport fills.\n\nFixes #2154
2026-06-09 02:27:33 +00:00
Can BölükandGitHub c03b7c26d8 Merge branch 'main' into farm/7680ea37/fix-opencode-zen-login-clipboard-paste 2026-06-08 22:36:54 +02:00
roboomp 336b993126 fix(tui): route enhanced-paste text to the focused Input prompt
On kitty (Linux/Wayland) and any terminal supporting OSC 5522 enhanced
paste, the /login API-key prompt for OpenCode Zen — and any other modal
Input prompt (Perplexity OTP, GitHub Enterprise URL, manual OAuth
redirect URL, …) — silently dropped pasted content. The user could not
paste their API key; on Enter or Esc the key surfaced in the main
prompt.

Cause: InputController.#setupEnhancedPaste enables kitty's enhanced
clipboard protocol on TUI start and consumes the resulting OSC 5522
packets in an addInputListener that runs *before* focus dispatch in
tui.#handleInput. The controller's pasteText callback then routed
unconditionally to this.ctx.editor.pasteText(text) — the main
CustomEditor — even when selector-controller had cleared editorContainer
and focused a temporary Input. The text accumulated in the detached
editor and only resurfaced when the modal was dismissed.

Fix:
- TUI.getFocused() exposes the currently focused component.
- Input.pasteText(text) mirrors Editor.pasteText so any Input can absorb
  a payload from a non-bracketed transport.
- InputController's enhanced-paste callback consults getFocused() and
  routes text to the focused component when it exposes pasteText,
  falling back to the editor only when no modal target is in focus.
- Image pastes refuse with a status message when a modal Input is
  focused, instead of stuffing a binary blob into the hidden editor.

Regression tests in packages/tui/test/input.test.ts and
packages/coding-agent/test/issue-2127-repro.test.ts pin both the new
TUI/Input contract and the InputController routing source.

Fixes #2127
2026-06-08 20:34:50 +00:00
roboomp be4990b6e0 fix(tui): reset scrollbackHighWater on full repaint inside multiplexers
Inside tmux/screen/zellij, rewinding/branching after a streamed reply had
grown past the viewport permanently anchored the input box to the pane
top: every subsequent frame repainted the visible viewport blank and
parked the cursor at screen row 0.

`requestRender(true, { clearScrollback: true })` dispatches the
`sessionReplace` intent, which re-emits the entire transcript through
`#emitFullPaint` with `options.clearScrollback === false` inside
multiplexers (ED 3 is a no-op in pane history). `#emitFullPaint` only
reset `#scrollbackHighWater` inside the `clearScrollback` branch and
otherwise raised it monotonically (`if (pushedNow > #scrollbackHighWater)`),
so the high-water from the pre-rewind streaming peak survived the
shrink. On the next frame `#planLiveRegionPinnedRender` saw
`#scrollbackHighWater` far above the new natural viewport top, computed
`renderViewportTop` past the actual content, and `#emitLiveRegionPinnedRepaint`
clamped `viewportTop` to `lines.length` — writing eight empty viewport
rows and parking the cursor at screen row 0.

A full repaint with `clearViewport: true` physically re-emits the entire
transcript from row 0, so the rows committed to native scrollback by that
paint are exactly `max(0, lines.length - height)` regardless of whether
ED 3 cleared pre-paint scrollback. Mirror the existing `#maxLinesRendered`
pattern and assign instead of monotonic-max so the renderer's bookkeeping
matches the freshly painted transcript in both mux and non-mux modes.

Fixes #2130
2026-06-08 20:34:33 +00:00
can1357 d0d5b547ff feat(editor): added atomic deletion and metadata display for image and paste markers
- Added atomicTokenPattern support to treat image and paste markers as indivisible tokens, enabling atomic deletion on backspace and forward-delete instead of character-by-character removal.
- Extended image marker format to include pixel dimensions [Image #N, WxH] and updated paste marker format to [Paste #N, +X lines] or [Paste #N, Y chars] with consistent comma-separated metadata.
- Implemented accent styling for paste markers in editor rendering while preserving hyperlinks for image markers, improving visual distinction between marker types.
- Refactored image reference handling to unified placeholder system supporting both image and paste markers with kind-aware rendering callbacks and regex patterns.
2026-06-08 13:34:22 +02:00
can1357 f6b0a690ab perf(tui): optimized bracketed paste processing for large inputs
- Reworked `StdinBuffer` to collect bracketed-paste chunks incrementally and flush them once the end marker is found, while carrying an overlap tail so split markers are still detected.
- Simplified pasted-text sanitization in `Editor` to use a single regex pass that strips control characters while preserving newlines.
- Added tests covering split-marker boundaries and large chunked pastes to verify the new paste assembly behavior.
2026-06-08 13:11:18 +02:00
roboomp b0992a4cf1 fix(tui): absorb mid-paint requestRender into ConPTY settle (#2095)
Codex review on #2101 caught that `ImageBudget.endPass()` (and any other
mid-composition caller) fires `requestRender(false)` synchronously from
*inside* the in-flight paint composition — before `#armPostFullPaintSettle()`
runs at the tail of the intent dispatch. The request slipped past the new
settle gate, set `#renderRequested = true`, and queued a `scheduleImmediate`
that landed in `#scheduleRender` after the paint returned. `#scheduleRender`
then created a `#renderTimer` on the standard 30 Hz throttle (~33 ms), which
fired well inside the 150 ms quiet window the settle was meant to enforce —
defeating the coalescing and reintroducing the cascade.

`#armPostFullPaintSettle` now reclaims the in-flight request: it records
`hadPendingRender = #renderRequested || #renderTimer`, clears both, then —
if a request was reclaimed — eagerly arms the settle's trailing timer at the
full window length so the absorbed request still rides one coalesced render
at the end of the window. Subsequent `requestRender(false)` calls during
the settle see the timer and fold into it via the existing gate.

Added a regression test that drives a sessionReplace through a component
whose `render()` calls `tui.requestRender()` synchronously (mirroring
`endPass()`'s exact shape). The test uses an injected `RenderScheduler`
that records every `scheduleRender(cb, delayMs)` call, then asserts no
short-delay (< 100 ms) timer is queued after the arm — only the settle's
trailing timer at ~150 ms. Verified the test fails without the fix
(records a 32 ms throttled timer) and passes with it (only the settle
trailing timer).
2026-06-08 07:44:27 +00:00
roboomp 98e1eb7e5e fix(tui): cap ConPTY writes by encoded UTF-8 bytes (#2095)
Codex review on #2101 caught that `chunkForConPTY` compared the code-unit
length of the JS string to the cap, while `process.stdout.write(string)`
UTF-8-encodes before `WriteFile`. A CJK transcript row encodes to ~3 bytes
per BMP code unit, so the 16 KiB code-unit cap raised in #2101 could land at
~48 KiB of actual `WriteFile` traffic and reintroduce the #2034 parked-
viewport bug for non-ASCII content.

Renamed the constant to `MAX_CONPTY_WRITE_CHUNK_BYTES`, rewrote the chunker
to walk code units while summing each one's UTF-8 width (1 byte for <0x80,
2 for <0x800, 4 across surrogate-pair pairs, 3 for other BMP and unpaired
surrogates), and switched `#safeWrite`'s gate to `Buffer.byteLength(data,
"utf8")`. Surrogate pairs are kept together so the chunker never produces
an unpaired surrogate that would round-trip as U+FFFD.

Added regression coverage:
- `chunkForConPTY` directly: a CJK payload whose byte length is 4× the cap
  splits into chunks each ≤ cap bytes; a non-BMP emoji-only payload never
  splits a surrogate pair.
- `ProcessTerminal` end-to-end on win32: a 30-row CJK payload whose code-unit
  length fits within 16 KiB but whose UTF-8 length doesn't is still chunked
  (the old code-unit gate would have let this through as one oversized
  `WriteFile`).
2026-06-08 07:23:47 +00:00
roboomp 85d1cf624b style: bun run fix 2026-06-08 07:04:25 +00:00
roboomp e66e1f8049 fix(tui): settle ConPTY after big paints to stop session-resume tail truncation
Resuming a session on Windows ConPTY paints the entire transcript through
#emitFullPaint so the historical rows scroll into native scrollback. WT's
viewport-follow logic gets lossy during that burst: spinner/blink-driven
requestRender(false) calls firing at 30 Hz immediately afterwards each emit
another viewport repaint, the host can't keep up, and the visible tail ends up
parked a few rows above the actual last row until any focus event (Alt+Tab)
forces a host repaint. The drift is non-deterministic (1-15 rows per resume).

Fix: after sessionReplace/historyRebuild/overlayRebuild paints whose lines
overflow the viewport, arm a 150 ms ConPTY settle window keyed on
isConPTYHosted(). Inside the window, non-forced requestRender(false) calls are
coalesced into a single trailing render that fires when the window expires —
the host fully drains the big paint before any new bytes hit the buffer.
Forced renders preempt the settle so user-driven Ctrl+L stays immediate, and
stop() cancels the trailing timer. The first-ever initial paint is exempt:
nothing has been on screen yet so no drift can have accumulated.

Also bumped MAX_CONPTY_WRITE_CHUNK from 8 KiB to 16 KiB. A 3 MB session-resume
paint emits ~192 WriteFile calls instead of ~384, halving the surface area
where WT's viewport tracker can lose the cursor mid-burst, and still well
under the ~32 KiB threshold from #2034 that the original 8 KiB cap defends
against.

Inlined the (now single-caller) isWindowsSubsystemForLinux helper and exported
the new isConPTYHosted predicate that both #safeWrite and the renderer's
#armPostFullPaintSettle hang off, so the WSL/win32 detection stays in one
place.

Fixes #2095
2026-06-08 07:03:47 +00:00
can1357 a37e6c26ca Merge remote-tracking branch 'origin/farm/b7f4bc6d/fix-tmux-resize-viewport-flash' 2026-06-08 06:20:09 +02:00
roboomp 9ec17f51a9 fix(tui): routed forced renders through the multiplexer resize debounce
The earlier guard only suppressed throttled `requestRender(false)`; forced repaints (e.g. `#finishSixelProbe`, image-budget eviction, `resetDisplay`) bypassed it through `requestRender(true)` or the direct `#doRender` path and could still paint into a still-reflowing pane inside the 50 ms settle window — recreating the flash race.

Extracted `#armMultiplexerResizeTimer(clearScrollback)` as the single arm-or-extend point used by the SIGWINCH callback, the deferral branch in `requestRender(true)`, and `resetDisplay()`. Each call cancels any queued throttled render, ORs the callers `clearScrollback` intent into `#deferredForcedClearScrollback`, and re-arms the timer. When the timer fires it consumes the flag and re-enters `requestRender(true, { clearScrollback })`; clearing `#multiplexerResizeTimer` before re-entry lets the deferred call pass straight to `#prepareForcedRender`. `stop()` clears the flag.

Added regression tests for forced `requestRender(true)` and `resetDisplay()` landing inside the debounce window.
2026-06-08 04:04:34 +00:00
roboomp a31f088ffc fix(tui): superseded queued renders during multiplexer resize debounce
A streamed-token tick that landed `requestRender(false)` in the same 30fps frame as the SIGWINCH left `#renderTimer` armed, so the throttled render could fire inside the 50 ms settle window and paint at the new geometry while tmux was still reflowing — the same race the debounce is meant to avoid. The immediate-force path used to clear that timer through `#prepareForcedRender`; the debounce path now does the same when arming, and `#scheduleRender` short-circuits while the multiplexer timer is active so any follow-on `requestRender(false)` is held off until the eventual forced render. Added a regression test covering a queued render + SIGWINCH in the same frame.
2026-06-08 03:57:22 +00:00
can1357 bd079eef6a feat(coding-agent/modes): enabled mouse-hover highlighting for plan review options
- Handled SGR mouse motion reports in `PlanReviewOverlay` to set hovered options from hit-test rows and clear highlights outside option rows.
- Updated option rendering to paint a background band on hovered, non-disabled options while preserving keyboard selection behavior.
- Enabled any-motion mouse tracking for overlays and expanded regression coverage to verify hover tracking setup and teardown.
2026-06-08 05:53:33 +02:00
roboomp 227aaef984 fix(tui): coalesced multiplexer resize events into one settled render
Tmux/screen/zellij send SIGWINCH while the pane is still mid-reflow and fire several events during drag-resize or pane-close animations. Forcing an immediate render on each event raced those mid-reflow paints — the multiplexer overwrote the TUI output and the user saw the viewport flash blank before the next throttled frame.

The SIGWINCH callback now debounces inside multiplexer sessions through a 50 ms render-scheduler timer; subsequent events cancel and re-arm it, so a single forced render fires at the final geometry once the pane is quiet. `#resizeEventPending` is still set on every event so the eventual render classifies as a resize, and stop() cancels the pending timer.

Fixes #2088
2026-06-08 03:50:50 +00:00
can1357 46578e57ba fix(tui): fixed handling of split DEC 2048 in-band resize reports
- Added a reassembly buffer for DEC 2048 in-band resize reports split across stdin reads.
- Dropped invalid or stale partial resize sequences to prevent leaked trailing bytes from entering terminal input.
- Added tests that verify split resize reports are handled and split kitty key sequences are forwarded correctly.
2026-06-08 04:42:26 +02:00
can1357 733d1dabb1 fix(tui): triggered viewport repaint when focus changes
- Tracked focus transitions in TUI and flagged renders after focus changes to allow unknown viewport mutations.
- Used the new focus-change flag when deciding explicit viewport mutation so subsequent frames repaint even when the terminal lacks a viewport-oracle position.
- Added a regression test covering menu teardown focus changes on unknown-viewport terminals with eager scrollback risk.
2026-06-08 02:45:16 +02:00
can1357 a3efb62998 feat(coding-agent/modes): added canonical key matching behavior and exported alias utilities
- Built canonical and alias key maps for action/custom handlers and rebuilt them on changes.
- Preserved the first handler when custom key alias collisions occurred during rebuilds.
- Exported canonicalKeyId and addKeyAliases from keybindings for external use.
2026-06-08 02:29:30 +02:00
can1357 6d77bafe84 fix(tui): suppressed redundant cursor writes during no-op terminal rerenders
- Added hardware cursor state caching to reuse last known position and visibility.
- Updated cursor flow to preserve target row/column/visibility across rerenders.
- Reworked cursor output emission to skip unchanged cursor moves and hide/show writes.
2026-06-08 02:22:18 +02:00
can1357 078dac7ea8 fix(tui): skipped loader render requests when text unchanged
- Returned a changed flag from setText to gate render requests.
- Avoided redundant renders when composed loader text matches prior frame.
2026-06-08 02:15:24 +02:00
can1357 a89bb76444 fix(tui): preserved visible ANSI content when truncating terminal rows
- Replaced the TUI row truncation path with per-character and ANSI-sequence iteration that tracks visible cells before clipping output.
- Added ANSI helpers to parse sequence boundaries and preserve OSC66 visible payloads while enforcing max source length during truncation.
- Added natives regression coverage for Ghostty super+alt backspace key matching and parsing.
2026-06-08 01:22:26 +02:00
can1357 d6830b4b8b fix(packages/tui): resolved width-based truncation with issue-2045 test
- Switched long-row truncation to visible-cell width, preserving suffixes after zero-width prefixes.
- Added issue-2045 regression test in issue-2045-repro.test.ts for combining-prefix truncation.
2026-06-08 01:09:47 +02:00
Can BölükandGitHub 6ef7548fcb Merge branch 'main' into farm/9483b02b/fix-alt-backspace-kitty-ghostty 2026-06-08 00:45:21 +02:00
can1357 4f085de299 Merge remote-tracking branch 'origin/farm/cc1fae18/chunk-large-writes-conpty' 2026-06-08 00:30:45 +02:00
can1357 988b51ffad Merge remote-tracking branch 'origin/farm/da3d94d7/fix-deferred-shrink-viewport-clears' 2026-06-08 00:30:11 +02:00
can1357 d3290c3ee5 Merge remote-tracking branch 'origin/farm/e60e06f3/bound-tui-line-fitting' 2026-06-08 00:29:59 +02:00
roboomp 5bfad210a6 fix(tui): recognized Ghostty's super+alt+backspace as word delete
Ghostty on macOS reports Option+Backspace as kitty modifier 11
(wire) = 10 (mask) = super(8) | alt(2). The native key matcher only
recognised shift/ctrl/alt, so:

- format_kitty_key in crates/pi-natives/src/keys.rs rejected any
  sequence whose effective modifier had bits beyond shift/ctrl/alt
  set; parseKey returned undefined for \x1b[127;11u.
- parse_key_id recognised only those three modifiers, so even an
  explicit 'super+alt+backspace' degraded to 'alt+backspace' and
  could not match an actual super+alt encoded press.
- The editor's matchesKey(data, 'alt+backspace') branch was never
  reached and Option+Backspace either no-op'd or fell through to
  single-character delete.

Added a MOD_SUPER constant, taught parse_key_id, format_with_mods,
and format_kitty_key about the super bit, added super+alt+backspace
(and super+alt+delete / super+alt+d) to tui.editor.deleteWord*
defaults, and broadened the editor's direct matchesKey calls to
accept the super+alt variants. Existing alt+backspace / alt+d /
alt+delete paths are untouched, so the legacy ESC+DEL workaround
keeps working too.

Added Rust unit tests for the super modifier (positive and
negative for hyper/meta sequences we still drop) and TS
regressions in keys.test.ts and editor.test.ts that exercise
\x1b[127;11u end-to-end.

Fixes #2064
2026-06-07 16:17:29 +00:00
roboomp 4053fadf71 fix(tui): preserved osc66 spans in bounded fitting
Detected OSC 66 text-sizing sequences in the bounded line fitter and included them whole so payloads (e.g. Markdown headings) are not skipped as zero-width.

Added a regression assertion that OSC 66 visible cells survive the bound when the row begins with the sized span.
2026-06-07 11:26:09 +00:00
roboomp d78e29e17a fix(tui): preserved text after long ansi prefixes
Scanned oversized render rows by ANSI sequence so bounded line fitting skips leading zero-width control spans without dropping subsequent visible cells.

Extended issue #2045 regression coverage to assert visible text survives long SGR and OSC hyperlink prefixes.
2026-06-07 11:20:41 +00:00
roboomp ee7e1e3ae3 fix(tui): preserved tmux viewport on offscreen shrink
Skipped repainting tmux panes when an offscreen shrink leaves the visible tail unchanged, and added regression coverage for the no-content-bytes contract.

Fixes #2046
2026-06-07 11:14:29 +00:00
roboomp c3c4c2749d fix(tui): bounded oversized render rows
Bounded the raw row span passed into TUI line fitting so ANSI-heavy subagent output cannot grow terminal write buffers independently of viewport width.

Added issue #2045 regression coverage for pathological zero-width ANSI rows.

Fixes #2045
2026-06-07 11:14:02 +00:00
roboomp d5183a14f9 fix(tui): honored kitty reply when DA1 sentinel arrives first
The kitty keyboard progressive-enhancement probe sends `CSI ? u CSI c`
and used the DA1 reply as a fallback sentinel: if no kitty reply arrived
the modifyOtherKeys fallback was engaged. The kitty handler then refused
to enable kitty whenever modifyOtherKeysActive was already true, which
silently dropped the reply on terminals that answer DA1 before CSI ? u
(Superset / xterm-on-Electron). The protocol never enabled, so
Shift+Enter was delivered as a bare \r and the input box submitted.

Make the kitty reply authoritative regardless of ordering: drop the
`!modifyOtherKeysActive` guard, and if the premature DA1 already engaged
the modifyOtherKeys fallback, send `CSI > 4 ; 0 m` to disable it before
pushing the kitty stack frame. Kitty is strictly preferred (per the
existing #3259-fallback comment), so this is a clean cutover.

Add a regression test that drives a real ProcessTerminal through the
existing render harness across three orderings: kitty-then-DA1 (control),
DA1-then-kitty (#2042 regression), and DA1-only (modifyOtherKeys
fallback stays).

Fixes #2042
2026-06-07 10:41:31 +00:00
roboomp 271b9ed620 fix(tui): extended ConPTY chunking gate to WSL
WSL processes report process.platform === 'linux', but their stdout still
funnels through wslhost into the same Windows ConPTY pipe, so the >32 KB
viewport-tracking regression reproduces there as well. The gate now fires
whenever process.platform === 'win32' OR isWindowsSubsystemForLinux()
detects WSL_DISTRO_NAME/WSL_INTEROP. Genuine POSIX terminals keep the
single-write fast path.

Added a WSL-specific test alongside the existing win32 / clean-linux
cases to lock the broader gate.

Fixes #2034
2026-06-07 08:54:07 +00:00
roboomp 1c2e22068a fix(tui): chunked oversized writes on Windows ConPTY so viewport tracks the cursor
Windows ConPTY ties viewport tracking to per-WriteFile boundaries: when a
single process.stdout.write exceeds ~32-64 KB, the pseudo-console stops
following the cursor and the host UI's scroll position stays parked at
wherever the write began. Full paints (resume, history rebuild, large
permission dialogs) showed only the first screenful until Alt+Tab forced
the host to re-query the cursor; the bytes always landed in scrollback.

ProcessTerminal#safeWrite now routes win32 writes through a new
chunkForConPTY helper that splits oversized buffers into <=8 KiB pieces
at \n boundaries, keeping every WriteFile well below the threshold and
escape sequences intact (TUI paints separate rows with \r\n, so a line
break almost always exists in range). Non-win32 platforms keep the
single-write fast path.

Fixes #2034
2026-06-07 08:49:15 +00:00
can1357 a40f01c1e9 fix(tui): fixed no-append live-region repaints during viewport shifts
- Constrained the no-append repaint optimization to execute only when the logical viewport position was unchanged.
- Documented the live-region pinned repaint cursor re-anchoring fix in the TUI changelog entry.
2026-06-07 08:50:14 +02:00
can1357 8543dcaee4 fix(tui): fixed uppercase Shift preservation for printable keybindings
- Updated key canonicalization in keybinding parsing to parse modifiers case-insensitively and infer `shift` from printable uppercase keys.
- Added regression tests for `shift+a` matching uppercase `A` and for cursor anchoring during no-append live repaint scenarios.
- Recorded the uppercase Shift keybinding fix in the package changelog entry.
2026-06-07 08:49:12 +02:00
can1357 9d80a7d5c6 perf(packages/tui): optimized tui core by skipping redundant work paths
- Added animated message-color gating so loader timers run at 30fps only when needed.
- Added no-op checks in loader and text updates to skip work for unchanged values.
- Optimized key matching by canonicalizing key IDs and precomputing alias sets.
- Added prepared-line caching and bounded terminal/width scans to reduce repaint overhead.
2026-06-07 08:42:51 +02:00
can1357 76af35dcd6 fix(tui): preserved TUI initial scrollback unless clear was requested
- Updated the initial render intent to carry a `clearScrollback` flag.
- Adjusted first-frame rendering logic to preserve existing scrollback by default and clear only when requested.
- Added `resolver` stubs to coding-agent and web-search test registries for interface compatibility.
2026-06-07 07:54:50 +02:00
can1357 becc44ccef fix(tui): fixed startup scrollback clearing and ED3 streaming replay duplication
- Added a clear-initial-terminal-history flag to interactive mode initialization and passed it from cold-launch startup.
- Extended TUI.start to accept clearScrollback and clear native scrollback on initial full paint for non-multiplexer sessions.
- Preserved ED3 streaming scrollback accounting during capped streaming frames and added tests to prevent committed prefix duplication across terminal history rewrites.
2026-06-07 07:42:51 +02:00
can1357 e78352abc7 fix(tui): fixed live viewport anchoring during repaint updates
- Replaced the repaint viewport clamp with strict bottom anchoring at lines.length - height.
- Removed the anti-duplication clamping path that could persist off-tail #viewportTopRow values between frames.
- Kept native scrollback cleanup to the existing dirty checkpoint mechanism for stale committed prefixes.
2026-06-07 07:36:08 +02:00
can1357 4a3984eb0a fix(coding-agent/tools): fixed Markdown cache contamination from mutable render arrays
- Marked Markdown cached lines as readonly snapshots and returned copied arrays from `render()`.
- Stored fresh snapshot arrays in both L1 and L2 caches so callers appending rows cannot mutate shared cached Markdown output.
- Added an AskTool test that renders calls and results twice to verify repeated renders remain stable and do not duplicate option labels.
2026-06-07 07:33:43 +02:00
can1357 1a9d898b8a feat(coding-agent): added immediate steering flush on empty streaming submit
- Updated InputController streaming submit handling to interrupt and resume when queued steering exists, refreshing the pending-message UI and render.
- Added AgentSession.interruptAndFlushQueuedMessages to abort active work and continue processing queued messages immediately.
- Added tests for queued steering interruption in both agent-session concurrency and input-controller keybinding flows.
2026-06-07 07:18:02 +02:00
can1357 6adf98fd10 feat(coding-agent): added OSC5522 enhanced paste support for local image and text inserts
- Added OSC5522 parsing and a payload controller that prefers image MIME and reassembles chunks.
- Added bracketed image-path detection and `onPasteImagePath` callback wiring in the editor.
- Added local image-path paste insertion with fallback to text paste on image-size errors.
- Added `Editor.pasteText()` and OSC5522 disable sequencing on terminal cleanup.
2026-06-07 06:43:52 +02:00
can1357 021b621865 perf(tui): removed per-frame line-fit cache from tui render path
- Removed the per-frame `#fitLineCache` in `TUI` rendering to avoid rope-string hashing during repaints.
- Deleted `packages/tui/bench/width.ts`, retiring an obsolete visibleWidth benchmark script.
2026-06-07 06:26:49 +02:00
can1357 705c937c56 fix(tui): fixed visibleWidth parity and OSC66 width metadata behavior
- Fixed `visibleWidth` parity with native width using `Bun.stringWidth` ANSI stripping.
- Fixed tab and OSC66 width parsing with `s=`/`w=` metadata handling.
- Added ASCII and long-string fast paths in `visibleWidth` for faster width checks.
- Consolidated visible-width regression coverage by replacing removed IME/Arabic/Jamo tests with `visible-width.test.ts`.
2026-06-07 06:24:07 +02:00
can1357 d9f09a07b7 fix(tui): prevented committed-prefix duplication on viewport repaint
- Clamped repaint anchor to min(firstChanged, scrollbackHighWater) so byte-identical committed rows are not re-painted into the active grid.
- Added submitPinsViewportToTail capability to reconcile native scrollback at submit on unprobeable local terminals.
- Renamed allowUnknownViewportMutation to allowUnknownViewportTransientRepaint to clarify it never authorizes a settled transcript commit.
2026-06-07 06:01:25 +02:00
can1357 1ebe2c7484 feat(packages/tui-and-shared-tooling-primitives): added TUI terminal support
- Made `Component.invalidate` optional in `packages/tui/src/tui.ts` and callsites.
- Added `iconOverride` support to status-line rendering in `status-line.ts`.
- Added `borderColor` and `framedBlock()` primitives in `output-block.ts` for framed output styling.
- Adjusted `renderOutputBlock()` to draw a continuous border when no header text is present.
- Added terminal checkpoint-reconciliation tests in `submit-checkpoint-reconcile.test.ts` for pinned-tail behavior.
2026-06-07 05:13:58 +02:00
can1357 66583b414b fix(tui): allowed checkpoint replay when submit-scroll pins viewport to tail
- Added terminal detection for submit-key actions that pin the viewport to tail, and added a test override for that capability.
- Converted TERMINAL into a clone-based RuntimeTerminal so mutable capability fields can be updated directly by runtime setters.
- Updated checkpoint replay gating to allow ED3 rebuild when viewport position is unprobeable but submit-pin behavior is reported true.
2026-06-07 05:08:44 +02:00
can1357 9a8bf1c81c feat(coding-agent): added mouse interactions and preserved deleted headings in plan reviews
- Added SGR mouse input handling for wheel and click actions, mapped to option, ToC, and body targets.
- Added deleted-heading state to undo snapshots so removed titles persist and restore correctly.
- Updated the plan-mode active prompt to require decision-complete, implementation-ready plans.
- Enabled mouse tracking for fullscreen overlays and asserted mouse-on/off sequences in tests.
2026-06-07 04:53:59 +02:00
can1357 fceff5e6b6 feat(coding-agent): overhauled plan-review overlay with TOC sidebar
- Added section-based plan parsing with per-section delete, undo, and annotate.
- Added Tab/Shift+Tab focus regions and per-line scroll navigation.
- Added Refine feedback loop emitting annotations back to the model.
- Added `OverlayOptions.fullscreen` borrowing the terminal's alt screen buffer.
2026-06-07 04:32:52 +02:00