Prototype: replace SGR mouse tracking with alternate-scroll mode (?1007h)#215
Conversation
…racking Replaces the application's startup call to EnableMouse() (which enabled CSI ?1000h + CSI ?1006h to capture mouse-wheel events) with a new EnableWheelScroll() call that emits CSI ?1007h. While in the alternate screen buffer, the terminal translates wheel events into cursor up/down key sequences without any mouse tracking, so: - Native click-drag text selection works again (fixes Aaronontheweb#192). - Triple-click word/line selection works. - Middle-click paste works. - OS clipboard integration via the terminal works. - PR Aaronontheweb#194's F6 selection-mode toggle becomes unnecessary. - The tmux ESC-keypress crash class that drove the ?1002h → ?1000h migration goes away — the terminal sends no mouse escape sequences. EnableMouse()/DisableMouse() are kept for apps that genuinely need clicks/drags (e.g. an in-app file picker) and can opt in explicitly. EscapeSequenceParser keeps its silent consumption of SGR mouse events as a defensive safety net. Caveats: - Wheel events arrive as bare UpArrow/DownArrow keypresses and are indistinguishable from real keyboard arrows. Apps with always-focused text inputs (e.g. chat) may want to treat Shift+Wheel separately or focus the scrollable explicitly. The Streaming demo's existing MouseScrollEvent subscription will no longer fire under the default wheel-scroll mode; PgUp/PgDn is the documented keyboard fallback. - Legacy conhost.exe ignores ?1007h. Modern Windows Terminal supports it. Affected users keep keyboard scrolling as a fallback. Adds tests for the new VirtualTerminal/DiffingTerminal/AnsiCodes surfaces. Full suite: 1026 / 1026 passing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Demonstrates the wheel-vs-arrow ambiguity introduced by switching from SGR mouse tracking (?1000h + ?1006h) to xterm alternate-scroll mode (?1007h). The terminal translates wheel events into bare Up/Down arrow keypresses, which a focused TextInputNode happily consumes as cursor movement — so spinning the wheel over a scrollable history panel moves the input cursor instead of scrolling the history. Layout: - Top: pre-populated StreamingTextNode (100 lines, scrollable) - Middle: pre-filled TextInputNode (always focused) - Bottom: live counters for bare Up/Down arrows and MouseScrollEvents PgUp/PgDn still scrolls the history (keyboard fallback), Ctrl+Q quits. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
BuildLayout() runs inside base.OnNavigatedTo() before any code after the base call executes, so _history/_input were still null when the layout was first built. Move node construction to OnBound() (called at bind time, before navigation) and keep input subscriptions in OnNavigatedTo so they're re-created on each visit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Counterpart to Termina.Demo.WheelAmbiguity. The ViewModel injects IAnsiTerminal and calls EnableMouse() to re-enable full SGR mouse mode (?1000h + ?1006h) on top of the framework's default ?1007h. The EscapeSequenceParser then emits MouseScrollEvent for wheel ticks, which the page forwards to the (non-focusable) StreamingTextNode. This demonstrates that the framework's auto-routing of MouseScrollEvent only fires when the focused widget is itself IScrollable; for chat-shaped pages where TextInputNode is focused, the page must subscribe and forward manually. Tradeoff documented in code: while EnableMouse() is active, the host terminal stops handling click-drag text selection (Shift/Option to override). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Disambiguate wheel-as-arrow events from real keyboard arrows by also
enabling DECCKM (CSI ?1h) when alternate scroll is turned on:
- Real keyboard arrows: SS3 form (ESC O A/B), decoded by
Console.ReadKey directly to ConsoleKey.UpArrow/DownArrow. Never
reach the EscapeSequenceParser.
- Mouse wheel under ?1007h: CSI form (ESC [ A/B), unchanged by
DECCKM, falls through Console.ReadKey as raw bytes.
The parser now recognizes bare CSI A/B in InBracketSequence as
MouseScrollEvent(+1)/(-1) — the same event type already routed by
TerminaApplication to the focused IScrollable. Pages can keep their
existing MouseScrollEvent subscriptions; they will now fire for
wheel ticks while native click-drag text selection remains available.
- AnsiCodes: add Enable/DisableCursorKeyApplicationMode constants
- AnsiTerminal.EnableWheelScroll: emit ?1007h + ?1h (disable both
on teardown)
- EscapeSequenceParser: recognize ESC[A / ESC[B as MouseScrollEvent
- Tests: parser tests for the new wheel-as-CSI-arrow path,
AnsiCodes tests for the new constants
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Console.ReadKey on Unix has hardcoded recognition for both CSI (ESC[A) and SS3 (ESC OA) arrow forms, so DECCKM (?1h) alone can't disambiguate mouse-wheel-as-arrow events from real arrow keypresses — the raw bytes never reach EscapeSequenceParser. To make ?1007h alternate-scroll mode actually scroll, we have to bypass Console.ReadKey entirely. UnixConsole now puts the terminal into raw mode via termios+cfmakeraw (preserving OPOST so logging output isn't staircased), reads bytes directly from Console.OpenStandardInput, and emits one ConsoleKeyEvent per byte. EscapeSequenceParser gets a new InSs3Sequence state so that real arrow keys (which now arrive as raw ESC O A/B/C/D bytes under DECCKM) are reassembled into normal KeyPressed events while wheel ticks (ESC [ A/B) continue to map to MouseScrollEvent. PlatformConsoleFactory wires UnixConsole on Linux/macOS when stdin is a TTY; piped/redirected scenarios still use FallbackConsole. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The raw-termios + raw-stdin path made interactive behavior worse on macOS Terminal (per user report). Likely culprits include termios c_cc offsets, OPOST handling, ReadAsync vs VMIN/VTIME interaction, and turning Ctrl+C into a byte (cfmakeraw clears ISIG) which breaks app shutdown for demos that don't bind a quit shortcut. Reverting UnixConsole back to the issue-Aaronontheweb#80 stub and reverting PlatformConsoleFactory to fall through to FallbackConsole on Unix. The SS3-arrow and CSI-arrow parser cases (and their tests) stay — they're inert until we get a raw-byte input source and are useful for the future fix. Net result for now: ?1007h alternate-scroll mode is still not usable on its own through Console.ReadKey. Use Termina.Demo.WheelScrollWorks (which opts into ?1000h via IAnsiTerminal.EnableMouse) as the recommended wheel-scroll path. A proper Unix raw-stdin implementation remains tracked under issue Aaronontheweb#80. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Standalone raw-stdin probe — no Termina dependency — used to verify termios layout and raw byte streams on the user's actual terminal before the real UnixConsole implementation lands. Prints computed VMIN/VTIME/ c_oflag offsets, enters raw mode, reads bytes directly via libc.read(), and annotates recognized wheel/arrow escape sequences so the outcome of ?1007h + DECCKM is readable at a glance. Restores termios on Dispose / ProcessExit / Console.CancelKeyPress / unhandled exception. Quits on 'q' or Ctrl+C (0x03 byte, since cfmakeraw clears ISIG). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replaces the NotImplementedException stub with a real implementation that bypasses Console.ReadKey on Unix so ?1007h alternate-scroll-mode wheel events can be distinguished from real arrow keys (DECCKM SS3 forms). Hidden behind the TERMINA_UNIX_RAW_INPUT=1 env var until the opt-in default is flipped in a later change. Design points learned from the prior reverted attempt (8be2d4e): * Direct libc.read() on a dedicated background thread instead of Console.OpenStandardInput().ReadAsync — the Stream wrapper's interaction with VMIN/VTIME wasn't reliable. * termios offsets (c_oflag, c_cc base, VMIN, VTIME) match what the RawStdinProbe runtime-verifies via tcgetattr readback. * OPOST re-enabled after cfmakeraw so logging isn't staircased. * ISIG cleared by cfmakeraw — Ctrl+C arrives as 0x03 and is mapped to KeyPressed(C, Control), matching FallbackConsole's TreatControlCAsInput behavior (user-confirmed in plan). * UTF-8 multi-byte text input reassembled via System.Text.Decoder so typing non-ASCII chars produces one ConsoleKeyEvent per codepoint; ASCII bytes (incl. all escape-sequence bytes) flow one-per-event so EscapeSequenceParser can keep reassembling CSI/SS3. * Channel<IConsoleInputEvent> for the reader→consumer queue; cancellation flows through Channel.ReadAsync naturally. * termios restored on Dispose, AppDomain.ProcessExit, UnhandledException, and Console.CancelKeyPress. Resize detection is still poll-based at the 100 ms VTIME cadence; SIGWINCH integration is the next change. 26 new tests cover byte→KeyInfo mapping for control/printable/Ctrl+ letter/high-bit bytes, CSI arrow → MouseScrollEvent integration, SS3 arrow → KeyPressed integration, and UTF-8 multi-byte decoding through the same one-byte-at-a-time path the reader thread uses. Full suite: 1062/1062 passing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use PosixSignalRegistration.Create(PosixSignal.SIGWINCH, ...) so terminal resizes are observed immediately instead of waiting for the next 100 ms VTIME tick in the reader thread. The handler runs on a thread-pool thread, so CheckResize() now takes a lock to coordinate with the reader thread's opportunistic polling. The VTIME-tick poll is kept as a belt-and-suspenders fallback for environments where the signal registration is unavailable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Per xterm.ctlseqs, ?1007h alternate-scroll-mode only emits CSI A/B for wheel ticks while on the alternate screen buffer. The real Termina framework enters alt screen via ?1049h on startup, so wheel-as-arrow works there; the bare probe was missing it, which is why a Ghostty test run showed arrows + typing working perfectly but wheel emitted nothing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds opt-in 'CSI > <flags> u' push (and matching 'CSI < u' pop on exit) gated on the TERMINA_KITTY_FLAGS env var. Extends AnnotateSequence to recognize: CSI <num>;<mods>[:event] u (kitty CSI-u key events) CSI 1;<mods> [ABCDEFHPQRS] (kitty second-form arrows / Home/End / F1-F4) CSI [ABCD] with no params (wheel under ?1007h OR plain arrow) and decodes the standard kitty PUA functional keycodes (Up=57352..) and modifier bitmask. Default behavior unchanged when env var is unset. Usage: TERMINA_KITTY_FLAGS=8 dotnet run --project demos/Termina.Demo.RawStdinProbe Goal: empirically determine which kitty flag combo produces distinguishable bytes for wheel-up vs Up-arrow in Ghostty under ?1007h. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add KittyKeyboardActive flag to EscapeSequenceParser. When active: - Bare CSI A/B/C/D/H/F/P-S route as KeyPressed (kitty canonical no-mod form) - SS3 OA/OB invert to MouseScrollEvent (wheel under ?1007h+DECCKM) - SS3 OC/OD stay as KeyPressed (wheel has no horizontal axis) Extend TryParseCsiU with optional :event-type subfield (swallow non-press) and trailing ;text-codepoints. Swallow PUA modifier-alone keycodes (57441-57454). Map PUA functional keycodes (57344-57375) to ConsoleKey. Add TryParseKittySecondForm for CSI 1;<mods>[:<event>] [ABCDEFHPQRS]. 25 new tests in EscapeSequenceParserKittyTests.cs cover bare-CSI routing in both modes, all functional finals, SS3 inversion, second-form modifier combos, event-type swallowing, PUA arrows / F5 / modifier-alone swallow. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When TERMINA_KITTY_KEYBOARD is set to a positive int (e.g. 8 for report_all_keys), UnixConsole pushes 'CSI > N u' after entering raw mode and pops 'CSI < u' before restoring termios. UnixConsole exposes the state as KittyKeyboardActive; PlatformInputSource propagates it to EscapeSequenceParser.KittyKeyboardActive so the parser routes bare CSI A/B as KeyPressed (real arrows) and SS3 OA/OB as MouseScrollEvent (wheel under ?1007h+DECCKM), giving structurally correct wheel-vs-arrow disambiguation while preserving native click-drag text selection. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
History panel scrolls on wheel, arrow keys only tick counters; native
click-drag selection still works because no mouse tracking is enabled.
Run:
TERMINA_UNIX_RAW_INPUT=1 TERMINA_KITTY_KEYBOARD=8 \
dotnet run --project demos/Termina.Demo.KittyScroll
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
First Ctrl+C shows a 'Press Ctrl+C again to quit' toast at the bottom center for 2 seconds; a second Ctrl+C within that window calls Shutdown(). Sits above page / focus handling so users can always exit, even from a focus-trapping input control. Under raw-mode UnixConsole (cfmakeraw clears ISIG) Ctrl+C arrives as a KeyPressed(C, Control) event, which this handler intercepts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
History panel + status line + focused input box. Enter echoes the message back into the history. Arrows move the input cursor (focused); wheel still scrolls history (falls through to the page since the input is not IScrollable). Removed the local Ctrl+Q binding — use the new framework Ctrl+C×2 to quit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
FocusPolicy.FirstFocusable so the TextInputNode actually receives keystrokes when the page loads. Without this the page defaulted to FocusPolicy.Manual and typing went nowhere visible. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…RawStdinProbe) Keep only Termina.Demo.KittyScroll — the final prototype demo demonstrating wheel-vs-arrow disambiguation via the kitty keyboard protocol + ?1007h, with a focused TextInput / AI-harness layout and Ctrl+C×2 to quit. The three removed demos were intermediate diagnostic/illustrative projects used during the investigation: - RawStdinProbe: standalone byte-level termios probe - WheelAmbiguity: reproduction of the original ?1007h ambiguity - WheelScrollWorks: intermediate proof that wheel-as-arrow scrolled in non-focus-trapping pages Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR prototypes a new input stack that preserves terminal-native text selection while still supporting scroll-wheel-driven UI scrolling, by switching from SGR mouse tracking to xterm alternate-scroll mode (?1007h) and optionally using the kitty keyboard protocol + raw-byte stdin to disambiguate wheel ticks from real arrow keys.
Changes:
- Replace default mouse-wheel transport from SGR mouse tracking (
?1000h/?1006h) to alternate-scroll mode (?1007h) via newEnableWheelScroll()/DisableWheelScroll()APIs. - Add raw-byte input consoles (Unix termios +
read(), Windows raw-VT +ReadFile) and propagate negotiated capabilities (kitty active) intoEscapeSequenceParser. - Extend
EscapeSequenceParserto handle SS3 sequences, kitty CSI-u / second-form parsing, and wheel-vs-arrow routing; add a new demo and extensive tests.
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Termina.Tests/Terminal/VirtualTerminalTests.cs | Adds coverage for EnableWheelScroll / DisableWheelScroll flags on VirtualTerminal. |
| tests/Termina.Tests/Terminal/DiffingTerminalTests.cs | Verifies DiffingTerminal passes wheel-scroll enable/disable through to the inner terminal. |
| tests/Termina.Tests/Terminal/AnsiCodesTests.cs | Adds assertions for new ANSI constants (?1007h/l, ?1h/l). |
| tests/Termina.Tests/Platform/UnixConsoleByteMappingTests.cs | New tests validating raw-byte → ConsoleKeyInfo mapping and parser behavior without a TTY. |
| tests/Termina.Tests/Input/EscapeSequenceParserTests.cs | Adds baseline tests for ?1007h CSI-arrow wheel behavior and SS3 arrow key behavior. |
| tests/Termina.Tests/Input/EscapeSequenceParserKittyTests.cs | New test suite for kitty keyboard protocol parsing and kitty-vs-wheel routing. |
| Termina.slnx | Adds the new KittyScroll demo project to the solution. |
| src/Termina/Terminal/VirtualTerminal.cs | Introduces WheelScrollEnabled and implements EnableWheelScroll/DisableWheelScroll. |
| src/Termina/Terminal/IAnsiTerminal.cs | Expands mouse docs and adds wheel-only scrolling API surface. |
| src/Termina/Terminal/DiffingTerminal.cs | Pass-through implementations for wheel-only scrolling toggles. |
| src/Termina/Terminal/AnsiTerminal.cs | Implements ?1007h + DECCKM toggling and ensures cleanup on Dispose(). |
| src/Termina/Terminal/AnsiCodes.cs | Adds ?1007h/l and DECCKM ?1h/l constants and documentation. |
| src/Termina/TerminaApplication.cs | Switches startup/shutdown to wheel-only scrolling and adds Ctrl+C double-press shutdown handling. |
| src/Termina/Platform/WindowsConsole.cs | Adds opt-in raw-VT input mode, byte reader thread/channel, UTF-8 input CP handling, and capability reporting. |
| src/Termina/Platform/UnixConsole.cs | Implements raw stdin console using termios + read(), background reader thread/channel, SIGWINCH resize, and capability reporting. |
| src/Termina/Platform/TerminalCapabilities.cs | New capability snapshot type (currently kitty-active flag) for parser configuration. |
| src/Termina/Platform/RawByteKeyMapper.cs | New shared raw-byte → ConsoleKeyInfo mapping helper. |
| src/Termina/Platform/PlatformConsoleFactory.cs | Adds cross-platform TERMINA_RAW_INPUT opt-in (with TERMINA_UNIX_RAW_INPUT alias) and selects raw consoles accordingly. |
| src/Termina/Platform/KittyKeyboardEnhancement.cs | Adds env-var-driven push/pop of kitty keyboard enhancement flags. |
| src/Termina/Platform/IPlatformConsole.cs | Adds default Capabilities property to surface negotiated terminal features. |
| src/Termina/Input/PlatformInputSource.cs | Wires console capabilities into EscapeSequenceParser.KittyKeyboardActive. |
| src/Termina/Input/EscapeSequenceParser.cs | Adds SS3 state, kitty second-form parsing, CSI-u enhancements, and wheel-vs-arrow routing logic. |
| demos/Termina.Demo.KittyScroll/Termina.Demo.KittyScroll.csproj | New demo project for validating the new wheel/arrow disambiguation approach. |
| demos/Termina.Demo.KittyScroll/README.md | Demo documentation and recommended env var settings across platforms/terminals. |
| demos/Termina.Demo.KittyScroll/Program.cs | Demo host setup and explanatory comments. |
| demos/Termina.Demo.KittyScroll/Pages/KittyScrollViewModel.cs | Tracks per-event counters and last key to visualize disambiguation correctness. |
| demos/Termina.Demo.KittyScroll/Pages/KittyScrollPage.cs | Demo UI: scrollable history + focused input, wheel routes to history, arrows to input. |
| benchmarks/Termina.Benchmarks/RenderingBenchmarks.cs | Updates benchmark terminal stub to satisfy new interface methods. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// Resize detection is currently polling-based (matches <see cref="FallbackConsole"/>); SIGWINCH | ||
| /// integration is a follow-up. |
There was a problem hiding this comment.
@copilot apply changes based on this feedback
| if (_restored || !_rawModeEntered) return; | ||
| _restored = true; | ||
| TryLeaveKittyKeyboard(); | ||
| var h = GCHandle.Alloc(_savedTermios, GCHandleType.Pinned); | ||
| try { _ = tcsetattr(StdInFd, Tcsanow, h.AddrOfPinnedObject()); } | ||
| catch { /* ignore */ } | ||
| finally { h.Free(); } | ||
| TerminaTrace.Platform.Debug(this, "UnixConsole termios restored"); |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
81112a7 to
e47414b
Compare
|
FYI, I'm not entirely sure this is the right thing to roll out, been looking at the other TUIs (OpenCode/Claude) and it seems they likely use SGR mouse tracking. |
|
I pushed a stacked hardening branch on top of this PR: Summary of what changed:
Validation on the stacked branch:
Branch for review:
|
Summary
Prototype that disambiguates mouse-wheel ticks from real arrow-key presses without breaking native click-drag text selection, by combining two terminal-protocol layers:
?1007h) routes wheel events through the cursor-key path (SS3 OA/OBunder DECCKM on /CSI A/Bunder DECCKM off).report_all_keys(CSI> 8 u) re-routes real key events throughCSI ... u/CSI 1;mods X(or PUA keycodes). Because terminals implement the kitty enhancement only in their keyboard subsystem, wheel events keep their legacy shape — and the two become structurally distinguishable at the byte level.Net effect on Ghostty / kitty / WezTerm / foot / iTerm2 ≥ 3.5: scroll-wheel scrolls a focused
IScrollable, arrows act as arrows even in an always-focused text input, and the terminal's native text selection (click-drag, triple-click, middle-paste) keeps working because we never enable?1000h/?1006h.Restores the symptoms reported in #192 and removes the need for the F6 selection-mode toggle in #194.
Changes
Default wheel-scroll transport (
?1007h)AnsiCodes.EnableAlternateScroll/DisableAlternateScroll(CSI?1007h/?1007l)IAnsiTerminal.EnableWheelScroll()/DisableWheelScroll()+ implementations inAnsiTerminal,DiffingTerminal,VirtualTerminal, benchmark stubTerminaApplicationcallsEnableWheelScroll()on startup andDisableWheelScroll()infinallyinstead ofEnableMouse()/DisableMouse()EnableMouse()/DisableMouse()and the?1000h/?1006hconstants are kept — apps that need true clicks/drags (e.g. an in-app file picker) can still opt in explicitlyRaw-stdin
UnixConsole(required so the parser can actually see the bytes)UnixConsoleinsrc/Termina/Platform/UnixConsole.csthat bypassesConsole.ReadKey(which foldsCSI AandSS3 OAto the sameUpArrowbefore any parser can see them) and reads raw bytes vialibc.readaftercfmakeraw+VMIN=0 VTIME=1. OPOST is preserved so post-shutdown logging isn't staircased.Dispose,ProcessExit,UnhandledException, andCancelKeyPress.PosixSignalRegistration, with VTIME-tick fallback when SIGWINCH isn't supported.TERMINA_UNIX_RAW_INPUT=1(default behavior unchanged unless the env var is set).Kitty keyboard protocol opt-in
UnixConsolepushesCSI > N uafter entering raw mode and popsCSI < ubefore restoring termios, gated onTERMINA_KITTY_KEYBOARD=<flags>(typical value8forreport_all_keys). Exposed asUnixConsole.KittyKeyboardActive.PlatformInputSourcepropagates the flag toEscapeSequenceParser.KittyKeyboardActive.EscapeSequenceParserextended to:CSI A/B/C/D/H/F/P-SasKeyPressedwhen kitty is active (kitty canonical no-mod form) and asMouseScrollEventwhen kitty is inactive (?1007hwheel emission).OA/OBtoMouseScrollEventwhen kitty is active (Ghostty's mouse subsystem ignores kitty, so wheel still emits the DECCKM-controlled SS3 form). SS3OC/ODstay asKeyPressed(wheel has no horizontal axis).CSI 1;<mods>[:<event>] Xsecond form.:event-typesubfield (swallow non-press),;text-codepointstrailer, and PUA modifier-alone keycodes (57441-57454).ConsoleKey.Framework Ctrl+C double-press to quit
Press Ctrl+C again to quittoast (ToastPosition.BottomCenter, 2s).Shutdown().TerminaApplication.ProcessEvent, above page / focus routing, so users can always exit even from a focus-trapping input (e.g. an AI-chat text box). Replaces the per-demo Ctrl+Q convention; required undercfmakeraw(ISIG cleared, so SIGINT no longer fires).Demo
demos/Termina.Demo.KittyScroll— AI-harness layout: pre-populatedStreamingTextNodehistory, status line with last-key + per-class counters, focusedTextInputNodeat the bottom. Enter echoes the message back into the history. Run withTERMINA_UNIX_RAW_INPUT=1 TERMINA_KITTY_KEYBOARD=8 dotnet run --project demos/Termina.Demo.KittyScroll.Tradeoffs / caveats
TERMINA_KITTY_KEYBOARD, bareCSI A/Bis still treated as wheel (i.e. an app with an always-focused text input still sees ambiguity on terminals that don't implement the kitty protocol — macOS Terminal.app being the main casualty). Apps targeting those terminals can fall back to PgUp/PgDn (still wired inStreamingTextNode.HandleInput).Console.ReadKeypath unchanged. The kitty disambiguation only works whenTERMINA_UNIX_RAW_INPUT=1is set, becauseConsole.ReadKeyfolds the two arrow forms before the parser runs. Flipping the default is a follow-up.conhost.exeignores?1007h. Modern Windows Terminal and ConPTY support it; the kitty path is Unix-only.Verification
dotnet build Termina.slnx— succeeds (incl. demos and benchmarks)dotnet test— 1092 / 1092 passing, including 30 newEscapeSequenceParserKittyTests,UnixConsoleByteMappingTests, and updatedAnsiCodes/DiffingTerminal/VirtualTerminaltestsCSI < upopped before termios restore.Suggested follow-ups (out of scope here)
TERMINA_UNIX_RAW_INPUT=1+TERMINA_KITTY_KEYBOARD=8).$TERM/$TERM_PROGRAMindicates a kitty-capable terminal (Ghostty / kitty / foot / wezterm / iTerm2 ≥ 3.5); opt-out via env var.Console.ReadKeypath.EnableMouse()opt-in for apps that want app-owned selection).docs/concepts/input-handling.mdanddocs/components/streaming-text-node.mdonce framing is decided.Closes (potentially) #192. Supersedes (potentially) #194.