Markout.Templates 0.1.1

dotnet add package Markout.Templates --version 0.1.1
                    
NuGet\Install-Package Markout.Templates -Version 0.1.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Markout.Templates" Version="0.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Markout.Templates" Version="0.1.1" />
                    
Directory.Packages.props
<PackageReference Include="Markout.Templates" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Markout.Templates --version 0.1.1
                    
#r "nuget: Markout.Templates, 0.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Markout.Templates@0.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Markout.Templates&version=0.1.1
                    
Install as a Cake Addin
#tool nuget:?package=Markout.Templates&version=0.1.1
                    
Install as a Cake Tool

Markout

Markup adds instructions to content. Markout removes structure from data.

Markout is a source-generated .NET serializer that projects objects into human-readable documents instead of data formats like JSON. You annotate view models with attributes that describe data relationships — identity, enumeration, tabulation, measurement, hierarchy — and the source generator emits code that writes through an abstract renderer. The same object graph produces Markdown tables, ANSI terminal output with colored bars, plain text with aligned columns, or one-line summaries, without the developer making visual decisions.

Quick Start

using Markout;

var artist = new ArtistView(
    Name: "Sarah McLachlan",
    Genre: "Pop / Adult Contemporary",
    Origin: "Halifax, Nova Scotia",
    DebutYear: 1988,
    BestKnownFor: "Angel, Building a Mystery, Adia");

MarkoutSerializer.Serialize(artist, Console.Out, ArtistContext.Default);

[MarkoutSerializable(TitleProperty = nameof(ArtistView.Name))]
public record ArtistView(
    string Name,
    string Genre,
    string Origin,
    int DebutYear,
    string BestKnownFor);

[MarkoutContext(typeof(ArtistView))]
public partial class ArtistContext : MarkoutSerializerContext { }

Output:

# Sarah McLachlan

Genre: Pop / Adult Contemporary | Origin: Halifax, Nova Scotia | DebutYear: 1988 | BestKnownFor: Angel, Building a Mystery, Adia

Three things: a record, a context, one line of serialization. The TitleProperty becomes a heading; everything else renders as fields.

Adding Sections and Tables

Add [MarkoutSection] to group properties with headings, and use List<T> for tables:

[MarkoutSerializable(TitleProperty = nameof(Title))]
public class CityReport
{
    public string Title { get; set; } = "";
    public string Province { get; set; } = "";
    public int Population { get; set; }

    [MarkoutSection(Name = "Landmarks")]
    public List<LandmarkRow>? Landmarks { get; set; }
}

[MarkoutSerializable]
public class LandmarkRow
{
    public string Name { get; set; } = "";
    public string Type { get; set; } = "";
    public int Year { get; set; }
}

[MarkoutContext(typeof(CityReport))]
[MarkoutContext(typeof(LandmarkRow))]
public partial class ReportContext : MarkoutSerializerContext { }

MarkoutSerializer.Serialize(city, Console.Out, ReportContext.Default);

Output:

# Vancouver

Province: British Columbia
Population: 2632000

## Landmarks

| Name             | Type       | Year |
| ---------------- | ---------- | ---- |
| Stanley Park     | Park       | 1888 |
| Gastown          | Historic   | 1867 |
| Science World    | Museum     | 1989 |

Real-World Example: GitHub Repository Report

The GitHubRepo sample fetches four GitHub API endpoints in parallel and projects the combined JSON into a single report — fields, bar charts, contributor metrics, and release tables — all from one view model:

[MarkoutSerializable(TitleProperty = nameof(Title), DescriptionProperty = nameof(Description))]
[MarkoutIgnoreFields(nameof(OneLineWriter))]
public class RepoView
{
    public string Title { get; set; } = "";

    [MarkoutIgnore]
    public string Description { get; set; } = "";

    [MarkoutDisplayFormat("{0:N0}")]
    public int Stars { get; set; }

    [MarkoutDisplayFormat("{0:N0}")]
    public int Forks { get; set; }

    [MarkoutPropertyName("Open Issues")]
    [MarkoutDisplayFormat("{0:N0}")]
    public int OpenIssues { get; set; }

    public string Language { get; set; } = "";
    public string License { get; set; } = "";

    [MarkoutIgnoreInTable]
    [MarkoutSkipDefault]
    public Callout ArchivedWarning { get; set; }

    [MarkoutSection(Name = "Languages")]
    [MarkoutIgnoreInTable]
    public List<Breakdown>? Languages { get; set; }

    [MarkoutSection(Name = "Top Contributors")]
    [MarkoutIgnoreInTable]
    public List<Metric>? TopContributors { get; set; }

    [MarkoutSection(Name = "Releases")]
    public List<ReleaseRow>? Releases { get; set; }
}

Run it:

dotnet run samples/GitHubRepo/GitHubRepo.cs                                     # ANSI terminal (default)
dotnet run samples/GitHubRepo/GitHubRepo.cs -- dotnet/runtime --format markdown # Markdown
dotnet run samples/GitHubRepo/GitHubRepo.cs -- dotnet/runtime --format oneline  # Compact table

Markdown output for dotnet/runtime:

# dotnet/runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.

Stars: 17,698 | Forks: 5,350 | Open Issues: 8,385 | Language: C# | License: MIT License

## Languages

| Category | Count | % |
| -------- | ----- | - |
| C#       | 80    | 83 |
| C++      | 9     | 9  |
| C        | 7     | 7  |

## Top Contributors

| Label       | Value |
| ----------- | ----- |
| vargaz      | 11910 |
| stephentoub | 10417 |
| kumpera     | 4074  |
| jkotas      | 3244  |

## Releases

| Tag     | Name        | Published  |
| ------- | ----------- | ---------- |
| v10.0.3 | .NET 10.0.3 | 2026-02-10 |
| v10.0.2 | .NET 10.0.2 | 2026-01-14 |
| v9.0.13 | v9.0.13     | 2026-02-10 |

One-line output — same view model, --oneline flag, shows the Releases table only:

TAG      NAME         PUBLISHED
v8.0.24  .NET 8.0.24  2026-02-10
v9.0.13  v9.0.13      2026-02-10
v10.0.3  .NET 10.0.3  2026-02-10

The pattern is always the same: deserialize your API data, project to a view model, serialize. Markout handles the rest.

Shape Library

Each property on a view model maps to a data relationship, not a visual element. Renderers decide how to present each shape.

Relationship C# type What it means Markdown ANSI
Identity string, int, bool Named value Key: value Bold key, value
Enumeration string[] Sequence of items - item Bullet list
Tabulation List<T> Uniform records \| col \| col \| Space-padded table
Section [MarkoutSection] Logical grouping ## Heading Colored heading
Description List<Description> Terms with explanations - **Term:** text Bold term, text
Measurement List<Metric> Comparative quantities Label ████░░ 45 Colored bars
Composition List<Breakdown> Parts of a whole ██▓▓▒░ stacked Colored segments
Hierarchy List<TreeNode> Parent-child structure ├── node Box-drawing tree
Quotation CodeSection Verbatim content ```code``` Syntax display
Attention Callout Important messages > [!WARNING] Colored label

Plus structural shapes: Quotation (prose quotation), Matrix (2D pivot grid), Rule (section separator).

Record Types

Shapes that need structured input provide record types named for what the data is, not what it looks like:

new Metric("Build Time", 4.2)                                    // measurement
new Description("dotnet-inspect", "API surface inspection tool")  // term + explanation
new Breakdown("Jan 2025", [new("Critical", 1), new("High", 3)])  // proportional composition
new Callout(CalloutSeverity.Warning, "3 vulnerabilities found")   // attention
new CodeSection("csharp", "public class Foo { }")                 // verbatim content

Renderers

Markout ships four renderers. The serializer writes through MarkoutWriter — swap the writer, change the output.

Renderer Output Use case
MarkdownWriter GitHub-Flavored Markdown Documentation, LLM tool output, rendered reports
MarkoutWriter Plain text, space-padded Log files, piped output, terminals without ANSI
OneLineWriter Tables only, no headings Compact summaries, grep-friendly output
DiagramWriter Trees and structural diagrams Dependency graphs, file trees

Optional packages:

Package Renderer Use case
Markout.Ansi AnsiWriter Colored terminal output with bold, gradients
Markout.Ansi.Spectre SpectreWriter Rich terminal UI via Spectre.Console

Renderers declare which shapes they support via SupportedShapes. Unsupported shapes are silently skipped — the data is never lost, only the visual sophistication changes.

Templates

Templates let you author document structure in Markdown and fill data slots at runtime. The same template renders to any format — Markdown, plain text, or ANSI terminal.

template.md:

# .NET Security Report for {{date}}

The following vulnerabilities were disclosed this month.

{{vuln-table}}

| Level | CVSS Range | Response |
| ----- | ---------- | -------- |
| Critical | 9.0–10.0 | Patch immediately |
| High | 7.0–8.9 | Patch within 30 days |

{{#if commits}}
## Commits

{{commit-table}}
{{/if}}

Program.cs:

using Markout;
using Markout.Templates;

var template = MarkoutTemplate.Load("template.md");
template.TableOptions = new TableFormatterOptions(); // smooth column widths

template.Bind("date", "February 2026");
template.Bind("vuln-table", vulnerabilityData);  // IMarkoutFormattable
template.Bind("commits", hasCommits ? "yes" : null);
template.Bind("commit-table", commitData);

// Markdown output
Console.WriteLine(template.Render(new MarkoutWriterOptions { PrettyTables = true }));

// Plain text output — same template, different writer
var plainWriter = new MarkoutWriter();
template.Render(plainWriter);

Templates support:

  • {{key}} — inline substitution in headings and prose, or block-level data rendering
  • {{#if key}} / {{/if}} — conditional sections (included when key is bound and non-null)
  • Pipe tables — parsed and re-rendered through the writer's table shape, with optional statistical column-width optimization
  • IMarkoutFormattable and MarkoutTypeInfo<T> bindings for shape-aware data rendering
dotnet add package Markout.Templates

Template Runner Tool

A basic CLI tool is included for rendering templates with string bindings:

dotnet run --project tools/markout-template -- template.md date="February 2026" title="Report"

Unbound block placeholders are skipped, so you can render partial templates. Pipe key=value lines on stdin for additional bindings.

Customization Layers

Markout provides multiple layers of control, from zero-config to full custom:

Layer 1 — Attributes (compile-time): Control what's rendered and how.

[MarkoutPropertyName("Born")]          // rename a field
[MarkoutSkipNull]                      // hide when null
[MarkoutSection(Name = "Details")]     // group into a section
[MarkoutDisplayFormat("N0")]           // format numbers
[MarkoutShowWhen(nameof(HasDetails))]  // conditional rendering
[MarkoutMaxItems(10)]                  // truncate long lists
[MarkoutValueMap("k=badge", ...)]      // map values to badge-prefixed output
[MarkoutUnwrap]                        // inline collection items without section heading
[MarkoutIgnoreColumnWhen(...)]         // conditionally hide table columns

Layer 2 — Writer Options (runtime): Control which sections appear.

var options = new MarkoutWriterOptions
{
    IncludeSections = new HashSet<string> { "Summary", "Errors" },  // only these sections
    BoldFieldNames = true
};
var writer = new MarkdownWriter(Console.Out, options);

Layer 3 — Renderer Subclass (code): Override any shape for custom visual treatment.

public class MyWriter : MarkdownWriter
{
    protected override void WriteDescription(Description item)
    {
        // Custom rendering for descriptions
        Writer.WriteLine($"{item.Term}: {item.Text}");
    }
}

Installation

dotnet add package Markout

The package includes the source generator — no additional packages needed.

Samples

  • HelloMarkout — Simplest possible example: a class, a context, one line of serialization
  • RecordDemo — Records as view models
  • GitHubRepo — GitHub API → fields, breakdowns, metrics, and tables with Spectre/Markdown/OneLineWriter output
  • GitHubActivity — User profile and recent events from the GitHub API
  • CanadianContent — Canadian actors and shows with tables, trees, metrics, and multiple renderers
  • LatestCves — .NET security advisories with trees and severity breakdowns
  • DotNetReleases — .NET release information from GitHub
  • Serialization — Shape gallery, section filtering, and writer API examples
  • TemplateDemo — Document template with inline tables, conditional sections, and multi-format rendering

For Coding Agents

If you are an LLM or coding agent building a CLI tool that needs structured, readable output, see SKILL.md for step-by-step integration instructions and attribute reference.

Real-World Usage

Markout was created for dotnet-inspect, which uses all ten data relationships across 49 view models to generate API inspection reports, diff analysis, dependency trees, and security summaries.

Documentation

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.1 81 2/25/2026
0.1.0 166 2/17/2026