Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
6d65844
Bump MyLittleContentEngine dependencies to v0.0.2-alpha.0.6, add vers…
phil-scott-78 Apr 5, 2026
930bb2c
Integrate SPA navigation with `SpectreArticleIslandRenderer`, add dyn…
phil-scott-78 Apr 5, 2026
90b5492
Migrate docs site from MyLittleContentEngine to Pennington
phil-scott-78 Jun 3, 2026
56d9ac7
Add auto-generated API reference under Console and CLI Reference sect…
phil-scott-78 Jun 3, 2026
103641b
Nest API Reference under the Reference sidebar group
phil-scott-78 Jun 3, 2026
ae76338
Make each API type page individually searchable + de-dupe by slug
phil-scott-78 Jun 3, 2026
81d0580
Bump Pennington to 0.1.0-alpha.0.282 and Spectre.Console to 0.55.3-al…
phil-scott-78 Jun 3, 2026
45d7af6
Publish async-patterns page and remove dead link
phil-scott-78 Jun 5, 2026
f403197
Repair dead folder sidecars and add CLI Diataxis ordering
phil-scott-78 Jun 5, 2026
f936841
Remove dead PageHeader Tags/Theme parameters
phil-scott-78 Jun 5, 2026
9374847
Simplify host wiring and reference rendering against Pennington built…
phil-scott-78 Jun 5, 2026
999d91c
Add landing page showcase samples and assets
phil-scott-78 Jun 5, 2026
05982c1
Regenerate widget and CLI screenshots
phil-scott-78 Jun 5, 2026
2dffe90
fix(docs): make spinner reference SPA-safe; merge screenshot scripts
phil-scott-78 Jun 14, 2026
d9a1ce2
chore(deps): migrate to net10 + Spectre.Console 0.57 / Pennington 0.318
phil-scott-78 Jun 14, 2026
3e297e0
fix(blog): normalize 0.56/0.57 release posts to Pennington front matter
phil-scott-78 Jun 14, 2026
aea00a8
docs(canvas): drop obsolete PixelWidth sections
phil-scott-78 Jun 14, 2026
f139128
bumping Penn to 0.1.1
phil-scott-78 Jun 16, 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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -486,4 +486,7 @@ $RECYCLE.BIN/
# MyLittleContentEngine files
output/

vcrsharp-logs/
vcrsharp-logs/

# Playwright MCP debugging artifacts (screenshots + console logs)
.playwright-mcp/
145 changes: 85 additions & 60 deletions Generate-WidgetScreenshots.ps1
Original file line number Diff line number Diff line change
@@ -1,85 +1,110 @@
#!/usr/bin/env pwsh

#Requires -Version 7.0
<#
.SYNOPSIS
Parallel screenshot generation for the Spectre docs tapes.

.DESCRIPTION
Drives the globally-installed `vcr` tool over every .tape in the docs example
projects. The global tool runs VcrSharp's native (browserless) backend — an
in-process ConPTY + VT parser, no ttyd and no Chromium — so each run is its own
isolated pseudo-console with no shared terminal server. That removes the
concurrency bug that once forced serial execution (a stray linefeed injected
into SVG/GIF output when ttyd was driven in parallel), so the tapes record
concurrently and parallel output is content-identical to serial modulo
timing-sampled frames.

On a 16-logical-core box the full set lands in well under a minute at the
default throttle, versus many minutes one-at-a-time. The floor is the single
longest tape (live.tape, ~37s).

.PARAMETER Throttle
Max concurrent tapes. The tapes spend most of their time idle-waiting on
Sleep/EndBuffer, so oversubscribing past core count helps. Default: 1.5x
logical cores.

.PARAMETER VcrExe
The vcr command (or a path to a specific build). Defaults to the global tool
resolved from PATH — install with `dotnet tool install --global vcr`. Override
to point at a local build if you're iterating on VcrSharp itself.

.PARAMETER SkipBuild
Skip building the example projects. They must already be built — the tapes
invoke `dotnet run --no-build`.
#>
param(
[int]$Throttle = [int][math]::Ceiling([Environment]::ProcessorCount * 1.5),
[string]$VcrExe = 'vcr',
[switch]$SkipBuild
)

# Build the projects
Write-Host "Building projects..." -ForegroundColor Cyan
dotnet build Spectre.Docs.Examples
$ErrorActionPreference = 'Stop'
$root = $PSScriptRoot

if ($LASTEXITCODE -ne 0) {
Write-Error "Build failed with exit code $LASTEXITCODE"
exit $LASTEXITCODE
# Resolve the vcr command up front so a missing tool fails fast with a clear
# message instead of erroring once per tape inside the parallel block. Get-Command
# handles both a command on PATH (the default) and an explicit path override.
$vcr = Get-Command $VcrExe -ErrorAction SilentlyContinue
if (-not $vcr) {
Write-Error "vcr not found ('$VcrExe'). Install the global tool with 'dotnet tool install --global vcr', or pass -VcrExe <path> to a local build."
exit 1
}

dotnet build Spectre.Docs.Cli.Examples

if ($LASTEXITCODE -ne 0) {
Write-Error "Build failed with exit code $LASTEXITCODE"
exit $LASTEXITCODE
$VcrPath = $vcr.Source

if (-not $SkipBuild) {
Write-Host "Building example projects..." -ForegroundColor Cyan
dotnet build "$root\Spectre.Docs.Examples" -c Debug --nologo -v q
if ($LASTEXITCODE -ne 0) { Write-Error "Build failed (Examples)"; exit $LASTEXITCODE }
dotnet build "$root\Spectre.Docs.Cli.Examples" -c Debug --nologo -v q
if ($LASTEXITCODE -ne 0) { Write-Error "Build failed (Cli.Examples)"; exit $LASTEXITCODE }
}

# Generate all screenshots
Write-Host "`nGenerating screenshots..." -ForegroundColor Cyan

$tapeFiles = @(
Get-ChildItem "Spectre.Docs.Examples\VCR\*.tape"
Get-ChildItem "Spectre.Docs.Cli.Examples\VCR\*.tape"
Get-ChildItem "$root\Spectre.Docs.Examples\VCR\*.tape"
Get-ChildItem "$root\Spectre.Docs.Cli.Examples\VCR\*.tape"
)
$totalFiles = $tapeFiles.Count
Write-Host "Found $totalFiles tape files to process`n" -ForegroundColor Green

$results = $tapeFiles | ForEach-Object -Parallel {
$file = $_
$fileName = $file.Name
$gifName = $file.BaseName + ".gif"

Write-Host "`nGenerating $($tapeFiles.Count) screenshots via '$VcrPath' — throttle=$Throttle, cores=$([Environment]::ProcessorCount)`n" -ForegroundColor Green

$sw = [System.Diagnostics.Stopwatch]::StartNew()
$results = $tapeFiles | ForEach-Object -ThrottleLimit $Throttle -Parallel {
$exe = $using:VcrPath; $root = $using:root
Set-Location $root # tapes use paths relative to the docs root
$name = $_.Name
$t = [System.Diagnostics.Stopwatch]::StartNew()
try {
Write-Host "[$(Get-Date -Format 'HH:mm:ss')] Processing: $fileName" -ForegroundColor Yellow


# Execute VCR command
vcr $file.FullName | Out-Null

& $exe $_.FullName *> $null
$t.Stop()
if ($LASTEXITCODE -eq 0) {
Write-Host "[$(Get-Date -Format 'HH:mm:ss')] ✓ Completed: $fileName" -ForegroundColor Green
return @{ Success = $true; File = $fileName }
Write-Host (" ✓ {0,-44} {1,5:N1}s" -f $name, $t.Elapsed.TotalSeconds) -ForegroundColor DarkGreen
[pscustomobject]@{ Success = $true; File = $name; Seconds = $t.Elapsed.TotalSeconds }
} else {
Write-Warning "[$(Get-Date -Format 'HH:mm:ss')] ✗ Failed: $fileName (Exit code: $LASTEXITCODE)"
return @{ Success = $false; File = $fileName; ExitCode = $LASTEXITCODE }
Write-Warning (" ✗ {0} (exit {1})" -f $name, $LASTEXITCODE)
[pscustomobject]@{ Success = $false; File = $name; ExitCode = $LASTEXITCODE }
}
} catch {
$t.Stop()
Write-Error " ✗ $name : $_"
[pscustomobject]@{ Success = $false; File = $name; Error = $_.Exception.Message }
}
catch {
Write-Error "[$(Get-Date -Format 'HH:mm:ss')] ✗ Error processing $fileName : $_"
return @{ Success = $false; File = $fileName; Error = $_.Exception.Message }
}
} -ThrottleLimit 1
# THEORETICALLY, this could be increased for more parallelism, but VCRSharp might have issues with concurrent runs.
# Either we do or ttyd does. It seems an extra line feed gets added to the output (svg and gif) when run in parallel
# and I don't even know how to begin debugging that.
}
$sw.Stop()

$ok = ($results | Where-Object Success).Count
$fail = ($results | Where-Object { -not $_.Success }).Count

# Report results
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "Screenshot Generation Summary" -ForegroundColor Cyan
Write-Host "Screenshot Generation Summary" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ("Total: {0} | Success: {1} | Failed: {2} | Wall: {3:N1}s" -f $tapeFiles.Count, $ok, $fail, $sw.Elapsed.TotalSeconds) `
-ForegroundColor $(if ($fail -eq 0) { 'Green' } else { 'Yellow' })

$successful = ($results | Where-Object { $_.Success }).Count
$failed = ($results | Where-Object { -not $_.Success }).Count

Write-Host "Total: $totalFiles | Success: $successful | Failed: $failed" -ForegroundColor $(if ($failed -eq 0) { 'Green' } else { 'Yellow' })

if ($failed -gt 0) {
if ($fail -gt 0) {
Write-Host "`nFailed files:" -ForegroundColor Red
$results | Where-Object { -not $_.Success } | ForEach-Object {
Write-Host " - $($_.File)" -ForegroundColor Red
if ($_.Error) {
Write-Host " Error: $($_.Error)" -ForegroundColor Gray
}
if ($_.ExitCode) {
Write-Host " Exit code: $($_.ExitCode)" -ForegroundColor Gray
}
if ($_.Error) { Write-Host " Error: $($_.Error)" -ForegroundColor Gray }
if ($_.ExitCode) { Write-Host " Exit: $($_.ExitCode)" -ForegroundColor Gray }
}
exit 1
}

Write-Host "`n✓ All screenshots generated successfully!" -ForegroundColor Green
Write-Host "`n✓ All screenshots generated!" -ForegroundColor Green
69 changes: 36 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ This is the documentation website for **Spectre.Console** (rich console UI libra

## Technology Stack

- **.NET 10.0** - Blazor Server application
- **MyLittleContentEngine** - Markdown-based content management framework
- **.NET 11.0** - Blazor Server application
- **Pennington** - Markdown-based content engine for .NET
- **MonorailCss** - Utility-first CSS framework
- **Roslyn** - Code analysis for live code sample integration
- **Pennington.TreeSitter** - Tree-sitter code-fragment extraction for live code samples

## Getting Started

Expand Down Expand Up @@ -119,68 +119,71 @@ Your blog content here...

Blog posts are automatically sorted by date (newest first) and appear at `/blog/your-file-name`.

## Linking to Code with xmldocid
## Linking to Code with `:symbol` fences

The site uses **Roslyn integration** to embed live code from your source files directly into documentation. This ensures code samples are always up-to-date with the actual implementation.
The site uses **Pennington.TreeSitter** to embed live code from your source files directly into documentation. This ensures code samples are always up-to-date with the actual implementation.

### Syntax

Use the special code fence `` ```csharp:xmldocid ``:
Use the special code fence `` ```csharp:symbol ``. The body is a source file path (relative to the repo root) optionally followed by `> Type.Member`:

````markdown
```csharp:xmldocid
M:Spectre.Docs.Examples.AsciiCast.Samples.ProgressSample.Run(Spectre.Console.IAnsiConsole)
M:Spectre.Docs.Examples.AsciiCast.Samples.ProgressSample.CreateTasks(Spectre.Console.ProgressContext,System.Random)
```csharp:symbol
Spectre.Docs.Examples/Showcase/ProgressSample.cs > ProgressSample.Run
Spectre.Docs.Examples/Showcase/ProgressSample.cs > ProgressSample.CreateTasks
```
````

### XML Doc ID Format
### Reference format

Use standard .NET XML documentation identifiers:
Address code by **file path + name path** — a name path survives the line shifts that break hard-coded ranges:

| Type | Prefix | Example |
|------|--------|---------|
| Method | `M:` | `M:Namespace.ClassName.MethodName(Param.Type)` |
| Type/Class | `T:` | `T:Spectre.Console.Examples.MyExample` |
| Property | `P:` | `P:Namespace.ClassName.PropertyName` |
| Field | `F:` | `F:Namespace.ClassName.FieldName` |
| Target | Body shape | Example |
|--------|------------|---------|
| Whole file | `<path>` | `Spectre.Docs.Examples/Showcase/ProgressSample.cs` |
| A type | `<path> > Type` | `... > ProgressSample` |
| A member | `<path> > Type.Member` | `... > ProgressSample.Run` |
| Nested member | `<path> > Outer.Inner.Member` | `... > DrawCommand.Settings` |

**Finding XML Doc IDs:**
Modifiers (comma-separated, order-independent) follow the suffix:

1. Navigate to the source file containing the code
2. For methods: `M:` + full namespace + class + method name + parameter types
3. For classes: `T:` + full namespace + class name
| Modifier | Effect |
|----------|--------|
| `,bodyonly` | Render only the member body, stripping the declaration and braces |
| `,imports` | Prepend the file's top-of-file `using` statements |
| `,signatures` | Replace member bodies with `{ … }` for an outline view |
| `symbol-diff` | Emit a unified diff between two members (exactly two references) |

**Example from progress.md:**

````markdown
```csharp:xmldocid
M:Spectre.Docs.Examples.AsciiCast.Samples.ProgressSample.Run(Spectre.Console.IAnsiConsole)
```csharp:symbol,bodyonly
Spectre.Docs.Examples/Showcase/ProgressSample.cs > ProgressSample.Run
```
````

This extracts the `Run` method from `ProgressSample.cs` and renders it with syntax highlighting.
This extracts the body of the `Run` method from `ProgressSample.cs` and renders it with syntax highlighting.

### How It Works

The site is configured to analyze the solution via Roslyn (`Program.cs:42-45`):
Tree-sitter is wired in `Program.cs`, reading source files directly — no MSBuild workspace, no compilation:

```csharp
.WithConnectedRoslynSolution(_ => new CodeAnalysisOptions
builder.Services.AddTreeSitter(treeSitter =>
{
SolutionPath = "../Spectre.Docs.sln",
})
treeSitter.ContentRoot = ".."; // repo root, so fence paths resolve against the sibling example projects
});
```

When MyLittleContentEngine encounters `csharp:xmldocid`:
1. Parses the XML Doc ID
2. Uses Roslyn to find the symbol in the compiled solution
3. Extracts the source code
When Pennington encounters a `csharp:symbol` fence:
1. Resolves the file path relative to `ContentRoot`
2. Uses tree-sitter to locate the named member within the file
3. Extracts the source text (applying any modifiers)
4. Renders as syntax-highlighted HTML

This approach provides:
- **Always up-to-date code** - Changes in source automatically reflect in docs
- **Type safety** - Broken references fail at build time
- **Rename-safe references** - A name path tolerates line shifts; broken references surface in the build report
- **No duplication** - Single source of truth for code


Expand Down
3 changes: 2 additions & 1 deletion Spectre.Docs.Cli.Examples/VCR/cli-command-hierarchies.tape
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ Set Shell "pwsh"
Set FontSize 22
Set Theme "one dark"
Set TransparentBackground "true"
Set Cols 80
Set Cols 82
Set Rows 20
Set Size "fit-height"
Set EndBuffer 5s
Set WorkingDirectory "Spectre.Docs.Cli.Examples/bin/Debug/net10.0"
Env SPECTRE_APP "M:Spectre.Docs.Cli.Examples.DemoApps.CommandHierarchies.Demo.RunAsync(System.String[])"
Expand Down
3 changes: 2 additions & 1 deletion Spectre.Docs.Cli.Examples/VCR/cli-configuring-app.tape
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ Set Shell "pwsh"
Set FontSize 22
Set Theme "one dark"
Set TransparentBackground "true"
Set Cols 80
Set Cols 82
Set Rows 22
Set Size "fit-height"
Set EndBuffer 5s
Set WorkingDirectory "Spectre.Docs.Cli.Examples/bin/Debug/net10.0"
Env SPECTRE_APP "M:Spectre.Docs.Cli.Examples.DemoApps.ConfiguringCommandApp.Demo.RunAsync(System.String[])"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ Set Shell "pwsh"
Set FontSize 22
Set Theme "one dark"
Set TransparentBackground "true"
Set Cols 80
Set Cols 82
Set Rows 18
Set Size "fit-height"
Set EndBuffer 5s
Set WorkingDirectory "Spectre.Docs.Cli.Examples/bin/Debug/net10.0"
Env SPECTRE_APP "M:Spectre.Docs.Cli.Examples.DemoApps.CustomTypeConverters.Demo.RunAsync(System.String[])"
Expand Down
3 changes: 2 additions & 1 deletion Spectre.Docs.Cli.Examples/VCR/cli-customizing-help.tape
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ Set Shell "pwsh"
Set FontSize 22
Set Theme "one dark"
Set TransparentBackground "true"
Set Cols 80
Set Cols 82
Set Rows 24
Set Size "fit-height"
Set EndBuffer 5s
Set WorkingDirectory "Spectre.Docs.Cli.Examples/bin/Debug/net10.0"
Env SPECTRE_APP "M:Spectre.Docs.Cli.Examples.DemoApps.CustomizingHelpText.Demo.RunAsync(System.String[])"
Expand Down
3 changes: 2 additions & 1 deletion Spectre.Docs.Cli.Examples/VCR/cli-defining-arguments.tape
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ Set Shell "pwsh"
Set FontSize 22
Set Theme "one dark"
Set TransparentBackground "true"
Set Cols 80
Set Cols 82
Set Rows 20
Set Size "fit-height"
Set EndBuffer 5s
Set WorkingDirectory "Spectre.Docs.Cli.Examples/bin/Debug/net10.0"
Env SPECTRE_APP "M:Spectre.Docs.Cli.Examples.DemoApps.DefiningCommandsAndArguments.Demo.RunAsync(System.String[])"
Expand Down
5 changes: 3 additions & 2 deletions Spectre.Docs.Cli.Examples/VCR/cli-di-tutorial.tape
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
Output "Spectre.Docs/Content/assets/cli-di-tutorial.svg"
Output "Spectre.Docs/Content/assets/cli-di-tutorial.svg"

Set Shell "pwsh"
Set FontSize 22
Set Theme "one dark"
Set TransparentBackground "true"
Set Cols 70
Set Cols 82
Set Rows 16
Set Size "fit-height"
Set EndBuffer 5s
Set WorkingDirectory "Spectre.Docs.Cli.Examples/bin/Debug/net10.0"
Env SPECTRE_APP "M:Spectre.Docs.Cli.Examples.DemoApps.DependencyInjection.DIComplete.Demo.RunAsync(System.String[])"
Expand Down
3 changes: 2 additions & 1 deletion Spectre.Docs.Cli.Examples/VCR/cli-dictionary-options.tape
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ Set Shell "pwsh"
Set FontSize 22
Set Theme "one dark"
Set TransparentBackground "true"
Set Cols 90
Set Cols 82
Set Rows 20
Set Size "fit-height"
Set EndBuffer 5s
Set WorkingDirectory "Spectre.Docs.Cli.Examples/bin/Debug/net10.0"
Env SPECTRE_APP "M:Spectre.Docs.Cli.Examples.DemoApps.DictionaryOptions.Demo.RunAsync(System.String[])"
Expand Down
3 changes: 2 additions & 1 deletion Spectre.Docs.Cli.Examples/VCR/cli-error-handling.tape
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ Set Shell "pwsh"
Set FontSize 22
Set Theme "one dark"
Set TransparentBackground "true"
Set Cols 90
Set Cols 82
Set Rows 24
Set Size "fit-height"
Set EndBuffer 5s
Set WorkingDirectory "Spectre.Docs.Cli.Examples/bin/Debug/net10.0"
Env SPECTRE_APP "M:Spectre.Docs.Cli.Examples.DemoApps.HandlingErrorsAndExitCodes.Demo.RunAsync(System.String[])"
Expand Down
3 changes: 2 additions & 1 deletion Spectre.Docs.Cli.Examples/VCR/cli-flag-arguments.tape
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ Set Shell "pwsh"
Set FontSize 22
Set Theme "one dark"
Set TransparentBackground "true"
Set Cols 80
Set Cols 82
Set Rows 20
Set Size "fit-height"
Set EndBuffer 5s
Set WorkingDirectory "Spectre.Docs.Cli.Examples/bin/Debug/net10.0"
Env SPECTRE_APP "M:Spectre.Docs.Cli.Examples.DemoApps.FlagArguments.Demo.RunAsync(System.String[])"
Expand Down
Loading
Loading