Commit Graph

79 Commits

Author SHA1 Message Date
roboomp caace28295 fix(tui): honor tui.input.submit remap onto ctrl+enter
The multiline editor's key dispatch checked a hardcoded Ctrl/Shift+Enter
-> newline branch before the config-driven tui.input.submit branch, so a
user remap of submit onto Ctrl+Enter was swallowed as a newline and never
submitted. Gate the hardcoded newline fallbacks behind an explicit submit
binding; the bare-LF (iTerm2 Shift+Enter) case stays exempt because its
canonical form is indistinguishable from plain Enter.

Fixes #8906
2026-08-19 09:10:10 +00:00
can1357 adfa211bbf chore: bump version to 17.3.7
Retry: deflaked tui IME preedit border test (#5563) — VirtualTerminal.waitForRender's fixed 40ms sleep raced TUI's throttled render timer on starved CI runners, reading the pre-input frame; waitForRender now takes an optional settle predicate polled up to 2s and the test keys on the rendered content.
2026-08-18 11:24:23 +03:00
Shawn Koh c948d7a289 fix(tui): preserve lazy border render contract
Amp-Thread-ID: https://ampcode.com/threads/T-019ff125-f53d-739b-8462-6027fe70f914
Co-authored-by: Amp <amp@ampcode.com>
2026-08-12 15:56:33 +08:00
Shawn Koh 92b544b342 fix(tui): track editor border visibility revisions
Amp-Thread-ID: https://ampcode.com/threads/T-019ff125-f53d-739b-8462-6027fe70f914
Co-authored-by: Amp <amp@ampcode.com>
2026-08-12 15:40:26 +08:00
Shawn Koh 3285e6cf5b fix(tui): track lazy editor border revisions
Amp-Thread-ID: https://ampcode.com/threads/T-019ff125-f53d-739b-8462-6027fe70f914
Co-authored-by: Amp <amp@ampcode.com>
2026-08-12 15:26:22 +08:00
Shawn Koh ab6d04f121 fix(tui): track editor cursor layout revisions
Amp-Thread-ID: https://ampcode.com/threads/T-019ff125-f53d-739b-8462-6027fe70f914
Co-authored-by: Amp <amp@ampcode.com>
2026-08-12 15:11:44 +08:00
Shawn Koh 3c26443405 fix(tui): track editor height epochs
Amp-Thread-ID: https://ampcode.com/threads/T-019ff125-f53d-739b-8462-6027fe70f914
Co-authored-by: Amp <amp@ampcode.com>
2026-08-12 13:25:36 +08:00
Shawn Koh 8751a9571f fix(tui): preserve live output across width epochs
Amp-Thread-ID: https://ampcode.com/threads/T-019ff125-f53d-739b-8462-6027fe70f914
Co-authored-by: Amp <amp@ampcode.com>
2026-08-12 10:14:42 +08:00
Shawn Koh ef9fb774f6 fix(tui): propagate width-epoch content revisions
Amp-Thread-ID: https://ampcode.com/threads/T-019ff125-f53d-739b-8462-6027fe70f914
Co-authored-by: Amp <amp@ampcode.com>
2026-08-12 10:14:42 +08: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 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
roboomp 81e977d8f2 fix(tui): restored slash description wrapping
Re-enabled wrapped descriptions for slash-command autocomplete while preserving the existing visual-row popup budget.

Replaced the compact-row regression with an editor-level contract that proves long description tails remain visible.

Fixes #5848
2026-07-17 14:22:00 +00:00
can1357 f39c1ce103 merged PR #5412: fix(tui): anchor IME candidates in interactive inputs 2026-07-16 03:32:04 +02:00
can1357 f3aad14ee9 fix(tui): restored compact editor border by default
- Restored the compact two-row `Editor` layout by default and gated the dedicated IME-safe bottom border behind `setImeSafeCursorLayout()`.
- Added `tui.imeSafeCursor` as an opt-in appearance setting and applied it to initial and replacement editors.
- Added regression coverage for compact default rendering while retaining terminal-local IME preedit protection.
- Updated the TUI changelog for the opt-in compatibility layout.
2026-07-15 13:16:07 +02:00
roboomp d1c98e9607 fix(tui): protected editor border from IME preedit
- Kept the focused hardware-cursor row empty to its right so terminal-local marked text cannot shift box chrome.

- Rendered a dedicated bottom border and covered terminal insert-mode preedit with a regression test.

Fixes #5563
2026-07-15 10:40:41 +00:00
can1357 888d7e06c1 Merge PR #5512: fix(tui): stop PageUp/PageDown stepping prompt history at idle (@roboomp) 2026-07-14 23:11:10 +02:00
can1357 feb960da6f Merge PR #5487: fix(tui): classify underscore as word char for Ctrl+W (@roboomp) 2026-07-14 22:58:45 +02:00
roboomp 5a32d79d81 fix(tui): stop PageUp/PageDown stepping prompt history at idle
PageUp/PageDown on an empty editor aliased to prompt-history navigation, identical to Up/Down, so idle page keys loaded the previous prompt instead of paging. They now only page the editor viewport (a no-op on a short draft) and never touch prompt history.

Fixes #4754
2026-07-14 19:55:16 +00:00
roboomp 1626799f3d fix(tui): classified underscore as word char for ctrl+w
`getWordNavKind` ran the `\p{P}` punctuation test before the `_` check, so
underscore (Unicode `Pc`) returned "delimiter" and `moveWordLeft` stopped at
each underscore. Ctrl+W deleted only the trailing segment of snake_case
identifiers. Reordered the underscore check ahead of the punctuation/symbol
test and removed the dead `ch === "_"` branch.

Fixes #4776
2026-07-14 18:50:43 +00:00
roboomp e9dc1616b7 fix(tui): anchored IME cursors in interactive inputs
Preserved the editor cursor marker under autocomplete and forwarded dialog focus into Other-response editors.

Fixes #4760
2026-07-14 16:01:09 +00:00
roboomp 58336f5886 fix(tui): stopped slash tab from reopening files
Prevented no-argument slash commands from falling through to empty-prefix file suggestions immediately after Tab completion.

Added an editor regression covering /quit Tab completion with files present in the project root.

Fixes #4808
2026-07-09 18:27:21 +02:00
can1357 10745dfc94 fix(tui): tighten slash autocomplete test 2026-07-08 15:19:40 +02:00
Christian Stewart b9a12a815f fix(tui): keep autocomplete row fix behavior-preserving
Signed-off-by: Christian Stewart <christian@aperture.us>
2026-07-05 17:27:35 -07:00
Christian Stewart 22279bc03b fix: cramped slash autocomplete menu 2026-07-05 16:45:35 -07:00
roboomp 040846336d fix(tui): shrunk bordered Editor right chrome by cursor overflow cells, not a 1-cell boolean
When the end-of-line cursor glyph lands past a wide trailing grapheme
(CJK comma ',', emoji, etc.), the inverse-video cursor extends past
'lineContentWidth' by the full overflow. Prior cursorInPadding logic
dropped exactly one space of right padding but always re-emitted '─'
before '╯', so 'paddingX=1' overflow=1 stayed 1 cell too wide and
'paddingX=2' overflow=2 stayed 2 cells too wide. In the user's
terminal the '╯' wrapped to a new row, looking like the editor box
collapsed onto two lines.

Replaced the boolean with the actual overflow cell count and recomputed
the right chrome budget (padding + optional '─' + corner) from
'paddingX + 1 - overflow', floored at 1 so the corner/vertical bar
survives. The else branch (non-last lines) follows the same budget for
symmetry.

Fixes #3431
2026-06-25 03:34:24 +00:00
can1357 3998d95088 feat: standardized tab expansion to fixed width
- Removed configurable tab width support and the `display.tabWidth` setting across all packages.
- Deleted obsolete utility functions `getIndentation`, `getIndentationNoescape`, and `setDefaultTabWidth`.
- Standardized tab expansion logic to use a fixed `DEFAULT_TAB_WIDTH` globally.
- Cleaned up related configuration schemas, test suites, and internal API signatures to remove path-dependency.
2026-06-19 04:48:41 +02:00
can1357 746efd0c29 fix(tui): resolved leak of re-encoded control bytes during bracketed paste
- Centralized decoding of tmux's `extended-keys` control sequences (`csi-u` and `xterm` formats) into a shared utility.
- Updated the multi-line editor and single-line input to use this decoder, preventing escape tails (e.g., `[27;5;106~`) from leaking into the text buffer.
- Added parity to the single-line input by explicitly stripping any remaining control characters after decoding to ensure data integrity.
2026-06-18 19:54:22 +02:00
0xff26b9a8 d4b01d3a48 fix(tui): handle VS Code keypad digit input 2026-06-16 18:55:23 +08:00
can1357 b830f7912b feat: unified speech setup and introduced local STT/TTS capabilities
- Added unified `omp setup speech` flow with JSON/check modes and model picker.
- Added local STT pipeline with sherpa workers, recorder/download flow, and streaming inference.
- Added local TTS pipeline with `omp say`, backend selection, and streaming vocalization.
- Replaced legacy speech settings with unified `speech`/`speechgen` configuration keys.
2026-06-14 16:07:44 +02:00
can1357 c4ef8839d5 feat(tui): added large-paste collapse flow with threshold and attachment fallback
- Added `paste.largeMenuThreshold` setting with default 100 and values 0/100/250/500/1000.
- Added `Editor.onLargePaste` and `Editor.insertPaste` to intercept oversized pastes and expand markers.
- Added threshold-based large-paste routing to show the menu and wrap oversized content for code/XML or attachment fallback.
- Added tests for large-paste interception, fallback behavior, short-paste handling, and marker expansion.
2026-06-14 05:20:55 +02:00
roboomp 784fd56505 fix(magic-keywords): glow keywords through the cursor seam and animate a focused shimmer
The whole-line editor decorate ran on `displayText` after the editor appended
the zero-width CURSOR_MARKER and cursor glyph; both start with ESC, so the
magic-keyword regex's right-boundary `(?!\S)` rejected `ultrathink` glued to
the marker and dropped the gradient until a trailing character was typed.

- `Editor.#decorate` (pi-tui) now splits around CURSOR_MARKER and decorates
  each user-text segment independently, so word-boundary lookarounds resolve
  correctly on both sides; the matching comment block is corrected.
- `KeywordHighlighter` / `highlightMagicKeywords` gain an optional `phase` in
  [0, 1) that cyclically rotates the gradient stops. `0` (default) yields
  the static palette, so sent-bubble rendering is unaffected.
- `CustomEditor.decorateText` derives `phase` from `Date.now()` and chains
  `setTimeout(SHIMMER_FRAME_MS)` ticks while focused, the buffer holds a
  magic keyword, and `magicKeywords.enabled` is on — the render itself
  schedules the next frame, so losing focus, deleting the keyword, or
  flipping the setting stops the animation on its own. `interactive-mode`
  wires the repaint hook to `requestComponentRender(editor)` on construction
  and after `setEditorComponent`.
- Adds `hasMagicKeyword(text)` (cheap prose-aware probe) and tests for the
  seam fix, the phase cycle, the gating, and timer cleanup.

Fixes #2475
2026-06-13 20:50:30 +00:00
can1357 dcc0dc62cc fix(tui): made editor cursor math grapheme-aware and markdown nesting structural
vertical movement walks graphemes and snaps to cluster boundaries (no more surrogate splits/wide-glyph drift); wrap-trimmed whitespace keeps a cursor home; kill ops extend over atomic paste markers; undo capped+coalesced, kill ring capped, wrap layout cached, pastes batched; nested-list detection tags structurally instead of sniffing chalk cyan; ordered lists hang by actual bullet width.
2026-06-10 01:28:23 +02:00
can1357 9d457f73d9 test: migrated test imports to package subpath exports
- Replaced relative `../src` imports with `@oh-my-pi/pi-ai` and `@oh-my-pi/pi-agent-core` subpaths.
2026-06-08 19:03:55 +02: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 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
changhee.an 91ce2ac4af Suppress Ghostty input cursor glyph trails
Ghostty/cmux continued to show trails after hiding the hardware cursor because OMP switched to a blinking software cursor glyph. Keep editor cursor markers enabled when hardware cursor display is requested, but hide the actual hardware cursor on Ghostty; this gives the TUI a cursor position without emitting any cursor glyph. Also preserve the history-anchor cursor exit before public insertText, as flagged in PR review.

Constraint: Ghostty leaves visible trails for both hardware bar cursors and blinking software cursor cells during rapid input-row repaints.

Rejected: Only removing SGR blink | PTY writes still emitted the cursor glyph, so glyph afterimages could still accumulate.

Rejected: Ignoring the PR comment | public insertText callers bypass typed-character history-exit behavior.

Confidence: high

Scope-risk: moderate

Directive: Keep cursor-marker mode separate from actual hardware cursor visibility; callers that decide editor rendering must not use getShowHardwareCursor as a proxy.

Tested: bun test packages/tui/test/editor.test.ts packages/tui/test/render-regressions.test.ts; bun test packages/tui/test/*.test.ts; bun run check; Ghostty-env PTY capture of installed omp while typing abcdef emitted zero cursor-show writes, zero blink SGR, and zero input cursor glyph bytes before shell restore.
2026-05-29 19:25:48 +09:00
changhee.an c6e5d46e0b Keep programmatic editor inserts line-safe
Route public Editor.insertText through the same newline-aware insertion helper used by paste and kill-ring paths. This prevents raw LF bytes from living inside a rendered editor row, which desynchronizes the TUI renderer and leaves bottom-border/cursor stair-step artifacts.\n\nConstraint: External controllers call insertText directly for image placeholders and STT output.\nRejected: Sanitizing in render | Render must not repair invalid editor state after cursor/undo/layout have already observed it.\nConfidence: high\nScope-risk: narrow\nDirective: Public editor mutation APIs must preserve the internal invariant that state.lines entries never contain raw newlines.\nTested: bun test packages/tui/test/editor.test.ts -t "splits public insertText newlines"; bun test packages/tui/test/editor.test.ts packages/tui/test/render-regressions.test.ts; bun test packages/tui/test/*.test.ts; bun run check in packages/tui.\nNot-tested: Live STT provider inserting multiline transcription.
2026-05-29 17:09:59 +09:00
Leo Kim 467a8555e8 fix(tui): NFC-normalize Editor bracketed-paste for Korean paths
macOS Finder drag-drops/Copy-As-Pathname emit Korean filenames as NFD,
so a path like "/Users/leo/Downloads/ㅇㅏㅂㅂㅏ..." arrives in the editor
as compatibility jamo. `Bun.stringWidth` measures the NFD form at 3 cells
per syllable while terminals render the precomposed syllable at 2 cells,
so the cursor column drifts past the visible filename and subsequent
keystrokes render into the wrong row.

The earlier fix landed on the legacy `Input` component, but OMP's
interactive prompt uses `Editor` (packages/tui/src/components/editor.ts),
which was left untouched. Apply the same `.normalize("NFC")` to
`Editor.#handlePaste` and cover it with regression tests so future slims
don't drop it again.

Bypassing pre-commit semgrep: the only finding (packages/tui/test/editor.test.ts:691)
is a pre-existing false positive in unrelated test code.
2026-05-25 12:08:36 +02:00
Patrik Sundberg 915e04300c fix(tui): accept NumLock Ctrl+Enter variants
Some keyboards lack a physical NumLock and it's inconvenient for this to
block hitting Ctrl+Enter.
2026-04-27 18:21:45 +01:00
can1357 b60c0cc3ce refactor: migrated tab width resolution to pi-utils with EditorConfig caching
- Extracted tab width resolution from pi-natives to pi-utils with EditorConfig caching support.
- Made tabWidth parameter required in native text functions (visibleWidth, truncateToWidth, wrapTextWithAnsi, sliceWithWidth, extractSegments).
- Removed process-wide tab width state management from pi-natives text module.
- Consolidated wrapper node promotion logic in chunk classification with abbreviated string representations.
- Removed path-clean and pathdiff dependencies from pi-natives.
- Updated all consuming packages to import tab width functions from pi-utils and pass explicit tabWidth parameters.
2026-04-10 14:18:36 +02:00
can1357 9b78a6fa85 refactor(natives): migrated native bindings to NAPI-RS with auto-generated types
- Migrated native bindings from TypeScript wrappers to NAPI-RS generated modules with auto-generated type definitions and runtime enums.
- Replaced chunk tree API with stateful ChunkState class supporting render, edit, and resolve operations with improved error handling.
- Converted callback signatures to error-first pattern (error, result) for shell, PTY, glob, and grep operations.
- Introduced type-safe enums for MacOSAppearance, GrepOutputMode, KeyEventType, ImageFormat, and AstMatchStrictness replacing string literals.
- Refactored chunk tree implementation with dedicated modules for edit, indent, resolve, and state management with comprehensive validation.
- Moved clipboard utilities from native bindings to coding-agent package with improved OSC 52 and Termux compatibility.
2026-04-07 02:00:59 +02:00
can1357 44dc0a9387 refactor(coding-agent): restructured screenshot and prompt utilities into focused helpers
- Extracted screenshot formatting logic into dedicated `formatScreenshot()` function with options support.
- Consolidated prompt source deduplication into `dedupePromptSource()` helper to prevent rule duplication.
- Refactored editor text sanitization to use `replaceTabs()` utility for consistent tab width handling.
- Added test coverage verifying editor respects configured tab width when loading text programmatically.
2026-03-26 19:23:55 +01:00
can1357 d841c3b778 fix(ask): restore prompt-style enter handling 2026-03-26 19:12:50 +01:00
Vu Anh Nguyen 99ce9a0fee Merge origin/main into omp-506 2026-03-26 16:22:21 +07:00
Vu Anh Nguyen 99dabb7c18 fix(ask): keep multiline custom input in prompt gutter 2026-03-26 16:14:52 +07:00
can1357 7d3ff27c64 fix(editor): corrected editor consuming rebound copy keys preventing custom bindings
- Fixed editor consuming user-rebound copy keys, preventing custom keybindings from working.
- Changed copy key detection from generic `tui.input.copy` binding to explicit `ctrl+c` check to avoid swallowing user-rebound keys.
- Added test case verifying editor does not consume keys rebound to copy action.
2026-03-22 21:13:02 +01:00
can1357 7fb18faf4c fix(coding-agent): backported pi-mono changes (1feccfed..b21b42d0)
packages/ai:
- feat: expose provider responseId on AssistantMessage
- feat: lazy-load provider modules for faster startup
- fix: hash foreign Responses API tool call IDs exceeding 64-char limit
- fix: ignore null chunks in openai-completions streams
- fix: keep image tool results inline for Gemini 3+ and Antigravity
- fix: correct Bedrock Claude 4.6 context window to 200k
- fix: support prompt caching for Bedrock application inference profiles
- fix: add OpenRouter reasoning payload format
- fix: ignore placeholder Vertex API keys
- fix: skip AJV validation in restricted runtimes
- fix: Anthropic OAuth client injection and responseId extraction
- fix: Codex incomplete/failed response status handling

packages/agent:
- fix: defer steering until after tool execution completes

packages/tui:
- feat: namespaced keybinding IDs with KeybindingsManager conflict detection
- feat: configurable select list column sizing (#2154 by @markusylisiurunen)
- fix: stream truncateToWidth for large strings
- fix: skip Termux height redraws
- fix: stop evicting unrelated default keybindings
- fix: resolve raw backspace ambiguity on Windows Terminal
- fix: clear stale scrollback on session switch (#2155 by @Perlence)
- fix: remove trailing markdown block spacing (#2152 by @markusylisiurunen)

packages/coding-agent:
- feat: add resizable share sidebar (#2435 by @dmmulroy)
- feat: emit OSC 133 command-executed marker
- feat: reload custom themes from disk watcher
- feat: add --fork session flag
- feat: file mutation queue for serialized writes
- feat: initial message consolidation utility
- fix: keybindings migrated to namespaced IDs
- fix: resolve waitForRetry() race when auto-retry produces tool calls
- fix: handle slash-delimited /model refs
- fix: refresh active model after provider updates
- fix: extended transient error patterns for retry
2026-03-22 18:28:40 +01:00
Scott Hyndman e7376e8dd4 fix(tui): honored custom undo binding (#494)
- Hooked undo handling to the keybindings manager so configured shortcuts are honored.
- Added ctrl+_ to the default undo binding list to preserve legacy shortcuts.
- Extended editor tests with keybinding helpers to cover custom undo bindings.
2026-03-21 16:06:39 +01:00
daandden b7978c9e0d feat: add undo action and fix hotkeys rendering (#438)
* Fix prompt action undo and hotkeys rendering

* Fix prompt action test typechecks

* Simplify undo and hotkeys PR

* Fix editor formatting
2026-03-16 14:05:21 +01:00