Skip to content

Commit 214afbe

Browse files
tigharderCopilotclaudedependabot[bot]
authored
Release v2.4.6 (#5485)
* Fixes #5434. Track adornment redraw deltas Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Clean up adornment thickness-delta helpers Fix indentation of the Margin.ThicknessChanged handler body and factor the duplicated viewport-size formula out of GetViewportForAdornmentThickness and GetViewportFrameForAdornmentThickness into a shared GetViewportSizeForThickness helper. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Bump TextMateSharp and TextMateSharp.Grammars Bumps TextMateSharp from 2.0.3 to 2.0.4 Bumps TextMateSharp.Grammars from 2.0.3 to 2.0.4 --- updated-dependencies: - dependency-name: TextMateSharp dependency-version: 2.0.4 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: TextMateSharp.Grammars dependency-version: 2.0.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * Regenerate docfx Mandelbrot.gif with rendered sixel (#5471) * Bump Spectre.Console from 0.55.2 to 0.56.0 (#5475) --- updated-dependencies: - dependency-name: Spectre.Console dependency-version: 0.56.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fixes #5360. Cull fully occluded overlapped opaque subviews during draw DrawSubViews() now skips drawing an Overlapped sibling when it is entirely covered by the higher-Z opaque peers already drawn. Such a sibling produces no visible output — every cell it would draw, including its own RenderLineCanvas, is clipped away by the clip "holes" those peers punched in DoDrawComplete — so skipping its Draw is output-neutral. The culled view's NeedsDraw is cleared (mirroring a drawn-but-fully-clipped pass) so it does not keep the SuperView perpetually dirty. Culling is intentionally conservative — it only applies to a candidate that is itself Overlapped, opaque (content/Border/Padding not Transparent), without a Margin shadow, and NOT SuperViewRendersLineCanvas. The last guard is what keeps Tabs correct: tab pages render their borders (and headers) through the SuperView's LineCanvas painters'-algorithm composition, so they must never be culled or their header line art would be dropped. The whole check is gated on the presence of an Overlapped sibling, keeping the common non-overlapping draw path zero-overhead. Opaque coverage is the region inside a view's (transparent-by-default) Margin — the Border frame — which matches the area DoDrawComplete excludes from the clip. Adds OcclusionCullingTests covering: fully-occluded cull, NeedsDraw clearing, partial coverage, transparent occluder, transparent candidate, shadowed candidate, SuperViewRendersLineCanvas candidate, non-Overlapped candidate, the no-Overlapped-siblings gate, output-neutrality, and a Tabs regression that inactive pages are never culled. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Address review: don't cull TransparentMouse candidates The cull path skips Draw()/DoDrawComplete(), which repopulates a view's CachedDrawnRegion for ViewportSettingsFlags.TransparentMouse layers. That region is invalidated by SetNeedsDraw(), so culling an opaque TransparentMouse view left it with a null cache and dropped it from mouse hit-testing (GetViewsUnderLocation blanket-removes TransparentMouse views with a null cache) — a state divergence from the drawn-but-clipped path the cull is meant to mirror. Add ParticipatesInTransparentMouseHitTesting() and exclude such candidates: the view's own ViewportSettings, plus any adornment that is TransparentMouse with non-empty Thickness (adornment caches survive SetNeedsDraw(), but the first draw must still populate a thick transparent-mouse adornment's cache). Margin is TransparentMouse by default, so this only excludes views with an actual Margin thickness — the common empty-margin overlapped view is still culled. Adds regression tests: TransparentMouseCandidate_IsNotCulled_AndKeepsHitRegion (fails without the guard — CachedDrawnRegion is null) and TransparentMouseMarginCandidate_IsNotCulled. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Fixes stale/contradictory AI agent instructions; adds agent docs lint and tuirec verification guidance (#5478) * Fix inconsistencies in AI agent instruction files - Local function naming: camelCase -> PascalCase in REFRESH.md and copilot-instructions.md (matches .editorconfig local_functions_rule, AGENTS.md, and event-patterns.md) - Replace stale Tests/UnitTests references with current project names (UnitTestsParallelizable / UnitTests.NonParallelizable) across AGENTS.md, CONTRIBUTING.md, .aider.md, .cursorrules, .windsurfrules, copilot-instructions.md, and .claude workflows/tasks - Replace deprecated --filter "FullyQualifiedName~" syntax with xUnit v3 MTP --filter-method/--filter-class in copilot-instructions.md - Remove machine-local path (D:\s\...) from AGENTS.md planning section - build-app.md: use Accepted (post-event) for fire-and-forget handlers per event-patterns.md; show Accepting only for cancellation; fix v1-style new Button ("OK") to v2 object initializer - build-test-workflow.md: .NET SDK 8.0 -> 10.0.100 per global.json Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Add agent docs lint, tuirec verification guidance, promote local memories - Add Scripts/lint-agent-docs.ps1 + lint-agent-docs.yml CI workflow: fails when known rot patterns reappear in agent instruction files (stale test project names, machine-local paths, camelCase local-function guidance, deprecated FullyQualifiedName~ filter syntax, SDK version drift vs global.json). The lint immediately caught a stale .NET SDK 8.0 claim in CONTRIBUTING.md, now fixed. - Wire tuirec into agent entry points (CLAUDE.md, AGENTS.md, build-app.md): agents can verify TUI behavior by recording with tuirec and reading the asciinema .cast output back as text, per Scripts/tuirec/README.md. - Promote durable guidance from machine-specific .claude/projects/ memory files into shared rules (.claude/rules/logging-tracing.md and fragile-areas.md); untrack .claude/projects/ and gitignore it (the directory name encoded a local user path). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Document Windows ConPTY sixel limitation in tuirec README Discovered while verifying the About Box fire animation: ConPTY strips sixel DCS and the DA1 sixel handshake, so sixel content cannot be captured in tuirec recordings on Windows (apps detect Sixel support: False). Added to the troubleshooting table and validation checklist so the next agent does not burn recordings rediscovering it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * tuirec README: require measuring grid-anchored sixels, not eyeballing Agents (including me) recurrently verify the wrong invariant when checking sixel recordings: confirming the sixel appears, or that agg rendered it faithfully at the requested cursor cell, and calling it done. That misses size/position errors — notably the ~4% undersize from tuirec advertising a cell resolution that does not match agg's rendered font cell (tuirec #84). Adds a "Verifying Placement and Size (measure - don't eyeball)" section with the cell-calibration recipe (measure agg's real cell from a known grid reference; reconcile against the resolution the app used; confirm the rendered bbox covers the target region), a checklist item, a troubleshooting row for #84, and rewrites the workflow's "visual confirm" step to require measurement for grid-anchored content. Also states the general principle: verify the invariant the change was meant to satisfy, not a proxy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Sync CONTRIBUTING.md TFM to net10.0; lint stale TFMs Codex review on #5478 flagged that build-test-workflow.md cites CONTRIBUTING.md as its source of truth, but CONTRIBUTING.md still described the project as net8.0 (line 28) even though the Required Tools section was updated to .NET 10 — so the declared source could revert the fix and mislead readers onto the wrong toolchain. Test-first: added Rule 6 to lint-agent-docs.ps1 that derives the expected target-framework moniker from global.json's SDK major and fails on any mismatched `net<major>.0` reference. It flagged CONTRIBUTING.md:28 (net8.0); fixed that to `C# 14 (net10.0)`. Lint now passes (32 files). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * Adds doc snippet compile validation; fixes v1 API rot in agent docs (#5480) * Add doc snippet compile validation with obsolete-API rejection Roslyn-based validator that compiles every fenced C# block in the agent docs (ai-v2-primer.md, build-app.md, common-patterns.md) against the built Terminal.Gui assembly, so example rot fails CI instead of misleading readers. Statement fragments are wrapped in a Runnable<string?> harness; blocks marked WRONG / `snippet: ignore` are skipped. Obsolete-API use (CS0618/CS0612) is treated as a failure, not a warning, so v1 rot like the legacy static Application.Init cannot pass as "compiled". testdata/obsolete-api.md + a CI negative-test step assert the validator rejects obsolete APIs. The check caught real rot fixed here: two cookbook snippets used the obsolete TextView (now a plain content View, pointing at gui-cs/Editor's EditorView). Result: 29 compiled, 1 skipped, 0 failed; negative test green. Rebased onto develop after #5478 merged (squash), keeping only this PR's files; no overlap with the merged agent-docs lint changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Fix Validate Doc Snippets CI: full history for GitVersion The workflow built Terminal.Gui without fetch-depth:0, so GitVersion.MsBuild failed on the shallow clone ("Repository is a shallow clone"). Match the build-validation workflow's checkout (fetch-depth: 0). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * Notify gui-cs/Editor on NuGet publish (#5483) Dispatches `terminal-gui-published` (payload: the published SemVer) to gui-cs/Editor after each publish. Editor's bump-terminal-gui.yml picks it up, pins the new version, validates with its full test suites, and republishes Editor against it - making Editor a continuous canary for TG API churn (see Editor specs/decisions.md DEC-010). No-op until an EDITOR_DISPATCH_TOKEN secret (PAT with repo scope on gui-cs/Editor) is configured; Editor also polls NuGet on a 6h schedule as a fallback, so nothing breaks without it. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * Fix publish.yml: secrets context is invalid in step if conditions The Editor-notify guard from #5483 used `if: secrets.X != ''`, which is not an available context for step ifs and invalidated the whole workflow (publish run 27422643812). Mirror the secret into job-level env and gate on that, same pattern as the TEMPLATE/clet dispatches in gui-cs/Editor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Kevin Harder <kevin@kevinharder.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1 parent f174be6 commit 214afbe

32 files changed

Lines changed: 1509 additions & 175 deletions

.aider.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Terminal.Gui v2 is a **complete rewrite**. Pre-2025 training data about Terminal
3434
dotnet restore
3535
dotnet build --no-restore
3636
dotnet test --project Tests/UnitTestsParallelizable --no-build
37-
dotnet test --project Tests/UnitTests --no-build
37+
dotnet test --project Tests/UnitTests.NonParallelizable --no-build
3838
```
3939

4040
---

.claude/REFRESH.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
3. **Use `[...]`** not `new () { ... }` for collections
1010
4. **SubView/SuperView** - never say "child", "parent", or "container"
1111
5. **Unused lambda params** - use `_` discard: `(_, _) => { }`
12-
6. **Local functions** - use camelCase: `void myLocalFunc ()`
12+
6. **Local functions** - use PascalCase: `void MyLocalFunc ()`
1313
7. **Backing fields** - place immediately before their property (ReSharper bug, must do manually)
1414
8. **SPACE BEFORE PARENTHESES** - `Method ()` not `Method()`, `array [i]` not `array[i]` (see `formatting.md`)
1515
9. **Braces on next line** - ALL opening braces on next line (Allman style)

.claude/cookbook/common-patterns.md

Lines changed: 90 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public sealed class FormWindow : Runnable<FormData?>
4646
X = 1,
4747
Y = 7,
4848
Width = Dim.Fill (1),
49-
ColorScheme = Colors.ColorSchemes ["Error"]
49+
SchemeName = "Error"
5050
};
5151

5252
Button submitButton = new ()
@@ -96,11 +96,13 @@ public record FormData (string Name, string Email, int Age);
9696
A scrollable list with item selection and actions.
9797

9898
```csharp
99+
using System.Collections.ObjectModel;
100+
99101
public sealed class ListWindow : Runnable
100102
{
101103
private readonly ListView _listView;
102104
private readonly Label _detailLabel;
103-
private readonly List<string> _items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"];
105+
private readonly ObservableCollection<string> _items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"];
104106

105107
public ListWindow ()
106108
{
@@ -131,21 +133,21 @@ public sealed class ListWindow : Runnable
131133
Y = Pos.AnchorEnd (1)
132134
};
133135

134-
_listView.SelectedItemChanged += (_, e) =>
136+
// ListView is IValue<int?> — the selected index. SelectedItem is int?.
137+
_listView.ValueChanged += (_, e) =>
135138
{
136-
if (e.Value >= 0 && e.Value < _items.Count)
139+
if (e.NewValue is int index && index < _items.Count)
137140
{
138-
_detailLabel.Text = $"Selected: {_items [e.Value]}";
141+
_detailLabel.Text = $"Selected: {_items [index]}";
139142
}
140143
};
141144

142-
selectButton.Accepting += (_, e) =>
145+
selectButton.Accepted += (_, _) =>
143146
{
144-
if (_listView.SelectedItem >= 0)
147+
if (_listView.SelectedItem is int selected)
145148
{
146-
MessageBox.Query (App!, "Selection", $"You selected: {_items [_listView.SelectedItem]}", "OK");
149+
MessageBox.Query (App!, "Selection", $"You selected: {_items [selected]}", "OK");
147150
}
148-
e.Handled = true;
149151
};
150152

151153
Add (_listView, _detailLabel, selectButton);
@@ -166,43 +168,33 @@ public sealed class MenuApp : Runnable
166168
{
167169
Title = "Menu Demo";
168170

169-
MenuBar menuBar = new ()
170-
{
171-
Menus =
172-
[
173-
new MenuBarItem (
174-
"File",
175-
[
176-
new MenuItem ("New", "", () => NewFile (), null, null, KeyCode.N | KeyCode.CtrlMask),
177-
new MenuItem ("Open...", "", () => OpenFile (), null, null, KeyCode.O | KeyCode.CtrlMask),
178-
new MenuItem ("Save", "", () => SaveFile (), null, null, KeyCode.S | KeyCode.CtrlMask),
179-
null, // Separator
180-
new MenuItem ("Exit", "", () => App!.RequestStop (), null, null, KeyCode.Q | KeyCode.CtrlMask)
181-
]),
182-
new MenuBarItem (
183-
"Edit",
184-
[
185-
new MenuItem ("Cut", "", null, null, null, KeyCode.X | KeyCode.CtrlMask),
186-
new MenuItem ("Copy", "", null, null, null, KeyCode.C | KeyCode.CtrlMask),
187-
new MenuItem ("Paste", "", null, null, null, KeyCode.V | KeyCode.CtrlMask)
188-
]),
189-
new MenuBarItem (
190-
"Help",
191-
[
192-
new MenuItem ("About...", "", () => ShowAbout ())
193-
])
194-
]
195-
};
196-
197-
TextView editor = new ()
171+
MenuBar menuBar = new ();
172+
173+
menuBar.Add (new MenuBarItem ("_File",
174+
[
175+
new MenuItem { Title = "_New", Key = Key.N.WithCtrl, Action = NewFile },
176+
new MenuItem { Title = "_Open...", Key = Key.O.WithCtrl, Action = OpenFile },
177+
new MenuItem { Title = "_Save", Key = Key.S.WithCtrl, Action = SaveFile },
178+
new Line (), // Separator
179+
new MenuItem { Title = "_Quit", Key = Key.Q.WithCtrl, Action = () => App!.RequestStop () }
180+
]));
181+
182+
menuBar.Add (new MenuBarItem ("_Help",
183+
[
184+
new MenuItem { Title = "_About...", Action = ShowAbout }
185+
]));
186+
187+
// Main content area below the menu bar. (For a real multi-line editor,
188+
// use gui-cs/Editor's EditorView — the core TextView is deprecated.)
189+
View content = new ()
198190
{
199191
X = 0,
200-
Y = 1,
192+
Y = Pos.Bottom (menuBar),
201193
Width = Dim.Fill (),
202194
Height = Dim.Fill ()
203195
};
204196

205-
Add (menuBar, editor);
197+
Add (menuBar, content);
206198
}
207199

208200
private void NewFile () => MessageBox.Query (App!, "New", "New file created", "OK");
@@ -216,7 +208,7 @@ public sealed class MenuApp : Runnable
216208

217209
## Split View Layout
218210

219-
Horizontal or vertical split layout with resizable panes.
211+
Side-by-side panes positioned with `Pos`/`Dim`. (`TileView` does not exist in v2 — use plain views and `ViewArrangement` for user-resizable panes.)
220212

221213
```csharp
222214
public sealed class SplitWindow : Runnable
@@ -225,27 +217,27 @@ public sealed class SplitWindow : Runnable
225217
{
226218
Title = "Split View Demo";
227219

228-
TileView tileView = new ()
220+
FrameView leftPane = new ()
229221
{
230-
X = 0,
231-
Y = 0,
232-
Width = Dim.Fill (),
222+
Title = "Left Pane",
223+
Width = Dim.Percent (50),
233224
Height = Dim.Fill (),
234-
Orientation = Orientation.Vertical // or Horizontal
235-
};
236225

237-
// Left pane
238-
FrameView leftPane = new () { Title = "Left Pane" };
226+
// Let the user resize this pane with mouse or keyboard (optional)
227+
Arrangement = ViewArrangement.RightResizable
228+
};
239229
leftPane.Add (new Label { Text = "Left content", X = 1, Y = 1 });
240230

241-
// Right pane
242-
FrameView rightPane = new () { Title = "Right Pane" };
231+
FrameView rightPane = new ()
232+
{
233+
Title = "Right Pane",
234+
X = Pos.Right (leftPane),
235+
Width = Dim.Fill (),
236+
Height = Dim.Fill ()
237+
};
243238
rightPane.Add (new Label { Text = "Right content", X = 1, Y = 1 });
244239

245-
tileView.Tiles.ElementAt (0).ContentView.Add (leftPane);
246-
tileView.Tiles.ElementAt (1).ContentView.Add (rightPane);
247-
248-
Add (tileView);
240+
Add (leftPane, rightPane);
249241
}
250242
}
251243
```
@@ -254,7 +246,7 @@ public sealed class SplitWindow : Runnable
254246

255247
## Tab View
256248

257-
Tabbed interface with multiple content pages.
249+
Tabbed interface with multiple content pages. (v1's `TabView` is now `Tabs`: each added `View` becomes a tab, and its `Title` is the tab label.)
258250

259251
```csharp
260252
public sealed class TabbedWindow : Runnable
@@ -263,29 +255,25 @@ public sealed class TabbedWindow : Runnable
263255
{
264256
Title = "Tabbed Interface";
265257

266-
TabView tabView = new ()
267-
{
268-
X = 0,
269-
Y = 0,
270-
Width = Dim.Fill (),
271-
Height = Dim.Fill ()
272-
};
258+
Tabs tabs = new ();
273259

274-
// Tab 1: Settings
275-
View settingsTab = new ();
260+
// Tab 1: Settings — Title becomes the tab label
261+
View settingsTab = new () { Title = "Settings" };
276262
settingsTab.Add (
277263
new Label { Text = "Enable Feature:", X = 1, Y = 1 },
278264
new CheckBox { X = 20, Y = 1, Text = "Enabled" }
279265
);
280266

281267
// Tab 2: About
282-
View aboutTab = new ();
268+
View aboutTab = new () { Title = "About" };
283269
aboutTab.Add (new Label { Text = "Version 1.0.0", X = 1, Y = 1 });
284270

285-
tabView.AddTab (new Tab { DisplayText = "Settings", View = settingsTab }, false);
286-
tabView.AddTab (new Tab { DisplayText = "About", View = aboutTab }, false);
271+
tabs.Add (settingsTab, aboutTab);
272+
273+
// Tabs is IValue<View?> — set Value to switch tabs programmatically
274+
tabs.Value = settingsTab;
287275

288-
Add (tabView);
276+
Add (tabs);
289277
}
290278
}
291279
```
@@ -340,9 +328,14 @@ public sealed class ProgressWindow : Runnable
340328
Add (_statusLabel, _progressBar, cancelButton);
341329
}
342330

343-
public override void OnLoaded ()
331+
protected override void OnIsRunningChanged (bool newIsRunning)
344332
{
345-
base.OnLoaded ();
333+
base.OnIsRunningChanged (newIsRunning);
334+
335+
if (!newIsRunning)
336+
{
337+
return;
338+
}
346339

347340
// Start simulated work
348341
App!.AddTimeout (TimeSpan.FromMilliseconds (100), () =>
@@ -376,7 +369,7 @@ private void OpenFile ()
376369
{
377370
Title = "Open File",
378371
AllowsMultipleSelection = false,
379-
DirectoryPath = Environment.CurrentDirectory
372+
Path = Environment.CurrentDirectory // Starting directory
380373
};
381374

382375
// Optional: filter by extension
@@ -397,15 +390,15 @@ private void SaveFile ()
397390
SaveDialog dialog = new ()
398391
{
399392
Title = "Save File",
400-
DirectoryPath = Environment.CurrentDirectory,
401-
FileName = "document.txt"
393+
Path = Environment.CurrentDirectory // Starting directory
402394
};
403395

404396
App!.Run (dialog);
405397

398+
// FileName is the name portion of the chosen Path (null if canceled)
406399
if (!dialog.Canceled && !string.IsNullOrEmpty (dialog.FileName))
407400
{
408-
string path = dialog.FileName;
401+
string path = dialog.Path;
409402
// Save to path...
410403
}
411404
}
@@ -438,6 +431,8 @@ private void OpenMultipleFiles ()
438431
Display tabular data with TableView.
439432

440433
```csharp
434+
using System.Data;
435+
441436
public sealed class DataTableWindow : Runnable
442437
{
443438
public DataTableWindow ()
@@ -474,12 +469,21 @@ public sealed class DataTableWindow : Runnable
474469
Text = "Select a row"
475470
};
476471

477-
tableView.SelectedCellChanged += (_, e) =>
472+
// TableView is IValue<TableSelection?> — ValueChanged fires when the selection moves.
473+
// SelectedCell is a Point: X = column, Y = row.
474+
tableView.ValueChanged += (_, e) =>
478475
{
479-
if (e.NewRow >= 0 && e.NewRow < table.Rows.Count)
476+
if (e.NewValue is not { } selection)
477+
{
478+
return;
479+
}
480+
481+
int row = selection.SelectedCell.Y;
482+
483+
if (row >= 0 && row < table.Rows.Count)
480484
{
481-
DataRow row = table.Rows [e.NewRow];
482-
statusLabel.Text = $"Selected: {row ["Name"]} - ${row ["Price"]}";
485+
DataRow dataRow = table.Rows [row];
486+
statusLabel.Text = $"Selected: {dataRow ["Name"]} - ${dataRow ["Price"]}";
483487
}
484488
};
485489

@@ -495,6 +499,8 @@ public sealed class DataTableWindow : Runnable
495499
Hierarchical data display.
496500

497501
```csharp
502+
using System.IO;
503+
498504
public sealed class TreeWindow : Runnable
499505
{
500506
public TreeWindow ()
@@ -568,7 +574,9 @@ public sealed class StatusBarApp : Runnable
568574
{
569575
Title = "Status Bar Demo";
570576

571-
TextView editor = new ()
577+
// Main content area. (For a real multi-line editor, use gui-cs/Editor's
578+
// EditorView — the core TextView is deprecated.)
579+
View content = new ()
572580
{
573581
Width = Dim.Fill (),
574582
Height = Dim.Fill (1) // Leave 1 row for StatusBar
@@ -604,7 +612,7 @@ public sealed class StatusBarApp : Runnable
604612
};
605613

606614
statusBar.Add (saveShortcut, quitShortcut);
607-
Add (editor, statusBar);
615+
Add (content, statusBar);
608616
}
609617
}
610618
```
@@ -652,7 +660,7 @@ if possible.
652660

653661
## Tips for All Patterns
654662

655-
1. **Always use `e.Handled = true`** in `Accepting` event handlers
663+
1. **Use `-ed` events (`Accepted`) for side effects**; use `Accepting` + `e.Handled = true` only to inspect or cancel
656664
2. **Use `Dim.Fill()` and `Pos.Center()`** instead of hardcoded values
657665
3. **Call `App!.RequestStop()`** to close the current window
658666
4. **Use `MessageBox.Query`** for simple dialogs

.claude/projects/C--Users-Tig-s-gui-cs-Terminal-Gui/memory/feedback_no_console_writeline.md

Lines changed: 0 additions & 32 deletions
This file was deleted.

.claude/projects/C--Users-Tig-s-gui-cs-Terminal-Gui/memory/feedback_textview_fragile.md

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)