Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
119 commits
Select commit Hold shift + click to select a range
b52d889
docs: add charts and shape primitives design spec
RoboNET Jun 12, 2026
e265e74
docs: rename canvas element to draw in charts spec
RoboNET Jun 12, 2026
20ea8fc
docs: add shapes phase 1 implementation plan
RoboNET Jun 12, 2026
2a3a38f
feat(core): add MaxShapesPerDraw resource limit
RoboNET Jun 12, 2026
8b36b0e
feat(ast): add Rect, Circle, Ellipse, Draw element types
RoboNET Jun 12, 2026
f01d447
test(core): cover MaxShapesPerDraw default in consolidated limits test
RoboNET Jun 12, 2026
67f9f52
feat(parser): add hand-written absolute-only path data tokenizer
RoboNET Jun 12, 2026
d2913dd
fix(parser): reject non-finite, relative, and unbounded path data
RoboNET Jun 13, 2026
c0bdcfa
feat(ast): add RectElement shape
RoboNET Jun 13, 2026
44e7b20
feat(ast): add CircleElement shape
RoboNET Jun 13, 2026
706a24f
feat(ast): add EllipseElement shape
RoboNET Jun 13, 2026
84223f6
feat(ast): add draw shape DTOs
RoboNET Jun 13, 2026
d01f751
feat(ast): add DrawElement
RoboNET Jun 13, 2026
5a1740b
feat(layout): measure and lay out shape leaf elements
RoboNET Jun 13, 2026
482141c
feat(parser): convert gradient object form to css gradient string
RoboNET Jun 13, 2026
46cfa0e
feat(parser): parse rect, circle, ellipse shapes with gradient fill
RoboNET Jun 13, 2026
de7e91a
feat(parser): parse draw element with absolute shapes and shape limit
RoboNET Jun 13, 2026
ab4f8b4
fix(parser): reject malformed draw shapes and polyline points
RoboNET Jun 13, 2026
d7ca4dc
feat(renderer): draw rect, circle, ellipse shapes via skia
RoboNET Jun 13, 2026
f8c6b58
refactor(renderer): tighten ShapeRenderer draw method visibility to i…
RoboNET Jun 13, 2026
4b13fac
feat(renderer): draw line, polyline, rect, circle, path inside draw e…
RoboNET Jun 13, 2026
e776237
test(renderer): add shape and draw snapshot golden images
RoboNET Jun 13, 2026
2ff4173
docs: document rect, circle, ellipse, draw shapes in llms files
RoboNET Jun 13, 2026
cf1cf9d
docs(wiki): add rect, circle, ellipse, draw to element and visual ref…
RoboNET Jun 13, 2026
404d4cb
docs(playground): add shape and draw schemas to template json
RoboNET Jun 13, 2026
2ff4397
docs(wiki): host shape doc images under examples/visual-docs, add yam…
RoboNET Jun 13, 2026
2bbd4ec
docs: record post-Phase-1 follow-ups in shapes plan
RoboNET Jun 13, 2026
9edef92
fix(parser): reject non-finite coordinates in draw shapes
RoboNET Jun 13, 2026
85559c5
docs: record holistic-review follow-ups and non-finite guard in shape…
RoboNET Jun 13, 2026
e4e69fd
docs: add charts phase 2 implementation plan
RoboNET Jun 13, 2026
48db635
feat(core): add MaxSeriesPerChart and MaxDataPointsPerSeries limits
RoboNET Jun 13, 2026
174e573
feat(ast): add Chart element type
RoboNET Jun 13, 2026
8abf414
feat(charts): add nice-tick axis scale math
RoboNET Jun 13, 2026
ba41b85
test(charts): exhaustive axis-scale edge cases
RoboNET Jun 13, 2026
0277562
feat(charts): add ChartType, LegendPosition, PieLabelMode enums
RoboNET Jun 13, 2026
b4019c9
feat(charts): add named series palettes
RoboNET Jun 13, 2026
74d1d2e
feat(charts): add light/dark/minimal themes
RoboNET Jun 13, 2026
129389e
feat(ast): add ChartSeries DTO
RoboNET Jun 13, 2026
1c6ff9d
feat(ast): add ChartElement
RoboNET Jun 13, 2026
6a97b05
feat(layout): treat chart as a leaf box
RoboNET Jun 13, 2026
5aa30ee
docs(charts): restore ChartElement cref in ChartSeries doc
RoboNET Jun 13, 2026
10e11e1
feat(parser): parse chart element with inline series, palettes, themes
RoboNET Jun 13, 2026
bd0a03f
feat(parser): register chart known properties with typo suggestions
RoboNET Jun 13, 2026
6416080
feat(charts): resolve data-bound series to numeric arrays
RoboNET Jun 13, 2026
fcc615c
feat(charts): add plot-area subdivision math
RoboNET Jun 13, 2026
4a18c20
feat(renderer): dispatch chart element and draw no-data placeholder
RoboNET Jun 13, 2026
4d55007
feat(charts): add value-to-pixel mapper
RoboNET Jun 13, 2026
a9f3354
feat(renderer): draw vertical bar charts with grid and axes
RoboNET Jun 13, 2026
19529f1
feat(renderer): support horizontal bar charts
RoboNET Jun 13, 2026
42051d0
feat(renderer): draw line and area charts
RoboNET Jun 13, 2026
2a12e07
feat(renderer): draw pie and donut charts
RoboNET Jun 13, 2026
da50bf6
feat(renderer): draw chart title and legend
RoboNET Jun 13, 2026
618d3ae
test(charts): add golden snapshots for chart types and themes
RoboNET Jun 13, 2026
468bb66
docs: document chart element in llms.txt and llms-full.txt
RoboNET Jun 13, 2026
606e0fd
docs(wiki): document chart element reference and visual examples
RoboNET Jun 13, 2026
a66cb5b
docs(playground): add chart to schema and autocomplete
RoboNET Jun 13, 2026
1d57528
docs(wiki): rename chart example dir to singular so regenerate maps t…
RoboNET Jun 13, 2026
803282e
feat(renderer): stack bars when stacked:true
RoboNET Jun 13, 2026
7c7d38e
feat(renderer): smooth line and area curves when smooth:true
RoboNET Jun 13, 2026
4ebedd4
feat(renderer): draw pie and donut slice labels
RoboNET Jun 13, 2026
475de08
docs: add charts phase 3 implementation plan
RoboNET Jun 13, 2026
52a10dc
feat(charts): add scatter/bubble/gauge/progress/sparkline chart types
RoboNET Jun 13, 2026
5ae5e81
feat(charts): add ChartPoint tuple representation to ChartSeries
RoboNET Jun 13, 2026
ed7704b
feat(ast): add Value/Max/ValueLabel to ChartElement for gauge and pro…
RoboNET Jun 13, 2026
2defff2
feat(yaml): parse scatter/bubble tuple series data
RoboNET Jun 13, 2026
94686b6
feat(yaml): parse gauge/progress value/max/label and register chart p…
RoboNET Jun 13, 2026
d0994da
feat(skia): add chart tuple-point bounds and gauge data detection
RoboNET Jun 13, 2026
3c415bf
feat(skia): render sparkline charts
RoboNET Jun 13, 2026
597871d
feat(skia): render scatter and bubble charts
RoboNET Jun 13, 2026
7917ee5
feat(skia): render gauge charts
RoboNET Jun 13, 2026
33f75e6
feat(skia): render progress ring charts
RoboNET Jun 13, 2026
8475bea
test(skia): end-to-end smoke tests for Phase-3 chart types
RoboNET Jun 13, 2026
0b83e67
test(charts): snapshot goldens for scatter/bubble/gauge/progress/spar…
RoboNET Jun 13, 2026
8d53bca
fix(renderer): suppress title and legend chrome for sparkline charts
RoboNET Jun 13, 2026
37198cb
docs(playground): add Phase-3 chart types and gauge props to schema
RoboNET Jun 13, 2026
18cd49e
docs(wiki): document Phase-3 chart types with visual examples
RoboNET Jun 13, 2026
5dded56
docs: document Phase-3 chart types in llms files
RoboNET Jun 13, 2026
2c0405a
docs: add charts phase 4 implementation plan
RoboNET Jun 13, 2026
912a0b1
feat(charts): add heatmap and radar chart types
RoboNET Jun 13, 2026
d21c32c
feat(ast): add XLabels/YLabels/ShowCellValues to ChartElement for hea…
RoboNET Jun 13, 2026
2c6fc09
feat(yaml): parse heatmap x-labels/y-labels/cell-values and register …
RoboNET Jun 13, 2026
4bc9adc
test(skia): no-data placeholder for empty heatmap and radar
RoboNET Jun 13, 2026
d6aefce
feat(skia): add heatmap two-color value-to-color interpolation
RoboNET Jun 13, 2026
39a4261
feat(skia): render heatmap charts
RoboNET Jun 13, 2026
ddcf77a
feat(charts): add radar spoke geometry helper
RoboNET Jun 13, 2026
d54bf03
feat(skia): render radar charts
RoboNET Jun 13, 2026
f65c49f
test(skia): end-to-end smoke tests for heatmap and radar
RoboNET Jun 13, 2026
d9e771d
test(charts): snapshot goldens for heatmap and radar
RoboNET Jun 13, 2026
0e53927
docs(playground): add heatmap/radar chart types and grid props to schema
RoboNET Jun 13, 2026
3823f8c
docs(wiki): document heatmap and radar chart types
RoboNET Jun 13, 2026
e1d5023
docs: document heatmap and radar in llms files
RoboNET Jun 13, 2026
3f08b93
docs: add FlexRender.Xml parser phase 5 implementation plan
RoboNET Jun 13, 2026
82c988d
build: scaffold FlexRender.Xml project
RoboNET Jun 13, 2026
eaa8a8b
refactor: extract ParseDocumentRoot entry point for parser reuse
RoboNET Jun 13, 2026
a0d2ba0
feat: XML template parser with canvas, text, separator
RoboNET Jun 13, 2026
8954fd8
test: XML flex nesting and qr/barcode/image elements
RoboNET Jun 13, 2026
f4dd145
test: XML each/if control-flow parsing
RoboNET Jun 13, 2026
03a436e
test: XML table columns/rows parsing
RoboNET Jun 13, 2026
24483a6
feat: XML draw shapes wrapper translation
RoboNET Jun 13, 2026
173529a
feat: XML chart series, categories, palette, tuple data
RoboNET Jun 13, 2026
ed9ec3b
test: XML unknown attribute/element validation and typo hints
RoboNET Jun 13, 2026
0714f30
test: XML parser enforces resource limits
RoboNET Jun 13, 2026
82746f6
test: XML/YAML AST equivalence cross-check
RoboNET Jun 13, 2026
a15c34e
feat: RenderXml extension method
RoboNET Jun 13, 2026
1ecc9fa
build: include FlexRender.Xml in meta-package
RoboNET Jun 13, 2026
6aa5f7d
docs: document XML template syntax
RoboNET Jun 13, 2026
18ad84e
fix(charts): enforce MaxDataPointsPerSeries on data-bound series
RoboNET Jun 13, 2026
bb2f9f0
refactor(renderer): remove dead default branch in chart DrawSeries
RoboNET Jun 13, 2026
2067eec
fix(xml): reject malformed else-if instead of silently dropping
RoboNET Jun 13, 2026
18a0d88
fix(charts): reserve top headroom so axis labels and bars are not cli…
RoboNET Jun 14, 2026
4b725c8
docs(wiki): re-sync chart doc image mirrors with current goldens
RoboNET Jun 14, 2026
6257c0f
docs: add Xml/Yaml decoupling refactor plan
RoboNET Jun 14, 2026
58cc825
feat(parser): add format-neutral template node model to Core
RoboNET Jun 14, 2026
5b5e0f3
feat(parser): add YamlDotNet-to-neutral node converter
RoboNET Jun 14, 2026
64bdf01
refactor(parser): move shared parsing engine into Core on neutral nod…
RoboNET Jun 14, 2026
45307d8
refactor(xml): emit neutral nodes and depend only on Core
RoboNET Jun 14, 2026
4e26c6a
chore(yaml): drop unused InternalsVisibleTo to FlexRender.Xml
RoboNET Jun 14, 2026
202244e
docs: reflect Xml-depends-only-on-Core and engine moved to Core
RoboNET Jun 14, 2026
ca9d645
docs: fix broken image URLs after Git LFS removal (media -> raw githu…
RoboNET Jun 14, 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
26 changes: 16 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@ src/FlexRender.Core/ # Core library (0 external dependencies)
Units/ # Unit, UnitParser, PaddingValues, PaddingParser, MarginValue, MarginValues
Loaders/ # FileResourceLoader, Base64ResourceLoader, EmbeddedResourceLoader
Parsing/Ast/ # Template, CanvasSettings, TemplateElement, TextElement, FlexElement, TableElement, TableColumn, TableRow, etc.
Parsing/Nodes/ # Format-neutral node model (TemplateMapping, TemplateSequence, TemplateScalar, TemplateNode)
Parsing/Engine/ # Shared parsing engine (TemplateEngine, ElementParsers, ChartParsers, ShapeParsers, KnownProperties, NodePropertyHelpers)
TemplateEngine/ # TemplateProcessor, ExpressionLexer, ExpressionEvaluator, InlineExpressionParser, InlineExpressionEvaluator, FilterRegistry
Filters/ # ITemplateFilter, CurrencyFilter, NumberFilter, UpperFilter, LowerFilter, TrimFilter, TruncateFilter, FormatFilter
Values/ # TemplateValue hierarchy (StringValue, NumberValue, etc.)

src/FlexRender.Yaml/ # YAML template parser (-> Core + YamlDotNet)
Parsing/ # TemplateParser
src/FlexRender.Yaml/ # YAML facade: YamlDotNet -> neutral nodes -> Core engine (-> Core + YamlDotNet)
Parsing/ # TemplateParser (facade), YamlNodeConverter
src/FlexRender.Xml/ # XML facade: XDocument -> neutral nodes -> Core engine (-> Core only); RenderXml extension
Parsing/ # XmlTemplateParser, XmlNodeConverter
src/FlexRender.Http/ # HTTP resource loader (-> Core)
src/FlexRender.Skia.Render/ # SkiaSharp renderer (-> Core + SkiaSharp)
Abstractions/ # ISkiaRenderer, IFontLoader, IImageLoader, IFontManager
Expand Down Expand Up @@ -89,6 +93,8 @@ FlexRender.Yaml FlexRender.Http FlexRender.Skia.Render FlexRender.ImageSharp.
FlexRender.MetaPackage (references all)
```

Note: `FlexRender.Xml` depends only on `FlexRender.Core` -- the shared parsing engine (`TemplateEngine` + element/chart/shape parsers) lives in Core and operates on the format-neutral node model. `FlexRender.Yaml` = Core + YamlDotNet; `FlexRender.Xml` = Core only. Neither format package depends on the other.

## SkiaSharp Native Assets on Linux

SkiaSharp is a managed C# binding over the native Skia engine. On Linux (including CI runners and Docker), the native `libSkiaSharp.so` library is not bundled automatically and must be provided via a separate NuGet package.
Expand Down Expand Up @@ -249,16 +255,16 @@ All new features and non-trivial changes must be developed in separate branches.
- **Do NOT merge into `main`** -- leave the feature branch as-is after completing work. Merging is done manually by the maintainer or via GitHub PR
- **Do NOT use git worktrees** -- work directly in the repository checkout. Worktrees add unnecessary complexity and cause issues with stash conflicts and asset path resolution

### Git LFS & Image URLs
### Image URLs

All binary assets (PNG images, fonts) are stored in Git LFS. When referencing images in README or documentation:
Binary assets (PNG images, fonts) are stored **directly in Git** (Git LFS was removed). When referencing images in README or documentation:

- **Use `media.githubusercontent.com`** for LFS-tracked files:
- **Use `raw.githubusercontent.com`**:
```
https://media.githubusercontent.com/media/RoboNET/FlexRender/main/examples/output/receipt.png
https://raw.githubusercontent.com/RoboNET/FlexRender/main/examples/output/receipt.png
```
- **Do NOT use `raw.githubusercontent.com`** -- it returns the LFS pointer file (text), not the actual image content
- LFS-tracked paths: `examples/output/*.png`, `examples/assets/fonts/*.ttf`, `examples/assets/placeholder/*.png`, `examples/visual-docs/output/*.png`
- **Do NOT use `media.githubusercontent.com/media/...`** -- that is the LFS delivery path and now returns 404, since the files are no longer LFS-tracked
- Asset paths: `examples/output/*.png`, `examples/assets/fonts/*.ttf`, `examples/assets/placeholder/*.png`, `examples/visual-docs/output/*.png`

### Commits

Expand Down Expand Up @@ -464,8 +470,8 @@ When debugging or testing the WASM playground (`src/FlexRender.Playground/`), us
### Add new element type

1. Create AST model in `Parsing/Ast/` (sealed class extending `TemplateElement`)
2. Add parser function in `TemplateParser.cs` -- register in `_elementParsers` dictionary
3. Register all YAML properties in `KnownProperties.cs` (for YAML validation and typo suggestions)
2. Add parser function in `Parsing/Engine/ElementParsers.cs` (Core) -- register in `TemplateEngine._elementParsers` dictionary
3. Register all properties in `Parsing/Engine/KnownProperties.cs` (Core; for validation and typo suggestions)
4. Add flex-item property support via `switch` pattern matching in layout engine
5. Add rendering in `SkiaRenderer.RenderNode()` or create a provider
6. Write tests for each step
Expand Down
1 change: 1 addition & 0 deletions FlexRender.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
</Folder>
<Folder Name="/src/Parsers/">
<Project Path="src/FlexRender.Yaml/FlexRender.Yaml.csproj" />
<Project Path="src/FlexRender.Xml/FlexRender.Xml.csproj" />
</Folder>
<Folder Name="/src/QR/">
<Project Path="src/FlexRender.QrCode.Svg.Render/FlexRender.QrCode.Svg.Render.csproj" />
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ A .NET library for rendering images from YAML templates with a full CSS flexbox

| Receipt | Dynamic Receipt | Ticket | Label |
|---------|-----------------|--------|-------|
| ![Receipt](https://media.githubusercontent.com/media/RoboNET/FlexRender/main/examples/output/receipt.png) | ![Dynamic](https://media.githubusercontent.com/media/RoboNET/FlexRender/main/examples/output/receipt-dynamic.png) | ![Ticket](https://media.githubusercontent.com/media/RoboNET/FlexRender/main/examples/output/ticket.png) | ![Label](https://media.githubusercontent.com/media/RoboNET/FlexRender/main/examples/output/label.png) |
| ![Receipt](https://raw.githubusercontent.com/RoboNET/FlexRender/main/examples/output/receipt.png) | ![Dynamic](https://raw.githubusercontent.com/RoboNET/FlexRender/main/examples/output/receipt-dynamic.png) | ![Ticket](https://raw.githubusercontent.com/RoboNET/FlexRender/main/examples/output/ticket.png) | ![Label](https://raw.githubusercontent.com/RoboNET/FlexRender/main/examples/output/label.png) |

| Table Invoice | Expressions | Visual Effects |
|---------------|-------------|----------------|
| ![Table Invoice](https://media.githubusercontent.com/media/RoboNET/FlexRender/main/examples/output/table-invoice.png) | ![Expressions](https://media.githubusercontent.com/media/RoboNET/FlexRender/main/examples/output/expressions-demo.png) | ![Visual Effects](https://media.githubusercontent.com/media/RoboNET/FlexRender/main/examples/output/visual-effects.png) |
| ![Table Invoice](https://raw.githubusercontent.com/RoboNET/FlexRender/main/examples/output/table-invoice.png) | ![Expressions](https://raw.githubusercontent.com/RoboNET/FlexRender/main/examples/output/expressions-demo.png) | ![Visual Effects](https://raw.githubusercontent.com/RoboNET/FlexRender/main/examples/output/visual-effects.png) |

<details>
<summary>All Features Showcase (click to expand)</summary>

![Showcase](https://media.githubusercontent.com/media/RoboNET/FlexRender/main/examples/output/showcase-all-features.png)
![Showcase](https://raw.githubusercontent.com/RoboNET/FlexRender/main/examples/output/showcase-all-features.png)

</details>

Expand All @@ -50,7 +50,7 @@ A .NET library for rendering images from YAML templates with a full CSS flexbox

| Skia | ImageSharp | SVG |
|------|------------|-----|
| ![Skia](https://media.githubusercontent.com/media/RoboNET/FlexRender/main/examples/output/showcase-capabilities-skia.png) | ![ImageSharp](https://media.githubusercontent.com/media/RoboNET/FlexRender/main/examples/output/showcase-capabilities-imagesharp.png) | [SVG output](examples/output/showcase-capabilities.svg) |
| ![Skia](https://raw.githubusercontent.com/RoboNET/FlexRender/main/examples/output/showcase-capabilities-skia.png) | ![ImageSharp](https://raw.githubusercontent.com/RoboNET/FlexRender/main/examples/output/showcase-capabilities-imagesharp.png) | [SVG output](examples/output/showcase-capabilities.svg) |
| Native rendering, gradients, shadows, SVG elements | Pure .NET, zero native deps | Vector output, scalable |

</details>
Expand Down
Loading
Loading