Add scrollbar, mouse wheel scroll, and paste features#138
Merged
Conversation
) ## StreamingTextNode scrollbar (#135) - Expose `PersistedStreamBuffer.GetMaxScrollOffset()` as public - Add `ScrollbarOptions` record (TrackChar, ThumbChar, TrackColor, ThumbColor, AutoHide) - `StreamingTextNode.WithScrollbar()` / `WithScrollbar(options)` fluent API - Scrollbar rendered in rightmost column; thumb position inverted to match the 0=bottom scroll convention; auto-hides when content fits the viewport ## Bracketed paste mode (#134) - `AnsiCodes.EnableBracketedPaste` / `DisableBracketedPaste` (ESC[?2004h/l) - `PasteEvent(string Content) : IInputEvent` - `IPasteReceiver` interface; `TextInputNode` implements it — pastes insert text at cursor without firing `Submitted`, newlines skipped, MaxLength respected - `TerminaApplication` enables bracketed paste on startup and disables in finally - `ConsoleInputSource` extended with a 4-state machine (Normal → AfterEscape → InBracketSequence → PasteBuffering) with 50 ms ESC timeout - `TerminaApplication.ProcessEvent` routes `PasteEvent` to focused `IPasteReceiver` or falls through to the ViewModel input observable ## Mouse wheel scroll (#136) - `AnsiCodes.EnableMouseButtonTracking` / `DisableMouseButtonTracking` (ESC[?1002h/l) - `MouseScrollEvent(int Delta) : IInputEvent` (positive = up / older content) - `IScrollable` interface (CanScrollUp, CanScrollDown, ScrollUp, ScrollDown) - `StreamingTextNode` implements `IScrollable`; caches viewport dims in `Render()` so scroll calls outside the render loop use the correct wrap width - `ConsoleInputSource` detects SGR mouse sequences ESC[<64;…M (up) / ESC[<65;…M (down) - `TerminaApplication` enables mouse button tracking + SGR on startup; routes `MouseScrollEvent` to focused `IScrollable`, or falls through to ViewModel ## Streaming demo wired up - `StreamingChatPage`: `.WithScrollbar()` on chat history, paste subscription, mouse scroll subscription via `IScrollable`, updated status bar hints
…se mode ?1002h (EnableMouseButtonTracking) generates click/release events for every tmux pane interaction, producing escape sequences that could be partially parsed into stray ESC keypresses and trigger RequestShutdown(). ?1000h (EnableMouseNormal) + ?1006h (SGR) is sufficient for scroll wheel events (button 64/65 in SGR format) without the noisy button tracking noise. Replace direct Console.Write of ?1002h with _terminal.EnableMouse() which already uses the correct combination, and update the AnsiCodes doc comment to warn about the tmux incompatibility.
…arser PlatformInputSource was wrapping every ConsoleKeyEvent directly as KeyPressed with no escape sequence detection. Mouse click events (ESC[<0;x;yM) arrived as a ConsoleKey.Escape followed by raw chars; that bare ESC reached the ViewModel and triggered RequestShutdown(). Extract EscapeSequenceParser (internal, testable with injectable clock) that handles: - SGR mouse scroll (button 64/65) → MouseScrollEvent - SGR mouse clicks/releases → silently consumed - Bracketed paste → PasteEvent - Standalone ESC (50ms timeout) → KeyPressed(Escape) Wire it into PlatformInputSource so all ConsoleKeyEvents pass through the parser. When the parser is buffering an escape, ReadInputWithTimeoutAsync races against a 50ms CTS to detect a standalone ESC without blocking. Add EscapeSequenceParserTests covering scroll, click consumption, timeout, paste, and post-event key routing (19 tests).
Update the demo status bar hint to explicitly show Ctrl+Shift+V as the paste shortcut, clarifying that terminal-native paste (not tmux paste buffer) is required for bracketed paste mode detection to work. Add a debug trace in PlatformInputSource when a PasteEvent is emitted, making it easier to confirm whether ESC[200~...ESC[201~ markers are reaching the application during diagnostic sessions.
When running inside tmux, the inner-pane ESC[?2004h is intercepted by tmux and never reaches the outer terminal emulator. This means Ctrl+Shift+V pastes from the outer terminal arrive as raw text (no ESC[200~...ESC[201~ markers), so bracketed paste detection never fires. Fix: detect the $TMUX environment variable and also send ESC[?2004h to the outer terminal via a DCS passthrough sequence (\ePtmux;\e\e[?2004h\e\\). Once the outer terminal has bracketed paste mode enabled, it wraps Ctrl+Shift+V pastes with the markers, which tmux then forwards to the inner pane. Requires set -g allow-passthrough on in ~/.tmux.conf (tmux 3.3+). Add AnsiCodes.TmuxPassthrough(string seq) helper that wraps any escape sequence in the DCS passthrough format with properly doubled ESC bytes.
TextInputNode.HandlePaste previously called string.Insert per character, causing quadratic performance for large pastes (41K+ chars). Now filters newlines in a single StringBuilder pass and does one bulk Insert. Added trace logging to EscapeSequenceParser state transitions for diagnosing paste/mouse escape sequence processing. Zero overhead when tracing is disabled (single inlined boolean check).
Paste now shows a summary placeholder (e.g., "[Pasted 500 lines, 12345 chars]") instead of inserting raw content. The full paste content including newlines is preserved and submitted verbatim on Enter. Any editing action (typing, backspace, delete, escape) clears the paste and returns to normal input mode. This prevents newlines in pasted content from triggering individual submissions.
- streaming-text-node.md: Document WithScrollbar(), ScrollbarOptions, mouse wheel scrolling, IScrollable, and CanScrollUp/CanScrollDown - text-input-node.md: Document paste handling behavior, IPasteReceiver, and HandlePaste method - input-handling.md: Add MouseScrollEvent and PasteEvent to input events, document automatic routing to IScrollable/IPasteReceiver components
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements three input/display enhancements to Termina:
StreamingTextNodeshowing scroll positionIScrollablecomponentsChanges
Scrollbar
ScrollbarOptionsrecord for customization (track/thumb chars and colors, AutoHide)WithScrollbar()fluent methods onStreamingTextNodeGetMaxScrollOffset()exposed inPersistedStreamBufferPaste Mode
PasteEventandIPasteReceiverinterfacesTextInputNodeimplementsIPasteReceiver[Pasted 500 lines, 12345 chars]AnsiCodes.EnableBracketedPaste/DisableBracketedPasteconstantsMouse Wheel Scroll
MouseScrollEventinput eventIScrollableinterface for scrollable componentsIScrollableEscapeSequenceParserdetects SGR mouse scroll sequencesDocumentation
streaming-text-node.mdwith scrollbar and mouse scroll sectionstext-input-node.mdwith paste handling documentationinput-handling.mdwithPasteEventandMouseScrollEventdetailsCloses #135
Closes #134
Closes #136