Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
c2e4a58
Initial plan
Copilot May 8, 2026
33e1753
Refactor AppTestHelper to RunAsync
Copilot May 8, 2026
149bea6
Address AppTestHelper review feedback
Copilot May 8, 2026
cdcaec9
Address final AppTestHelper validation feedback
Copilot May 8, 2026
66546aa
Clarify AppTestHelper cancellation token
Copilot May 8, 2026
2616a0b
Clarify AppTestHelper assertions
Copilot May 8, 2026
cee64bd
Merge pull request #5269 from gui-cs/backmerge/v2.1.0
tig May 8, 2026
ad1127d
Fixes #5275. Clear stale TextView OSC 8 hyperlinks
harder May 8, 2026
f3dcdd9
Address review feedback on #5276 OSC 8 cleanup
harder May 9, 2026
d3e2bdb
Fixes #5127. Anchor button delimiters to edges; fix highlight continu…
YourRobotOverlord May 9, 2026
53a7254
Fixes #5270, #5273, #5281. Fix MarkdownView selection overlay bugs (#…
YourRobotOverlord May 9, 2026
2fd5492
Fixes #5272. Ctrl+Click on link in Markdown no longer shows context m…
YourRobotOverlord May 9, 2026
976c0be
Fixes #5284. Right-clicking a link in MarkdownView adds Copy Link to …
YourRobotOverlord May 9, 2026
56eef0c
Fixes #5275. Reset OSC 8 rows before redraw
harder May 9, 2026
dfabab0
Fixes #5143. ColorBar mouse updates use MouseBindings with grab/ungra…
YourRobotOverlord May 10, 2026
719614d
feat: add ImageView support with Sixel rendering and optimized dirty-…
mrazza May 9, 2026
74a63c6
perf: optimize ImageView rendering and cache
oca-agent May 10, 2026
7896a6d
Handle TypeLoadException in config property scans
duskfold May 10, 2026
b72d795
fix: remove unused _sixelImage field in Images scenario
oca-agent May 10, 2026
ee88931
refactor: update ImageView to use destination buffer for image scalin…
mrazza May 10, 2026
2005a4a
docs: update Viewport references to View.Viewport in ImageView docume…
mrazza May 10, 2026
2940e24
fix: handle null SixelEncoder in ImageView pixel calculation to preve…
mrazza May 10, 2026
d155c2e
Fix release build for config-property fixtures
duskfold May 10, 2026
7d30ac7
Revert #5276 over-eager OSC 8 close; fix _rowsWithUrls stale tracking
harder May 11, 2026
37cf686
Initial plan
Copilot May 11, 2026
144ef9e
Add scrolling benchmarks, performance smoke tests, and CI performance…
Copilot May 11, 2026
aa2c41b
Add hui to showcase between TerminalGuiDesigner and Capital and Cargo…
YourRobotOverlord May 11, 2026
c5bfe84
Merge branch 'develop' of https://github.com/gui-cs/Terminal.Gui into…
tig May 11, 2026
e646c04
Refactor scrolling benchmarks and tests for code style
tig May 11, 2026
eb48365
Fix UPPER_CASE constants and increase ListView mid-viewport threshold…
Copilot May 11, 2026
cb9eae7
Merge branch 'copilot/add-end-to-end-scrolling-benchmarks' of https:/…
tig May 11, 2026
9f447dd
Raise TextView perf test threshold to 1000 ms
tig May 11, 2026
bced649
Restrict performance smoke tests to Ubuntu runner only
Copilot May 11, 2026
35d50de
fix: fixes and optimizations for PR 5292 (#5)
oca-agent May 11, 2026
d4eb9ef
refactor: move SixelSupportChanged event subscription from layout eve…
mrazza May 11, 2026
e79e00d
fix: clear IsDirty flag on cells that will be overwritten with sixel …
mrazza May 12, 2026
8c9f013
Merge pull request #5293 from duskfold/duskfold/fix-configproperty-ty…
tig May 12, 2026
c6470ea
Fix perf tests to skip on non-Linux via Assert.Skip; revert broken --…
Copilot May 12, 2026
53f110b
Add blank lines after SkipIfNotLinux() calls (style fix)
Copilot May 12, 2026
ecd7681
Initial plan
Copilot May 12, 2026
97cfa6d
Move perf smoke tests to dedicated Tests/PerformanceTests csproj with…
Copilot May 12, 2026
b30a248
Initial plan
Copilot May 12, 2026
cb8aec7
Fixes #3975. Add specs/constitution.md to consolidate tenets and engi…
Copilot May 12, 2026
bbdcfbc
Docs: clarify when to use -ing vs -ed events (Accepting vs Accepted, …
Copilot May 12, 2026
379dcbc
Merge branch 'develop' of https://github.com/gui-cs/Terminal.Gui into…
tig May 12, 2026
2e5ab4f
Fix perf-gate CI: add PerformanceTests to Terminal.sln so dotnet rest…
Copilot May 12, 2026
d201fe9
new delist script
tig May 12, 2026
b022cb5
Merge pull request #5297 from gui-cs/copilot/add-specs-constitution-file
tig May 12, 2026
0723238
Merge pull request #5298 from gui-cs/copilot/docs-clarify-ing-vs-ed-e…
tig May 12, 2026
2b288bc
Merge branch 'develop' of https://github.com/gui-cs/Terminal.Gui into…
tig May 12, 2026
423fb9c
Fix race condition: add [Collection("Application Tests")] to Applicat…
Copilot May 12, 2026
f4c56ec
Fix: use ContentView.FrameToScreen() in PopoverMenu overlap test for …
Copilot May 12, 2026
79fd154
Merge branch 'develop' into copilot/add-end-to-end-scrolling-benchmarks
Copilot May 12, 2026
f07ff65
Merge pull request #5295 from gui-cs/copilot/add-end-to-end-scrolling…
tig May 12, 2026
e21615e
Merge pull request #5276 from harder/harder/issue-5275-textview-osc8
tig May 13, 2026
1bda107
Initial plan
Copilot May 13, 2026
4f459ff
docs: improve discoverability of CommandNotBound bubbling pattern for…
Copilot May 13, 2026
5466310
Merge pull request #5302 from gui-cs/copilot/improve-command-not-boun…
tig May 13, 2026
7a463e5
Initial plan
Copilot May 13, 2026
af2fee1
Mark TextView, TextViewAutocomplete, and ContentsChangedEventArgs as …
Copilot May 13, 2026
87cd7db
Merge pull request #5306 from gui-cs/copilot/mark-textview-as-obsolete
tig May 13, 2026
c301f28
Merge pull request #5292 from mrazza/feat/imageview-sixel
tig May 14, 2026
d213373
Initial plan
Copilot May 14, 2026
286986d
Add editor-oriented Command enum values (Find, Replace, InsertTab, Un…
Copilot May 14, 2026
5cc4b7f
Merge pull request #5309 from gui-cs/copilot/add-command-enum-values
tig May 14, 2026
810628d
Initial plan
Copilot May 15, 2026
615d6ff
Initial plan
Copilot May 15, 2026
4753931
Add ConfigurationManager / Scheme / Theme benchmark baseline
Copilot May 15, 2026
8c099fb
Remove local_packages build artifacts and add to .gitignore
Copilot May 15, 2026
fb8c1f7
Add code token visual roles
Copilot May 15, 2026
0ae15fe
Restore local_packages and .gitignore to original state
Copilot May 15, 2026
e4a7ca8
Address AppTestHelper RunAsync review feedback
Copilot May 15, 2026
1301e51
Merge pull request #5267 from gui-cs/copilot/refactor-apptesthelper-r…
tig May 15, 2026
7d1e9f6
Fix benchmarks to reset state per invocation, not per iteration
Copilot May 16, 2026
f0f2b54
Remove accidentally committed 1.0.0 nupkg build artifacts from local_…
Copilot May 16, 2026
45ac4fc
Fix default code view coloring
Copilot May 16, 2026
dc05426
Merge branch 'copilot/add-code-token-visualrole-vocabulary' of https:…
tig May 16, 2026
a87b3c1
Code cleanup.
tig May 16, 2026
a5e4751
Fix code colors across themes
Copilot May 16, 2026
ba602b8
Merge branch 'copilot/add-code-token-visualrole-vocabulary' of https:…
tig May 16, 2026
07aeabe
Show all code visual roles in samples
Copilot May 16, 2026
8520dd8
Merge branch 'copilot/add-code-token-visualrole-vocabulary' of https:…
tig May 16, 2026
7cf0489
Update baseline.json with real benchmark numbers from actual run
Copilot May 16, 2026
e582153
Remove accidentally re-added 1.0.0 nupkg build artifacts
Copilot May 16, 2026
ddb441b
Preserve code roles in derived Accent scheme
Copilot May 16, 2026
3364b56
Merge branch 'copilot/add-code-token-visualrole-vocabulary' of https:…
tig May 16, 2026
8a7b8fd
Fixes accent issue reported in CR
tig May 16, 2026
86631a7
Refactor code role legend highlighter
Copilot May 16, 2026
e98c01d
Fix accent scheme code role copy
Copilot May 16, 2026
529d1d5
Fix README --filter syntax: use space-separated globs instead of pipe…
Copilot May 17, 2026
b58bb4e
Fixes #5316. Fix FileDialog End key in path field (#5317)
YourRobotOverlord May 17, 2026
336531a
Merge branch 'copilot/add-code-token-visualrole-vocabulary' of https:…
tig May 17, 2026
f426b06
fixed xml doc warning
tig May 17, 2026
cec8623
Merge pull request #5313 from gui-cs/copilot/add-code-token-visualrol…
tig May 17, 2026
209c0a3
Merge pull request #5312 from gui-cs/copilot/add-benchmark-baseline-c…
tig May 17, 2026
2ada646
feat: add Command.InsertCaretAbove / .InsertCaretBelow (#5318)
tig May 17, 2026
73e75d0
test: cover Command.InsertCaretAbove / .InsertCaretBelow (#5318)
tig May 17, 2026
62aa655
Merge pull request #5319 from gui-cs/claude/command-insertcaret-5318
tig May 17, 2026
8269e6c
Set release label to 'rc' for v2.2.0-rc.1
github-actions[bot] May 17, 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
21 changes: 21 additions & 0 deletions .claude/rules/event-patterns.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Event Patterns

## When to Use `-ing` vs `-ed` Events

Terminal.Gui exposes paired events — `Accepting`/`Accepted`, `Activating`/`Activated`, `ValueChanging`/`ValueChanged`, etc.

**Rule:** Use `-ed` (past-tense) for side-effects. Use `-ing` (present-progressive) only when you need to inspect or cancel the in-flight operation.

```csharp
// ✅ Correct — fire-and-forget side-effect
button.Accepted += (_, _) => DoTheThing ();

// ✅ Correct — actually cancels
button.Accepting += (_, e) => { if (!CanProceed ()) e.Handled = true; };

// ❌ Wrong — handler ignores EventArgs; use Accepted instead
button.Accepting += (_, _) => DoTheThing ();
```

If the handler body doesn't reference `e` at all (or ignores `e.Handled`, `e.Cancel`, and the candidate value), it belongs on the `-ed` event.

The `-ing` event runs synchronously in the middle of the dispatch path; subscribing when you don't need to cancel adds unnecessary overhead and misleads readers.

## Lambda Parameters

**Replace unused parameters with discards `_`:**
Expand Down
212 changes: 212 additions & 0 deletions .github/workflows/perf-gate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
name: Performance Gate

on:
push:
branches: [ main, develop ]
paths-ignore:
- '**.md'
pull_request:
branches: [ main, develop ]
paths-ignore:
- '**.md'

# Only run on Linux to keep results comparable across runs.
# Windows/macOS times vary too much to use as a performance baseline.
permissions:
contents: read

jobs:
perf-smoke-tests:
name: Performance Smoke Tests (Linux)
runs-on: ubuntu-latest
timeout-minutes: 20
env:
DisableRealDriverIO: "1"

steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0 # GitVersion needs full history

- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.x
dotnet-quality: 'ga'

- name: Restore dependencies
run: dotnet restore

- name: Build (Release)
run: dotnet build --configuration Release --no-restore -property:NoWarn=0618%3B0612

- name: Build Tests (Debug — smoke tests run in Debug to match CI unit tests)
run: dotnet build Tests/PerformanceTests --no-restore -property:NoWarn=0618%3B0612

- name: Run performance smoke tests (Layer 1 gate)
id: smoke_tests
run: |
dotnet test \
--project Tests/PerformanceTests \
--no-build \
--verbosity normal

- name: Upload smoke test logs
if: always()
uses: actions/upload-artifact@v7
with:
name: perf-smoke-test-logs
path: |
TestResults/
if-no-files-found: ignore
retention-days: 7

perf-benchmarks:
name: Benchmarks (Linux, ShortRun)
runs-on: ubuntu-latest
# Only run on pushes to develop/main, not on every PR (slow and not blocking).
if: github.event_name == 'push'
timeout-minutes: 30
env:
DisableRealDriverIO: "1"

steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.x
dotnet-quality: 'ga'

- name: Restore dependencies
run: dotnet restore

- name: Build Release
run: dotnet build --configuration Release --no-restore -property:NoWarn=0618%3B0612

- name: Run benchmarks (ShortRun ≈ 30–60 s)
id: run_benchmarks
run: |
dotnet run \
--project Tests/Benchmarks \
--configuration Release \
--no-build \
-- \
--filter '*Scroll*' '*Config*' '*Scheme*' '*Theme*' \
--job short \
--exporters json \
--artifacts ./BenchmarkResults
continue-on-error: true # Don't block the workflow; comparison step decides outcome

- name: Compare results to baseline
id: compare
run: |
python3 - << 'PYEOF'
import json, os, sys, glob

REGRESSION_FACTOR = 3.0 # Fail if any benchmark is >3× baseline
IMPROVEMENT_FACTOR = 0.8 # Celebrate 🎉 if any benchmark drops below 0.8× baseline

baseline_path = "Tests/Benchmarks/baseline.json"
results_dir = "BenchmarkResults"

# --- Load baseline ---
try:
with open(baseline_path) as f:
baseline_data = json.load(f)
baseline = {
f"{b['type']}/{b['method']}/{b['params']}": b["meanNs"]
for b in baseline_data["benchmarks"]
}
except FileNotFoundError:
print("::warning::baseline.json not found — skipping comparison")
sys.exit(0)

# --- Find BenchmarkDotNet JSON results ---
result_files = glob.glob(f"{results_dir}/**/*.json", recursive=True)
result_files = [f for f in result_files if "results" in f.lower() or "report" in f.lower()]
if not result_files:
print("::warning::No BenchmarkDotNet result files found — skipping comparison")
sys.exit(0)

# --- Parse results ---
results = {}
for fpath in result_files:
try:
with open(fpath) as f:
data = json.load(f)
for bm in data.get("Benchmarks", []):
key = f"{bm['Type']}/{bm['Method']}/{bm.get('Parameters', '')}"
results[key] = bm.get("Statistics", {}).get("Mean", None)
except Exception as e:
print(f"::warning::Could not parse {fpath}: {e}")

# --- Build comparison table ---
rows = []
regressions = []
improvements = []

for key, base_ns in baseline.items():
if base_ns <= 0:
continue
cur_ns = results.get(key)
if cur_ns is None:
rows.append(f"| {key} | {base_ns/1000:.1f} µs | — (not measured) | — |")
continue

ratio = cur_ns / base_ns
emoji = "✅"
if ratio >= REGRESSION_FACTOR:
emoji = "❌"
regressions.append((key, base_ns, cur_ns, ratio))
elif ratio <= IMPROVEMENT_FACTOR:
emoji = "🎉"
improvements.append((key, base_ns, cur_ns, ratio))
rows.append(
f"| {key} | {base_ns/1000:.1f} µs | {cur_ns/1000:.1f} µs | {ratio:.2f}× {emoji} |"
)

# --- Write step summary ---
summary = "## 📊 Benchmark Comparison\n\n"
summary += "| Benchmark | Baseline | Current | Ratio |\n"
summary += "|-----------|----------|---------|-------|\n"
summary += "\n".join(rows) + "\n\n"

if improvements:
summary += "### 🎉 Performance Improvements\n"
for k, b, c, r in improvements:
summary += f"- **{k}**: {b/1000:.1f} µs → {c/1000:.1f} µs ({r:.2f}×)\n"
summary += "\n"

if regressions:
summary += "### ❌ Regressions Detected\n"
for k, b, c, r in regressions:
summary += f"- **{k}**: {b/1000:.1f} µs → {c/1000:.1f} µs ({r:.2f}×) — exceeds {REGRESSION_FACTOR}× threshold\n"
summary += "\n"

with open(os.environ.get("GITHUB_STEP_SUMMARY", "/dev/null"), "a") as f:
f.write(summary)

print(summary)

if regressions:
print(f"::error::Performance regressions detected: {len(regressions)} benchmark(s) exceeded {REGRESSION_FACTOR}× baseline")
sys.exit(1)

if improvements:
print(f"Performance improvements detected: {len(improvements)} benchmark(s) improved!")
PYEOF

- name: Upload benchmark results
if: always()
uses: actions/upload-artifact@v7
with:
name: benchmark-results-${{ github.sha }}
path: BenchmarkResults/
if-no-files-found: ignore
retention-days: 30
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

> **Guidance for AI agents working with Terminal.Gui.**
> For humans, see [CONTRIBUTING.md](./CONTRIBUTING.md).
> For Terminal.Gui's mission, tenets, and engineering philosophy, see [specs/constitution.md](./specs/constitution.md).
> See also: [llms.txt](./llms.txt) for machine-readable context.

## CRITICAL: Discard v1 Training Data
Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Contributing to Terminal.Gui

> **📘 This document is the single source of truth for all contributors (humans and AI agents) to Terminal.Gui.**
>
> For Terminal.Gui's product mission, design tenets, and engineering philosophy, see **[specs/constitution.md](./specs/constitution.md)**.

Welcome! This guide provides everything you need to know to contribute effectively to Terminal.Gui, including project structure, build instructions, coding conventions, testing requirements, and CI/CD workflows.

Expand Down
25 changes: 25 additions & 0 deletions Examples/Themes/code-dark.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json",
"Themes": [
{
"Dark": {
"Schemes": [
{
"Base": {
"CodeKeyword": {
"Foreground": "#ff79c6",
"Background": "None",
"Style": "Bold"
},
"CodeString": {
"Foreground": "#f1fa8c",
"Background": "None",
"Style": "None"
}
}
}
]
}
}
]
}
32 changes: 29 additions & 3 deletions Examples/UICatalog/Resources/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,20 @@
"Disabled": {
"Foreground": "BrightGreen",
"Background": "Gray"
}
},
"Code": { "Foreground": "Black", "Background": "#FFFF00", "Style": "None" },
"CodeComment": { "Foreground": "#008000", "Background": "None", "Style": "None" },
"CodeKeyword": { "Foreground": "#FF0000", "Background": "None", "Style": "None" },
"CodeString": { "Foreground": "#0000CC", "Background": "None", "Style": "None" },
"CodeNumber": { "Foreground": "#800080", "Background": "None", "Style": "None" },
"CodeOperator": { "Foreground": "Black", "Background": "None", "Style": "None" },
"CodeType": { "Foreground": "#C00000", "Background": "None", "Style": "None" },
"CodePreprocessor": { "Foreground": "#008000", "Background": "None", "Style": "None" },
"CodeIdentifier": { "Foreground": "Black", "Background": "None", "Style": "None" },
"CodeConstant": { "Foreground": "#800080", "Background": "None", "Style": "None" },
"CodePunctuation": { "Foreground": "Black", "Background": "None", "Style": "None" },
"CodeFunctionName": { "Foreground": "#0000CC", "Background": "None", "Style": "None" },
"CodeAttribute": { "Foreground": "#008000", "Background": "None", "Style": "None" }
}
},
{
Expand Down Expand Up @@ -213,7 +226,20 @@
"Disabled": {
"Foreground": "BrightGreen",
"Background": "Gray"
}
},
"Code": { "Foreground": "White", "Background": "Green", "Style": "None" },
"CodeComment": { "Foreground": "LightGray", "Background": "None", "Style": "None" },
"CodeKeyword": { "Foreground": "Yellow", "Background": "None", "Style": "None" },
"CodeString": { "Foreground": "LightCyan", "Background": "None", "Style": "None" },
"CodeNumber": { "Foreground": "LightYellow", "Background": "None", "Style": "None" },
"CodeOperator": { "Foreground": "White", "Background": "None", "Style": "None" },
"CodeType": { "Foreground": "Cyan", "Background": "None", "Style": "None" },
"CodePreprocessor": { "Foreground": "BrightRed", "Background": "None", "Style": "None" },
"CodeIdentifier": { "Foreground": "White", "Background": "None", "Style": "None" },
"CodeConstant": { "Foreground": "Yellow", "Background": "None", "Style": "None" },
"CodePunctuation": { "Foreground": "White", "Background": "None", "Style": "None" },
"CodeFunctionName": { "Foreground": "LightCyan", "Background": "None", "Style": "None" },
"CodeAttribute": { "Foreground": "LightGray", "Background": "None", "Style": "None" }
}
},
{
Expand Down Expand Up @@ -292,4 +318,4 @@
}
}
]
}
}
Loading
Loading