Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
1fb8d3b
Add initial MarkdownView implementation and tests
Copilot Apr 13, 2026
47a0155
Fix MarkdownView compile and style issues
Copilot Apr 13, 2026
f78a7b2
Merge branch 'develop' into copilot/add-markdown-renderer
tig Apr 14, 2026
eb67c5a
Merge branch 'copilot/add-markdown-renderer' of tig:gui-cs/Terminal.G…
tig Apr 14, 2026
6f5af6e
Refactor MarkdownView parsing and style for consistency
tig Apr 14, 2026
9a3e885
Rewrite Markdown scenario as GitHub docs browser with async loading
tig Apr 14, 2026
941b642
Update MarkdownView/SpinnerView behaviors and scenario metadata
tig Apr 14, 2026
57b0fe4
Add tests for stray markdown characters in MarkdownView
tig Apr 14, 2026
564c652
Refactor MarkdownView styling and word wrap; add tests
tig Apr 14, 2026
1220cab
Code blocks: full-width dimmed bg, IsCodeBlock refactor
tig Apr 14, 2026
7aec873
Code cleanup and API docs
tig Apr 14, 2026
7113b50
Add code block copy button to MarkdownView
tig Apr 14, 2026
ca3d661
Refactor MarkdownView copy buttons as SubViews
tig Apr 14, 2026
cf12e9e
Add GitHub-style Markdown table support to MarkdownView
tig Apr 14, 2026
5db513a
Refactor: Centralize inline Markdown parsing & styling
tig Apr 14, 2026
6d8cf87
Word-wrap and accurate sizing for MarkdownTable cells
tig Apr 14, 2026
76e322c
Enforce early return/guard clause rule project-wide
tig Apr 14, 2026
02ee0c5
Fix MarkdownView wrapping for links with parentheses
tig Apr 14, 2026
b0425e5
Fix content width calculation in MarkdownView
tig Apr 14, 2026
00c1a92
Refactor Line and MarkdownView: thematic breaks as subviews
tig Apr 14, 2026
b078622
Refactor spinner, Line, and MarkdownView rendering
tig Apr 14, 2026
4618648
Align copy button with Pos.AnchorEnd, fix anchor slugs
tig Apr 14, 2026
4fc7b47
Improve link navigation and dynamic layout in MarkdownView
tig Apr 14, 2026
ce196c8
Improve MarkdownView layout with external content width
tig Apr 14, 2026
f28be76
Refactor code block rendering to use MarkdownCodeBlock
tig Apr 14, 2026
5896236
Make MarkdownCodeBlock/Table public, designable, and testable
tig Apr 14, 2026
fe3086f
Fix link rendering, layout infinite loops, and focus handling
tig Apr 14, 2026
dff7012
Refactor MarkdownView API: property-based init, UI tweaks
tig Apr 14, 2026
26575d8
Merge branch 'develop' into copilot/add-markdown-renderer
tig Apr 14, 2026
8620bb1
Add session learnings doc for MarkdownView refactor
tig Apr 14, 2026
4424291
Add mdv example — a Terminal.Gui Markdown viewer
tig Apr 14, 2026
93f573c
Merge branch 'copilot/add-markdown-renderer' of tig:gui-cs/Terminal.G…
tig Apr 14, 2026
2969765
Update code block styling to use Editable role attribute
tig Apr 14, 2026
3912262
Add VisualRole.Code and plan TextMateSharp integration
tig Apr 15, 2026
93c1ec4
Syntax highlighting basically working.
tig Apr 15, 2026
af31efb
theme selection
tig Apr 15, 2026
2359dab
thene selection
tig Apr 15, 2026
4ca4e25
Moved TextMate support into lib
tig Apr 15, 2026
73ed0c0
new plan
tig Apr 15, 2026
10e1b71
Unify Markdown* views on Text property, add theme styling
tig Apr 15, 2026
3f35daf
Rename MarkdownView to Markdown across codebase
tig Apr 15, 2026
4debf99
Remove CalculateTableWidth and update sample Markdown table
tig Apr 15, 2026
0abfb08
Add theme selection, CWP fixes, and style improvements
tig Apr 16, 2026
cb4dd5d
Add UseThemeBackground and theme selectors to Markdown
tig Apr 16, 2026
d398482
Fix theme background fill in Markdown tables and lines
tig Apr 16, 2026
94ef1c9
Fix Markdown viewport scroll, DropDownList, autocomplete
tig Apr 16, 2026
d770eb7
Refactor inline mode, cleanup code blocks and tests
tig Apr 16, 2026
5070274
Switch to System.CommandLine; add --print and --theme options
tig Apr 16, 2026
f603804
Custom --help: render embedded README as markdown
tig Apr 16, 2026
031b876
removed bogus file
tig Apr 16, 2026
9393889
removed bogus folder
tig Apr 16, 2026
f4f63bf
cleanup
tig Apr 16, 2026
88ba86d
Update Terminal.Gui/Drawing/Markdown/MarkdownStyleRole.cs
tig Apr 16, 2026
e72e40f
Fix Markdown sample API doc links
Copilot Apr 16, 2026
239a7c4
Fix MarkdownTable API name and MarkdownCodeBlock autosize refresh
Copilot Apr 16, 2026
134a6c7
Document content-managed size tracking in MarkdownCodeBlock
Copilot Apr 16, 2026
6982435
Merge branch 'develop' into copilot/add-markdown-renderer
tig Apr 16, 2026
1977e65
Improve UI layout, add AttributeViewer, update docs
tig Apr 16, 2026
dff98c0
Merge branch 'copilot/add-markdown-renderer' of tig:gui-cs/Terminal.G…
tig Apr 16, 2026
619df45
Initial plan
Copilot Apr 16, 2026
dd51819
Merge remote-tracking branch 'origin/pr-4955' into copilot/md-pipelin…
Copilot Apr 16, 2026
18a232a
Implement AST-based Markdown lowering (Phases 0-6)
Copilot Apr 16, 2026
fa4bc84
Fix: use compiled static Regex for HTML tag stripping in HandleHtmlBlock
Copilot Apr 16, 2026
36ecb17
Merge branch 'develop' into copilot/add-markdown-renderer
tig Apr 16, 2026
a6b7780
Merge branch 'develop' into copilot/md-pipeline-refactor
tig Apr 16, 2026
7507308
Fix table alignment with wide glyphs; add tests/examples
tig Apr 16, 2026
64dd267
Merge branch 'copilot/add-markdown-renderer' of github.com:gui-cs/Ter…
tig Apr 16, 2026
5b73001
Merge branch 'copilot/md-pipeline-refactor' into copilot/add-markdown…
tig Apr 16, 2026
27d5be5
Refactor: modernize C# style and improve Markdown UI
tig Apr 16, 2026
9201f1c
Add option to hide copy buttons in Markdown code blocks
tig Apr 16, 2026
9d4880d
Expand copy button hit area, add tests for copy glyph
tig Apr 16, 2026
f9ad2d3
Merge branch 'develop' into copilot/add-markdown-renderer
tig Apr 16, 2026
33920b4
Refactor tests to use FakeClipboard and improve assertions
tig Apr 17, 2026
f96bf18
Merge branch 'copilot/add-markdown-renderer' of github.com:gui-cs/Ter…
tig Apr 17, 2026
0220e88
Set FakeClipboard in MarkdownCodeBlockTests
tig Apr 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
66 changes: 65 additions & 1 deletion .claude/POST-GENERATION-VALIDATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,71 @@ public class MyClass

**Rule:** 4 spaces per indentation level. NO tabs.

## Part 2: Code Style Violations
## Part 2: Control Flow Violations

### Early Return / Guard Clauses ⚠️ COMMONLY VIOLATED

```csharp
// CORRECT ✓ — guard clause, return early
if (view is null)
{
return;
}

DoWork (view);

// WRONG ✗ — happy path wrapped in conditional
if (view is not null)
{
DoWork (view);
}

// CORRECT ✓ — early return in lambda
button.Accepting += (_, args) =>
{
if (_target is null)
{
return;
}

_target.DoWork ();
};

// WRONG ✗ — lambda body wrapped in conditional
button.Accepting += (_, args) =>
{
if (_target is not null)
{
_target.DoWork ();
}
};

// CORRECT ✓ — continue in loop
foreach (View subView in SubViews)
{
if (!subView.Visible)
{
continue;
}

subView.Draw ();
}

// WRONG ✗ — loop body wrapped in conditional
foreach (View subView in SubViews)
{
if (subView.Visible)
{
subView.Draw ();
}
}
```

**Scan pattern:** Look for `if (condition) { ... } return` — the `if` block likely wraps happy-path code and should be inverted into a guard clause.

**Rule:** ALWAYS invert conditions and return/continue early. NEVER wrap the happy path in a conditional. See `.claude/rules/early-return.md` for full guidance.

## Part 3: Code Style Violations

### No `var` for Non-Built-In Types
```csharp
Expand Down
2 changes: 2 additions & 0 deletions .claude/REFRESH.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
8. **SPACE BEFORE PARENTHESES** - `Method ()` not `Method()`, `array [i]` not `array[i]` (see `formatting.md`)
9. **Braces on next line** - ALL opening braces on next line (Allman style)
10. **Blank lines** - before `return`/`break`/`continue`/`throw`, after control blocks
11. **Early return / guard clauses** - ALWAYS invert conditions and return/continue early. NEVER wrap the happy path in a conditional. This applies in methods, lambdas, loops — everywhere. (see `early-return.md`)

## Before Each File Edit

Expand All @@ -26,6 +27,7 @@ Ask yourself:
- [ ] **Did I add space BEFORE parentheses and brackets?**
- [ ] **Are ALL braces on the next line?**
- [ ] **Did I add blank lines before returns and after control blocks?**
- [ ] **Am I using guard clauses / early return instead of nesting?**

## If Unsure

Expand Down
70 changes: 70 additions & 0 deletions .claude/rules/early-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Early Return / Guard Clauses

**Invert conditions, return/continue early, keep happy path at lowest indentation. Applies everywhere: methods, lambdas, loops.**

## ✅ Do / ❌ Don't

```csharp
// ✅ Guard clause
if (view is null) { return; }
DoWork (view);

// ❌ Wrapped happy path
if (view is not null) { DoWork (view); }
```

```csharp
// ✅ Sequential guards in lambda
button.Accepting += (_, args) =>
{
if (_target is null) { return; }
if (args.Cancel) { return; }
_target.DoWork ();
};

// ❌ Compound condition wrapping lambda body
button.Accepting += (_, args) =>
{
if (_target is not null && !args.Cancel)
{
_target.DoWork ();
}
};
```

```csharp
// ✅ Continue in loops
foreach (View subView in SubViews)
{
if (!subView.Visible) { continue; }
subView.Draw ();
}

// ❌ Loop body inside conditional
foreach (View subView in SubViews)
{
if (subView.Visible) { subView.Draw (); }
}
```

```csharp
// ✅ Guard early before tail work
int total = widths.Sum ();
if (total <= available) { return widths; }
int excess = total - available;
ReduceEvenly (widths, minWidths, excess);
return widths;

// ❌ Tail work wrapped in conditional
int total = widths.Sum ();
if (total > available)
{
int excess = total - available;
ReduceEvenly (widths, minWidths, excess);
}
return widths;
```

## Key Principle

When adding a null check or validation, **add a guard at the top** — don't indent existing code into a new `if` block. Compound conditions are fine in guard clauses (`if (x <= 0 || y <= 0) { return; }`) but never for wrapping the happy path.
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,8 @@ dotnet test --project Tests/UnitTestsParallelizable --no-build --filter-method "
5. **Unused lambda params** - Use `_` discard: `(_, _) => { }`
6. **Local functions** - Use PascalCase: `void MyLocalFunc ()`
7. **Backing fields** - Place immediately before their property
8. **Early return** - Prefer guard clauses over nested `if`/`else`
8. **Early return / guard clauses (CRITICAL)** - ALWAYS prefer guard clauses over nested `if`/`else`. Invert the condition, return/continue early, keep happy path at lowest indentation. This applies to methods, lambdas, loops — everywhere. See [early-return.md](/.claude/rules/early-return.md) for detailed examples.
9. **One type per file** - Public and internal types each get their own file
10. **Prefer early exit `if`** - Reduce nesting and return early when using `if`

## Detailed Coding Rules

Expand All @@ -101,6 +100,7 @@ Consult these files in `.claude/rules/` before editing code:
- [Collection Expressions](/.claude/rules/collection-expressions.md) - `[...]` syntax
- [Terminology](/.claude/rules/terminology.md) - SubView/SuperView terms
- [Event Patterns](/.claude/rules/event-patterns.md) - Lambdas, handlers, closures
- [Early Return](/.claude/rules/early-return.md) - **Guard clauses, minimal nesting** (commonly violated!)
- [CWP Pattern](/.claude/rules/cwp-pattern.md) - Cancellable Workflow Pattern
- [Code Layout](/.claude/rules/code-layout.md) - Member ordering, backing fields
- [Testing Patterns](/.claude/rules/testing-patterns.md) - Test writing conventions
Expand Down
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ See `.claude/rules/` for detailed guidance:
- `target-typed-new.md` - Use `new ()` not `new TypeName()`
- `terminology.md` - **SubView/SuperView**, never "child/parent"
- `event-patterns.md` - Lambdas, closures, handlers
- `early-return.md` - **Guard clauses, minimal nesting** (commonly violated!)
- `collection-expressions.md` - Use `[...]` syntax
- `cwp-pattern.md` - Cancellable Workflow Pattern
- `code-layout.md` - Backing fields, member ordering
Expand Down Expand Up @@ -105,7 +106,7 @@ dotnet test --project Tests/UnitTests --no-build
6. **Use `[...]`** not `new () { ... }` for collections
7. **SubView/SuperView** for containment (Parent/Child only for non-containment refs)
8. **Unused lambda params** - use `_`: `(_, _) => { }`
9. **Early return** - Prefer guard clauses over nested `if`/`else`
9. **Early return / guard clauses** - ALWAYS invert conditions and return/continue early. Never wrap the happy path in a conditional. Applies to methods, lambdas, and loops. See `.claude/rules/early-return.md`.
10. **One type per file** - Public and internal types each get their own file

## Testing
Expand Down Expand Up @@ -133,6 +134,7 @@ dotnet test --project Tests/UnitTests --no-build
- Don't use `var` for non-built-in types
- Don't use redundant type names with `new`
- Don't say "child/parent" for containment (use SubView/SuperView)
- Don't wrap the happy path in a conditional — use guard clauses and return early
- Don't modify unrelated code
- Don't introduce new warnings
- Don't skip POST-GENERATION-VALIDATION.md after writing code
3 changes: 3 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.5" />
<PackageVersion Include="System.IO.Abstractions" Version="22.1.1" />
<PackageVersion Include="TextMateSharp" Version="2.0.3" />
<PackageVersion Include="Wcwidth" Version="4.0.1" />
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
<PackageVersion Include="Serilog" Version="4.3.1" />
Expand All @@ -37,6 +38,8 @@
<PackageVersion Include="ReactiveUI.SourceGenerators" Version="2.6.1" />
<PackageVersion Include="GitHub.Copilot.SDK" Version="0.2.2" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="Markdig" Version="0.39.0" />
<PackageVersion Include="TextMateSharp.Grammars" Version="2.0.3" />
<PackageVersion Include="ReportGenerator" Version="5.5.4" />
<PackageVersion Include="TestableIO.System.IO.Abstractions.TestingHelpers" Version="22.1.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.4.0" />
Expand Down
32 changes: 10 additions & 22 deletions Examples/AI/ChatView.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Text;
using GitHub.Copilot.SDK;
using Terminal.Gui.App;
using Terminal.Gui.Drawing;
Expand All @@ -15,7 +16,8 @@ internal sealed class ChatView : Window
private readonly IApplication _app;
private readonly CopilotClient _client;
private string _model;
private readonly TextView _conversationView;
private readonly Markdown _conversationView;
private readonly StringBuilder _conversationText = new ();
private readonly TextField _inputField;
private readonly View _inputIndicator;
private readonly SpinnerView _spinner;
Expand Down Expand Up @@ -47,32 +49,17 @@ public ChatView (IApplication app, CopilotClient client, string model)
Width = 5,
Visible = false
};
var spinnerShortcut = new Shortcut { CommandView = _spinner, MouseHighlightStates = MouseState.None, Enabled = false };
Shortcut spinnerShortcut = new () { CommandView = _spinner, MouseHighlightStates = MouseState.None, Enabled = false };
Shortcut quitShortcut = new (Application.GetDefaultKey (Command.Quit), "Quit", RequestStop);

_statusBar = new StatusBar { AlignmentModes = AlignmentModes.IgnoreFirstOrLast, SchemeName = SchemeName, BorderStyle = LineStyle.None };
_statusBar.Add (spinnerShortcut, quitShortcut);

_conversationView = new TextView
_conversationView = new Markdown
{
Width = Dim.Fill (),
Height = Dim.Auto (minimumContentDim: 1, maximumContentDim: Dim.Func (_ => GetMaxConversationHeight ())),
ReadOnly = true,
WordWrap = true
Width = Dim.Fill (), Height = Dim.Auto (minimumContentDim: 1, maximumContentDim: Dim.Func (_ => GetMaxConversationHeight ()))
};

_conversationView.GettingAttributeForRole += (sender, args) =>
{
var view = sender as View;

if (args.Role != VisualRole.ReadOnly)
{
return;
}
args.Result = view?.GetAttributeForRole (VisualRole.Normal);
args.Handled = true;
};

_inputIndicator = new View
{
Text = $"{Glyphs.RightArrow}",
Expand All @@ -88,7 +75,7 @@ public ChatView (IApplication app, CopilotClient client, string model)
_inputField.Border.LineStyle = _inputIndicator.Border.LineStyle;
_inputField.Border.Thickness = new Thickness (0, 1, 0, 1);

_inputField.Autocomplete.SuggestionGenerator = new SlashCommandSuggestionGenerator ();
_inputField.Autocomplete?.SuggestionGenerator = new SlashCommandSuggestionGenerator ();
_inputField.Accepted += OnInputAccepted;

Add (_conversationView, _inputIndicator, _inputField, _statusBar);
Expand Down Expand Up @@ -222,8 +209,8 @@ private async Task ValidateAndSwitchModel (string newModel)

private void AppendToConversation (string text)
{
_conversationView.Text += text;
_conversationView.MoveEnd ();
_conversationText.Append (text);
_conversationView.Text = _conversationText.ToString ();
}

private void HandleSlashCommand (string command)
Expand All @@ -239,6 +226,7 @@ private void HandleSlashCommand (string command)
break;

case "clear":
_conversationText.Clear ();
_conversationView.Text = string.Empty;
AppendToConversation ($"{Glyphs.Diamond} Conversation cleared.\n");

Expand Down
12 changes: 6 additions & 6 deletions Examples/AI/SingleTurnView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal sealed class SingleTurnView : Window
private readonly CopilotClient _client;
private readonly string _model;
private readonly string _prompt;
private readonly Label _responseLabel;
private readonly Markdown _responseView;

public SingleTurnView (IApplication app, CopilotClient client, string model, string prompt)
{
Expand All @@ -30,9 +30,9 @@ public SingleTurnView (IApplication app, CopilotClient client, string model, str
Height = Dim.Auto ();
Border.LineStyle = LineStyle.Rounded;

_responseLabel = new Label { Width = Dim.Fill (), Height = Dim.Auto () };
_responseView = new Markdown { Width = Dim.Fill (), Height = Dim.Auto () };

Add (_responseLabel);
Add (_responseView);
}

/// <inheritdoc/>
Expand Down Expand Up @@ -67,7 +67,7 @@ private async Task streamResponse ()
case AssistantMessageDeltaEvent delta:
responseText.Append (delta.Data.DeltaContent);

_app.Invoke (() => { _responseLabel.Text = responseText.ToString (); });
_app.Invoke (() => { _responseView.Text = responseText.ToString (); });

break;

Expand All @@ -77,7 +77,7 @@ private async Task streamResponse ()
break;

case SessionErrorEvent err:
_app.Invoke (() => { _responseLabel.Text = $"Error: {err.Data.Message}"; });
_app.Invoke (() => { _responseView.Text = $"Error: {err.Data.Message}"; });
done.TrySetResult ();

break;
Expand All @@ -89,7 +89,7 @@ private async Task streamResponse ()
}
catch (Exception ex)
{
_app.Invoke (() => _responseLabel.Text = $"Error: {ex.Message}");
_app.Invoke (() => _responseView.Text = $"Error: {ex.Message}");
}

// Give the UI a moment to render the final update, then exit
Expand Down
Loading
Loading