Charting
Skill839 starsChanged 6 days agoMITView on GitHub
ReactorCharting is a C# port of D3.js that renders charts as Reactor Element trees on WinUI3 Canvas. Charts are pure functions: data => Element. The goal is a single return D3Canvas(...) expression per chart with no imperative mutation. Every chart file needs these: The best charts follow this structure — one return, one collection expression, data transforms via LINQ, all primitives from the DSL: Key elements: D3Canvas wraps everything, [.. spread] composes flat element lists, .Select() / .SelectMany() map data…
3 other projects use a version of this.
Adopted by
Most recently taken up by
- mthines/agent-skillstheir own version, first seen Oct 30, 2026
- zhanyinx/skillstheir own version, first seen Sep 3, 2026
- oaustegard/claude-skillstheir own version, first seen Mar 11, 2026
Other versions3 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 3
- oaustegard/claude-skills★ 158same name
- mthines/agent-skills★ 20same name
- zhanyinx/skills★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository23 other files it gives its agents.
- AGENTS.mdAGENTS.md
- analyzer-dymSkill
- coverage-upliftSkill
- perf-compareSkill
- pr-lifecycleSkill
- pr-reviewSkill
- reactor-advancedSkill
- reactor-asyncSkill
- reactor-build-and-checkSkill
- reactor-chartsSkill
- reactor-commandingSkill
- reactor-designSkill
- reactor-devtoolsSkill
- reactor-dockingSkill
- reactor-dslSkill
- reactor-formsSkill
- reactor-getting-startedSkill
- reactor-inputSkill
- reactor-navigationSkill
- reactor-recipesSkill
- reactor-windowingSkill
- reactor-wrappingSkill
- reactor-appSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
14 rules. 0 are common, 5 are distinctive, and 9 are very specific to this project.
0
Common
Most of the 3 edited versions we read keep these.
5
Distinctive
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 7
Every chart file needs these:
- Very specific: line 51
Always call .Nice() on Y scales to get clean tick numbers.
- Very specific: line 55
All return Reactor Elements.
- Very specific: line 66
Use named parameters for fontSize:, foreground:, r:, width:, alpha:, opacity: — bare numeric literals are ambiguous without names.
- Distinctive: line 70
Use the one-shot DSL helpers instead of constructing generator instances:
- Very specific: line 90
Always use named parameters x:, y:, y0:, y1:, value: on these helpers — three consecutive lambdas are unreadable without names.
- Distinctive: line 127
When each data item produces one element:
- Distinctive: line 138
When each data item produces multiple elements (e.g., bar + label):
- Distinctive: line 150
When you need 3+ intermediate values before the final element, use query syntax instead of a multi-line lambda:
- Very specific: line 167
Keep .Select() for simple 1-line projections.
- Very specific: line 191
Use
D3LinePath/D3AreaPath: instead of constructing LineGenerator/AreaGenerator instances - Very specific: line 192
Use
D3ArcPath: instead of constructing ArcGenerator instances - Very specific: line 193
Use
D3Grid/D3Axes/D3Legend: instead of manually building grid lines, axis labels, or legends - Very specific: line 214
Don't manually build axis labels: when D3Axes(xs, ys, ...) covers your case
“Common” is measured: kept by at least 6 in 10 of the 3 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
ReactorCharting Chart Authoring Guide
ReactorCharting is a C# port of D3.js that renders charts as Reactor Element trees on WinUI3 Canvas. Charts are pure functions: data => Element. The goal is a single return D3Canvas(...) expression per chart with no imperative mutation.
Imports
Every chart file needs these:
using Microsoft.UI.Reactor.Core;
using Microsoft.UI.Reactor.Charting.D3;
using Microsoft.UI.Reactor.Charting;
using static Microsoft.UI.Reactor.Charting.D3; // D3Rect, D3Circle, D3LinePath, Brush, Gray, etc.
using static Microsoft.UI.Reactor.Factories; // Text, Canvas, ScrollView, etc.The Ideal Shape
The best charts follow this structure — one return, one collection expression, data transforms via LINQ, all primitives from the DSL:
return D3Canvas(W, H,
[.. D3Grid(ys, left, pw),
.. D3Axes(xs, ys, left, top, pw, ph),
.. data.Select(d => D3Circle(xs.Map(d.x), ys.Map(d.y), r: 4)
with { Fill = fill, Stroke = stroke }),
D3Text(left, 6, "Title", fontSize: 14, foreground: Gray(40))]
);Key elements: D3Canvas wraps everything, [.. spread] composes flat element lists, .Select() / .SelectMany() map data to elements, with { } sets visual properties.
Scales
Scales map data values to pixel positions. Create them, then call .Map(value) in accessors.
// Continuous: numeric domain -> pixel range
var xs = new LinearScale([0, 100], [left, left + pw]).Nice();
var ys = new LinearScale([0, maxVal], [top + ph, top]).Nice(); // Y is inverted (0 at bottom)
// Categorical: discrete labels -> bands
var band = BandScale.Create(labels)
.SetRange(0, plotW)
.SetPaddingInner(0.2)
.SetPaddingOuter(0.1);
double x = left + band.Map("Jan");
double barWidth = band.Bandwidth;Always call .Nice() on Y scales to get clean tick numbers. The Y range goes from [bottom, top] (inverted) so larger values map to smaller Y pixel values.
DSL Primitives
All return Reactor Elements. Customize with with { } for Fill, Stroke, etc.
D3Rect(x, y, width, height) // Rectangle
D3Circle(cx, cy, r: 4) // Circle (name r: when literal)
D3Line(x1, y1, x2, y2) // Line segment
D3Text(x, y, "label", fontSize: 11, foreground: brush) // Left-aligned text
D3TextRight(x, y, "label", width: 50, fontSize: 10) // Right-aligned (for Y axis labels)
D3TextCenter(x, y, "label", width: 40, fontSize: 10) // Center-alignedUse named parameters for fontSize:, foreground:, r:, width:, alpha:, opacity: — bare numeric literals are ambiguous without names.
Generator Helpers
Use the one-shot DSL helpers instead of constructing generator instances:
// Line from data -> PathElement (one expression, no intermediate variables)
D3LinePath(data, x: d => xs.Map(d.x), y: d => ys.Map(d.y),
stroke: Brush(Palette[0]), strokeWidth: 2, curve: D3Curve.MonotoneX)
// Area from data -> PathElement
D3AreaPath(data, x: d => xs.Map(d.x), y0: d => ys.Map(0), y1: d => ys.Map(d.y),
fill: Brush(Palette[0], opacity: 0.3))
// Arc sector -> PathElement (for pie slices, sunbursts)
D3ArcPath(startAngle, endAngle, cx, cy,
innerRadius: 80, outerRadius: 150, fill: sliceBrush)
// Pie slices from data -> Element[] (simple case, no labels)
D3Pie(data, value: d => d.Value, cx, cy,
outerRadius: 150, innerRadius: 0, sort: false)Always use named parameters x:, y:, y0:, y1:, value: on these helpers — three consecutive lambdas are unreadable without names.
For pie charts with labels, use the static one-shot + D3ArcPath:
var arcs = PieGenerator.Generate(data, value: d => d.Value, sort: false, padAngle: 0.02);
arcs.SelectMany((a, i) => new Element[]
{
D3ArcPath(a.StartAngle, a.EndAngle, cx, cy, outerRadius: 150, fill: Brush(Palette[i])),
D3Text(cx + lx, cy + ly, a.Data.Name, fontSize: 10, foreground: brush),
})Composite Helpers
D3Grid(yScale, left, plotWidth) // Horizontal grid lines -> Element[]
D3Axes(xScale, yScale, left, top, width, height) // Axis lines + tick labels -> Element[]
D3Legend(x, y, items.Select((label, brush) => (label, brush))) // Vertical legend -> Element[]
D3Link(x1, y1, x2, y2, stroke: brush) // Bezier tree link -> PathElementColor Helpers
Brush(Palette[0]) // Categorical color from D3 Category10
Brush(Palette[i], opacity: 0.6) // With opacity (always name opacity:)
Brush("#2ca02c") // From hex string
Gray(100) // Grayscale
Gray(100, alpha: 180) // Grayscale with alpha (always name alpha:)Palette is D3Color.Category10 — 10 categorical colors. Access with Palette[i % Palette.Length] to avoid index errors.
Data-to-Elements Patterns
Simple projection — .Select()
When each data item produces one element:
.. points.Select(p => (Element)(D3Circle(xs.Map(p.x), ys.Map(p.y), r: 4)
with { Fill = fill }))The (Element) cast is sometimes needed for spread in collection expressions — this is a C# limitation, not a DSL issue.
Multi-element projection — .SelectMany()
When each data item produces multiple elements (e.g., bar + label):
.. items.SelectMany(item => new Element[]
{
D3Rect(x, y, w, h) with { Fill = fill },
D3Text(x + 4, y + 3, item.Name, fontSize: 9, foreground: Gray(20)),
})Query syntax — from/let/select
When you need 3+ intermediate values before the final element, use query syntax instead of a multi-line lambda:
.. (from t in candles.Select((c, i) => (c, i))
let cx = xs.Map(t.i)
let bullish = t.c.Close >= t.c.Open
let brush = bullish ? bullBrush : bearBrush
let bodyTop = ys.Map(Math.Max(t.c.Open, t.c.Close))
let bodyH = Math.Max(ys.Map(Math.Min(t.c.Open, t.c.Close)) - bodyTop, 1)
from el in new Element[]
{
D3Line(cx, ys.Map(t.c.High), cx, ys.Map(t.c.Low)) with { Stroke = brush },
D3Rect(cx - barW / 2, bodyTop, barW, bodyH) with { Fill = brush },
}
select el)Keep .Select() for simple 1-line projections. Switch to query syntax at 3+ let bindings.
Stacked/grouped series
.. series.SelectMany((s, si) =>
{
var fill = Brush(Palette[si]);
return months.Select((month, j) =>
{
var pt = s.Points[j];
double x = left + band.Map(month);
return D3Rect(x, y1Screen, band.Bandwidth, height) with { Fill = fill };
});
})Things to Do
- Return a single
D3Canvas(W, H, [...])expression — the entire chart is one return statement - Use
[.. spread]syntax to compose flat element arrays:[.. grid, .. axes, .. bars, title] - Use LINQ
.Select()/.SelectMany()to map data to elements — no for-loops - Use
with { Fill = ..., Stroke = ... }to set visual properties on elements - Use named parameters on calls with multiple double/brush args:
fontSize:,foreground:,opacity:,alpha:,r:,x:,y:,y0:,y1:,value:,width: - Use
D3LinePath/D3AreaPathinstead of constructing LineGenerator/AreaGenerator instances - Use
D3ArcPathinstead of constructing ArcGenerator instances - Use
D3Grid/D3Axes/D3Legendinstead of manually building grid lines, axis labels, or legends - Use
Palette[i % Palette.Length]to avoid index-out-of-range on category colors - Use
D3Extent.Extent(data, accessor)for finding min/max — not manual loops - Use
.Nice()on scales to get clean tick boundaries - Pre-compute brushes outside the element expression when reused:
var fill = Brush(Palette[0], opacity: 0.6);
Things to Avoid
- Don't construct generator instances (e.g.,
new ArcGenerator(),LineGenerator.Create(...)) when a DSL helper exists — useD3LinePath,D3AreaPath,D3ArcPath,D3Pieinstead - Don't use for-loops to build element arrays — use
.Select(),.SelectMany(), or query syntax - Don't use
XamlHostElementor raw XAML — everything should go through the D3 DSL primitives - Don't use
.Set(tb => tb.TextAlignment = ...)— useD3TextCenterorD3TextRightinstead - Don't use
Text(...).Foreground(...).Canvas(...)— useD3Text(x, y, text, fontSize:, foreground:)instead - Don't create mutable state — no
var elements = new List<Element>(); elements.Add(...)patterns - Don't use
Array.Empty<Element>()in ternary spreads for null paths —D3Pathaccepts null gracefully - Don't duplicate generators — e.g., creating two identical AreaGenerators when one suffices; or creating a separate
labelArcArcGenerator just for centroids (useArcGenerator.Centroid(...)static method instead) - Don't use
.Where().Select()chains that shift indices — either inline the filter or use query syntax withwherebeforeselectto preserve the original index - Don't write bare numeric literals for optional parameters —
Gray(100, 180)is ambiguous; writeGray(100, alpha: 180) - Don't use
Microsoft.UI.Xaml.Media.SolidColorBrush(Microsoft.UI.Colors.White)— writeBrush("#ffffff") - Don't manually build legends with
SelectManyproducing(rect, text)pairs — useD3Legend(x, y, items) - Don't manually build grid lines with tick iteration — use
D3Grid(yScale, left, width) - Don't manually build axis labels when
D3Axes(xs, ys, ...)covers your case - Don't use
D3Color.Parse(...)thenBrush(...)— just useBrush("#hex")directly
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

