Chart Generation
How It Works
Charts are generated server-side as PNG images using chartjs-node-canvas (Chart.js) and served via /api/media/. The generate-chart script renders charts and returns a fully qualified URL for embedding in chat markdown.
Script Usage
pnpm action generate-chart --type=bar --title="Title" --labels='["A","B","C"]' --data='[1,2,3]' --color="#18B4F4"
Parameters
| Param |
Values |
Default |
--type |
bar, line, area |
bar |
--title |
Chart title (keep SHORT) |
required |
--subtitle |
Optional subtitle — avoid unless needed |
— |
--labels |
JSON array of x-axis labels |
required |
--data |
JSON array of numbers, OR array of {label, data, color} for multi-series |
required |
--color |
Primary hex color |
#18B4F4 |
--theme |
dark, light |
auto from media/theme.json |
--stacked |
true for stacked bars |
false |
--filename |
Output filename (no ext) |
auto from title |
--width / --height |
Image dimensions |
800x400 |
Output
{ filename, url, width, height } — the url is fully qualified with cache buster.
Color Palette
| Color |
Hex |
Use |
| Blue |
#18B4F4 |
Default/primary for all charts |
| Purple |
#8b5cf6 |
Secondary |
| Green |
#22c55e |
Tertiary |
| Amber |
#f59e0b |
|
| Indigo |
#6366f1 |
|
| Red |
#ef4444 |
|
| Teal |
#14b8a6 |
|
| Orange |
#f97316 |
|
Style Preferences (from Steve)
- Minimal text on chart — short title only, no subtitle
- Stats and context in chat text around the chart, not on the image
- Large fonts: title 22px, axis 13px
- Few axis labels: maxTicksLimit 8 on x-axis, 5 on y-axis
- No rotated labels (maxRotation: 0)
Good pattern:
**Example Corp Product Usage — Last 30 Days**

**528 messages** over 18 active days | Peak: **89** (Feb 14)
QuickChart Inline Styling (for non-script charts)
When using QuickChart (https://quickchart.io/chart/create) with Chart.js v2:
- Background:
#09090b (zinc-950)
- Grid: horizontal only, dashed
[3,3], color #27272a
- Axis ticks:
#52525b, 11px
- Title:
#fafafa, 16px bold
- Legend:
#a1a1aa, 11px
- Default bar/line color:
#18B4F4 (primary blue)
- Tooltips: bg
#09090b, border #27272a, text #fafafa
Multi-Series & Stacked Charts
- Use
--stacked=true flag
- Pass data as
--data='[{"label":"User A","data":[...],"color":"#..."}]'
- Script sets
stack: "stack1" on all datasets
Key Gotchas
- Relative paths do NOT work — chat UI is on a different origin. Always use the fully qualified URL from script output (uses
APP_ORIGIN)
- Cache busting (
?v=<timestamp>) auto-appended
chartjs-node-canvas depends on canvas native module — pnpm.onlyBuiltDependencies must include "canvas". If not built, run pnpm rebuild canvas
- Theme auto-detected from
media/theme.json (set by the sidebar toggle via the set-theme action)
1---2name: charts3description: Generate inline chart images (PNG) for embedding in chat responses. Use this skill when you need to create bar, line, or area charts for data visualization in chat.4---56# Chart Generation78## How It Works910Charts are generated server-side as PNG images using `chartjs-node-canvas` (Chart.js) and served via `/api/media/`. The `generate-chart` script renders charts and returns a fully qualified URL for embedding in chat markdown.1112## Script Usage1314```bash15pnpm action generate-chart --type=bar --title="Title" --labels='["A","B","C"]' --data='[1,2,3]' --color="#18B4F4"16```1718### Parameters1920| Param | Values | Default |21| ---------------------- | -------------------------------------------------------------------------- | ---------------------- |22| `--type` | `bar`, `line`, `area` | `bar` |23| `--title` | Chart title (keep SHORT) | required |24| `--subtitle` | Optional subtitle — avoid unless needed | — |25| `--labels` | JSON array of x-axis labels | required |26| `--data` | JSON array of numbers, OR array of `{label, data, color}` for multi-series | required |27| `--color` | Primary hex color | `#18B4F4` |28| `--theme` | `dark`, `light` | auto from `media/theme.json` |29| `--stacked` | `true` for stacked bars | `false` |30| `--filename` | Output filename (no ext) | auto from title |31| `--width` / `--height` | Image dimensions | 800x400 |3233### Output3435`{ filename, url, width, height }` — the `url` is fully qualified with cache buster.3637## Color Palette3839| Color | Hex | Use |40| ------ | --------- | ---------------------------------- |41| Blue | `#18B4F4` | **Default/primary for all charts** |42| Purple | `#8b5cf6` | Secondary |43| Green | `#22c55e` | Tertiary |44| Amber | `#f59e0b` | |45| Indigo | `#6366f1` | |46| Red | `#ef4444` | |47| Teal | `#14b8a6` | |48| Orange | `#f97316` | |4950## Style Preferences (from Steve)5152- **Minimal text on chart** — short title only, no subtitle53- **Stats and context in chat text** around the chart, not on the image54- Large fonts: title 22px, axis 13px55- Few axis labels: maxTicksLimit 8 on x-axis, 5 on y-axis56- No rotated labels (maxRotation: 0)5758### Good pattern:5960```markdown61**Example Corp Product Usage — Last 30 Days**6263**528 messages** over 18 active days | Peak: **89** (Feb 14)64```6566## QuickChart Inline Styling (for non-script charts)6768When using QuickChart (`https://quickchart.io/chart/create`) with Chart.js v2:6970- Background: `#09090b` (zinc-950)71- Grid: horizontal only, dashed `[3,3]`, color `#27272a`72- Axis ticks: `#52525b`, 11px73- Title: `#fafafa`, 16px bold74- Legend: `#a1a1aa`, 11px75- Default bar/line color: `#18B4F4` (primary blue)76- Tooltips: bg `#09090b`, border `#27272a`, text `#fafafa`7778## Multi-Series & Stacked Charts7980- Use `--stacked=true` flag81- Pass data as `--data='[{"label":"User A","data":[...],"color":"#..."}]'`82- Script sets `stack: "stack1"` on all datasets8384## Key Gotchas8586- **Relative paths do NOT work** — chat UI is on a different origin. Always use the fully qualified URL from script output (uses `APP_ORIGIN`)87- Cache busting (`?v=<timestamp>`) auto-appended88- `chartjs-node-canvas` depends on `canvas` native module — `pnpm.onlyBuiltDependencies` must include `"canvas"`. If not built, run `pnpm rebuild canvas`89- Theme auto-detected from `media/theme.json` (set by the sidebar toggle via the `set-theme` action)