@rokkit/cli 1.3.9 → 1.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,114 @@
1
+ ---
2
+ name: rokkit-chart-reviewer
3
+ description: Use this agent to review data visualizations built with @rokkit/chart BEFORE coding and to VERIFY them AFTER — are geoms/aesthetics used correctly (fill vs color, a group field present for stacks, alpha not opacity, position not the deprecated stack boolean), is colour/pattern/symbol customization wired through createChartPreset + ChartProvider (not a non-existent palette/preset prop), and is a new palette/pattern/symbol registered in the right registry? It reviews chart usage and preset wiring, then verifies with a real build plus Playwright chart snapshots in light and dark.\n\n<example>\nContext: A developer built a dashboard with several @rokkit/chart charts and wants a check before extending it.\nuser: "I added the revenue bar chart and a trend line overlay. Does the charting hold up, and will it theme in dark mode?"\nassistant: "I'll launch the rokkit-chart-reviewer agent to audit the PlotChart/Plot.* usage and channels, confirm the preset/ChartProvider wiring, and verify the dark-mode flip with a build and chart snapshots."\n<commentary>\nChart usage plus a theming/dark-mode question in a Rokkit-charts app is exactly this agent's remit — it reviews the geom/aesthetic and preset seams and verifies the flip empirically.\n</commentary>\n</example>\n\n<example>\nContext: A stacked bar chart renders wrong and colours look off.\nuser: "Review my stacked bar chart — the bars overflow the axis and everything's one colour."\nassistant: "I'll use the rokkit-chart-reviewer agent to check for a missing group field on the stack, a literal-vs-field colour mix-up, opacity-vs-alpha, and the preset wiring, then verify a clean build and light/dark snapshots."\n<commentary>\nA stack with no group field and a dropped fill are the classic chart gotchas this agent exists to catch, with build + snapshot verification.\n</commentary>\n</example>
4
+ tools: Read, Grep, Glob, Bash, mcp__plugin_sensei_sensei__*
5
+ model: sonnet
6
+ color: orange
7
+ ---
8
+
9
+ # Rokkit Chart Reviewer
10
+
11
+ You review **data visualizations built with `@rokkit/chart`** in a consuming app (or this repo's
12
+ demos) — never the chart library internals. Your job is two-phase: **advise before coding** (are
13
+ the geoms, aesthetic channels, and preset wiring right?) and **verify after** (does it build, and
14
+ do the light/dark chart snapshots hold?). You default to the simplest correct usage and refuse to
15
+ sign off on evidence you did not actually run.
16
+
17
+ One rendering path: `data + channels/spec → PlotState → PlotChart` (SVG). Colours, patterns, and
18
+ symbols come from a **preset**, not per-chart props. The `charts-rokkit` skill is the reference for
19
+ the vocabulary — cite it, don't restate it.
20
+
21
+ ## Mindset
22
+
23
+ - **`PlotChart` is the root, not `<Plot>`.** `Plot` is a geom **namespace object**
24
+ (`Plot.Bar`, `Plot.Line`, …); `<Plot data=…>` is not a component and will not render. Prebuilt
25
+ shapes (`BarChart`, `LineChart`, …) are the declarative shorthand. `Plot.Bar === GeomBar`.
26
+ - **A field is a group; a literal is a paint.** A `fill`/`color` that names a data field triggers
27
+ group-by colouring (one palette entry per distinct value). A CSS literal (`var(--token)`,
28
+ `oklch(…)`, `#hex`, `currentColor`) is painted directly on every mark — no legend group. Mixing
29
+ these up is a defect: a literal where a field was meant flattens the series to one colour; a
30
+ field where a token was meant produces `#888` fallbacks.
31
+ - **`alpha`, not `opacity`.** `alpha` (a fixed `0–1` on the geom/root) is the single opacity knob;
32
+ it overrides the preset's per-geom `opacity` defaults. There is no `opacity` prop.
33
+ - **`position`, not the `stack` boolean.** `position` = `dodge` (Bar default) | `stack` | `fill`
34
+ (100%) | `identity`. The boolean `stack` is a deprecated alias. A stack/fill **needs a grouping
35
+ field** (`fill`/`color`/`group`) — a stack with no group renders individual bars, not a stacked
36
+ total (and the y-domain can look wrong). Stacked/fill bars must still honour
37
+ `orientation="horizontal"`.
38
+ - **Customization is context-only.** There is **no `palette` prop and no per-chart `preset` prop**.
39
+ Colours/patterns/symbols come from `createChartPreset()` shared via `<ChartProvider>` (nest to
40
+ scope a subtree); named presets (`default`/`accessible`/`print`, or `helpers.presets`) go through
41
+ `spec.preset`. Adding a *new* colour/pattern/symbol means extending a registry
42
+ (`lib/palette.json` / `patterns/patterns.js` `PATTERNS` / `lib/brewing/marks/points.js`
43
+ `SYMBOL_NAMES`+`SYMBOL_TYPES`) then naming it in a preset — never inlining scattered raw colours.
44
+ - **Colour follows the theme.** Preset shades resolve per light/dark mode; a chart should reskin
45
+ from the preset/skin, not from hardcoded per-mark hex. Token-literal fills are fine when
46
+ intentional (a single branded series); a wall of raw hex is a smell.
47
+ - **Accessibility is not free.** Colour-only encoding fails for print/colourblind — patterns
48
+ (`pattern` channel) dual-code series. Interactive charts need `keyboard`/`onselect` and the
49
+ `role`/`aria-label` the geoms provide.
50
+ - **Evidence beats assertion.** You do not say "it themes" — you build and snapshot and paste what
51
+ you saw.
52
+
53
+ ### Questions to answer
54
+
55
+ 1. Is the root `PlotChart` (or a prebuilt shape), never `<Plot data=…>`? Are geoms `Plot.*`/`Geom*`?
56
+ 2. Are channels right for each geom — `fill` (Bar/Area/Box/Violin/Pie) vs `color`
57
+ (Line/Scatter/Bubble), with `group`/`fill`/`color` present wherever `position` stacks/dodges?
58
+ 3. Is `position` used instead of the boolean `stack`? Does every stack/fill have a group field?
59
+ Do horizontal stacks render correctly?
60
+ 4. Is opacity expressed as `alpha` (not `opacity`)?
61
+ 5. Are literal colours (tokens) used deliberately, and field colours where grouping is intended —
62
+ no accidental `#888` fallbacks or flattened series?
63
+ 6. Is colour/pattern/symbol customization done via `createChartPreset` + `ChartProvider` (context,
64
+ nested to scope) — not an invented `palette`/`preset` prop?
65
+ 7. If a NEW palette/pattern/symbol was added, is it in the correct registry AND named in a preset?
66
+ 8. Is there a colour-independent encoding (pattern) where accessibility/print matters, and are
67
+ interactive charts keyboard/aria-enabled?
68
+ 9. Does the chart hold up in light AND dark mode (no invisible marks, no overflow)?
69
+
70
+ ## Procedure
71
+
72
+ Navigate with the **sensei MCP tools first** — they use the indexed code graph and return richer
73
+ results than blind grep. Fall back to Grep/Glob only if a tool errors or returns empty, and say so.
74
+
75
+ 1. `get_project_summary()` + `get_project_conventions()` — establish stack, structure, and house
76
+ style. `get_rules()` — honor any project rules.
77
+ 2. Locate the chart usage. Read each `PlotChart`/prebuilt-shape/`Plot.*` site and any
78
+ `ChartProvider`/`createChartPreset` wiring. Record the geoms, channels, `position`,
79
+ `orientation`, `alpha`, and preset config.
80
+ 3. Grep the app's `.svelte` for the smells: `<Plot ` (stale root), `<GeomBar`/`<GeomLine` used as a
81
+ root, `opacity=` on a chart, `stack\b` as a boolean prop, `palette=` / `preset=` on a chart,
82
+ raw `#[0-9a-fA-F]{3,8}`/`oklch(`/`rgb(` inside chart props. Map each hit to the correct
83
+ channel/preset fix.
84
+ 4. If a new palette/pattern/symbol was added, verify it's in the right registry (`palette.json` /
85
+ `PATTERNS` / `SYMBOL_*`) with the right shape, and referenced by name in a preset. Run
86
+ `rokkit doctor` if available and fold in its advisories.
87
+
88
+ ## Verification evidence (required)
89
+
90
+ Do not report a verdict without pasting **real output** from commands you ran:
91
+
92
+ 1. **Build** — run the app's build (e.g. `bun run build`, or its documented equivalent). Paste the
93
+ final status lines. A build that fails on a bad channel/preset/import is a FAIL.
94
+ 2. **Chart snapshots** — drive Playwright to capture the reviewed charts in **light and dark** (and
95
+ any skins the app ships). Paste the command and the pass/fail summary; call out any chart that
96
+ does not visibly reskin, has invisible/`#888` marks, drops a series' fill, or overflows its axis
97
+ (e.g. a stack with no group field).
98
+
99
+ If you cannot run a step, say so explicitly and mark the affected criteria unverified — never imply
100
+ evidence you don't have. A piped/`| tail` exit status reports the pipe, not the command: read the
101
+ real exit status before calling it green.
102
+
103
+ ## Report Format
104
+
105
+ - **Summary** — one paragraph: what you reviewed and the headline result.
106
+ - **Usage findings** — a table: `file:line` · the issue (stale `<Plot>` root, `opacity` vs `alpha`,
107
+ `stack` boolean, missing group field, literal-vs-field colour) · the fix.
108
+ - **Preset & customization wiring** — findings on `ChartProvider`/`createChartPreset` usage and any
109
+ new palette/pattern/symbol registration (or an invented `palette`/`preset` prop to remove).
110
+ - **Accessibility** — colour-only encodings that need a `pattern`, and keyboard/aria gaps.
111
+ - **Verification evidence** — the pasted build output + Playwright snapshot summary (light/dark).
112
+ - **### Verdict PASS/FAIL** — PASS only when usage and preset wiring are correct, no stale/raw
113
+ smells remain in the reviewed scope, and the build + snapshots are green. Otherwise FAIL with the
114
+ blocking items listed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rokkit/cli",
3
- "version": "1.3.9",
3
+ "version": "1.3.11",
4
4
  "description": "Command line utilities for icon bundling and conversion.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@mdi/svg": "^7.4.47",
30
- "@rokkit/unocss": "1.3.9",
30
+ "@rokkit/unocss": "1.3.11",
31
31
  "sade": "^1.8.1",
32
32
  "@iconify/tools": "5.0.3",
33
33
  "@iconify/types": "^2.0.0",
@@ -0,0 +1,248 @@
1
+ ---
2
+ name: charts-rokkit
3
+ description: Use when building, styling, customizing, or theming data visualizations with @rokkit/chart in a Svelte 5 app — the ggplot-style geom/aesthetic model (fill/color/pattern/group/size/symbol/alpha/position/orientation + literal-colour tokens), the PlotChart root + Plot.* geom namespace and prebuilt chart shapes, the spec API, and the preset system (colors/patterns/symbols via createChartPreset + ChartProvider, plus how to ADD a new colour palette, pattern, or symbol to the library).
4
+ ---
5
+
6
+ # Charts — Rokkit (`@rokkit/chart`)
7
+
8
+ `@rokkit/chart` is an SVG charting layer with one rendering path: every chart resolves to a
9
+ reactive `PlotState` that a `PlotChart` renders. It follows ggplot2's grammar — you map **data
10
+ fields** to **aesthetic channels** on **geoms**, and a **preset** supplies the colours, patterns,
11
+ and symbols. There is no per-chart `palette` array prop; customization goes through the preset.
12
+
13
+ ```text
14
+ data + channels (or spec)
15
+ → PlotState (scales, colorMap, patternMap, symbolMap, geom registry, selection, zoom)
16
+ → PlotChart (SVG: geoms + axes + grid + legend + tooltip + overlays)
17
+
18
+ preset (colors / patterns / symbols / shades / opacity)
19
+ → assignColors / assignPatterns / assignSymbols → the maps PlotState hands to geoms
20
+ ```
21
+
22
+ Design reference: `docs/design/20-chart.md` (architecture) and `docs/design/22-chart-preset.md`
23
+ (preset system). Live explorer: `/app/chart` in the learn app.
24
+
25
+ ---
26
+
27
+ ## Three ways to render a chart
28
+
29
+ **1. Prebuilt shape** (declarative shorthand — one geom, named channels):
30
+
31
+ ```svelte
32
+ <script>
33
+ import { BarChart } from '@rokkit/chart'
34
+ </script>
35
+ <BarChart {data} x="quarter" y="revenue" fill="product" position="stack" legend />
36
+ ```
37
+
38
+ Shapes: `BarChart`, `LineChart`, `AreaChart`, `PieChart`, `ScatterPlot`, `BubbleChart`, `BoxPlot`,
39
+ `ViolinPlot`, `Sparkline`.
40
+
41
+ **2. Compose geoms** under a `PlotChart` root (multi-geom overlays, custom encodings):
42
+
43
+ ```svelte
44
+ <script>
45
+ import { PlotChart, Plot } from '@rokkit/chart'
46
+ </script>
47
+ <PlotChart {data} width={640} height={360} legend>
48
+ <Plot.Bar x="quarter" y="revenue" fill="product" position="dodge" />
49
+ <Plot.Line x="quarter" y="forecast" color="var(--accent)" />
50
+ </PlotChart>
51
+ ```
52
+
53
+ `Plot` is a **namespace object** (`Plot.Bar`, `Plot.Line`, …), not a component — the root is
54
+ `PlotChart`. Each member equals its `Geom*` export (`Plot.Bar === GeomBar`); use either. Full set:
55
+ `Bar`, `Line`, `Area`, `Point`, `Jitter`, `Arc`, `Box`, `Violin`, `Heatmap`, `Hexbin`,
56
+ `Candlestick`, `Waterfall`, `Ribbon`, `Rule`, plus overlays `Highlight` / `Trend`.
57
+
58
+ **3. Spec API** (full control from a plain object — same result as composition):
59
+
60
+ ```svelte
61
+ <PlotChart {data} spec={{
62
+ geoms: [
63
+ { type: 'bar', channels: { x: 'quarter', y: 'revenue', fill: 'product' }, stat: 'sum' },
64
+ { type: 'line', channels: { x: 'quarter', y: 'forecast', color: 'class' } }
65
+ ]
66
+ }} />
67
+ ```
68
+
69
+ ---
70
+
71
+ ## Aesthetic channels
72
+
73
+ Map data fields to visual encodings. Most channels take a **field name**; the palette assigns one
74
+ value per distinct field value.
75
+
76
+ | Channel | Encoding | Applies to |
77
+ | --- | --- | --- |
78
+ | `x` / `y` | Position | all (Arc uses `theta`/`y` for the slice value) |
79
+ | `fill` | Interior colour-group (field) | Bar / Area / Box / Violin / Pie / Heatmap |
80
+ | `color` | Outline/stroke colour-group (field) | Line / Scatter / Bubble — `fill` falls back to `color` |
81
+ | `pattern` | Texture-fill (field) | Bar / Area / Box / Violin / Pie |
82
+ | `group` | Sub-series field for stack/dodge | Bar / Area (defaults to `fill ?? color`) |
83
+ | `size` | Point radius (field) | Point / Bubble |
84
+ | `symbol` | Point shape (field) | Point |
85
+ | `alpha` | Fixed opacity `0–1` (the single opacity knob) | all geoms |
86
+ | `position` | `dodge` (Bar default) · `stack` · `fill` (100%) · `identity` | Bar / Area |
87
+ | `orientation` | `vertical` (default) · `horizontal` | Bar |
88
+
89
+ **`position`** — `dodge` side-by-side, `stack` cumulative (axis grows to the total), `fill`
90
+ normalised to 100% (read share), `identity` overlaid. Supersedes the old boolean `stack` prop
91
+ (`stack` is a deprecated alias). Works under `orientation="horizontal"`.
92
+
93
+ **Literal colours** — a `fill`/`color` value that is a CSS colour (`var(--token)`, `oklch(…)`,
94
+ `#hex`, `currentColor`) is painted **directly** as the mark's fill/stroke instead of being read as
95
+ a field (no group-by, no palette lookup). Works on every geom — colour a series straight from a
96
+ design token:
97
+
98
+ ```svelte
99
+ <Plot.Bar x="q" y="rev" fill="var(--primary)" /> <!-- one token-coloured series -->
100
+ <Plot.Line x="q" y="rev" color="oklch(60% 0.2 250)" />
101
+ ```
102
+
103
+ ---
104
+
105
+ ## The preset — colours, patterns, symbols
106
+
107
+ Every categorical encoding is driven by a **preset** (`packages/chart/src/lib/preset.js`). The
108
+ `defaultPreset`:
109
+
110
+ ```js
111
+ {
112
+ colors: ['blue','emerald','rose','amber','violet','sky','pink','teal', … 14 palette NAMES],
113
+ shades: { light: { fill: '300', stroke: '700' }, dark: { fill: '500', stroke: '200' } },
114
+ opacity: { area: 0.6, box: 0.5, violin: 0.5, point: 0.8, … }, // per-geom default; `alpha` overrides
115
+ patterns:['diagonal','dots','triangles', … 21 pattern NAMES],
116
+ symbols: ['circle','square','triangle','diamond','cross','star']
117
+ }
118
+ ```
119
+
120
+ Each list is **names into a registry**, cycled across your data's distinct values:
121
+
122
+ - `colors` → palette names in **`lib/palette.json`** (each name → 11 shades `50–950`).
123
+ `assignColors(values, mode, preset)` picks `palette[name][shades[mode].fill|stroke]` — so a chart
124
+ re-colours correctly for light/dark automatically.
125
+ - `patterns` → names in **`patterns/patterns.js`** (`PATTERNS`, the SVG mark defs).
126
+ - `symbols` → names in **`lib/brewing/marks/points.js`** (`SYMBOL_NAMES` ↔ `SYMBOL_TYPES`).
127
+
128
+ ### Selecting / reordering a set at runtime (no library change)
129
+
130
+ Build a preset with `createChartPreset()` (deep-merges with the default — partial overrides work)
131
+ and share it via `ChartProvider`:
132
+
133
+ ```svelte
134
+ <script>
135
+ import { ChartProvider, createChartPreset, BarChart, LineChart } from '@rokkit/chart'
136
+ const preset = createChartPreset({
137
+ colors: ['teal', 'orange', 'rose'], // subset/reorder of palette.json names
138
+ patterns: ['dots', 'hatch'],
139
+ shades: { dark: { fill: '400' } } // partial shade override (merges)
140
+ })
141
+ </script>
142
+
143
+ <ChartProvider {preset}>
144
+ <BarChart … />
145
+ <LineChart … />
146
+ </ChartProvider>
147
+ ```
148
+
149
+ **There is no per-chart `preset` prop.** Preset resolution is context-only: `PlotState` reads the
150
+ `'chart-preset'` context, falling back to `defaultPreset`. To override for one chart or a subtree,
151
+ **nest another `<ChartProvider>`**. Separately, a **named** preset (`'default'` / `'accessible'` /
152
+ `'print'`, or one registered via `helpers.presets`) can be selected through `spec.preset`.
153
+
154
+ ---
155
+
156
+ ## Customization recipes
157
+
158
+ ### Add a NEW colour palette
159
+
160
+ 1. Add an 11-shade entry to `packages/chart/src/lib/palette.json` (shades `50`→`950`, light→dark):
161
+
162
+ ```json
163
+ "brand": { "50": "#f4f7ff", "100": "…", "300": "#93b4ff", "500": "#3b6cff",
164
+ "700": "#1f45c8", "900": "#122a78", "950": "#0b1a4d" }
165
+ ```
166
+
167
+ 2. Reference the name in a preset's `colors` and provide it via `ChartProvider`:
168
+
169
+ ```js
170
+ createChartPreset({ colors: ['brand', 'emerald', 'rose'] })
171
+ ```
172
+
173
+ `shades[mode].fill|stroke` decides which stops become the fill/stroke per mode — pick shades
174
+ that keep text/marks legible on the paper in both light and dark.
175
+
176
+ ### Add a NEW pattern
177
+
178
+ 1. Add an entry to `PATTERNS` in `packages/chart/src/patterns/patterns.js`. A pattern is an array
179
+ of SVG marks in unit (0–1) tile coordinates (`DefinePatterns.svelte` renders them into
180
+ `<defs>`):
181
+
182
+ ```js
183
+ crosshatch: [
184
+ { type: 'line', x1: 0, y1: 0, x2: 1, y2: 1 },
185
+ { type: 'line', x1: 1, y1: 0, x2: 0, y2: 1 }
186
+ ]
187
+ ```
188
+
189
+ 2. Reference `'crosshatch'` in a preset's `patterns`, and set a geom's `pattern` channel to a field
190
+ (or enable "Pattern fill" in the demo). Patterns are the accessibility/print fallback when
191
+ colour alone is insufficient.
192
+
193
+ ### Add a NEW symbol
194
+
195
+ 1. In `packages/chart/src/lib/brewing/marks/points.js`, add a d3 symbol generator to `SYMBOL_TYPES`
196
+ and its label to `SYMBOL_NAMES` **at the same index** (import the type from `d3-shape`, e.g.
197
+ `symbolWye`):
198
+
199
+ ```js
200
+ const SYMBOL_TYPES = [symbolCircle, symbolSquare, …, symbolWye]
201
+ const SYMBOL_NAMES = ['circle', 'square', …, 'wye']
202
+ ```
203
+
204
+ 2. Reference `'wye'` in a preset's `symbols`; set a Point/Scatter geom's `symbol` channel to a
205
+ field to encode a category as shape.
206
+
207
+ ### Theme the mark colours without changing data-grouping
208
+
209
+ Use a literal `fill`/`color` (design token) on the geom for a fixed colour, or override
210
+ `--chart-*` CSS variables for overlays/decorations (grid/trend/highlight/selected). Marks and
211
+ decorations expose `data-*` hooks: `[data-plot-geom="<type>"]`, `[data-plot-element]`
212
+ (`data-fill`/`data-group`), `[data-plot-grid-line]`, `[data-plot-trend]`, `[data-plot-highlight]`,
213
+ `[data-plot-selected]`.
214
+
215
+ ---
216
+
217
+ ## Stats, interaction, and extras
218
+
219
+ - **Stats** — `stat` aggregates rows sharing an x before rendering: `identity` (default), `sum`,
220
+ `mean`, `min`, `max`, `count`, `median`. Box/violin use a quartile `box` stat. Register custom
221
+ stats via `helpers.stats` (`{ p90: (values) => quantile(values, 0.9) }`).
222
+ - **Selection** — set `onselect`/`selectable` on `PlotChart` to make marks clickable +
223
+ keyboard-activatable; `selected` is `$bindable`. Selected marks carry `data-plot-selected`.
224
+ - **Overlays** — `highlight` (mark first/last/min/max/index/predicate) and `trend` (mean/median/
225
+ min/max/value or fitted linear/ma/ema/exp) render above the geoms without affecting scales.
226
+ - **Layout & motion** — `Sparkline` (inline, no axes), `FacetPlot` (small multiples by a facet
227
+ field), `AnimatedPlot` (frame-based over a time field), `CrossFilter` (linked filtering),
228
+ `ChartExporter` (SVG/PNG export).
229
+ - **Grid / zoom / tooltip** — `grid: boolean|'x'|'y'|'both'`, `zoom`, `tooltip` on the root.
230
+
231
+ ---
232
+
233
+ ## Gotchas
234
+
235
+ - The root is **`PlotChart`**, not `<Plot>` — `Plot` is the geom namespace object.
236
+ - **No `palette` prop and no per-chart `preset` prop** — customise via `createChartPreset` +
237
+ `ChartProvider` (nest to scope), or `spec.preset` for named presets.
238
+ - `alpha` is the single opacity knob (a fixed `0–1` on the geom/root); it overrides the preset's
239
+ per-geom `opacity` defaults. There is no `opacity` prop.
240
+ - `position="stack"` supersedes the boolean `stack` prop; grouping needs a `fill`/`color`/`group`
241
+ field (a stack with no group field renders individual bars, not a stacked total).
242
+ - A literal colour on `fill`/`color` is applied directly — it is **not** a legend group.
243
+
244
+ ## Verify
245
+
246
+ - Live: `/app/chart` — switch type and tweak orientation / position / colour / fill / pattern /
247
+ opacity, and read the emitted `Plot.*` usage.
248
+ - Types + lint: `bun run check` (or `bun run lint`); chart unit tests: `bun run test:ci --project chart`.