19 Commits

Author SHA1 Message Date
roboomp e6b3e1acf0 fix(tui): buffered split raw paste bursts before classifying
Raw unbracketed paste data may span adjacent stdin reads. The prior call-local check drained a first chunk such as "line 1\r" before later lines could classify the burst, leaving the original per-line submit bug.

- Held ESC-free break-bearing input in a fixed 10 ms classification window and appended adjacent raw reads before classification.
- Coalesced candidates after two completed logical line breaks; replayed ambiguous candidates unchanged through the normal per-key path on expiry or before escape-bearing input.
- Cleared and exposed pending candidates through the existing flush, clear, getBuffer, and destroy lifecycle.
- Added regressions for split reads, a break-only boundary, and delayed replay of ordinary Enter input.

Fixes #5841
2026-07-17 13:26:38 +00:00
roboomp bacad80b56 fix(tui): require 3+ lines to coalesce a raw paste burst
The single-interior-break heuristic misclassified a single Enter batched with a following keystroke in one stdin read ("a\rb") as a paste, swallowing the submit. Terminal read boundaries are not key boundaries.

- Tightened RAW_MULTILINE_BURST to content-break-content-break-content so only 3+ line bursts (two interior break runs; CRLF counts as one) coalesce; a single Enter can never produce two interior breaks.
- Added regression tests for CRLF three-line bursts, a batched single Enter, and an ambiguous two-line burst staying on the key path.

Fixes #5841
2026-07-17 13:16:39 +00:00
roboomp 838fbca3d1 fix(tui): coalesced raw multiline paste bursts without bracketed markers
- Detected an ESC-free stdin burst with an interior CR/LF in StdinBuffer.process and routed it through the paste channel instead of per-key splitting, so a Cmd+V multiline block whose terminal omitted the \x1b[200~…\x1b[201~ markers no longer fires one submit per line.
- Guarded to ESC-free buffers so real bracketed pastes, CSI keys, and mouse reports keep their path; a lone Enter, a single trailing newline, and a run of bare Enters stay normal keypresses.
- Added regression tests for the raw CR/LF burst, single Enter, trailing Enter, bare-Enter run, single-line burst, and escape-bearing chunk.

Fixes #5841
2026-07-17 13:07:57 +00:00
can1357 d4182835dc fix(tui): bounded escape sequence scanning to chunk limits
- Replaced unbounded `String#indexOf` lookups with structured byte loops to enforce `MAX_STRING_SEQ_BYTES` cap.
- Stopped unterminated OSC, DCS, and APC sequences from scanning past their limits and blocking the event loop.
- Maintained a one-byte overlap when checking chunk boundaries to ensure split escape terminators are correctly reassembled.
- Added comprehensive unit tests covering split boundary terminators and oversized payload scenarios.
2026-07-02 01:51:09 +02:00
can1357 cfdd369d9b fix(tui): reset stdin escape hint before paste 2026-07-01 21:53:17 +02:00
roboomp 089db21333 fix(tui): bound terminal input parsing so malformed sequences and non-bracketed pastes do not block the event loop
Three failure modes shared the same root cause on the StdinBuffer.process
hot path — unbounded synchronous work in a single call.

- `StdinBuffer.extractCompleteSequences` grew each escape candidate one code
  unit at a time and re-tested every prefix, so a malformed CSI/OSC/DCS/APC
  streamed across chunks re-inspected the accumulated buffer on every
  `process()` call (issue A). Replaced the grow-and-recheck loop with a
  single linear scan bounded by a per-type cap (CSI 4 KiB, OSC/DCS/APC 16
  MiB) and a resume-search offset carried on `StdinBuffer` so a legit
  chunked OSC 5522 image paste stays O(total) instead of O(total²).
- `BracketedPasteHandler` had no cap or timeout: a caller that bypassed
  StdinBuffer (fed the start marker with no end marker) grew `#buffer`
  forever (issue B, defense in depth). Added an optional `byteLimit` option
  (default 64 MiB) that aborts paste mode and delivers accumulated bytes as
  `pasteContent`, mirroring `StdinBuffer#abortPaste`.
- The `ProcessTerminal` data handler ran six always-executed escape-probe
  regex tests per `data` event; a 100 KB non-bracketed paste (terminal
  without DEC 2004 support) turned into ~600K regex executions (issue C).
  Added a fast path that forwards non-ESC sequences straight to the input
  handler when no reassembly buffer is holding state.

Verified with new stdin-buffer + bracketed-paste tests plus empirical
repros: a streamed 1 MiB malformed CSI drops from ~4300 ms to ~50 ms of
synchronous work, and a 1 MiB chunked OSC 5522 payload drops from ~6800 ms
to ~25 ms.

Fixes #4073
2026-07-01 07:19:28 +00:00
roboomp c2d6c8cf18 fix(tui): deliver buffered double-Esc as two events and re-arm loader after task completion
StdinBuffer held a bare `\x1b\x1b` chunk and timer-flushed it as one
sequence. `parseKey("\x1b\x1b")` returns undefined, so CustomEditor
fell through to the base editor and never fired the configured `onEscape` —
the double-escape gesture and the second-press single-Esc handler both went
dead whenever the terminal batched the two presses into one stdin read.

Split an exact bare `\x1b\x1b` into two ESC events only after the
flush window proves no follower arrived. If a follower does arrive, emit the
first ESC and restart parsing at the second ESC so legacy Alt chords
(`\x1bd`, `\x1b\x7f`) remain one downstream keypress. Meta-CSI/SS3
chords (`\x1b\x1b[A`, `\x1b\x1bO…`) still emit as one combined
sequence.

EventController.tool_execution_update re-armed the working loader when a
transient overlay (auto-compaction / auto-retry / handoff) had torn it down
mid-tool; tool_execution_end did not. A subagent (`task`) call only fires
_end, so a task result landing after such an overlay left the UI looking
idle even though the session was still streaming. Mirror the reconciler
call in #handleToolExecutionEnd.

Fixes #3857
2026-06-30 04:30:42 +00:00
roboomp 64734021a7 fix(tui): deliver buffered double-Esc as two events and re-arm loader after task completion
StdinBuffer held a bare `\x1b\x1b` chunk (or emitted it as one when followed by
a non-CSI byte). `parseKey("\x1b\x1b")` returns undefined, so CustomEditor
fell through to the base editor and never fired the configured `onEscape` —
the double-escape gesture and the second-press single-Esc handler both went
dead whenever the terminal batched the two presses into one stdin read.

Split a bare `\x1b\x1b` into two ESC events at the buffer layer, mirroring
the existing split for ESC + SGR mouse report. Meta-CSI/SS3 chords
(`\x1b\x1b[A`, `\x1b\x1bO…`) still emit as one combined sequence.

EventController.tool_execution_update re-armed the working loader when a
transient overlay (auto-compaction / auto-retry / handoff) had torn it down
mid-tool; tool_execution_end did not. A subagent (`task`) call only fires
_end, so a task result landing after such an overlay left the UI looking
idle even though the session was still streaming. Mirror the reconciler
call in #handleToolExecutionEnd.

Fixes #3857
2026-06-30 03:41:24 +00:00
can1357 15a4047785 fix(tui): fixed kitty mode reapply and double-ESC parsing across alt-screen
- Stored the active kitty keyboard enable sequence in the terminal and re-applied it after entering the alternate screen, then emitted `\x1b[<u` before leaving the alternate screen.
- Updated StdinBuffer parsing so `\x1b\x1b` waits for CSI/SS3 followers, splits `\x1b\x1b[` SGR mouse reports into a standalone Esc plus report, and reduced `PARTIAL_HOLD_MAX_MS` to 150ms.
2026-06-12 04:25:14 +02:00
can1357 197f374801 fix(coding-agent): improved tree gutter anchoring & fixed tests
- Ensured flattened chain rows under a `└─` branch are anchored by a vertical line one level right of the suppressed gutter (below the branch head's content), never in the `└─` corner column itself, resolving visual issues #2298 and #2325.

Fixes #2325.
2026-06-12 03:57:09 +02:00
can1357 3fd0dc3b58 feat(tui): added bounded holding for split escape partials in StdinBuffer
- Added `partialHoldTimeout` to `StdinBufferOptions` to configure extra wait for dangling partial escapes.
- Reworked `StdinBuffer` flush scheduling to defer emits and avoid stale partials merging with new escapes.
- Held SGR mouse and kitty partial escapes until cap expiry to prevent tail bytes leaking as typed text.
2026-06-12 03:27:50 +02:00
can1357 2d00ac3e45 fix(tui): restored terminal state on crash and unwedged paste-mode input
emergency restore leaves the alt screen and disables mouse tracking; bracketed paste gets an inactivity watchdog and byte cap so a lost end marker cannot eat input forever; split-escape flush window raised to 50ms; kitty printable dedup expires; resetDisplay repaints on the alt screen; input scanning is index-based instead of O(n^2) slicing; appearance poll no longer clears selection every 2s.
2026-06-10 01:28:23 +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
can1357 61f11a6ce0 fix(tui): blocked destructive scrollback replay on unknown terminal viewports
- Blocked checkpoint scrollback replay unless native viewport-at-bottom proof succeeded.
- Expanded ED3-risk terminal detection to include SSH, multiplexer, WT-like, and unknown profiles.
- Added sync-output defaults with PI_TUI_SYNC_OUTPUT and PI_FORCE_SYNC_OUTPUT overrides.
- Deferred transcript thawing until native scrollback refresh returned true.
- Adjusted non-escape parsing to emit one Unicode scalar and raised timeout to 75ms.
2026-06-05 17:47:47 +02:00
can1357 975941aba4 chore: remove garbage tests 2026-05-12 04:09:33 +02:00
can1357 fcbfab765b refactor: consolidated module import paths
- Removed custom TypeScript path aliases and the associated transformation script.
- Replaced aliased imports with direct `@oh-my-pi` package imports for improved clarity.
- Simplified internal module references by removing explicit `/index` suffixes.
2026-01-23 21:01:13 +01:00
can1357 7b5af2dd9f refactor(build): migrated imports to path aliases with per-package tsconfig support
- converted relative imports to path aliases ($c/*, $ai/*, $tui/*, etc.) across all packages
- added per-package tsconfig.json with complete path mappings for runtime resolution
- set importModuleSpecifier to non-relative for IDE auto-import preferences
- updated dev script to run from monorepo root for consistent path resolution
2026-01-23 11:56:32 +01:00
can1357 b2c94bef1a refactor(deps): restructured codebase to use centralized pi-utils package
- Created new @oh-my-pi/pi-utils workspace package with shared utilities for logging, process management, stream handling, and temporary directory management.
- Migrated all packages to use centralized logger from @oh-my-pi/pi-utils instead of local winston implementations.
- Replaced custom process spawning and stream reading implementations with standardized cspawn and readLines utilities across all modules.
- Converted synchronous file operations and process spawning to async patterns using Bun shell syntax and fs/promises.
- Added streaming edit abort functionality with configurable setting to abort on patch preview failures.
- Updated test framework from vitest to bun:test across all test suites.
2026-01-20 05:33:21 +01:00
can1357 100061accb chore: merged in upstream changes
- Added ExtensionRuntime shared state with async extension factory support
- Introduced pluggable tool operations (BashOperations, FileOperations, etc.) for remote execution
- Added new CLI flags: --no-tools, --no-extensions, --no-skills
- Added login dialog, countdown timer, and component barrel export
- Added blockImages and thinkingBudgets settings
- Refactored stdin handling with StdinBuffer for improved key parsing
- Updated DEVELOPMENT.md with new architecture documentation
2026-01-10 06:32:51 +01:00