gmdiagram — GM Data Chart Skill
What This Skill Does
Generate publication-quality data visualization charts as standalone files. The output can be:
- HTML: Single
.htmlfile with inline SVG and embedded CSS (default, no JavaScript) - SVG: Standalone
.svgfile - Mermaid: Text syntax (limited chart support, see Mermaid rules)
- PNG/PDF: Via the export script at
../gm-architecture/skills/gm-architecture/scripts/export.sh
Output Directory Structure
All example files must be output to the correct directory structure:
assets/examples/
├── {example-name}.json # JSON schema file
├── {example-name}.html # HTML rendered output (same name as JSON)
└── images/ # For preview screenshots only
└── {example-name}-preview.png
Rules
Correct approach:
- HTML and JSON files go in the
examples/root directory - Preview images go in the
examples/images/subdirectory - Filenames must be consistent (only extensions differ)
Prohibited approach:
- Do NOT put HTML files in the
images/directory - Do NOT generate any non-image files in the
images/directory - Do NOT put JSON files in subdirectories (unless there's explicit categorization need)
Example
For the chrome-architecture example:
assets/examples/chrome-architecture.jsonassets/examples/chrome-architecture.htmlassets/examples/images/chrome-architecture-preview.pngassets/examples/images/chrome-architecture.html(WRONG: HTML should not be in images/)
Interactive Selection
When the user's request does NOT already specify all three choices (chart type, style, output format), use the AskUserQuestion tool to let the user choose.
Important: AskUserQuestion supports at most 4 options per question and 4 questions per call.
Selection Strategy
For each dimension, follow this logic:
- Can the choice be inferred from the user's description? → Skip asking, use the inferred value directly.
- Does the user explicitly state it? → Skip asking, use the stated value.
- Still ambiguous? → Ask via
AskUserQuestion.
Question 1 — Chart Type (ask only if ambiguous)
The user's description usually makes the type obvious. Only ask when multiple types could fit. Since there are 9 chart types, split into groups:
First ask which group:
question: "Which chart category fits your needs?"
header: "Category"
options:
- label: "Comparison"
description: "Bar chart — compare categories with bars"
- label: "Proportion"
description: "Pie / Donut — show proportions of a whole"
- label: "Trend / Distribution"
description: "Line / Area / Scatter — show trends, area, or correlation"
- label: "Special"
description: "Radar / Funnel / Bubble / Table — multi-axis, pipeline, 3D data, or grid"
Then drill down within the category:
- Comparison → Bar Chart (no further question needed)
- Proportion → Ask: "Pie or Donut?" (2 options, simple)
- Trend / Distribution → Ask: "Line / Area / Scatter?"
- Special → Ask: "Radar / Funnel / Bubble / Table?"
Question 2 — Visual Style
Use the same 3-family approach as architecture-diagram:
First ask:
"Which style family?" → Dark / Light+Clean / Creative
Then drill down within the family.
Infer from context when possible: technical content → dark, business → light-corporate, creative → cyberpunk/pastel.
Question 3 — Output Format
Default is HTML. Only ask if the user mentions a specific use case (GitHub → Mermaid, print → SVG/PDF).
Metadata Version Field
生成 JSON 时,metadata.version 必须遵循以下规则:
- 格式: 必须符合 SemVer (Semantic Versioning) 格式:
major.minor.patch - 示例:
"0.6.1","1.2.0","2.0.0" - 禁止的占位符版本:
"0.0.0","1.0","1.0.0"
推荐的版本策略: 建议使用与插件版本一致的值,但不做强制要求。重要的是保持格式规范。
JSON 示例:
"metadata": {
"author": "gmdiagram",
"date": "2026-04-14",
"version": "0.6.1"
}
验证规则:
- 必须包含 3 个数字段,用点号分隔
- 每段必须是纯数字
- 不允许前导零(如
"01.02.03"无效)
Two-Step Generation Process
ALL chart generation follows exactly two steps. Never skip to output directly.
Step 1 — Extract to JSON Schema
Extract data from the user's natural language description into the typed JSON schema for the chosen chart type.
Schema files:
| Chart Type | Schema File |
|---|---|
| bar | assets/schema-bar.json |
| pie | assets/schema-pie.json |
| line | assets/schema-line.json |
| area | assets/schema-area.json |
| scatter | assets/schema-scatter.json |
| radar | assets/schema-radar.json |
| funnel | assets/schema-funnel.json |
| bubble | assets/schema-bubble.json |
| table | assets/schema-table.json |
Shared schema definitions are in assets/schema-shared.json (styleEnum, formatEnum, legendConfig, axisConfig, scatterAxisConfig, sanitizedString, etc.). Individual schemas reference shared definitions via $ref.
Critical: Nice Numbers algorithm runs in this step.
Read references/axis-and-grid.md for the full algorithm. Compute Y-axis ticks and store them in axis.y.ticks. For scatter and bubble charts, compute Nice Numbers for both X and Y axes and store in axis.x.ticks and axis.y.ticks. This avoids complex math in Step 2.
Data extraction rules:
- Extract numeric values and labels from user text
- Infer series names when multiple data sets are mentioned
- When user provides percentages, store as raw values (not pre-multiplied)
- When user says "compare X and Y", infer grouped bar chart with 2 series
- All string fields have maxLength constraints — truncate if necessary with a note to the user
- Scatter/Bubble: Extract x/y pairs from user data. Infer axis labels from context (e.g., "height vs weight" → x label "Height", y label "Weight"). For bubble, also extract size values.
- Radar: Extract dimension labels (axes) and values per series. Ensure all series have the same number of data points as axes.
- Funnel: Extract stage names and values. Note that values should be in descending order; if not, auto-sort will be applied during rendering.
- Table: Extract column headers and row data. Ensure each row has a value for every column.
Validation:
- Bar/Line/Area:
seriesmax 8,dataper series max 30. If exceeded, must ask user to simplify. - Scatter:
seriesmax 8,dataper series max 50. - Bubble:
seriesmax 5,dataper series max 30. - Radar:
axesmin 3 max 12,seriesmax 6,datalength must matchaxeslength. - Pie/Funnel:
datamax 12 (pie) / max 8 (funnel). If exceeded, suggest merging smallest items into "Others" (pie) or simplifying stages (funnel). - Table:
columnsmin 2 max 12,rowsmax 50. Each row must contain all column keys. - All
valuefields must be finite numbers (no NaN, Infinity). - All
colorfields must match^#[0-9a-fA-F]{6}$. - Radar
axes[].maxValue: either all specified or all omitted (all-or-nothing). Partial specification is rejected in Step 1 validation.
Step 2 — Render Output
Read the corresponding render reference and template, then generate SVG wrapped in HTML.
Render references:
| Chart Type | Render Reference |
|---|---|
| bar | references/render-bar.md |
| pie | references/render-pie.md |
| line | references/render-line.md |
| area | references/render-area.md |
| scatter | references/render-scatter.md |
| radar | references/render-radar.md |
| funnel | references/render-funnel.md |
| bubble | references/render-bubble.md |
| table | references/render-table.md |
Shared references (read for all chart types):
references/axis-and-grid.md— axis, grid, tick renderingreferences/color-palettes.md— palette selection and color assignment
Template: Use ../../assets/template-{style}.html (shared templates with chart CSS classes already added).
Rendering approach — Hybrid Mode:
- Data marks (bars, arcs, lines, dots): pure SVG native elements (
<rect>,<path>,<circle>,<line>) - Text (labels, legend, axis titles):
foreignObject+ CSS layout - All
foreignObjectroot elements must includexmlns="http://www.w3.org/1999/xhtml"
HTML Language Attribute
根据内容语言设置正确的 <html lang="..."> 属性:
| 内容语言 | lang 属性值 | 使用场景 |
|---|---|---|
| 英文 | en |
标题、标签、描述均为英文 |
| 简体中文 | zh-CN |
标题、标签、描述包含简体中文 |
| 繁体中文 | zh-TW |
标题、标签、描述包含繁体中文 |
| 日文 | ja |
标题、标签、描述包含日文 |
自动检测规则
使用以下启发式规则判断内容语言:
- 检查 title 字段: 如果包含中文字符(
\u4e00-\u9fff),使用zh-CN - 检查 subtitle 字段: 如果包含中文字符,使用
zh-CN - 检查主要 labels: 如果超过 50% 的标签包含中文,使用
zh-CN - 默认: 如果不确定或混合语言,使用
en
示例
中文内容示例:
<html lang="zh-CN">
<head><title>2025年各季度营收</title></head>
英文内容示例:
<html lang="en">
<head><title>Q1-Q4 Revenue Comparison</title></head>
重要性
正确的 lang 属性有助于:
- 屏幕阅读器正确发音
- 浏览器正确渲染字体
- SEO 优化
Chart Type Reference
| Type | Trigger Keywords | Status |
|---|---|---|
| bar | bar chart, column chart, 柱状图, 条形图, 对比 | Available |
| pie | pie chart, donut chart, 饼图, 环形图, 占比, 比例, proportion, percentage | Available |
| line | line chart, line graph, 折线图, 趋势, trend, time series, 时间序列 | Available |
| area | area chart, 面积图, stacked area, 堆叠面积 | Available |
| scatter | scatter chart, scatter plot, 散点图, 相关性, correlation, distribution | Available |
| radar | radar chart, spider chart, 雷达图, 蜘蛛图, 多维, multi-dimensional | Available |
| funnel | funnel chart, 漏斗图, 转化率, conversion, pipeline, 管道 | Available |
| bubble | bubble chart, 气泡图, 三维数据, three-dimensional data | Available |
| table | data table, table chart, 表格, 数据表 | Available |
Mermaid output matrix:
| Type | Mermaid Support | Syntax |
|---|---|---|
| Bar | Yes | xychart-beta |
| Pie | Yes | pie title |
| Line | Yes | xychart-beta |
| Area | No | — |
| Scatter | No | — |
| Radar | No | — |
| Funnel | No | — |
| Bubble | No | — |
| Table | No | — |
For chart types without Mermaid support, inform the user of the limitation and suggest HTML or SVG instead.
Data Extraction Rules
- Extract numbers and labels directly from the user's description
- Support formats like "Q1=120万, Q2=150万" or "Chrome 65%, Safari 19%"
- Remove units from values (store "120" not "120万"), put units in
axis.y.labelorsubtitle - When multiple series are mentioned, create separate series objects
- Handle percentage input: if all values sum to ~100, they're percentages; store as-is
File Naming Convention
示例文件名必须使用 kebab-case(短横线连接的小写字母)。
命名格式
{context}-{type}[-{variant}].{ext}
- context: 场景描述(如 chrome, saas, ecommerce, quarterly-revenue)
- type: 图表类型(可选,如 architecture, flowchart, bar-chart)
- variant: 变体标识(可选,如 cyberpunk, minimalist, dark)
- ext: 文件扩展名(json 或 html)
正确与错误示例
| 命名风格 | 示例 | 状态 |
|---|---|---|
| kebab-case ✅ | chrome-architecture.json |
正确 |
| kebab-case ✅ | quarterly-revenue.json |
正确 |
| kebab-case ✅ | team-skills-radar.json |
正确 |
| camelCase ❌ | chromeArchitecture.json |
错误 |
| PascalCase ❌ | QuarterlyRevenue.json |
错误 |
| snake_case ❌ | team_skills.json |
错误 |
| 空格 ❌ | chrome architecture.json |
错误 |
规则
- 全部小写: 文件名中不包含大写字母
- 单词分隔: 使用连字符
-分隔单词,不使用下划线或空格 - 无特殊字符: 仅使用字母、数字和连字符
- 清晰语义: 文件名应清晰表达示例内容
批量命名示例
对于同一系统的多个风格变体:
architecture-system.json(默认风格)architecture-system-cyberpunk.jsonarchitecture-system-minimalist.jsonarchitecture-system-terminal.json
Quality Checklist
Before finalizing any chart output, verify ALL of the following:
Data & Schema
- JSON validates against the chart type's schema file
-
axis.y.ticksuses nice numbers (no awkward decimals unless data has decimals) - For scatter/bubble:
axis.x.ticksalso uses nice numbers - All values are finite numbers (no NaN or Infinity)
- Array sizes within limits (series, data, axes, columns, rows as per type constraints)
Visual
- Bar width and spacing visually balanced
- Text labels readable (>= 11px, contrast >= 3:1 against background)
- ViewBox fits content — no clipping, no more than 20% whitespace
- Legend matches actual data series/colors
- Grid lines don't overlap with axis line
Technical
- All
foreignObjectroot elements havexmlns="http://www.w3.org/1999/xhtml" - All user text is HTML-entity-escaped (
<,>,&in text content; additionally"and control chars in attribute values) - No user text in element name or attribute name positions
- Colors in
#rrggbbformat only — norgb(),rgba(), named colors,url() - No JavaScript in output
- ViewBox width and height are integers (no decimals)
- CSS classes match template definitions (
.axis-line,.grid-line,.bar,.tick-label, etc.)
Security
- No
<scripttags in output - No
onerror,onload,onclickor other event handler attributes - No
url()in inline style attributes - No
<iframe>,<object>,<embed>tags - No extra
<style>tags beyond what the template provides
Bar Chart Specific
- Bars are proportional to data values
- Y-axis ticks evenly spaced
- X-axis labels centered under each bar/group
- Negative values handled correctly (bars extend below baseline)
Pie Chart Specific
- Slice angles are proportional to data values
- Arcs start from 12 o'clock position (-90 degree offset)
- Donut variant: inner arc path has correct annular sector (sweep-flag = 0 for inner arc)
- Percentages computed and displayed correctly:
(item.value / totalValue * 100).toFixed(1) - Slices < 5% have no external label (still shown in legend)
Line Chart Specific
- Edge-to-edge X spacing used (NOT grouped slots like bar chart):
pointX(g) = plotX + g * (plotWidth / (numDataPoints - 1)) - Catmull-Rom smooth mode: phantom points computed correctly for boundary segments (
P[-1] = 2*P[0] - P[1],P[n] = 2*P[n-1] - P[n-2]) - Data point markers match the specified shape (circle, square, diamond, none)
Area Chart Specific
- Stacked mode: cumulative values computed correctly, top-to-bottom render order
- Overlaid mode: opacity applied correctly to each series fill
- Nice Numbers applied to cumulative max (stacked) or individual max (overlaid)
- Each area path closed to the baseline correctly
Scatter Chart Specific
- Dual-axis Nice Numbers computed (both X and Y axes have ticks)
- Both axes display tick labels
- Triangle point shape rendered correctly (upright equilateral triangle)
- Data point labels only shown when
data[].labelis present
Radar Chart Specific
- Concentric polygon grid drawn (not circular grid)
-
axes[].maxValueis all-or-nothing: either all specified or all auto-computed - Polygon vertices computed correctly using polar coordinates from center
- Data polygon normalized against per-axis maxValue
- Axis labels positioned outside the outer polygon edge
Funnel Chart Specific
- Data auto-sorted in descending order if not already sorted
- Last stage has flat bottom (not tapered to a point)
- Conversion rates displayed between adjacent stages (when
showConversionRate: true) - Stage widths proportional to values relative to the maximum value
Bubble Chart Specific
- Size mapped via area (r-squared), not linear radius:
area = minR^2 + (size - minSize) / (maxSize - minSize) * (maxR^2 - minR^2) - Render order: ascending by size (small bubbles drawn first, large on top)
- Dual numeric axes with Nice Numbers on both X and Y
- Size legend included showing min/max bubble mapping
Table Chart Specific
- No SVG graphic elements — pure foreignObject + HTML table
-
sanitizedStringenforced on all cell content (no<>characters) - Highlight colors are fixed system values (Max: green, Min: red), not user-customizable
-
columns[].widthmatches pattern^(\d{1,4}px|auto)$— no arbitrary CSS values - Table width matches column widths appropriately
Iteration Workflow
Follow-up requests to modify an existing chart:
- Modify existing JSON — add/remove data points, change series
- Re-render from modified JSON
- Style change — re-render with different template (keep JSON unchanged)
- Format change — re-export to different output format
Common modifications:
- "Add another series" → add to
seriesarray, re-render - "Change to horizontal" → set
direction: "horizontal", re-render - "Stack the bars" → set
variant: "stacked", re-render - "Switch to hand-drawn style" → change
style, re-render with different template - "Convert pie to donut" → set
variant: "donut", configuredonutobject, re-render - "Smooth the lines" → set
smooth: true, re-render - "Show as stacked area" → set
variant: "stacked"on area chart, re-render