Commit Graph
6 Commits
Author SHA1 Message Date
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
oldschoola 65f5bdac03 fix(tui): clear stale lex prefix on non-append replacement (render-prefix cache)
Address review: the render-prefix cache guards on #streamPrefixText, which #freezeStablePrefix left untouched when the new text had no freezable "\n\n" boundary. After a transient setText() replacement by single-line (or otherwise boundary-less) content, the stale frozen prefix survived, so the cache reused the OLD prefix's rendered lines — a correctness regression. Fix: in the #lexTokens fallback path (non-append edit / cold start), clear #streamPrefixText/#streamPrefixTokens before #freezeStablePrefix, so a boundary-less text leaves them cleared and the render-prefix cache falls back to a full render.

Added a transient non-append replacement regression test (verified it fails on the bug, passes on the fix).
2026-06-29 18:38:00 -07:00
oldschoola 8288ebb525 perf(tui): reduce streaming reveal + render cost ~2.3x
Three changes to the streaming text-reveal path (the 30fps typewriter animation during model streaming), cutting real per-tick render CPU ~2.3x (measured ~73ms -> ~31ms to animate a 12k-char response; ~1.35ms -> ~0.57ms per tick).

- streaming-reveal: incremental grapheme slicing. BlockUnitCounter.slice resumes segmentation from the cached boundary cluster each tick instead of re-segmenting the whole revealed prefix (O(delta) vs O(revealed)). Handles fixed-text catch-up and append-only live streaming via startsWith + boundary-cluster re-segment.

- assistant-message: fix the transient flag dropped on the fast path. #tryFastPathUpdate(message) was called without opts, so streaming updates always set Markdown.transientRenderCache=false, running code syntax highlighting + L2 cache-key work every tick. Now passes opts through; highlighting applies once at message finalization.

- markdown: render-prefix cache for transient renders. Caches content lines (render+wrap+margin) for the stable streaming-lex prefix tokens [0..frozenCount-2] and re-renders only the changed tail each tick. Byte-identical to the full pipeline (OSC66 margin state resets per token; nextTokenType boundary handled; content-identity guard).

Correctness: byte-identity verified (transient split == full render across 57 reveal steps); BlockUnitCounter.slice verified against a pure Intl.Segmenter reference across fixed/append/cluster-extend/ZWJ-merge/shrink/replace/fuzz cases. Regression tests added in both packages.
2026-06-29 16:59:10 -07: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
metaphorics 31f6afa529 perf(tui): incremental markdown lex during streaming reveal
marked has no resumable lexer, but block tokenization is local across a
blank-line boundary with balanced fences, so freeze the largest stable
prefix's block tokens and re-lex only the grown tail
(lex(prefix) ++ lex(tail)). The render loop is unchanged, so output is
byte-identical to a full lex; a full-lex fallback covers non-append
edits, reference-link definitions, CRLF input, and list boundaries
(where CommonMark loose-list continuation would otherwise merge/renumber
across the cut). Turns the O(N^2) cost of revealing a long single-block
message into O(N): a 6,000-grapheme reveal drops from ~575ms to ~106ms of
CPU. Covered by a byte-identical contract test.
2026-06-09 18:58:40 +09:00