Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
7fca62c
Update Examples link in README.md (#5104)
tig Apr 28, 2026
e4388c8
Reframe layout docs around responsive UI and instructional style (#5110)
Copilot Apr 28, 2026
3df9137
Merge branch 'develop' into backmerge/v2.0.0
tig Apr 29, 2026
8442c55
Merge pull request #5113 from gui-cs/backmerge/v2.0.0
tig Apr 29, 2026
bf83d8d
Fix XML comment has cref attribute 'Buttons' that could not be resolv…
BDisp Apr 29, 2026
1b4f221
docs: tweak hero gif stats per #5108 (#5121)
livlign Apr 29, 2026
b43d101
Fixes #5088 - Margin Shadow sizing wrong (#5101)
tig Apr 29, 2026
41da905
Fixes #5114. Remove main from publish branch triggers; use tags only …
tig Apr 29, 2026
0410158
Fixes #5072. TableView reserves space for later columns when laying o…
harder Apr 29, 2026
96fbe8a
Fixes #5119. Popover isn't recalculating it's position when terminal …
BDisp Apr 29, 2026
af8bbe5
Fixes #5122. Visible popovers are always layout and redrawn in every …
BDisp Apr 30, 2026
d079d36
Fixes #5075. TableView ShowVerticalCellLines renders incorrectly with…
harder Apr 30, 2026
6361f33
Fixes #5132. TableView Home/End navigation with FullRowSelect (#5133)
Copilot May 1, 2026
c50a81e
Fixes #5068. TableView appends ellipsis indicator when cell content i…
harder May 1, 2026
688c292
Reverts #5133. Revert TableView Home/End FullRowSelect change (#5136)
tig May 1, 2026
57f49fd
Fixes #5130. Fix OutputBufferImpl race condition on Contents referenc…
tig May 1, 2026
4b4bcfd
Fixes #5137 - BREAKING CHANGE - Rename `TableSelection.Cursor` to `Ta…
Copilot May 3, 2026
b2167f6
Fixes #5145. ReadOnly TextView mouse selection still cannot include f…
BDisp May 3, 2026
93f0d23
Fixes #5137. Add custom header styling for TableView (#5138)
Copilot May 3, 2026
8157d73
Fixes #5112. AOT config warning cleanup (#5134)
harder May 3, 2026
ef9a96a
Fixes #5149. Improve DropDownList keyboard use when closed (#5151)
Copilot May 4, 2026
4718f6f
Fixes #5157. Add `RunAsync(IRunnable, CancellationToken)` (#5159)
Copilot May 4, 2026
020c3ff
Fixes #5158. Refactor `FileDialog` base type from Dialog to Dialog<IR…
Copilot May 4, 2026
b152222
Fixes #5103. Disable NerdFonts default in FileDialogExamples scenario…
Copilot May 4, 2026
3a782c9
fix: Key.Equals honors GetHashCode contract; drops Handled from ident…
tig May 5, 2026
2d6b1a6
docs: clarify AI test markers — // Claude and // CoPilot are both acc…
tig May 5, 2026
74aa1d4
fix: UnixRawModeHelper safe restore on crash + valid-state guard (#51…
tig May 5, 2026
d6c9519
docs: clarify SessionBegun/SessionEnded as token lifecycle hooks (#51…
tig May 5, 2026
ea8f237
fix: ApplicationImpl.Invoke throws NotInitializedException after Disp…
tig May 5, 2026
9cbb8b8
fix: ConcurrentDictionaryJsonConverter rejects duplicate keys (#5173)…
tig May 5, 2026
787d306
refactor: remove Region.DrawOuterBoundary debug API (#5168) (#5183)
tig May 5, 2026
5cfc86f
fix: KeyBindings preserves Key on bindings created via Add (#5171) (#…
tig May 5, 2026
a6c72fa
fix: Region.XOR computes correct symmetric difference (#5167) (#5178)
tig May 5, 2026
7fd4806
Fixes #issue. Correct AI guidance API entries for typed views and wra…
Copilot May 5, 2026
64502ab
Fixes #5148. Add selection and copy-to-clipboard to Markdown view (#5…
Copilot May 5, 2026
80f741e
Fixes #3995. Add notify-clet.yml: dispatch to gui-cs/clet on develop …
Copilot May 5, 2026
e409f3a
Fixes #5192. Fix MarkdownView GetContentHeight overestimating height …
Copilot May 5, 2026
ceddb29
Fixes #5193. Emit OSC 8 hyperlink sequences in Driver.ToAnsi() (#5195)
Copilot May 5, 2026
4003aca
notify-clet: deterministic version handoff + flat-container poll (#5198)
tig May 5, 2026
842b607
Update package versions and remove nullable from tests
tig May 6, 2026
59962ad
Fixes #3961. Replace ColorPicker TextField+autocomplete with DropDown…
Copilot May 6, 2026
03d26c7
Fixes #5203 - Add TrySetValueFromString to IValue for IParsable-based…
Copilot May 6, 2026
4f000d0
Fixes #4859. Render to /dev/tty when stdout is redirected (#5208)
Copilot May 6, 2026
14c7834
Fixes #5209. TableView header separator lines draw with focus attribu…
Copilot May 6, 2026
ed1d7f4
Fixes #5211. FlagSelector ENTER should not toggle, just accept. (#5212)
Copilot May 6, 2026
9a37328
Fixes #5202 - BREAKING CHANGE- Refactor LinearRange to implement IVal…
tig May 6, 2026
85c955f
Fixes #5213. Security: fix PowerShell command injection in WSLClipboa…
Copilot May 6, 2026
ffab857
Fixes #5216. FileDialog: reject path-traversal in Rename/New and skip…
Copilot May 6, 2026
985536d
Fixes #5215. Sanitize control characters to prevent terminal escape i…
Copilot May 6, 2026
f031d10
Fixes #5214. Security: restrict Link.OpenUrl to safe URI schemes; rep…
Copilot May 6, 2026
af8b27d
Fixes #5215. Bound ANSI parser held content to prevent memory DoS (#5…
Copilot May 6, 2026
814be23
Fixes #5227. Make links inside markdown table cells fully interactive…
Copilot May 7, 2026
981f3ca
Revise security policy for supported versions (#5235)
tig May 7, 2026
f979f73
Fixes #5206. Add IValue<int> support to ScrollSlider (#5229)
Copilot May 7, 2026
c0387f3
Fixes #5153. Make Markdown work like Label (#5231)
harder May 7, 2026
ff37f84
Fixes #5140. TableView: support CollectionNavigator = null to disable…
harder May 7, 2026
385d224
Fixes #5126. TableView: don't raise Activating when click is outside …
harder May 7, 2026
15033b3
Fixes #5076. Allow hiding TableView outer vertical lines (#5234)
harder May 7, 2026
836db66
Fixes #5230 - Markdown checklist glyph rendering (#5236)
Copilot May 7, 2026
6ec0175
Fixes #5237. Fix Markdown table row height calculation (#5238)
Copilot May 7, 2026
86c3fde
Fixes #5239. AOT configuration initialization crash (#5240)
Copilot May 7, 2026
c75553c
Set release label to 'beta' for v2.1.0-beta.1
github-actions[bot] May 7, 2026
46c7626
Merge remote-tracking branch 'origin/main' into release/v2.1.0-beta.1
Copilot May 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .claude/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ public class MyDialog : Runnable<string?>

**Disposal**: "Whoever creates it, owns it" - `Run<T>()` auto-disposes, `Run(instance)` requires manual disposal.

### RunnableWrapper<TView, TResult>

`RunnableWrapper<TView, TResult>` wraps a `View` as a runnable without adding prompt buttons.

- Clears wrapper `KeyBindings` and `MouseBindings` so the wrapped view handles input.
- Sets `CommandsToBubbleUp = [Command.Accept]`.
- On accept, extracts result via `ResultExtractor` if set, otherwise from `IValue<TResult>.Value` when implemented.
- Unlike `Prompt`, it does not add OK/Cancel buttons.

---

## View Hierarchy
Expand Down Expand Up @@ -443,6 +452,24 @@ using (IApplication app = Application.Create ().Init ())

## Quick Reference

### IValue<T> Pattern

All typed views expose values through `IValue<T>.Value`. Avoid guessing member names like `.Date`, `.Time`, or `.Color`.

| View | IValue<T> |
|------|-----------|
| TextField | `IValue<string>` |
| NumericUpDown<T> | `IValue<T>` |
| DatePicker | `IValue<DateTime>` |
| TimeEditor | `IValue<TimeSpan>` |
| ColorPicker | `IValue<Color?>` |
| AttributePicker | `IValue<Attribute?>` |
| CheckBox | `IValue<CheckState>` |
| OptionSelector | `IValue<int?>` |
| FlagSelector | `IValue<int?>` |

Implementing `IValue<T>` requires `ValueChanging`, `ValueChanged`, and `ValueChangedUntyped`.

### View Properties

| Property | Purpose |
Expand Down Expand Up @@ -478,3 +505,9 @@ DriverRegistry.Names.WINDOWS // "windows"
DriverRegistry.Names.UNIX // "unix"
DriverRegistry.Names.DOTNET // "dotnet"
```

### Gotchas

- `Terminal.Gui.Drawing.Attribute` may conflict with `System.Attribute` with implicit usings. Use `using TgAttribute = Terminal.Gui.Drawing.Attribute;` or fully qualify.
- `Color.TryParse (string, out Color?)` uses nullable out; `Color.TryParse (string?, IFormatProvider?, out Color)` uses non-nullable out.
- `OpenDialog` results are exposed through `FilePaths`; related state includes `AllowsMultipleSelection`, `Canceled`, and `OpenMode`.
8 changes: 6 additions & 2 deletions .claude/rules/testing-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@

**⚠️ AI-created tests MUST follow these patterns exactly:**

1. **Add comment indicating the test was AI generated**
- Example: `// CoPilot - ChatGPT v4`
1. **Add a comment indicating the test was AI generated**
- Either of these forms is acceptable; both are well-established in the codebase:
- `// Claude - <model>` (e.g. `// Claude - Opus 4.7`)
- `// CoPilot - <model>` (e.g. `// CoPilot - ChatGPT v4`)
- Use whichever matches the agent that produced the test, with the model identifier.
- **Reviewers (human or automated) should not flag inconsistency between these two forms** — both have been used widely; consistency *of* a marker matters, not which one.

2. **Make tests granular**
- Each test should cover smallest area possible
Expand Down
77 changes: 77 additions & 0 deletions .github/workflows/notify-clet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Notify clet

# Fires repository_dispatch to gui-cs/clet so that every TG develop NuGet
# publish and every TG release tag drives a matching clet build/publish.
# See gui-cs/clet#30 and gui-cs/clet D-020 for context.
#
# Design notes:
# - The version comes from the `published-version` artifact uploaded by
# publish.yml. This is deterministic — no NuGet search-API race.
# - We then poll NuGet's flat-container API (the endpoint that
# `dotnet restore` actually hits) until the package is downloadable.
# The flat-container is updated soon after publish; the search API
# (queried by `dotnet package search`) lags by minutes, which broke
# this workflow once before — see gui-cs/clet workflow run 25406348354.
# - Channel is derived from the SemVer suffix: contains '-' ⇒ develop.

on:
workflow_run:
workflows: ["Publish Terminal.Gui to Nuget"]
types: [completed]

permissions:
contents: read
actions: read # required to download artifacts from the triggering run

jobs:
notify-clet:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- name: Download published-version artifact
uses: actions/download-artifact@v4
with:
name: published-version
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Determine version + channel
id: version
run: |
TG_VER=$(cat version.txt | tr -d '[:space:]')
if [ -z "$TG_VER" ]; then
echo "::error::Empty version.txt from upstream publish workflow"
exit 1
fi
echo "tg_version=$TG_VER" >> "$GITHUB_OUTPUT"
if [[ "$TG_VER" == *-* ]]; then
echo "event_type=tg-develop-published" >> "$GITHUB_OUTPUT"
echo "Channel: develop ($TG_VER)"
else
echo "event_type=tg-released" >> "$GITHUB_OUTPUT"
echo "Channel: release ($TG_VER)"
fi

- name: Wait for NuGet flat-container to index the version
run: |
VER="${{ steps.version.outputs.tg_version }}"
for i in $(seq 1 60); do
if curl -fsS https://api.nuget.org/v3-flatcontainer/terminal.gui/index.json \
| jq -r '.versions[]' | grep -qx "$VER"; then
echo "Indexed on flat-container: $VER (after $((i*10))s)"
exit 0
fi
echo "Waiting for $VER on NuGet flat-container ($i/60, 10s each)..."
sleep 10
done
echo "::error::Timed out after 10min waiting for $VER on flat-container"
exit 1

- name: Dispatch to gui-cs/clet
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.CLET_DISPATCH_PAT }}
repository: gui-cs/clet
event-type: ${{ steps.version.outputs.event_type }}
client-payload: |
{"tg_version": "${{ steps.version.outputs.tg_version }}"}
15 changes: 15 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ jobs:

- name: Publish Terminal.Gui.${{ steps.gitversion.outputs.SemVer }} to NuGet.org
run: dotnet nuget push Terminal.Gui/bin/Release/Terminal.Gui.${{ steps.gitversion.outputs.SemVer }}.nupkg --skip-duplicate --api-key ${{ secrets.NUGET_API_KEY }}

# Deterministic version handoff to notify-clet.yml. Avoids the NuGet
# search-API indexing lag that caused gui-cs/clet to skip TG develop.37
# (see clet workflow run 25406348354). notify-clet.yml downloads this
# artifact via workflow_run + actions/download-artifact and then polls
# NuGet flat-container until the version is restorable.
- name: Record published version for notify-clet
run: echo "${{ steps.gitversion.outputs.SemVer }}" > version.txt

- name: Upload published-version artifact
uses: actions/upload-artifact@v4
with:
name: published-version
path: version.txt
retention-days: 7

# - name: Delist old NuGet packages
# if: startsWith(github.ref, 'refs/tags/v')
Expand Down
35 changes: 32 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,32 @@ See `.claude/cookbook/` for common UI patterns:
|ViewArrangement|Enum|Movable,Resizable,Overlapped
```

### IValue<T> Pattern (Critical)

All typed views expose their data through `IValue<T>.Value`. Do not guess property-specific names such as `.Date`, `.Time`, or `.Color`.

| View | IValue<T> |
|------|-----------|
| TextField | `IValue<string>` |
| NumericUpDown<T> | `IValue<T>` |
| DatePicker | `IValue<DateTime>` |
| TimeEditor | `IValue<TimeSpan>` |
| ColorPicker | `IValue<Color?>` |
| AttributePicker | `IValue<Attribute?>` |
| CheckBox | `IValue<CheckState>` |
| OptionSelector | `IValue<int?>` |
| FlagSelector | `IValue<int?>` |

Implementing `IValue<T>` requires `ValueChanging`, `ValueChanged`, and `ValueChangedUntyped`.

### RunnableWrapper<TView, TResult>

- Wraps a `View` as a runnable with typed results.
- Clears wrapper `KeyBindings` and `MouseBindings` so the wrapped view handles input.
- Does not add OK/Cancel buttons (unlike `Prompt`).
- Sets `CommandsToBubbleUp = [Command.Accept]`.
- On accept, it extracts results via `ResultExtractor` if provided; otherwise via `IValue<TResult>.Value` when available.

### Terminal.Gui.Views (180+ types)
```
[Core Controls]
Expand All @@ -364,7 +390,7 @@ See `.claude/cookbook/` for common UI patterns:
|DropDownList|Class|Dropdown,Source,SelectedItem
|ProgressBar|Class|Fraction,BidirectionalMarquee
|ScrollBar|Class|Position,Size,Orientation
|NumericUpDown<T>|Class|Value,Increment,Min,Max
|NumericUpDown<T>|Class|Value,Increment

[Containers]
|Window|Class|Top-level,Title,MenuBar support
Expand All @@ -390,7 +416,7 @@ See `.claude/cookbook/` for common UI patterns:

[File Dialogs]
|FileDialog|Class|Base,Path,AllowedFileTypes
|OpenDialog|Class|OpenFile,AllowsMultipleSelection
|OpenDialog|Class|FilePaths,AllowsMultipleSelection,Canceled,OpenMode
|SaveDialog|Class|SaveFile,FileName

[Specialized]
Expand Down Expand Up @@ -429,6 +455,10 @@ See `.claude/cookbook/` for common UI patterns:
|Gradient|Class|Colors[],Spectrum
```

**Gotchas**
- `Terminal.Gui.Drawing.Attribute` can conflict with `System.Attribute` with implicit usings. Use `using TgAttribute = Terminal.Gui.Drawing.Attribute;` or fully qualify.
- `Color.TryParse (string, out Color?)` is nullable out. `Color.TryParse (string?, IFormatProvider?, out Color)` is non-nullable out.

### Terminal.Gui.Drivers (80+ types)
```
|IDriver|Interface|Init,End,Refresh,AddStr,Move
Expand Down Expand Up @@ -518,6 +548,5 @@ See `.claude/cookbook/` for common UI patterns:






2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ See `Tests/README.md` for the full list of test projects (including `Integration
## Testing

- Add new tests to `UnitTestsParallelizable`; use `UnitTests.NonParallelizable` only when static state is unavoidable. Never add to `UnitTests.Legacy`.
- Add comment: `// Claude - Opus 4.5`
- Add a comment marking the test as AI-generated. Either form is acceptable: `// Claude - <model>` or `// CoPilot - <model>` — just include the agent and the model that produced the test (e.g., `// Claude - Opus 4.5` or `// CoPilot - ChatGPT v4`). Both forms are established in the codebase; which marker is used is not a style concern and reviewers should not flag inconsistency between them.
- Never decrease coverage
- Avoid `Application.Init` in tests

Expand Down
43 changes: 21 additions & 22 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,47 @@
</PropertyGroup>
<ItemGroup>
<!-- Enable Nuget Source Link for github -->
<PackageVersion Include="AnsiConsoleToHtml" Version="0.2.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="5.3.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.14.0" />
<PackageVersion Include="Microsoft.Net.Compilers.Toolset" Version="5.3.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.202" />
<PackageVersion Include="ColorHelper" Version="1.8.1" />
<PackageVersion Include="JetBrains.Annotations" Version="2025.2.4" />
<PackageVersion Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.203" />
<PackageVersion Include="Microsoft.CodeAnalysis" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.3.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.6" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.7" />
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageVersion Include="TestableIO.System.IO.Abstractions.TestingHelpers" Version="22.1.1" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<PackageVersion Include="Xunit.Combinatorial" Version="2.0.24" />
<PackageVersion Include="System.IO.Abstractions" Version="22.1.1" />
<PackageVersion Include="TextMateSharp" Version="2.0.3" />
<PackageVersion Include="System.CommandLine" Version="2.0.7" />
<PackageVersion Include="Wcwidth" Version="4.0.1" />
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
<PackageVersion Include="JetBrains.Annotations" Version="2025.2.4" />
<PackageVersion Include="AnsiConsoleToHtml" Version="0.2.0" />
<PackageVersion Include="ColorHelper" Version="1.8.1" />
<PackageVersion Include="TextMateSharp" Version="2.0.3" />
<PackageVersion Include="TextMateSharp.Grammars" Version="2.0.3" />
<PackageVersion Include="Markdig" Version="1.1.3" />
<PackageVersion Include="Serilog" Version="4.3.1" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="10.0.0" />
<PackageVersion Include="Serilog.Sinks.Debug" Version="3.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.12" />
<PackageVersion Include="CsvHelper" Version="33.1.0" />
<PackageVersion Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
<PackageVersion Include="System.CommandLine" Version="2.0.6" />
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.6" />
<PackageVersion Include="ReactiveUI" Version="23.2.1" />
<PackageVersion Include="ReactiveUI" Version="23.2.27" />
<PackageVersion Include="ReactiveMarbles.ObservableEvents.SourceGenerator" Version="1.3.1" />
<PackageVersion Include="ReactiveUI.SourceGenerators" Version="2.6.1" />
<PackageVersion Include="GitHub.Copilot.SDK" Version="0.3.0" />
<PackageVersion Include="ReactiveUI.SourceGenerators" Version="2.6.30" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="Markdig" Version="1.1.3" />
<PackageVersion Include="TextMateSharp.Grammars" Version="2.0.3" />
<PackageVersion Include="ReportGenerator" Version="5.5.6" />
<PackageVersion Include="TestableIO.System.IO.Abstractions.TestingHelpers" Version="22.1.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<PackageVersion Include="Xunit.Combinatorial" Version="2.0.24" />
<PackageVersion Include="ReportGenerator" Version="5.5.9" />
<PackageVersion Include="coverlet.collector" Version="10.0.0" />
<PackageVersion Include="GitVersion.MsBuild" Version="6.7.0" />
</ItemGroup>
Expand Down
50 changes: 42 additions & 8 deletions Examples/InlineColorPicker/Program.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,68 @@
// Inline ColorPicker — demonstrates using RunnableWrapper<ColorPicker, Color?> in inline mode.
//
// NOTE: See https://github.com/gui-cs/clet that turns every Terminal.Gui View into a CLI command
// NOTE: — typed inputs, a real file picker, a Markdown viewer — with consistent JSON output,
// NOTE: predictable exit codes, and full keyboard/mouse support. Works for humans and AI agents alike.
//
// Renders a ColorPicker inline in the terminal (primary buffer) without dialog buttons.
// If the user accepts (double-click), the selected color name is written to stdout.
// If the user cancels (Esc), nothing is output and exit code is 1.
//
// Usage:
// dotnet run --project Examples/InlineColorPicker
// dotnet run --project Examples/InlineColorPicker -- --initial "#FF0000"
// dotnet run --project Examples/InlineColorPicker -- --initial Red
// $color = dotnet run --project Examples/InlineColorPicker # capture in shell

using Terminal.Gui.App;
using Terminal.Gui.Drawing;
using Terminal.Gui.ViewBase;
using Terminal.Gui.Views;

// Parse command-line arguments
string? initialValue = null;

for (var i = 0; i < args.Length; i++)
{
if (args [i] is "--initial" or "-i")
{
if (i + 1 < args.Length)
{
initialValue = args [++i];
}
else
{
Console.Error.WriteLine ("Error: --initial requires a color value (e.g., \"#FF0000\" or \"Red\").");

return 1;
}
}
}

// Enable inline mode before Init
Application.AppModel = AppModel.Inline;

IApplication app = Application.Create ().Init ();

// Wrap ColorPicker in a RunnableWrapper — no dialog buttons, just the picker.
// ColorPicker raises Command.Accept on double-click.
RunnableWrapper<ColorPicker, Color?> wrapper = new ()
{
Title = "Select a Color (Double-click to accept, Esc to cancel)",
ResultExtractor = cp => cp.Value
};
RunnableWrapper<ColorPicker, Color?> wrapper = new () { Title = "Select a Color (Double-click to accept, Esc to cancel)", ResultExtractor = cp => cp.Value };

// Enable color name display
wrapper.GetWrappedView ().Style.ShowColorName = true;
wrapper.GetWrappedView ().ApplyStyleChanges ();

// Apply initial value via IValue.TrySetValueFromString if provided
if (initialValue is { })
{
if (!((IValue)wrapper.GetWrappedView ()).TrySetValueFromString (initialValue))
{
Console.Error.WriteLine ($"Error: '{initialValue}' is not a valid color (use e.g., \"#FF0000\" or \"Red\").");
app.Dispose ();

return 1;
}
}

// Run inline — blocks until user accepts or cancels
app.Run (wrapper);
Expand All @@ -39,9 +75,7 @@
{
StandardColorsNameResolver resolver = new ();

string output = resolver.TryNameColor (selectedColor, out string? name)
? name
: selectedColor.ToString ();
string output = resolver.TryNameColor (selectedColor, out string? name) ? name : selectedColor.ToString ();

Console.WriteLine (output);

Expand Down
Loading
Loading