Commit Graph
274 Commits
Author SHA1 Message Date
roboomp 809a8a348d fix(tui): coalesce editor top-border rebuild to render tempo
EventController.handleEvent rebuilt the editor's status-line top border
synchronously on every session event via updateEditorTopBorder(). During
a long-running eval that fires 5-10 events/s, each rebuild ran
StatusLine.getTopBorder → #buildSegmentContext → getCachedContextBreakdown
→ session.getContextUsage → estimateTokens (with JSON.stringify per
toolCall block) — the render pipeline is throttled to ~30 fps, so most
rebuilds were dropped before painting. Combined with a scheduler that
collapsed cadenceDelay to zero whenever a frame overran the 33ms budget,
the TUI busy-looped at ~40-50% CPU.

Fix:
- Editor gains setTopBorderProvider(): a lazy builder invoked once per
  editor render. InteractiveMode installs it in the constructor and on
  setEditorComponent, so the rebuild coalesces to the render tempo
  regardless of event rate.
- Delete updateEditorTopBorder wrapper (now equivalent to
  ui.requestRender) and inline every call site.
- Add adaptive render backpressure: a frame that exceeds
  MIN_RENDER_INTERVAL_MS inflates the next scheduling delay to
  2 * last_frame_cost, capped at 200 ms, targeting a 50% render duty
  cycle instead of pinning the CPU at t=0.

New regression tests:
- editor-top-border-provider.test.ts: provider fires exactly once per
  render, wins over eager setTopBorder, falls back when cleared, gets
  the correct availableWidth.
- adaptive-render-backpressure.test.ts: cheap frames keep the 33 ms
  cadence, a slow frame idles proportionally, pathological frames are
  capped at 200 ms.

Verified with bun test packages/tui/test (all 246 relevant tests pass)
and bun test packages/coding-agent/test/modes (455 tests pass). Three
pre-existing agent-session-handoff snapcompact failures on main are
unrelated (snapcompactSupportedChars binding).

Fixes #4145
2026-07-01 13:51:43 +00:00
roboompandcan1357 3822a83b46 fix(tui): cleared stale streaming prefix caches
Cleared frozen-prefix render state after full-lex renders cannot establish a current appendable prefix, preventing truncated transient text from reusing spacer lines produced with a former tail.

Added regression coverage for truncating back to a previously frozen prefix.

Fixes #3975
2026-07-01 05:31:53 +02:00
roboomp 7054265483 fix(tui): cached streaming markdown prefix renders
Reused rendered frozen-prefix lines during transient Markdown renders so streaming assistant updates avoid re-rendering stable code blocks and other completed blocks.

Added focused regression coverage for prefix reuse and cache advancement across newly frozen blocks.

Fixes #3975
2026-07-01 02:46:36 +00:00
can1357 5cc84db4c4 feat(tui): enabled programmatic form submission via editor APIs
- Added a public `submit()` method to the `Editor` class to trigger form submission.
- Updated the `EditorComponent` interface to expose an optional `submit()` method.
2026-06-30 16:16:39 +02:00
can1357 670d60fa60 feat(tui): added file-path completion support to autocomplete provider
- Added `getForceFileSuggestions` and `shouldTriggerFileCompletion` to `AutocompleteProvider` interface.
- Updated `Editor` to invoke file completion logic dynamically when providers support it.
- Corrected log viewer scroll offset calculation to account for viewport height.
- Added comprehensive unit test for log row selection and expansion.
2026-06-28 08:25:32 +02:00
can1357 3ccef0a8bf fix(tui): preserve path tab completion after prose 2026-06-28 07:44:28 +02:00
roboomp 1c05d059a7 fix(tui): opened mid-prompt skill autocomplete
Detected slash skill lookup after existing prompt text and filtered the popup to skill commands only.

Replaced the whole draft when a mid-prompt skill completion is accepted so the submitted prompt invokes /skill:<name>.

Fixes #3654
2026-06-27 13:14:59 +00:00
can1357 f366e7bea8 Merge PR #3402: Shared TUI mouse-routing helpers and overlay selector migration (@wolfiesch)
# Conflicts:
#	packages/coding-agent/src/modes/components/settings-selector.ts
2026-06-27 01:40:11 +02:00
can1357 a427449d69 feat(tui): extended markdown rendering with html component support
- Added support for `<code>`, `<hr>`, and `<blockquote>` tags to the markdown renderer.
- Improved HTML entity decoding and tag handling logic for consistent terminal output.
- Refactored internal block parsing methods to facilitate specialized styling for new HTML elements.
- Verified new rendering behaviors and entity handling through updated test suites.
2026-06-26 22:45:01 +02: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
Wolfgang Schoenberger e5e29219fe feat(tui): add shared SGR mouse-routing helpers and SelectList.routeMouse
Add routeSgrMouseInput() and routeSelectListMouse() to centralize the
repeated SGR parse + SelectList hit-test/hover/wheel/click patterns, and
make SelectList implement MouseRoutable via routeMouse(). The structural
SelectListMouseTarget interface keeps mouse.ts free of component imports.

Extend the emergency-restore tests to assert mouse-off sequences
(1006l/1003l/1000l) are emitted while keeping the alt-screen 1049 gating.
2026-06-24 20:09:48 -07:00
roboomp e261848293 fix(settings): coerced rendered settings values
Coerced malformed YAML-derived settings values to strings before they reach settings row truncation. Added regression coverage for numeric enum display values.

Fixes #3338
2026-06-24 13:53:06 +00:00
can1357 be3687a5f1 feat(tui): improved settings list wheel interaction handling
- Implement `handleWheelAt` to restrict wheel scrolling to the settings pane.
- Update selection movement to support clamping when scrolling at boundaries.
- Add tests to verify boundary behavior and spatial constraints.
2026-06-22 06:10:17 +02:00
can1357 571eb5c4db fix(tui): cleaned up markdown rendering of inline tags and entities
- Stripped `<span>` and `<text>` tags from rendered markdown while preserving their contents.
- Updated HTML entity normalization to handle common special characters and numeric codes within the TUI.
- Ensured whitespace surrounding stripped inline tags is preserved, preventing content merging.
2026-06-20 08:11:31 +02:00
roboomp ec47aca355 fix(tui): wired editor enter handlers to leading-space slash detection
Preserved the leading whitespace in the suggestion prefix and routed both Enter paths in editor.ts through findLeadingSlashCommandStart so the popup apply and sync fallback no longer bail out on '  /sk'.

Fixes #3095
2026-06-20 02:57:01 +00:00
can1357 afef9fc68a feat(tui): added configurable box border support
- Added `BoxBorder` interface and optional border support to the `Box` component.
- Implemented `setBorder()` to allow dynamic toggling of box outlines.
- Added intelligent border dropping logic to prevent overflows in narrow containers.
- Added comprehensive unit tests for rendering, styling, and boundary conditions.
2026-06-19 07:39:24 +02:00
can1357 e760673fa3 feat(tui): added custom markdown horizontal rule support
- Implemented `customHr` tokenizer to support varied Unicode horizontal rule characters.
- Added logic to intelligently map divider characters to theme-appropriate symbols.
- Updated theme object ID tracking to use `WeakMap` for better handle integrity.
2026-06-19 06:58:48 +02:00
can1357 82a5b24ed7 fix(tui): drop HTML formatting whitespace between block list tags
Pretty-printed HTML lists (e.g. <ul>\n  <li>...) leaked source
indentation before bullets and inserted blank rows between items
because whitespace-only text between block-level tags was appended
literally. Skip insignificant formatting whitespace and pin it with
a regression test.
2026-06-19 00:57:45 +02:00
can1357 59cf966ffc Merge PR #2573: fix(tui): parse and format raw HTML tags in Markdown renderer (@usr-bin-roygbiv) 2026-06-19 00:57:45 +02:00
can1357 0b437b4df7 Merge PR #2775: fix(tui): reduce loader repaint flicker (@roboomp) 2026-06-19 00:57:45 +02:00
usr_bin_roygbiv 9c6979d98a fix(tui): parse and format raw HTML tags in Markdown renderer 2026-06-18 16:05:06 -05: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
can1357 9f16ca4797 feat(tui): enabled tight layout mode with optional 1-char padding reduction
- Added new `tui.tight` setting with default `false` and documented behavior.
- Added global tight-mode APIs and `setIgnoreTight` propagation across Box, Text, and Markdown.
- Handled `tui.tight` updates to refresh UI borders and trigger rerendering.
- Updated key message components to ignore tight mode selectively and preserve intended spacing.
2026-06-17 15:09:55 +02:00
can1357 4678c80cd8 fix(tui): fixed inline image transmit replay for full paint
- Adjusted image budget IDs to use a shared 24-bit seed and wrap safely so new budgets no longer collide.
- Added transmit-state reset and changed image output ordering so full paints can replay image data after terminal clears.
- Updated image and protocol tests to match the new wrapped-id behavior and cursor-save/restore-wrapped transmit sequences.
2026-06-17 15:03:32 +02:00
can1357 0075014fe9 fix(tui): used cursor save/restore for direct image placement
- Added cursor save/restore escape handling around direct image emission.
- Replaced the previous up-and-down cursor movement sequence with restore logic to keep renderer output positioned correctly.
- Ensured direct image replay path no longer depends on moving back down after placement to align the cursor.
2026-06-17 13:54:48 +02:00
can1357 e4a87fa3ce feat(coding-agent): added matplotlib rendering and image persistence across session reload
- Added Matplotlib figure PNG rendering and display tracking in Python runner to emit PNG output immediately when figures are displayed via display(fig).
- Extended session persistence to externalize oversized image payloads in both content and details.images, enabling tool result images to survive session reload.
- Enhanced session loader to resolve image data payloads and blob references across content and details.images during session reconstruction.
- Added image cache invalidation in TUI image component when image protocol, cell dimensions, or Kitty Unicode placeholder mode changes.
- Added comprehensive test coverage for Matplotlib display, image persistence across reload, and TUI image rendering with protocol and dimension changes.
2026-06-17 13:19:28 +02:00
roboomp c451e37f97 fix(tui): restored cursor after inline images
Restored the cursor below direct inline-image placements after emitting the graphics sequence so following chat rows cannot overwrite the reserved image area. Added coverage for the placement row terminator.\n\nFixes #2863
2026-06-17 10:10:10 +00:00
can1357 d30bec5126 feat(tui): added stacked math rendering through latexToBlock
- Added latexToBlock module that renders stacked fraction math with baseline-aware rows.
- Updated markdown rendering to route display math through latexToBlock with sole-display handling.
- Exported latexToBlock from the package entrypoint and updated changelog docs accordingly.
- Added advisor resume and suppression repro tests in coding-agent for interrupted turn recovery.
2026-06-16 22:53:29 +02:00
can1357 bfe574443a feat(tui): added LaTeX color and display-math rendering support in TUI output
- Added own-line display-math tokenizers for `$$...$$` and `\[...]` markdown blocks.
- Added delimiter-free `\begin...\end` math parsing in text conversion with multiline row preservation.
- Added ANSI rendering for `\textcolor`, `\color`, `\colorbox`, and `\fcolorbox` with scoped color resets.
- Added tests for LaTeX color and math parsing edge cases, including inline code and nested scopes.
2026-06-16 22:09:32 +02:00
can1357 0f013b455e feat: added LaTeX math rendering support for terminal and TUI outputs
- Added LaTeX math and Mermaid allowances in terminal and final-chat prompts.
- Added inline math tokenization in TUI for $, $$, \(\), and \[\].
- Added LaTeX-to-Unicode conversion helpers and exports for math rendering.
- Fixed inline math detection to skip escaped dollars and currency-like spans.
2026-06-16 21:45:10 +02:00
can1357 508a9f543e feat: removed render_mermaid tool and added width-aware Mermaid clipping
- Removed render_mermaid from tool discovery, task definitions, and registries.
- Removed renderMermaid setting and prompt/docs references tied to the deleted tool.
- Added maxWidth and theme color options to Mermaid ASCII resolution in markdown flow.
- Re-rendered Mermaid ASCII in both directions and clipped output to available width.
2026-06-16 21:32:39 +02:00
roboomp f2b92059cf fix(tui): reduced loader repaint flicker
Throttled animated loader message-only repaints when synchronized output is unavailable so unsupported terminals only repaint on spinner frame changes.

Fixes #2771
2026-06-16 12:16:40 +00: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 ff0fc74f53 feat(coding-agent/modes): implemented space-bar hold push-to-talk STT gesture
- Added a space-hold gesture state machine in CustomEditor, tracking repeated spaces, detecting holds beyond SPACE_HOLD_THRESHOLD, and firing start/end callbacks via a release timer.
- Hooked editor space-hold callbacks in InputController so STT toggles on hold start and again on release when STT is enabled.
- Added tests for space-hold start/stop behavior and updated keybinding docs to describe the hold-to-record STT workflow.
2026-06-14 08:34:56 +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
can1357 81bf6ee965 Merge PR #2506: glow magic keywords through cursor seam + focused shimmer 2026-06-14 02:57:08 +02:00
can1357 686f23a925 Merge PR #2488: TUI event-loop watchdog + phase breadcrumbs 2026-06-14 02:56:31 +02:00
metaphorics 2b126ffb80 feat(tui): add event-loop watchdog and phase breadcrumbs for stall diagnosis
Always-on LoopWatchdog (armed in TUI.start/stop) logs ui.loop-blocked with blockedMs and the current loop phase on the rising edge of a late probe tick. New pushLoopPhase/popLoopPhase/currentLoopPhase stack in pi-utils feeds it; breadcrumbs at in-process subagent dispatch (subagent:<id>) and the SelectList fuzzy filter (ui.select-filter) attribute residual main-thread stalls.
2026-06-14 08:23:05 +09: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 ec312a2e81 fix(tui): fixed inline image demotion replay during resize viewport fast-path
- ImageBudget gained a stable partial-pass mode that replayed the last committed suppressed-image id set during beginPass(true), avoiding reordered call-order decisions.
- TUI resize viewport painting switched to beginPass(true) and stopped calling endPass so tail-only drag repaints no longer mutated image demotion state.
- An ImageBudget test now verifies stable partial passes preserved live/text decisions by id and did not trigger purge or reset in the next full pass.
2026-06-13 22:40:53 +02:00
can1357 d214e2f022 Merge branch 'pr-2182'
# Conflicts:
#	packages/coding-agent/package.json
#	packages/coding-agent/src/config/model-registry.ts
#	packages/coding-agent/src/main.ts
#	packages/coding-agent/src/modes/components/assistant-message.ts
#	packages/coding-agent/src/sdk.ts
#	packages/tui/src/components/markdown.ts
2026-06-12 11:24:26 +02:00
can1357 12bada4ce0 fix: hardened deferred MCP discovery and streaming render fast paths
- Recomputed `tools.discoveryMode: "auto"` in the deferred MCP closure in `sdk.ts` once the real tool count is known: a toolset crossing the threshold now flips discovery on, registers and activates `search_tool_bm25`, and skips `activateAll` instead of force-activating every MCP tool.
- Guarded the deferred MCP task against disposed sessions: added `AgentSession.isDisposed` and `enableMCPDiscovery()`, and the late connect now calls `disconnectAll()` instead of refreshing tools onto a dead session.
- Cleared `#fastPathKey`/`#fastPathItems` in `AssistantMessageComponent.invalidate()` so theme/symbol changes rebuild reused Markdown children instead of keeping stale captured themes.
- Memoized unusable read summaries as a `false` sentinel in `read.ts` so the per-session LRU no longer retains full sources of unsummarizable files.
- Broadened `HAS_REF_DEF` in `markdown.ts` to match backslash-escaped reference labels (`[a\]b]: x`) and cleared frozen stream-lex state on blank `setText()`.
- Added regression tests: deferred auto-discovery flip and mid-connect dispose (`sdk-mcp-auto-discovery.test.ts` + `many-tools-mcp.ts` fixture), fast-path child rebuild on invalidate, and escaped-ref-def incremental-lex equivalence.
2026-06-12 11:14:39 +02:00
can1357 228d536a36 feat(coding-agent/modes): enabled editor-style input in settings search banner
- Replaced global search query mutation logic with a dedicated `Input` instance so typing now supports cursor movement, word deletion, and other editor hotkeys.
- Updated search banner rendering to display the embedded input line with live cursor while preserving match-count formatting and prompt width handling.
- Extended `Input` with a configurable prompt and end-of-value cursor placement, then updated memory-search tests for the new hotkey-driven editor behavior.
2026-06-12 04:54:07 +02:00
can1357 2e0ffcca46 feat(tui): added section-focus navigation support for settings interfaces
- Added `SettingsList` section-focus state with `sectionFocused`, `hasSectionFocusTargets`, and `toggleSectionFocus()`.
- Implemented section-heading navigation with Tab/Shift+Tab, Up/Down focus jumps, and Enter/Escape exits.
- Updated `/settings` input hints and fallback routing to tab-bar keys when section focus is unavailable.
- Added tests for section-focus navigation, mouse parsing, and tab-bar behavior coverage.
2026-06-12 03:27:50 +02:00
can1357 c35b41ed41 feat(coding-agent): added mouse-driven /settings overlay navigation across tabs and sections
- Added mouse hover, wheel, and click routing for `/settings` rows and tabs.
- Changed `/settings` to open as a fullscreen alternate-screen overlay.
- Added hover, hit-testing, and wheel APIs across TabBar, SelectList, and SettingsList.
- Added SGR mouse parsing plus `routeMouse` dispatch and exported it from the `tui` entrypoint.
2026-06-12 03:27:50 +02:00
can1357 a7cb9c17f8 feat(tui): added selection tracking, layout options, and muted tabs to settings components
- Added `SettingsListOptions` to `SettingsList` with a `layout` override ("auto" sidebar vs "flat" inline headings) and a `typeToSearch: false` switch for hosts that own filtering globally.
- Added `getSelectedItem()` and an `onSelectionChange` callback to `SettingsList`, replaced index-based submenu selection restore with id-based item resolution, and exported `getSettingItemFilterText` for external filtering.
- Padded short section panes to the full viewport in `SettingsList` so the panel height stays constant across sections.
- Added `muted` tabs to `TabBar` (skipped during keyboard navigation, styled via the new `TabBarTheme.mutedTab`), a `setTabs()` replace API that preserves the active tab by id without firing `onTabChange`, a `showHint` toggle for the trailing "(tab to cycle)" hint, and omission of the label prefix when the label is empty.
2026-06-12 02:33:48 +02:00
can1357 0325aa25e9 feat(tui): added section headings, split layout, and section paging to SettingsList 2026-06-12 02:33:48 +02:00
ben dd71790924 Merge remote-tracking branch 'origin/main' into fix/render-highlight-streaming 2026-06-11 22:48:01 +08:00
ben db23b721b5 Fix markdown transient cache invalidation 2026-06-11 22:27:59 +08:00
can1357 b1338b8f57 feat(tui): added component-scoped rendering with partial compose reuse
- Added `TUI.requestComponentRender`, enabling component-scoped render requests.
- Implemented partial compose reuse by resolving and memoizing affected root segments.
- Fixed component-scoped updates to keep frame-seam rows continuous across partial renders.
- Added component-render and loader tests for reuse, downgrade, and call-count coverage.
2026-06-11 16:13:18 +02:00