Fixes #4932 - Enable Inline apps in addition to Full Screen#4933
Fixes #4932 - Enable Inline apps in addition to Full Screen#4933
Inline apps in addition to Full Screen#4933Conversation
…nline mode, OutputBufferImpl.ClearContents(bool), InlineCLI example Agent-Logs-Url: https://github.com/gui-cs/Terminal.Gui/sessions/9087ee40-33d2-4446-9d01-60df80dba4a3 Co-authored-by: tig <585482+tig@users.noreply.github.com>
|
@copilot this misses the requirement that Application/Driver.Screen needs to be set based on the first layout. The example should be setting its runnable view.height = dim.auto(minimumContentSize: 10) and .y = pos.anchorend(). |
…ample to use Pos.AnchorEnd() and Dim.Auto(minimumContentDim: 10) Agent-Logs-Url: https://github.com/gui-cs/Terminal.Gui/sessions/7427eedb-1394-405b-a255-533e5a5fdea9 Co-authored-by: tig <585482+tig@users.noreply.github.com>
Addressed in 82b8fac:
|
|
@copilot what happens if the cursor is at the bottom of the terminal and the first layout results in a screen.height of 10? (For example) |
…ition cursor below on dispose Agent-Logs-Url: https://github.com/gui-cs/Terminal.Gui/sessions/03480b9f-9dd3-4a5d-acb0-a934f230e11a Co-authored-by: tig <585482+tig@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gui-cs/Terminal.Gui/sessions/03480b9f-9dd3-4a5d-acb0-a934f230e11a Co-authored-by: tig <585482+tig@users.noreply.github.com>
This comment was marked as outdated.
This comment was marked as outdated.
Begin() was calling LayoutAndDraw() directly before the main loop started, bypassing the IterationImpl deferral logic. In inline mode this caused a first draw using the default 80x25 fallback size (placing the view at the wrong row), followed by a second correct draw once the ANSI size monitor reported the real terminal dimensions. Changes: - Skip LayoutAndDraw() in Begin() when AppModel.Inline is active; the first render is deferred to IterationImpl until InitialSizeReceived is confirmed (or 500ms timeout). - Add InitialSizeReceived to ISizeMonitor/AnsiSizeMonitor so the main loop knows when the real terminal size has arrived. - Add inline size deferral logic in IterationImpl with Trace instrumentation for diagnostics. - Remove QueryNativeTerminalSize() from AnsiOutput (pure ANSI only). - Update InlineCLI example: replace Label with ListView + ObservableCollection. - Add InlineDrawTimingTests (4 trace-based tests proving deferral works). - Add AnsiSizeMonitor InitialSizeReceived tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
In inline mode, the view was always drawn at the bottom of the terminal because AnchorEnd() positioned against the full terminal height. When the cursor was near the top, the view appeared far below the expected position. Fix: query the cursor position via ANSI DECXCPR (CSI ?6n) at startup and use the response to: - Resize Screen.Height to (termHeight - cursorRow) so the view occupies only the rows available below the cursor. - Set InlineRowOffset on AnsiOutput so Screen row 0 maps to the cursor's terminal row in SetCursorPositionImpl. Changes: - ISizeMonitor: add InitialCursorRow property (default 0) - AnsiSizeMonitor: send CPR query in Initialize() for inline mode; parse DECXCPR response; InitialSizeReceived now requires both size AND cursor position responses before returning true - AnsiOutput: add InlineRowOffset property; apply in SetCursorPositionImpl - ApplicationImpl.Screen: LayoutAndDraw resizes Screen and sets row offset on first inline draw - ApplicationImpl: add InlineCursorRow internal property - IterationImpl: pass cursor row from SizeMonitor when inline confirmed - InlineCLI example: remove AnchorEnd(), view starts at Y=0 - Add 3 new tests for cursor position query behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gui-cs/Terminal.Gui/sessions/80eb1d41-0dcf-4d44-8d13-f5d2a11ca9f4 Co-authored-by: tig <585482+tig@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gui-cs/Terminal.Gui/sessions/80eb1d41-0dcf-4d44-8d13-f5d2a11ca9f4 Co-authored-by: tig <585482+tig@users.noreply.github.com>
Replaces scattered inline-mode state with a centralized InlineState struct, updating ApplicationImpl, AnsiOutput, DriverImpl, and IDriver to use it. All inline rendering, scrolling, and layout logic now references InlineState, supporting dynamic region growth. Updates MainLoopCoordinator and ApplicationMainLoop to wire up InlineState, and enhances the InlinePromptView demo to display live state. Includes minor UI and code style improvements.
Added InlineResizeTests to verify that after a terminal resize in inline mode, the inline region resets its Y position and width, and the driver's InlineCursorRow is reset to 0. Both tests use the ANSI driver and simulate inline mode scenarios.
Updated ApplicationImpl.Screen.cs to prevent cursor row underflow during overflow adjustment using Math.Max. Added InlineModeTests covering App.Screen positioning, sizing, overflow handling, dynamic growth, resize behavior, output buffer state, mouse event adjustment, and App.Screen vs Driver.Screen independence. All tests follow project conventions and are marked as Copilot-generated.
Inline apps in addition to Full Screen
There was a problem hiding this comment.
Pull request overview
Adds a new AppModel.Inline rendering mode to Terminal.Gui so apps can render inline in the terminal’s primary scrollback buffer (instead of switching to the alternate screen buffer), with supporting driver/runtime plumbing, tests, and examples.
Changes:
- Introduces
AppModeland propagates it throughIApplication→ component factory → driver/output to control inline vs fullscreen rendering. - Implements inline-mode output semantics (skip alt-buffer, cursor-row offset, “initially clean” output buffer, dirty-line skipping, startup-gate deferral).
- Adds new examples (
InlineCLI,InlineColorPicker) and extensive unit tests for inline rendering, resize, mouse offsetting, and startup-gate behavior.
Reviewed changes
Copilot reviewed 53 out of 53 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| Tests/UnitTestsParallelizable/Drivers/Output/InlineDrawTimingTests.cs | DEBUG-only tracing tests for first-render deferral timing in inline mode. |
| Tests/UnitTestsParallelizable/Drivers/Output/AppModelTests.cs | Tests AppModel enum and inline-mode OutputBufferImpl.ClearContents behavior. |
| Tests/UnitTestsParallelizable/Drivers/AnsiHandling/KittyKeyboardProtocolDetectorTests.cs | Adds coverage for startup-gate registration/completion. |
| Tests/UnitTestsParallelizable/Drivers/AnsiHandling/AnsiStartupGateTests.cs | New tests for gate readiness, completion, and timeout. |
| Tests/UnitTestsParallelizable/Drivers/AnsiDriver/AnsiSizeMonitorTests.cs | Extends tests for size readiness, CPR parsing, and inline/fullscreen readiness rules. |
| Tests/UnitTestsParallelizable/Drivers/Ansi/AnsiOutputInlineModeTests.cs | Verifies AnsiOutput behavior differences between fullscreen and inline. |
| Tests/UnitTestsParallelizable/Application/ScreenTests.cs | Updates Screen behavior tests and adds inline sizing assertions. |
| Tests/UnitTestsParallelizable/Application/Mouse/InlineMouseOffsetTests.cs | Regression test for inline mouse coordinate adjustment with synthesized events. |
| Tests/UnitTestsParallelizable/Application/InlineResizeTests.cs | Tests inline resize resets (screen origin and cursor-row state). |
| Tests/UnitTestsParallelizable/Application/ApplicationMainLoopTests.cs | Tests startup-gate deferral behavior in the main loop. |
| Terminal.sln | Adds InlineCLI/InlineColorPicker projects and extra solution platforms. |
| Terminal.Gui/Views/Runnable/RunnableWrapper.cs | New helper runnable wrapper to run a view and extract a typed result. |
| Terminal.Gui/Views/Color/ColorPicker.cs | Minor refactors/formatting and autocomplete setup adjustments. |
| Terminal.Gui/ViewBase/Adornment/BorderView.cs | Alters title focus rendering logic. |
| Terminal.Gui/Drivers/Output/OutputBufferImpl.cs | Adds inline-mode buffer semantics and ClearContents(initiallyDirty) overload. |
| Terminal.Gui/Drivers/Output/OutputBase.cs | Skips rendering rows that have no dirty cells. |
| Terminal.Gui/Drivers/Output/IOutputBuffer.cs | Exposes DirtyLines so outputs can skip clean rows. |
| Terminal.Gui/Drivers/ISizeMonitor.cs | Adds default interface properties for initial size readiness and cursor row. |
| Terminal.Gui/Drivers/InlineState.cs | New inline-mode state container (cursor row). |
| Terminal.Gui/Drivers/IDriver.cs | Adds AnsiStartupGate, InlineState, and AppModel to the driver API. |
| Terminal.Gui/Drivers/IComponentFactory.cs | Propagates AppModel into output creation. |
| Terminal.Gui/Drivers/DriverImpl.cs | Tracks terminal size separately from output buffer size; supports inline buffer resizing. |
| Terminal.Gui/Drivers/ComponentFactoryImpl.cs | Implements the AppModel property for factories. |
| Terminal.Gui/Drivers/AnsiHandling/TerminalColorDetector.cs | Registers terminal-color queries with the startup gate. |
| Terminal.Gui/Drivers/AnsiHandling/KittyKeyboardProtocolDetector.cs | Registers kitty keyboard probe with the startup gate. |
| Terminal.Gui/Drivers/AnsiHandling/IAnsiStartupGate.cs | New startup gate interface. |
| Terminal.Gui/Drivers/AnsiHandling/AnsiStartupQuery.cs | New enum for tracked startup queries. |
| Terminal.Gui/Drivers/AnsiHandling/AnsiStartupGate.cs | New gate implementation (query register/complete/timeout). |
| Terminal.Gui/Drivers/AnsiDriver/AnsiSizeMonitor.cs | Adds CPR query/parsing and gate completion for size/cursor readiness. |
| Terminal.Gui/Drivers/AnsiDriver/AnsiOutput.cs | Inline-mode output path (no alt-buffer) and cursor offset via App.Screen. |
| Terminal.Gui/Drivers/AnsiDriver/AnsiComponentFactory.cs | Creates AnsiOutput with the configured AppModel. |
| Terminal.Gui/Configuration/SourceGenerationContext.cs | Registers AppModel for source-gen JSON serialization. |
| Terminal.Gui/App/Mouse/ApplicationMouse.cs | Adjusts mouse coordinates for inline mode with save/restore semantics. |
| Terminal.Gui/App/MainLoop/MainLoopCoordinator.cs | Wires startup gate into driver and ANSI probes; wires App.Screen getter into AnsiOutput. |
| Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs | Defers first render until startup gate is ready; enables inline output buffer mode. |
| Terminal.Gui/App/Legacy/Application.Driver.cs | Adds static change events for AppModel and ForceInlineCursorRow. |
| Terminal.Gui/App/IApplication.cs | Adds instance properties for AnsiStartupGate, AppModel, ForceInlineCursorRow. |
| Terminal.Gui/App/AppModel.cs | New enum defining fullscreen vs inline rendering modes. |
| Terminal.Gui/App/ApplicationImpl.Screen.cs | Implements inline App.Screen sub-rectangle behavior, growth, and resize reset. |
| Terminal.Gui/App/ApplicationImpl.Run.cs | Defers initial LayoutAndDraw when startup gate isn’t ready. |
| Terminal.Gui/App/ApplicationImpl.Lifecycle.cs | Subscribes/unsubscribes to new static property change events. |
| Terminal.Gui/App/ApplicationImpl.Driver.cs | Propagates AppModel into factories and creates startup gate for inline mode. |
| Terminal.Gui/App/ApplicationImpl.cs | Initializes instance AppModel/ForceInlineCursorRow from legacy static properties. |
| Terminal.Gui/App/Application.cs | Adds configurable static Application.AppModel and ForceInlineCursorRow. |
| plans/inline-mode-plan.md | Adds an implementation plan/architecture notes for inline mode. |
| Examples/UICatalog/UICatalogRunnable.cs | Refactors About dialog logo rendering and adds a version label. |
| Examples/UICatalog/Logo.cs | New reusable logo view (gradient ASCII art). |
| Examples/PromptExample/Program.cs | Simplifies prompt example layout and sets main window text/alignment. |
| Examples/InlineColorPicker/Program.cs | New inline-mode runnable example that prints selected color to stdout. |
| Examples/InlineColorPicker/InlineColorPicker.csproj | New example project. |
| Examples/InlineCLI/README.md | Documents inline-mode example usage. |
| Examples/InlineCLI/InlineCLI.csproj | New example project. |
| Examples/InlineCLI/InlineCLI.cs | New inline CLI-style interactive example built with inline mode. |
Replaces InlineCursorRow/InlineState with Point InlinePosition (Y=row, X reserved). Updates all APIs, properties, and tests to use the new model. Removes InlineState struct. Improves flexibility and clarifies inline region start for future enhancements.
Comprehensive documentation for the new Inline Mode (`AppModel.Inline`) in Terminal.Gui, including rendering modes, usage examples, coordinate system, startup sequence, dynamic growth, resize handling, output buffer, clean exit, testing strategies, and the `RunnableWrapper<TView>` utility. Suitable for CLI tools that require output in the terminal scrollback buffer.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Added the InlineSelect example project demonstrating inline OptionSelector usage with command-line options and orientation flags. Updated drivers.md to document new ANSI driver inline mode features, including AppModel, InlinePosition, buffer handling, and startup/shutdown behavior. Configured solution and project files accordingly.
….Gui into copilot/enable-inline-rendering
The AnsiStartupGate property in IApplication was updated to use the concrete AnsiStartupGate? type instead of the IAnsiStartupGate? interface. This change affects how the property is referenced and assigned throughout the codebase.
Restores the original terminal-absolute mouse coordinates to mouseEvent.ScreenPosition before recursively calling RaiseMouseEvent. This ensures correct offset subtraction and accurate event handling below the popover after dismissal.
|
This is slicker than snot. An OptionSelector as a CLI: Here's the code // InlineSelect — demonstrates using RunnableWrapper<OptionSelector, int?> in inline mode.
//
// Renders an OptionSelector inline in the terminal with options from the command line.
// Supports horizontal or vertical orientation via --horizontal / --vertical flags.
// Hot keys are auto-assigned from option text.
//
// Usage:
// dotnet run --project Examples/InlineSelect -- Apple Banana Cherry
// dotnet run --project Examples/InlineSelect -- --horizontal Red Green Blue Yellow
// dotnet run --project Examples/InlineSelect -- --vertical One Two Three
using Terminal.Gui.App;
using Terminal.Gui.ViewBase;
using Terminal.Gui.Views;
// Parse command-line arguments
Orientation orientation = Orientation.Vertical;
List<string> options = [];
foreach (string arg in args)
{
if (arg is "--horizontal" or "-h")
{
orientation = Orientation.Horizontal;
}
else if (arg is "--vertical" or "-v")
{
orientation = Orientation.Vertical;
}
else
{
options.Add (arg);
}
}
if (options.Count == 0)
{
Console.Error.WriteLine ("Usage: InlineSelect [--horizontal|--vertical] <option1> <option2> ...");
return 1;
}
// Enable inline mode before Init
Application.AppModel = AppModel.Inline;
IApplication app = Application.Create ().Init ();
// Build the OptionSelector with command-line options
OptionSelector selector = new ()
{
Labels = options,
Orientation = orientation,
AssignHotKeys = true
};
// Wrap in RunnableWrapper — auto-extracts Value via IValue<int?>
RunnableWrapper<OptionSelector, int?> wrapper = new (selector)
{
Title = "Select an option (Enter to accept, Esc to cancel)",
Width = Dim.Fill ()
};
// Run inline — blocks until user accepts or cancels
app.Run (wrapper);
int? result = wrapper.Result;
app.Dispose ();
if (result is { } selectedIndex && selectedIndex >= 0 && selectedIndex < options.Count)
{
Console.WriteLine (options [selectedIndex]);
return 0;
}
return 1; |
Refines inline mode to prevent extra scrolling when the view fits exactly at the terminal's bottom row and ensures the shell prompt appears correctly after inline content. Adjusts mouse event handling to avoid double subtraction of inline offsets when dismissing popovers. Adds targeted tests for both cursor positioning and mouse coordinate adjustment. Also sets OptionSelector border style to Rounded.
Add WindowsVTInputHelper.Flush() using FlushConsoleInputBuffer to clear pending input events, preventing protocol-specific sequences from leaking into the shell. Update AnsiInput to call this method for WindowsVT, ensuring consistent input buffer flushing across platforms.
BDisp
left a comment
There was a problem hiding this comment.
Finally, this impressive feature has been implemented. Congratulations on your effort and resilience. Although the terminal gets all jumbled up in the back buffer when resizing, what's most relevant is that the currently visible app is all aligned and properly adjusted. Thanks.
YourRobotOverlord
left a comment
There was a problem hiding this comment.
This is a great addition to the library! I love it!
|
I've already tested all 3 examples and they're all fantastic. It's like running scripts from the command line and maintaining the results across all calls. I love that too. |
|
This is so much nicer than every one of the CLIs that I've tried, especially for editing your input. None of the others I've used allow modifier keys while editing, like Ctrl+Del. You're stuck hammering or holding the edit keys to make large changes. None of them support text selection, either. Or Undo/Redo. |



Fixes
Inlinerendering in addition to currentAlternateScreenBuffer#4932Adds
AppModel.Inlinemode so Terminal.Gui apps can render inline in the primary scrollback buffer (like Claude Code CLI / GitHub Copilot CLI) instead of switching to the alternate screen buffer viaCSI ?1049h.Here's the new
./Examples/InlineColorPickerexample which shows how easy it is to run any View as a CLI:How Inline Mode Works
Concept
In
AppModel.Inline, the app renders within the terminal's primary scrollback buffer starting at the current cursor position. No alternate screen buffer (CSI ?1049h) is used. On exit, the rendered content stays in scrollback history and the shell prompt appears immediately below.Architecture:
App.Screenas the coordinate systemThe key architectural insight is that
IApplication.Screenis the source of truth for where the app renders:App.Screen==Driver.Screen(full terminal, Y=0)App.Screenis a sub-rectangle ofDriver.ScreenApp.Screen.Y= terminal row where the inline region startsApp.Screen.Height= number of rows the app occupies (sized by content)App.Screen.Width= full terminal widthAll coordinate offsetting flows from this single property —
AnsiOutput.SetCursorPositionImpladdsApp.Screen.Yto all row coordinates, and mouse events subtract it.Startup sequence
AnsiSizeMonitorsendsESC[6n(Device Status Report) to discover the current cursor rowAnsiStartupGatedefers the firstLayoutAndDrawuntil both the CPR response and terminal size query completeLayoutAndDrawdoes a layout pass with full terminal size, measures the view's desired height, then setsApp.Screento the sub-rectangle starting at the cursor rowDynamic growth
As content is added (e.g., items added to a ListView), subsequent
LayoutAndDrawcalls detect when the view's desired height exceedsApp.Screen.Heightand grow the region:Resize handling
On terminal resize: reset cursor row to 0, clear the terminal (
CSI H+CSI 2J), and re-do initial sizing from scratch. This matches the behavior of Claude Code CLI.Output buffer
In inline mode,
OutputBufferImpl.ClearContents()initializes cells withIsDirty = false(nottrueas in fullscreen). Only cells explicitly drawn by the app are flushed to the terminal — the rest of the visible terminal stays untouched.Mouse coordinates
Mouse events arrive in absolute terminal coordinates.
ApplicationMouse.RaiseMouseEventsubtractsApp.Screen.YfromScreenPosition.Ybefore dispatching to views. A save/restore pattern prevents theMouseInterpretergenerator from seeing already-adjusted coordinates during synthesized double-click events.Clean exit
On shutdown, the cursor is moved to
App.Screen.Y + App.Screen.Height(the row below the rendered region) so the shell prompt appears in the right place.CSI ?1049lis NOT emitted.Usage
ForceInlineCursorRow(testing)For unit tests that can't do real CPR queries,
IApplication.ForceInlineCursorRowbypasses the startup gate and sets the cursor row directly:Examples
Examples/InlineCLI/Examples/InlineColorPicker/Test Coverage
InlineModeTests.csInlineResizeTests.csInlineMouseOffsetTests.csAnsiOutputInlineModeTests.csAnsiSizeMonitorTests.csAnsiStartupGateTests.csAppModelTests.csInlineDrawTimingTests.cs