Markout.Demo 0.2.1

dotnet tool install --global Markout.Demo --version 0.2.1
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local Markout.Demo --version 0.2.1
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=Markout.Demo&version=0.2.1
                    
nuke :add-package Markout.Demo --version 0.2.1
                    

Markout Demo

A demonstration app showing Markout's capabilities for rendering C# objects as Markdown.

Viewing Output

The markdown renders correctly when written to a file:

dotnet run -- simple > output/simple.md

Note: Copy/paste from a terminal loses trailing spaces. Markout uses two trailing spaces to force <br/> line breaks in fields—these are stripped by most terminal emulators during copy operations.

Data Source

All demos use a single JSON file (Data/shoes.json) containing Altra running shoe data:

  • Catalog metadata
  • Shoes with features and reviews
  • Inventory by size and color

Each demo projects this data differently to showcase various rendering patterns.

Running Demos

dotnet run --project src/Markout.Demo
dotnet run --project src/Markout.Demo -- <demo-name>

Demos

Demos are ordered from simplest to most complex.

simple

Intent: Show basic scalar field rendering.

Approach: Built-in serialization of a single object with string, decimal, and bool properties.

# Altra Torin 7

Name: Altra Torin 7  
Id: torin-7  
Category: Road  
Price: 149.99  
InStock: yes  

sections

Intent: Show how [MarkoutSection] creates headed sections with tables.

Approach: Built-in serialization. A single product with List<Feature> and List<Review> properties marked as sections, each rendering as an H2 with a table.

# Altra Lone Peak 8

Name: Altra Lone Peak 8  
Id: lone-peak-8  
Price: 144.99  

## Specifications

| Name | Value |
|------|-------|
| Stack Height | 25mm |
...

## Customer Reviews

| Author | Rating | Comment |
|--------|--------|---------|
| Alice | 5 | Best trail shoe I've ever worn! |
...

list

Intent: Show bullet list rendering with custom formatting.

Approach: Custom projection. The source data is transformed to List<string> with formatted entries like "Torin 7 ($149.99)" or "Olympus 6 ($179.99; backordered)". Markout renders List<string> as bullets.

## Available Models

- Torin 7 ($149.99)
- Escalante 4 ($139.99)
- Lone Peak 8 ($144.99)
- Olympus 6 ($179.99; backordered)
...

This demonstrates that when you need custom formatting, you project your data to strings before serialization.


table

Intent: Show List<T> rendered as a table.

Approach: Built-in serialization. A list of products with scalar properties renders as a Markdown table with one row per item.

## Products

| Model | Category | Price | In Stock |
|-------|----------|-------|----------|
| Torin 7 | Road | 149.99 | yes |
| Escalante 4 | Road | 139.99 | yes |
...

nested

Intent: Show subsection-per-item rendering for nested content.

Approach: Built-in serialization. When a List<T> contains items with nested List<U> properties, Markout detects this and renders each item as a subsection (H3) with its nested lists as tables (H4).

## Products

### Torin 7

Name: Torin 7  
Category: Road  
Price: 149.99  

#### Features

| Name | Value |
|------|-------|
| Stack Height | 28mm |
...

#### Reviews

| Author | Rating | Comment |
|--------|--------|---------|
| Alice | 5 | Great cushioning for long runs! |
...

pivot

Intent: Show how to handle List<List<T>> patterns by pivoting data.

Approach: Custom projection. Raw inventory data might be Size -> List<ColorQuantity>, which is unsupported in tables. Instead, we pivot to a flat structure where each row is a size and each column is a color. Markout then renders it as a simple table.

## Inventory by Size

| Size | Black | Green | Red |
|------|-------|-------|-----|
| 8 | 12 | 8 | 5 |
| 9 | 18 | 15 | 10 |
...

tree

Intent: Show hierarchical data as an ASCII tree.

Approach: Custom projection to TreeNode. Project data to List<TreeNode> where each node has a label and optional children. Markout's WriteTree() handles the connector logic (├─, └─, ).

var tree = shoes.Select(s => new TreeNode(
    $"{s.Model} ({s.Category}, ${s.Price})",
    s.Reviews?.Select(r => $"\"{r.Comment}\" — {r.Author}")
));
writer.WriteTree(tree);
## Products with Reviews

├─ Torin 7 (Road, $149.99)
│  ├─ "Great cushioning for long runs!" — Alice (5★)
│  └─ "Comfortable but runs a bit warm." — Bob (4★)
├─ Escalante 4 (Road, $139.99)
│  └─ "Fast and light, perfect for racing." — Charlie (5★)
└─ Lone Peak 8 (Trail, $144.99)
   ├─ "Best trail shoe ever!" — Diana (5★)
   ...

This follows the same pattern as list: reshape data to a Markout-recognized type, serializer does the right thing.


schema

Intent: Show how types map to Markout output via introspection.

Approach: Uses GetSchemaInfo<T>() to display type metadata. Shows how the same type renders differently as a document vs. as a table row (e.g., List<Review> becomes a table in document context but is ignored/unsupported in table context).

## ShoeTableRow

ShoeTableRow (as document)
├─ Model: string → Field
├─ Category: string → Column
├─ Price: decimal → Column
├─ InStock: bool → Column "In Stock"

Patterns Summary

Demo Serialization Key Concept
simple Built-in Scalar fields
sections Built-in [MarkoutSection] for headed tables
list Custom projection List<string> → bullets
table Built-in List<T> → table
nested Built-in Subsection-per-item for nested content
pivot Custom projection Flatten List<List<T>> to columns
tree Custom projection List<TreeNode> → hierarchical tree
schema Introspection GetSchemaInfo<T>()

Key Takeaways

  1. Built-in handling covers most cases: Scalar fields, tables, sections, and nested structures work automatically with attributes.

  2. Tables always have headings: When List<T> is serialized, it renders with a heading. The heading comes from:

    • [MarkoutSection(Name = "...")] if specified
    • Otherwise, the property name (e.g., Products## Products)
  3. Table columns must be scalars: Nested collections (List<T> properties) are skipped in table rows. Use [MarkoutIgnoreInTable] to silence the compile-time warning, or restructure with pivot/subsection patterns.

  4. Project data for custom formatting: When you need specific output formats, transform to Markout-recognized types:

    • List<string> → bullet list
    • List<TreeNode> → hierarchical tree
  5. Pivot for List<List<T>>: Nested collections are unsupported in tables. Pivot the data so nested items become columns.

  6. Use MarkoutWriter for full control: For non-standard layouts, use the writer API directly.

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.

This package has no dependencies.

Version Downloads Last Updated
0.2.1 91 2/25/2026
0.2.0 87 2/17/2026
0.1.1 94 1/27/2026
0.1.0 119 1/27/2026