mviz v1.7.0 mviz Generate clean, data-focused charts and dashboards from compact JSON specs or markdown. Maximizes data-ink ratio with minimal chartjunk, gridlines, and decorative elements. Uses a 16-column grid layout system. Setup No installation required. Use which auto-downloads from npm. The flag reduces npm output while still showing lint errors. For faster repeated use, install globally: What This Skill Does mviz is two things: 1. A spec language — markdown with fenced code blocks describing components (charts, KPIs, tables, notes, etc.). One markdown file (or a single JSON spec) descr…

+ v.toLocaleString('en-US')` (e.g. `1250000` → `$1,250,000`) |\n| `currency0k` | `'

mviz v1.7.0 mviz Generate clean, data-focused charts and dashboards from compact JSON specs or markdown. Maximizes data-ink ratio with minimal chartjunk, gridlines, and decorative elements. Uses a 16-column grid layout system. Setup No installation required. Use which auto-downloads from npm. The flag reduces npm output while still showing lint errors. For faster repeated use, install globally: What This Skill Does mviz is two things: 1. A spec language — markdown with fenced code blocks describing components (charts, KPIs, tables, notes, etc.). One markdown file (or a single JSON spec) descr…

+ Math.round(v/1000) + 'k'` (e.g. `125000` → `$125k`) |\n| `currency0m` | `'

mviz v1.7.0 mviz Generate clean, data-focused charts and dashboards from compact JSON specs or markdown. Maximizes data-ink ratio with minimal chartjunk, gridlines, and decorative elements. Uses a 16-column grid layout system. Setup No installation required. Use which auto-downloads from npm. The flag reduces npm output while still showing lint errors. For faster repeated use, install globally: What This Skill Does mviz is two things: 1. A spec language — markdown with fenced code blocks describing components (charts, KPIs, tables, notes, etc.). One markdown file (or a single JSON spec) descr…

+ (v/1e6).toFixed(1) + 'm'` (e.g. `1250000` → `$1.2m`) |\n| `pct`, `pct1` | `(v * 100).toFixed(1) + '%'` — mviz multiplies pct by 100, so pass the decimal (`0.15` → `15.0%`). For chart series mviz auto-detects whether values are already in pct units; mirror that if the data is ambiguous. |\n| `pct0` | `Math.round(v * 100) + '%'` (e.g. `0.15` → `15%`) |\n| `num0` | `v.toLocaleString('en-US')` (e.g. `1250` → `1,250`) |\n| `num1` | `v.toFixed(1)` (e.g. `1.234` → `1.2`) |\n| `num0k` | `Math.round(v/1000) + 'k'` (e.g. `1250` → `1k`) |\n\nThe `currency_auto` / `auto` helper, mirroring mviz's `smartFormatNumber`. ~4 significant digits, magnitude tier with suffix, negatives in parentheses — match these exact thresholds so the inline render and the standalone report show identical numbers:\n\n```js\nfunction fmtAuto(v, sym = '') {\n const neg = v \u003c 0, abs = Math.abs(v);\n let s;\n if (abs >= 1e9) {\n const x = abs / 1e9;\n s = sym + (x >= 100 ? x.toFixed(1) : x >= 10 ? x.toFixed(2) : x.toFixed(3)) + 'b';\n } else if (abs >= 1e6) {\n const x = abs / 1e6;\n s = sym + (x >= 100 ? x.toFixed(1) : x >= 10 ? x.toFixed(2) : x.toFixed(3)) + 'm';\n } else if (abs >= 1e4) {\n const x = abs / 1e3;\n s = sym + (x >= 100 ? x.toFixed(1) : x.toFixed(2)) + 'k';\n } else if (abs >= 1e3) {\n s = sym + abs.toLocaleString('en-US', { maximumFractionDigits: 0 });\n } else {\n s = sym + (abs === Math.floor(abs) ? abs : abs.toFixed(2));\n }\n return neg ? '(' + s + ')' : s;\n}\n// currency_auto: fmtAuto(v, '

mviz v1.7.0 mviz Generate clean, data-focused charts and dashboards from compact JSON specs or markdown. Maximizes data-ink ratio with minimal chartjunk, gridlines, and decorative elements. Uses a 16-column grid layout system. Setup No installation required. Use which auto-downloads from npm. The flag reduces npm output while still showing lint errors. For faster repeated use, install globally: What This Skill Does mviz is two things: 1. A spec language — markdown with fenced code blocks describing components (charts, KPIs, tables, notes, etc.). One markdown file (or a single JSON spec) descr…

) → 1_250_000 → \"$1.250m\", 12_500 → \"$12.50k\", 1_250 → \"$1,250\"\n// auto: fmtAuto(v) → 1_250_000 → \"1.250m\", 12_500 → \"12.50k\", 1_250 → \"1,250\"\n```\n\nAlways `Math.round` / `.toFixed(n)` displayed numbers — `0.1 + 0.2` is `0.30000000000000004` and that artifact will leak into the widget if you don't.\n\n**Color mapping**\n\nChart.js renders to `\u003ccanvas>` and cannot resolve CSS variables. Use hardcoded hex from claude.ai's color ramps (the `c-{ramp}` stops listed in the `visualize:read_me` design module). A safe default palette for mviz series:\n\n- Primary (mviz blue `#0777b3`) → `c-blue` 600 `#185FA5`\n- Secondary (mviz orange `#bd4e35`) → `c-coral` 600 `#993C1D`\n- Positive (mviz green `#2d7a00`) → `c-green` 600 `#3B6D11`\n- Warning (mviz amber `#e18727`) → `c-amber` 600 `#854F0B`\n- Error (mviz red `#bc1200`) → `c-red` 600 `#A32D2D`\n- Tertiary / neutral → `c-gray` 600 `#5F5E5A`\n\nFor HTML elements (cards, table borders, text), use `var(--color-text-primary)` / `var(--color-background-secondary)` / `var(--color-border-tertiary)` and friends — they auto-adapt to dark mode.\n\n### Chart.js setup (canonical patterns from `visualize:read_me`)\n\n```html\n\u003cscript src=\"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js\">\u003c/script>\n```\n\nWrap every canvas in a sized div:\n\n```html\n\u003cdiv style=\"position: relative; width: 100%; height: 240px;\">\n \u003ccanvas id=\"c1\" role=\"img\" aria-label=\"Sales by region — NA $540k, EU $380k, APAC $220k, LATAM $110k\">\n Sales by region (bar chart). NA 540, EU 380, APAC 220, LATAM 110.\n \u003c/canvas>\n\u003c/div>\n```\n\nConstraints to honor:\n\n- `responsive: true, maintainAspectRatio: false` — height goes on the wrapper div only, never on the canvas itself.\n- Horizontal bar charts: wrapper height ≥ `(bars * 40) + 80` px.\n- ≤12 categories where every label must be visible: `scales.x.ticks: { autoSkip: false, maxRotation: 45 }`.\n- Bubble/scatter: pad `scales.{x,y}.{min,max}` ~10 % beyond data range so radii don't clip.\n- Negative currency: `-$5M` not `$-5M` — sign before symbol. Use a formatter `(v) => (v \u003c 0 ? '-' : '') + '

mviz v1.7.0 mviz Generate clean, data-focused charts and dashboards from compact JSON specs or markdown. Maximizes data-ink ratio with minimal chartjunk, gridlines, and decorative elements. Uses a 16-column grid layout system. Setup No installation required. Use which auto-downloads from npm. The flag reduces npm output while still showing lint errors. For faster repeated use, install globally: What This Skill Does mviz is two things: 1. A spec language — markdown with fenced code blocks describing components (charts, KPIs, tables, notes, etc.). One markdown file (or a single JSON spec) descr…

+ Math.abs(v) + 'M'`.\n\nDefault Chart.js legends use round dots and no values — disable them and emit your own legend as inline HTML (small color squares, tight spacing, include the value/percentage when categorical):\n\n```js\nplugins: { legend: { display: false } }\n```\n\n```html\n\u003cdiv style=\"display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 8px;\n font-size: 12px; color: var(--color-text-secondary);\">\n \u003cspan style=\"display: flex; align-items: center; gap: 4px;\">\n \u003cspan style=\"width: 10px; height: 10px; border-radius: 2px; background: #185FA5;\">\u003c/span>NA $540k\n \u003c/span>\n \u003c!-- repeat -->\n\u003c/div>\n```\n\n### Metric card (for `big_value` and `delta`)\n\n```html\n\u003cdiv style=\"background: var(--color-background-secondary); border-radius: var(--border-radius-md);\n padding: 1rem;\">\n \u003cdiv style=\"font-size: 13px; color: var(--color-text-secondary);\">Q3 Revenue\u003c/div>\n \u003cdiv style=\"font-size: 24px; font-weight: 500; color: var(--color-text-primary);\n line-height: 1; margin-top: 4px;\">$1.250m\u003c/div>\n\u003c/div>\n```\n\nUse in grids of 2–4 with `gap: 12px`. For `delta`, add a small directional indicator with `color: var(--color-success)` or `var(--color-danger)`.\n\n### Worked example\n\nSpec (mviz markdown):\n\n````markdown\n---\ntitle: Q3 sales\n---\n\n```big_value size=[4,2]\n{\"value\": 1250000, \"label\": \"Q3 Revenue\", \"format\": \"currency_auto\"}\n```\n```big_value size=[4,2]\n{\"value\": 0.184, \"label\": \"Growth\", \"format\": \"pct1\"}\n```\n\n```bar size=[16,5]\n{\"title\": \"Sales by region\", \"x\": \"region\", \"y\": \"sales\", \"format\": \"currency0k\",\n \"data\": [{\"region\":\"NA\",\"sales\":540000},{\"region\":\"EU\",\"sales\":380000},\n {\"region\":\"APAC\",\"sales\":220000},{\"region\":\"LATAM\",\"sales\":110000}]}\n```\n````\n\nInline render (the `widget_code` you pass to `visualize:show_widget`):\n\n```html\n\u003ch2 class=\"sr-only\">Q3 sales overview: $1.250m revenue, 18.4% growth, regional breakdown.\u003c/h2>\n\u003cdiv style=\"display: grid; grid-template-columns: repeat(16, 1fr); gap: 12px;\">\n \u003cdiv style=\"grid-column: span 4; background: var(--color-background-secondary);\n border-radius: var(--border-radius-md); padding: 1rem;\">\n \u003cdiv style=\"font-size: 13px; color: var(--color-text-secondary);\">Q3 Revenue\u003c/div>\n \u003cdiv style=\"font-size: 24px; font-weight: 500; margin-top: 4px;\">$1.250m\u003c/div>\n \u003c/div>\n \u003cdiv style=\"grid-column: span 4; background: var(--color-background-secondary);\n border-radius: var(--border-radius-md); padding: 1rem;\">\n \u003cdiv style=\"font-size: 13px; color: var(--color-text-secondary);\">Growth\u003c/div>\n \u003cdiv style=\"font-size: 24px; font-weight: 500; margin-top: 4px;\n color: var(--color-success);\">+18.4%\u003c/div>\n \u003c/div>\n \u003cdiv style=\"grid-column: span 16;\">\n \u003cdiv style=\"font-size: 14px; font-weight: 500; margin-bottom: 8px;\">Sales by region\u003c/div>\n \u003cdiv style=\"position: relative; width: 100%; height: 240px;\">\n \u003ccanvas id=\"mviz-bar-1\" role=\"img\"\n aria-label=\"Sales by region — NA $540k, EU $380k, APAC $220k, LATAM $110k\">\n NA 540, EU 380, APAC 220, LATAM 110.\n \u003c/canvas>\n \u003c/div>\n \u003c/div>\n\u003c/div>\n\u003cscript src=\"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js\">\u003c/script>\n\u003cscript>\n new Chart(document.getElementById('mviz-bar-1'), {\n type: 'bar',\n data: {\n labels: ['NA', 'EU', 'APAC', 'LATAM'],\n datasets: [{ label: 'Sales', data: [540000, 380000, 220000, 110000], backgroundColor: '#185FA5' }],\n },\n options: {\n responsive: true, maintainAspectRatio: false,\n plugins: { legend: { display: false } },\n scales: {\n y: { ticks: { callback: (v) => '

mviz v1.7.0 mviz Generate clean, data-focused charts and dashboards from compact JSON specs or markdown. Maximizes data-ink ratio with minimal chartjunk, gridlines, and decorative elements. Uses a 16-column grid layout system. Setup No installation required. Use which auto-downloads from npm. The flag reduces npm output while still showing lint errors. For faster repeated use, install globally: What This Skill Does mviz is two things: 1. A spec language — markdown with fenced code blocks describing components (charts, KPIs, tables, notes, etc.). One markdown file (or a single JSON spec) descr…

+ Math.round(v/1000) + 'k' } },\n x: { ticks: { autoSkip: false, maxRotation: 45 } },\n },\n },\n });\n\u003c/script>\n```\n\n### Constraints + things you must not do\n\n- **Do not invoke the `mviz` CLI for `show_widget` output.** This path renders without it. The CLI is only for standalone HTML reports.\n- **Do not paste mviz's own HTML into `show_widget`.** mviz emits its own styled fragments — they will not match the chat host and the bytes are wasted.\n- No `\u003c!DOCTYPE>`, `\u003chtml>`, `\u003chead>`, or `\u003cbody>` in the widget — `show_widget` rejects documents.\n- No `position: fixed`. It collapses the iframe's auto-sized viewport.\n- Only load from the allowlist: `cdnjs.cloudflare.com`, `esm.sh`, `cdn.jsdelivr.net`, `unpkg.com`, `fonts.googleapis.com`, `fonts.gstatic.com`. Chart.js from `cdnjs.cloudflare.com` is the canonical source.\n- Tables with more than ~3 columns or any prose-y content: render as markdown in your response text instead of inside the widget. The `visualize:read_me` design guide is explicit about this.\n\n### Falling back to the CLI renderer\n\nWhen the inline renderer isn't a fit, the spec is still useful — pipe it through the CLI:\n\n- **Chart type with no Chart.js equivalent** (sankey, heatmap, calendar, etc.) — render via the CLI, offer the file. Mention that the mviz design system kicks in for the standalone output.\n- **`visualize:show_widget` is not available** — render via the CLI and present as an artifact.\n- **User is in Claude Code** — render via the CLI, write the file path, they'll open it in a browser. No inline widget surface in the TUI.\n- **Visualization exceeds the inline size budget** (15+ components, dense tables with sparklines, multi-section reports) — render via the CLI. Offer inline as a follow-up if they want a smaller summary view.\n- **Single tiny visual** (one sparkline, one 3-cell metric) — consider hand-rolling SVG directly via `show_widget` without going through the mviz spec at all. mviz earns its keep when composing multiple components; for a one-off icon it's overkill.\n\n## Custom Themes\n\nLoad custom brand colors and fonts from a YAML file:\n\n```bash\nnpx -y -q mviz --theme my_theme.yaml dashboard.md -o dashboard.html\n```\n\nExample theme file:\n```yaml\nname: brand-colors\nextends: light\n\ncolors:\n primary: \"#1a73e8\"\n secondary: \"#ea4335\"\n\npalette:\n - \"#1a73e8\"\n - \"#ea4335\"\n - \"#fbbc04\"\n\nfonts:\n family: \"'Roboto', sans-serif\"\n import: \"https://fonts.googleapis.com/css2?family=Roboto&display=swap\"\n```\n\nCustom themes merge with defaults - only specify what you want to override.\n\n## Print and PDF Support\n\nCharts are optimized for printing to PDF:\n\n- **High-Quality Rendering**: Uses SVG renderer for crisp vector graphics at any zoom level\n- **No Page Breaks**: CSS prevents charts and tables from being split across pages\n- **All Labels Visible**: Category labels always shown with 45° rotation to fit\n\nWhen printing dashboards to PDF, all content stays intact without being cut off mid-chart.\n\n## JSON Formatting for Editability\n\n**Use formatted (multi-line) JSON** when data may need editing. This enables smaller, more precise edits:\n\n````markdown\n```bar size=[8,5]\n{\n \"title\": \"Monthly Sales\",\n \"x\": \"month\",\n \"y\": \"sales\",\n \"data\": [\n {\"month\": \"Jan\", \"sales\": 120},\n {\"month\": \"Feb\", \"sales\": 150},\n {\"month\": \"Mar\", \"sales\": 180}\n ]\n}\n```\n````\n\n**Benefits:**\n- Each data point on its own line enables targeted edits\n- Changing one value: ~30 chars vs ~200+ chars with compact JSON\n- Easier to review diffs in version control\n\n**When to use compact JSON:**\n- Very small specs (\u003c 100 chars)\n- Data that won't change\n- Single-line values like `{\"value\": 1250000, \"label\": \"Revenue\"}`\n\n## JSON Schema\n\nmviz specs can be validated using the JSON Schema at:\n\n```\nhttps://raw.githubusercontent.com/matsonj/mviz/main/schema/mviz.schema.json\n```\n\nAdd `$schema` to enable editor autocomplete and validation:\n\n```json\n{\n \"$schema\": \"https://raw.githubusercontent.com/matsonj/mviz/main/schema/mviz.schema.json\",\n \"type\": \"bar\",\n \"title\": \"Sales\",\n ...\n}\n```\n\n## Color Palette (mdsinabox theme)\n\n| Color | Hex | Use |\n|-------|-----|-----|\n| Primary Blue | `#0777b3` | Primary series |\n| Secondary Orange | `#bd4e35` | Secondary series, accent |\n| Info Blue | `#638CAD` | Tertiary, informational |\n| Positive Green | `#2d7a00` | Success, positive values |\n| Warning Amber | `#e18727` | Warnings |\n| Error Red | `#bc1200` | Errors, negative emphasis |\n\nSee `reference/chart-types.md` for complete documentation.\n\n## Your Role\n\nYou are an analytics assistant helping a human who has decision-making context that you lack. Your job is to present data clearly and surface patterns worth investigating—not to draw conclusions or make recommendations.\n\n**Key principles:**\n- Use a matter-of-fact tone. State what the data shows, not what it means.\n- Design analysis that invites further questions, not analysis that closes them.\n- Surface anomalies and patterns without assuming their cause or significance.\n- Let the human add context and make decisions.\n\nFor additional guidance on creating effective data visualizations—including Tufte-inspired principles, anti-patterns to avoid, and layout examples—see `Best_practices.md`.\n\n## Feedback\n\nHaving issues with mviz? Ask Claude to create a friction log documenting the problem, then open it as an issue at https://github.com/matsonj/mviz/issues\n---","attachment_filenames":["Best_practices.md","README.md","reference/chart-types.md","schema/mviz.schema.json","schema/mviz.v1.schema.json"],"attachments":[{"filename":"Best_practices.md","content":"# mviz Best Practices\n\nGuidance for creating effective data visualizations as an analytics assistant.\n\n## Your Role\n\nYou are an analytics assistant helping a human who has decision-making context that you lack. Your job is to present data clearly and surface patterns worth investigating—not to draw conclusions or make recommendations.\n\n**Key principles:**\n- Use a matter-of-fact tone. State what the data shows, not what it means.\n- Design analysis that invites further questions, not analysis that closes them.\n- Surface anomalies and patterns without assuming their cause or significance.\n- Let the human add context and make decisions.\n\n## Presenting Data Effectively\n\n### Core Principles\n\n1. **Prose describes, graphics show.** Every visualization needs adjacent text explaining what it displays. But describe what's there—don't interpret why.\n\n2. **Tables over charts when data is small.** A 6-row bar chart wastes space. A 6-row table with sparklines shows ranking, values, trends, and percentages together.\n\n3. **Sparklines as data-words.** Embed them in tables to show patterns compactly. One table can replace 3-4 standalone visualizations.\n\n4. **No redundancy.** Never show a bar chart next to a table with the same data. Pick one.\n\n5. **Specific numbers in prose.** Write \"revenue was $64M in Q1 and $176M in Q2\" not \"revenue increased significantly.\" Let the human judge significance.\n\n6. **Earn every chart.** A chart is justified when:\n - The pattern requires visual inspection (time series with inflection points)\n - Comparing 2-3 series is the focus (not 10 series—use a table)\n - Spatial relationships matter (scatter plots)\n\n7. **Minimal decoration.** No \"insight\" boxes unless flagging data quality issues. No section headers that just label what's below.\n\n### Tone Examples\n\n| Don't (interpretive) | Do (matter-of-fact) |\n|---------------------|---------------------|\n| \"Revenue growth was strong in Q2\" | \"Revenue was $176M in Q2, up from $64M in Q1\" |\n| \"Poland's surge suggests a major deal\" | \"Poland revenue increased from $99K to $5.4M. This concentration may warrant investigation.\" |\n| \"The trend is concerning\" | \"The metric declined for 4 consecutive months\" |\n| \"Key takeaway: Focus on APAC\" | \"APAC accounts for 45% of growth. The breakdown by region is shown below.\" |\n\n### Inviting Further Analysis\n\nEnd sections by noting what might be worth exploring, not by drawing conclusions:\n\n**Don't:** \"The H2 surge is real but concentrated. Remove Poland and Austria, and growth looks modest.\"\n\n**Do:** \"Poland and Austria account for 60% of the H2 increase. Growth excluding these markets, or a breakdown of what drove their growth, may provide additional context.\"\n\n## Anti-Patterns\n\n| Don't | Do Instead |\n|-------|------------|\n| Use bar charts for \u003c8 categories | Use a table (more compact, can add columns) |\n| Show a chart and table with the same data | Pick one (usually the table) |\n| End with \"Key Findings\" that draw conclusions | End with \"Areas for further investigation\" |\n| Editorialize about what data means | State what data shows; note what's unusual |\n| Place charts without adjacent prose | Add a textarea describing what's displayed |\n| Assume you know why something happened | Present the pattern; invite the human to add context |\n\n## Good Patterns\n\n- **Dense tables with sparklines.** Category table with revenue, share, trend sparkline, and period comparison = 5+ dimensions in one component.\n\n- **Prose paragraph → chart → prose noting what's displayed.** The chart is sandwiched in descriptive context.\n\n- **Highlight anomalies neutrally:** \"Three of twelve markets show >50% growth; eight show \u003c10%.\" Let the human decide if that's good or bad.\n\n- **Specific numbers without judgment:** \"$32M in H2 compared to $2.7M in H1\" not \"significant growth in H2.\"\n\n- **Close with questions, not answers:** \"The monthly pattern shows acceleration in September. A breakdown by customer segment or product line could clarify what's driving this.\"\n\n## Layout Best Practices\n\n### 16-Column Grid System\n\nComponents are sized using `size=[cols,rows]` syntax. The grid is 16 columns wide, with row units approximately 32px tall.\n\n**Height Guidelines:**\n| Row Units | Approximate Height | Good For |\n|-----------|-------------------|----------|\n| 2 | ~64px | KPIs, single-line notes |\n| 4 | ~128px | Small tables, text blocks |\n| 5-6 | ~160-192px | Standard charts |\n| 8+ | ~256px+ | Dense tables, detailed charts |\n\nFor charts with many categories (10+ bars, 10+ rows in dumbbell), increase row units to prevent compression.\n\n### Side-by-Side Layout\n\n**Critical:** To place components side-by-side, their code blocks must have NO blank lines between them:\n\n````markdown\n```bar size=[8,5]\n{\"title\": \"Chart A\", ...}\n```\n```line size=[8,5]\n{\"title\": \"Chart B\", ...}\n```\n````\n\nThis renders Chart A and Chart B on the same row. Adding a blank line between them would put them on separate rows.\n\n### Headings and Section Breaks\n\n| Syntax | Effect |\n|--------|--------|\n| `# H1` | Major section with page break (for print/PDF) |\n| `## H2` | Section title, visual divider, no page break |\n| `### H3` | Light inline header (subtle, smaller text) |\n| `---` | Untitled section break: visual divider only |\n| `===` | Explicit page break: forces new page in PDF |\n\n**Section vs Page Breaks:**\n- Use `---` to separate logical sections visually. Content flows naturally to the next page when needed.\n- Use `===` only when you explicitly want to force a new page (e.g., separating chapters or major report sections for PDF output).\n- Never use `===` by default. Only add page breaks when the user specifically requests them.\n\n### Recommended Size Pairings\n\n| Layout Goal | Components | Sizes |\n|-------------|------------|-------|\n| 4 KPIs in a row | 4× `big_value` | [4,2] each |\n| 5 KPIs in a row | 4× `big_value` + 1 wider | [3,2] + [4,2] |\n| KPI + context | `big_value` + `textarea` | [3,2] + [13,2] |\n| KPI + chart | `big_value` + `bar` | [4,2] + [12,5] |\n| Two charts side-by-side | 2× chart | [8,5] each |\n\n### Dense, Information-Rich Layouts\n\n1. Pack 4-5 KPIs per row using `size=[3,2]` or `size=[4,2]`\n2. Use compact number formats (`currency0m` not `currency`)\n3. Place charts side-by-side with `size=[8,6]`\n4. Add textarea descriptions to each section\n\n### Example: Descriptive Report Structure\n\n````markdown\n```textarea size=[16,3]\n{\"content\": \"## Q3 Revenue Overview\\n\\nQ3 revenue was **$98.2M**, compared to $45M in Q1 and $52M in Q2 combined. The breakdown by market and monthly trend are shown below.\"}\n```\n\n```textarea size=[11,4]\n{\"content\": \"### Revenue by Market\\n\\nTwelve markets generated revenue in Q3. The table shows each market's contribution, share, and monthly pattern.\"}\n```\n```line size=[5,4]\n{\"title\": \"Q3 Monthly\", \"x\": \"month\", \"y\": \"revenue\", \"yMin\": 0, \"data\": [...]}\n```\n\n```table size=[16,6]\n{\"columns\": [{\"id\": \"market\", \"title\": \"Market\"}, {\"id\": \"revenue\", \"title\": \"Q3 Revenue\", \"fmt\": \"currency_auto\", \"bold\": true}, {\"id\": \"share\", \"title\": \"Share\", \"fmt\": \"pct\"}, {\"id\": \"trend\", \"title\": \"Monthly\", \"type\": \"sparkline\"}], \"data\": [...]}\n```\n\n```textarea size=[16,2]\n{\"content\": \"**Notable pattern:** Poland revenue increased from $99K in June to $5.4M in September. This represents 10% of Q3 total. A breakdown of Poland by customer or product may provide context.\"}\n```\n````\n\nThis structure:\n1. Opens with specific numbers, no judgment\n2. Describes what each visualization shows\n3. Places one chart where shape matters\n4. Uses a dense table as the primary data vehicle\n5. Closes by noting an anomaly and suggesting follow-up\n\n## Quick Reference\n\n**Default to tables.** A table with sparklines shows ranking, trends, and values in one component.\n\n**Charts earn their place when:**\n- Time series shape matters (use line)\n- Comparing 8+ categories (use bar)\n- Correlation analysis (use scatter)\n\n**Every visualization needs prose** describing what it displays. No orphan charts.\n\n**Your job is to present, not persuade.** Surface patterns. Invite questions. Let the human add context.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8072,"content_sha256":"0f18c1941ad9fd361422bd436735546bf10363114a34f3ef732e5d9b88fdc43f"},{"filename":"README.md","content":"# mviz\n\nGenerate beautiful static reports for ad hoc analysis. A Claude skill that turns compact JSON specs into professional HTML visualizations.\n\n![Light Mode Dashboard](LightMode.png)\n\n![Dark Mode Dashboard](DarkMode.png)\n\n## Why mviz?\n\n**The highest-value analysis in any company is point-in-time, highly contextual, and not reused once the decision is made.**\n\nTraditional BI tools optimize for reusability instead of usefulness. Useful analysis, the kind that drives critical decisions, needs something more:\n\n- **Fast iteration**: Query data → visualize → refine → share\n- **AI-native workflow**: Works seamlessly with Claude for data exploration\n- **Static output**: Beautiful HTML/PDF reports, no infrastructure required\n- **Minimal tokens**: Compact specs instead of verbose chart code\n\nInstead of writing 50-100 lines of chart boilerplate, write a compact JSON spec that gets expanded into a full HTML artifact with ECharts.\n\n## Quick Start\n\n### 1. Connect Your Database\n\nConnect Claude to your data using an MCP server:\n\n- **[MotherDuck MCP](https://motherduck.com/docs/sql-reference/mcp/)** - Cloud data warehouse with DuckDB compatibility\n- **[Local DuckDB MCP](https://github.com/motherduckdb/mcp-server-motherduck)** - Query local `.duckdb` files, Parquet, CSV, or S3 data\n\nIf you do not have a database available, you can also load CSV files directly, although the amount of data you can fit in context can be quite limiting.\n\n### 2. Add the Skill\n\n**Claude Web or Desktop:** Download [`mviz.skill`](https://gist.github.com/matsonj/1c13b656bca20b1d2cc6260309d8eb40) and add it to your project knowledge.\n\n**Claude Code:** Run `npx skills add matsonj/mviz` or clone this repo and work from the directory.\n\n### 3. Effective Use Tips\n\nThe best analysis follows four steps:\n\n1. **Build context** — Get the data right. Query, filter, and explore until you understand what you're looking at.\n2. **Develop narrative** — What's the story? What question are you answering? What pattern matters?\n3. **First pass on viz** — Create an initial visualization. Don't overthink it.\n4. **Refine based on what doesn't work** — Iterate. Change chart types, adjust formatting, add context.\n\nStart by exploring your data with natural questions. Claude writes SQL queries behind the scenes and brings the results into context:\n\n> *\"Show me revenue by region for Q4\"*\n\n> *\"What are our top 10 customers by lifetime value?\"*\n\n> *\"Are there any anomalies in last month's sales data?\"*\n\nOnce you've built up context and are ready to visualize, tell Claude to **\"use mviz to report on this analysis\"**. Claude generates a polished HTML report from the data you've explored.\n\n### 4. Iterate\n\nRefine your analysis by asking follow-up questions:\n\n> *\"Change that bar chart to a line chart\"*\n\n> *\"Drill into the APAC region—what's driving that spike?\"*\n\n> *\"Add a table showing the top 5 products by growth rate\"*\n\n### mviz Specific Guidance\n\nmviz uses a 16-column grid.\n\n> *\"Make the bar chart wider\"*\n\n> *\"Show two charts side by side at size=[8,6] each\"*\n\n> *\"Make the KPIs smaller: size=[3,2] so 5 fit in a row\"*\n\nBy default, it will use `size=auto` to let mviz calculate appropriate dimensions based on your data.\n\n> [!TIP]\n> There are more chart types available in the library than are included in the skill.md. You can tell Claude to look at the TypeScript source for more chart types if you really need them.\n\nEach iteration builds on your existing context. When you're done, save the HTML or print to PDF.\n\n## Supported Chart Types\n\n| Type | Description | mviz.skill |\n|------|-------------|:----------:|\n| `bar` | Vertical/horizontal, grouped, stacked | ✓ |\n| `line` | Single or multi-series with linear interpolation | ✓ |\n| `area` | Simple or stacked area charts | |\n| `pie` | Pie or donut charts | |\n| `scatter` | 2D scatter plots | ✓ |\n| `bubble` | Scatter with size dimension (auto-detects categorical axes) | |\n| `boxplot` | Statistical box plots | |\n| `histogram` | Distribution visualization | |\n| `sankey` | Flow diagrams | |\n| `funnel` | Conversion funnels | |\n| `heatmap` | 2D color matrices | |\n| `calendar` | GitHub-style calendar heatmaps | |\n| `sparkline` | Compact inline charts | |\n| `combo` | Combined bar + line with dual axes | |\n| `waterfall` | Cumulative effect charts | |\n| `xmr` | Statistical control charts (supports `yMin`/`yMax`) | |\n| `dumbbell` | Before/after comparisons with directional color-coding | |\n\n## UI Components\n\n| Type | Description | mviz.skill |\n|------|-------------|:----------:|\n| `big_value` | Large KPI metric display | |\n| `delta` | Change indicator with arrow | |\n| `table` | Data tables with formatting and inline sparklines | ✓ |\n| `alert` | Colored notification banners | |\n| `note` | Information callout boxes | ✓ |\n| `text` | Styled paragraphs | |\n| `textarea` | Markdown-rendered text blocks | ✓ |\n| `empty_space` | Layout spacing component | ✓ |\n\n## File References (JSON and CSV)\n\nReference external files instead of embedding large JSON specs:\n\n````markdown\n```bar file=data/monthly-sales.json\n```\n````\n\n### DuckDB Workflow\n\nCSV files work great for data exploration with DuckDB:\n\n```bash\n# Export query results\nduckdb -csv -c \"SELECT month, revenue FROM sales GROUP BY 1\" > data/monthly.csv\n```\n\n````markdown\n```bar file=data/monthly.csv\n{\"title\": \"Monthly Revenue\", \"x\": \"month\", \"y\": \"revenue\"}\n```\n````\n\nCSV provides data, inline JSON provides chart options. Auto-detects x/y from first two columns if no options given.\n\n## Report Markdown Format\n\n````markdown\n---\ntheme: light\ntitle: My Report\n---\n\n# Page Title\n\n## Section Name\n\n```big_value size=[4,2]\n{\"value\": 125000, \"label\": \"Revenue\", \"format\": \"currency0m\"}\n```\n```delta size=[4,2]\n{\"value\": 0.15, \"label\": \"vs Last Month\", \"format\": \"pct0\"}\n```\n\n```bar size=[8,6] file=data/sales.json\n```\n```line size=[8,6] file=data/trend.json\n```\n````\n\n### Layout Rules\n\n- `# Title` creates a new section (first one also sets page title)\n- `## Section` creates a subsection title (no visual divider)\n- `---` creates a visual section divider\n- `===` creates a page break for printing\n- `size=[cols,rows]` controls 16-column grid layout\n- `size=auto` auto-calculates size based on data\n- `file=path` references external JSON\n- Multiple blocks on same line = same row\n- Empty lines = new rows\n\n## 16-Column Grid System\n\n| Component | Default Size | Notes |\n|-----------|-------------|-------|\n| `big_value`, `delta`, `sparkline` | [4, 2] | Fits 4 per row |\n| `bar`, `line`, `area`, `pie` | [8, 5] | Half width |\n| `scatter`, `bubble`, `combo`, `funnel` | [8, 5] | Half width |\n| `dumbbell` | [12, 6] | 3/4 width for comparisons |\n| `table`, `heatmap` | [16, 4-10] | Full width |\n| `xmr`, `calendar` | [16, 6] | Full width, tall |\n\n## Table with Sparklines\n\nTables support inline sparkline columns for trend visualization:\n\n```json\n{\n \"type\": \"table\",\n \"columns\": [\n {\"id\": \"product\", \"title\": \"Product\"},\n {\"id\": \"sales\", \"title\": \"Sales\", \"fmt\": \"currency\"},\n {\"id\": \"trend\", \"title\": \"Trend\", \"type\": \"sparkline\", \"sparkType\": \"line\"},\n {\"id\": \"progress\", \"title\": \"Goal\", \"type\": \"sparkline\", \"sparkType\": \"pct_bar\", \"width\": 100}\n ],\n \"data\": [\n {\"product\": \"Widget\", \"sales\": 125000, \"trend\": [85, 92, 88, 95, 102, 110, 125], \"progress\": 0.85}\n ]\n}\n```\n\nSparkline types: `line`, `bar`, `area`, `pct_bar` (progress bar), `dumbbell` (before/after)\n\n## Format Options\n\n| Format | Output | Description |\n|--------|--------|-------------|\n| `auto` | 1.000m, 10.00k | **Smart auto-format (default)** |\n| `currency_auto` | $1.000m, $10.00k | Smart auto-format with currency symbol |\n| `currency0m` | $1.2m | Millions |\n| `currency0k` | $125k | Compact thousands |\n| `currency` | $1,250,000 | Full currency |\n| `pct0` | 15% | Percentage integer |\n| `pct` | 15.0% | Percentage with decimal |\n| `pct1` | 15.0% | Percentage with 1 decimal |\n| `num0` | 1,250 | Number with commas |\n\nSmart formatting automatically picks the right suffix (k, m, b) based on magnitude and shows 4 significant digits. Negative values display in parentheses: `(1.000m)`.\n\n### Auto-Detected Formatting\n\nChart axes automatically detect the appropriate format based on field names:\n\n| Field Pattern | Auto Format | Example |\n|---------------|-------------|---------|\n| revenue, sales, price, cost, profit | `currency_auto` | $1.250m |\n| pct, percent, rate, ratio | `pct` or `pct0` | 15.0% |\n| All other fields | `auto` | 1.250m |\n\nOverride with an explicit `format` field in the chart spec.\n\n## Theme Toggle\n\nReports include a theme toggle button (top right) that switches between light and dark modes. All charts dynamically update when the theme changes.\n\nSet the default theme in frontmatter:\n\n```yaml\n---\ntheme: dark\ntitle: My Report\n---\n```\n\n## Print and PDF Support\n\nCharts are optimized for printing to PDF:\n\n- **High-Quality Rendering**: Uses SVG renderer for crisp vector graphics at any zoom level\n- **No Page Breaks in Charts**: CSS prevents charts and tables from being split across pages\n- **All Labels Visible**: Category labels are always shown (with 45° rotation to fit)\n\nWhen printing reports to PDF, all content stays intact without visual elements being cut off.\n\n## Visual Style (mdsinabox theme)\n\nClean, data-focused styling:\n\n- **Font**: Helvetica Neue (system sans-serif)\n- **Signature**: Orange accent line at top of reports\n- **Background**: Paper (`#f8f8f8`) for light, dark (`#231f20`) for dark\n\n### Color Palette\n\n| Color | Hex | Use |\n|-------|-----|-----|\n| Primary Blue | `#0777b3` | Primary series |\n| Secondary Orange | `#bd4e35` | Secondary series, accent |\n| Info Blue | `#638CAD` | Tertiary |\n| Positive Green | `#2d7a00` | Success, positive values |\n| Warning Amber | `#e18727` | Warnings |\n| Error Red | `#bc1200` | Errors, negative emphasis |\n\n## Project Structure\n\n```\nchart-skill/\n├── ts-src/ # TypeScript implementation\n│ ├── cli.ts # CLI entry point\n│ ├── index.ts # Library exports\n│ ├── types.ts # TypeScript type definitions\n│ ├── core/ # Shared utilities\n│ │ ├── themes.ts # Colors, palettes, theme config\n│ │ ├── formatting.ts # Number formatting\n│ │ └── css.ts # CSS generation\n│ ├── charts/ # 17 chart type modules\n│ │ ├── bar.ts, line.ts, area.ts, pie.ts, scatter.ts, bubble.ts\n│ │ ├── boxplot.ts, histogram.ts, waterfall.ts, xmr.ts\n│ │ ├── sankey.ts, funnel.ts, heatmap.ts, calendar.ts\n│ │ └── sparkline.ts, combo.ts, dumbbell.ts\n│ ├── components/ # 8 UI component modules\n│ │ ├── big_value.ts, delta.ts, alert.ts, note.ts\n│ │ ├── text.ts, textarea.ts, empty_space.ts, table.ts\n│ └── layout/ # Report parser\n│ ├── parser.ts # Markdown layout parsing\n│ └── templates.ts # HTML templates\n├── build_skill.py # Builds .skill package for distribution\n├── tests/\n│ ├── harness/ # Visual test harness markdown\n│ ├── dashboard-inline/ # Test dashboard with inline JSON\n│ └── dashboard-with-refs/ # Test dashboard with file references\n├── docs/\n│ ├── MD-CHARTS-PROJECT.md # Original project specification\n│ └── agents.md # Skill authoring reference\n└── skill-bundle/ # Source files for the skill\n ├── SKILL.md # Skill instructions (with YAML frontmatter)\n ├── reference/\n │ └── chart-types.md # Complete API reference\n └── examples/ # JSON and markdown examples\n```\n\n## Warning Messages\n\nThe chart generator outputs helpful warnings to stderr when issues are detected:\n\n| Warning | Cause | Solution |\n|---------|-------|----------|\n| `Invalid JSON in 'bar' block` | Malformed JSON syntax | Check JSON syntax, ensure proper quoting |\n| `Unknown component type 'bars'` | Typo in chart type | Use suggested type (e.g., `bar` not `bars`) |\n| `Cannot resolve 'file=...'` | File reference without base directory | Use file path argument or inline JSON |\n| `Row exceeds 16 columns` | Too many components in one row | Reduce component widths or split into rows |\n| `Invalid value for 'value' in big_value` | Wrong data type (e.g., string instead of number) | Ensure values match expected types |\n\nWarnings include context like content previews, suggestions for similar types, and section/row information to help locate issues.\n\n## CLI Options\n\n```bash\nnpx mviz dashboard.md -o output.html # Generate HTML (requires -o flag)\nnpx mviz --lint dashboard.md # Validate only (no output)\nnpx mviz -l spec.json # Short form of --lint\n```\n\nThe `-o` flag is required and specifies the output file. The `--lint` flag validates your spec without generating HTML output. Useful for CI/CD pipelines or quick validation.\n\n## Running Tests\n\n```bash\ncd ts-src\nnpm test # TypeScript tests (vitest)\nnpm run build # Build TypeScript\nnpm run typecheck # Type checking only\n```\n\n## Skill Bundle\n\nThe skill bundle (`skill-bundle-compact/`) is optimized for Claude for Web with minimal token usage (~750 tokens). Supports essential types:\n- **Charts:** bar, line, scatter\n- **Components:** table (with sparklines), note, textarea, empty_space\n\nFor additional chart types (pie, area, heatmap, sankey, etc.), Claude can reference the TypeScript source code in this repository. See `Best_practices.md` for layout guidance and visualization principles.\n\n## Using with Claude\n\n### Claude Code (CLI)\n\nThe skill is automatically available when working in this project directory.\n\n### Claude Web (claude.ai)\n\n1. Create a new Claude project\n2. Upload the `.skill` file or all files from `skill-bundle/` to the project knowledge base\n3. Claude will have access to the skill, examples, and generator\n\n## Dependencies\n\n- Node.js 24.12+\n\n## Design Philosophy\n\nData visualization best practices:\n- Maximize data-ink ratio (minimal non-data elements)\n- Tight, dense layouts for reports\n- No gratuitous animations or visual clutter\n- Clean, minimal axes (no domain lines, subtle grid)\n- Linear interpolation for accurate data representation\n- Focus on data clarity over decoration\n\n## License\n\nMIT\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":14430,"content_sha256":"b595a2344ab3898edb9603abbe7bba77d03ddce3b01195653256c7c82336db10"},{"filename":"reference/chart-types.md","content":"# Chart Types Reference\n\nExamples and usage guidance for all chart types and components.\n\n**Field reference:** For complete field definitions, see the [JSON Schema](https://raw.githubusercontent.com/matsonj/mviz/main/schema/mviz.schema.json).\n\n---\n\n## Charts\n\n### bar\n\nVertical or horizontal bar chart, supports grouping and stacking.\n\n**Simple:**\n```json\n{\n \"type\": \"bar\",\n \"title\": \"Sales by Category\",\n \"x\": \"category\",\n \"y\": \"sales\",\n \"data\": [\n {\"category\": \"Electronics\", \"sales\": 45000},\n {\"category\": \"Clothing\", \"sales\": 32000},\n {\"category\": \"Home\", \"sales\": 28000}\n ]\n}\n```\n\n**Grouped** (y as array):\n```json\n{\n \"type\": \"bar\",\n \"title\": \"Q1 vs Q2 Sales\",\n \"x\": \"category\",\n \"y\": [\"q1\", \"q2\"],\n \"data\": [\n {\"category\": \"Electronics\", \"q1\": 45000, \"q2\": 52000},\n {\"category\": \"Clothing\", \"q1\": 32000, \"q2\": 38000}\n ]\n}\n```\n\n**Stacked:**\n```json\n{\n \"type\": \"bar\",\n \"title\": \"Sales Breakdown\",\n \"x\": \"month\",\n \"y\": [\"online\", \"retail\"],\n \"stacked\": true,\n \"data\": [\n {\"month\": \"Jan\", \"online\": 30000, \"retail\": 25000},\n {\"month\": \"Feb\", \"online\": 35000, \"retail\": 28000}\n ]\n}\n```\n\n---\n\n### line\n\nLine chart for trends, supports multi-series.\n\n**Simple:**\n```json\n{\n \"type\": \"line\",\n \"title\": \"Revenue Trend\",\n \"x\": \"month\",\n \"y\": \"revenue\",\n \"data\": [\n {\"month\": \"Jan\", \"revenue\": 85000},\n {\"month\": \"Feb\", \"revenue\": 92000},\n {\"month\": \"Mar\", \"revenue\": 88000},\n {\"month\": \"Apr\", \"revenue\": 105000}\n ]\n}\n```\n\n**Multi-series:**\n```json\n{\n \"type\": \"line\",\n \"title\": \"Revenue vs Costs\",\n \"x\": \"month\",\n \"y\": [\"revenue\", \"costs\"],\n \"data\": [\n {\"month\": \"Jan\", \"revenue\": 85000, \"costs\": 60000},\n {\"month\": \"Feb\", \"revenue\": 92000, \"costs\": 65000}\n ]\n}\n```\n\n---\n\n### area\n\nArea chart, supports stacking.\n\n```json\n{\n \"type\": \"area\",\n \"title\": \"Traffic Sources\",\n \"x\": \"week\",\n \"y\": [\"organic\", \"paid\", \"referral\"],\n \"stacked\": true,\n \"data\": [\n {\"week\": \"W1\", \"organic\": 1200, \"paid\": 800, \"referral\": 400},\n {\"week\": \"W2\", \"organic\": 1400, \"paid\": 900, \"referral\": 450}\n ]\n}\n```\n\n---\n\n### pie\n\nPie or donut chart.\n\n```json\n{\n \"type\": \"pie\",\n \"title\": \"Market Share\",\n \"donut\": true,\n \"data\": [\n {\"name\": \"Product A\", \"value\": 45},\n {\"name\": \"Product B\", \"value\": 30},\n {\"name\": \"Product C\", \"value\": 15},\n {\"name\": \"Other\", \"value\": 10}\n ]\n}\n```\n\n---\n\n### scatter\n\nScatter plot for correlation analysis. Supports multiple series via `series` field.\n\n**Simple:**\n```json\n{\n \"type\": \"scatter\",\n \"title\": \"Price vs Sales\",\n \"x\": \"price\",\n \"y\": \"units_sold\",\n \"data\": [\n {\"price\": 10, \"units_sold\": 500},\n {\"price\": 15, \"units_sold\": 420},\n {\"price\": 20, \"units_sold\": 350}\n ]\n}\n```\n\n**Multi-series (grouped by category):**\n```json\n{\n \"type\": \"scatter\",\n \"title\": \"Height vs Weight by Gender\",\n \"x\": \"height\",\n \"y\": \"weight\",\n \"series\": \"gender\",\n \"data\": [\n {\"height\": 170, \"weight\": 70, \"gender\": \"Male\"},\n {\"height\": 165, \"weight\": 55, \"gender\": \"Female\"},\n {\"height\": 180, \"weight\": 80, \"gender\": \"Male\"},\n {\"height\": 160, \"weight\": 50, \"gender\": \"Female\"}\n ]\n}\n```\n\n**With persistent labels** (`showLabels: true` + `label` field):\n```json\n{\n \"type\": \"scatter\",\n \"title\": \"City Populations\",\n \"x\": \"area\",\n \"y\": \"population\",\n \"label\": \"city\",\n \"showLabels\": true,\n \"data\": [\n {\"city\": \"New York\", \"area\": 302, \"population\": 8300000},\n {\"city\": \"LA\", \"area\": 469, \"population\": 3900000}\n ]\n}\n```\n\n---\n\n### bubble\n\nBubble chart (scatter with size dimension). Supports `series` for color grouping and `showLabels` for persistent labels.\n\n**Simple:**\n```json\n{\n \"type\": \"bubble\",\n \"title\": \"Market Analysis\",\n \"x\": \"market_size\",\n \"y\": \"growth_rate\",\n \"size\": \"revenue\",\n \"data\": [\n {\"market_size\": 100, \"growth_rate\": 0.15, \"revenue\": 5000000},\n {\"market_size\": 80, \"growth_rate\": 0.25, \"revenue\": 3000000}\n ]\n}\n```\n\n**Multi-series** (grouped by field, with label):\n```json\n{\n \"type\": \"bubble\",\n \"title\": \"LLM Cost vs Accuracy\",\n \"x\": \"cost\",\n \"y\": \"accuracy\",\n \"size\": \"samples\",\n \"series\": \"provider\",\n \"label\": \"model\",\n \"data\": [\n {\"model\": \"GPT-4\", \"cost\": 3.0, \"accuracy\": 95, \"samples\": 500, \"provider\": \"OpenAI\"},\n {\"model\": \"Claude 3\", \"cost\": 2.5, \"accuracy\": 93, \"samples\": 600, \"provider\": \"Anthropic\"}\n ]\n}\n```\n\n---\n\n### boxplot\n\nBox plot for statistical distribution. Data format: `[min, Q1, median, Q3, max]`.\n\n```json\n{\n \"type\": \"boxplot\",\n \"title\": \"Response Time Distribution\",\n \"categories\": [\"API A\", \"API B\", \"API C\"],\n \"data\": [\n [10, 25, 35, 50, 80],\n [15, 30, 45, 60, 95],\n [5, 20, 30, 40, 60]\n ]\n}\n```\n\n---\n\n### histogram\n\nHistogram for distribution visualization.\n\n```json\n{\n \"type\": \"histogram\",\n \"title\": \"Age Distribution\",\n \"bins\": 8,\n \"data\": [22, 25, 28, 30, 32, 35, 38, 40, 42, 45, 48, 50, 55, 60]\n}\n```\n\n---\n\n### waterfall\n\nWaterfall chart showing cumulative effect. Use `\"type\": \"total\"` for grounded bars, `null` value for auto-calculated totals.\n\n**Colors:** Green = increases, Red = decreases, Blue = totals\n\n```json\n{\n \"type\": \"waterfall\",\n \"title\": \"Revenue Breakdown\",\n \"data\": [\n {\"name\": \"Starting\", \"value\": 1000, \"type\": \"total\"},\n {\"name\": \"Product Sales\", \"value\": 450},\n {\"name\": \"Services\", \"value\": 180},\n {\"name\": \"Returns\", \"value\": -120},\n {\"name\": \"Discounts\", \"value\": -95},\n {\"name\": \"Ending\", \"value\": null, \"type\": \"total\"}\n ]\n}\n```\n\n---\n\n### xmr\n\nXmR (Individual-Moving Range) control chart for statistical process control.\n\n**Output:** Two stacked charts - X Chart (values with control limits) and MR Chart (moving ranges).\n\n**Nelson Rules:** By default, highlights points violating statistical control rules. Disable with `\"nelson_rules\": false`.\n\n**Simple array:**\n```json\n{\n \"type\": \"xmr\",\n \"title\": \"Daily Output\",\n \"data\": [45.2, 47.1, 44.8, 46.3, 45.9, 48.2, 44.1, 46.8, 45.5, 47.3]\n}\n```\n\n**With labels:**\n```json\n{\n \"type\": \"xmr\",\n \"title\": \"Weekly Measurements\",\n \"data\": [\n {\"label\": \"Week 1\", \"value\": 45.2},\n {\"label\": \"Week 2\", \"value\": 47.1},\n {\"label\": \"Week 3\", \"value\": 44.8}\n ]\n}\n```\n\n---\n\n### sankey\n\nSankey diagram for flow visualization.\n\n```json\n{\n \"type\": \"sankey\",\n \"title\": \"Budget Allocation\",\n \"data\": [\n {\"source\": \"Revenue\", \"target\": \"Operations\", \"value\": 500000},\n {\"source\": \"Revenue\", \"target\": \"Marketing\", \"value\": 200000},\n {\"source\": \"Revenue\", \"target\": \"R&D\", \"value\": 300000},\n {\"source\": \"Operations\", \"target\": \"Salaries\", \"value\": 350000},\n {\"source\": \"Operations\", \"target\": \"Infrastructure\", \"value\": 150000}\n ]\n}\n```\n\n---\n\n### funnel\n\nFunnel chart for conversion stages.\n\n```json\n{\n \"type\": \"funnel\",\n \"title\": \"Sales Funnel\",\n \"format\": \"currency_auto\",\n \"data\": [\n {\"name\": \"Visitors\", \"value\": 10000},\n {\"name\": \"Leads\", \"value\": 5000},\n {\"name\": \"Qualified\", \"value\": 2500},\n {\"name\": \"Proposals\", \"value\": 1000},\n {\"name\": \"Closed\", \"value\": 500}\n ]\n}\n```\n\n---\n\n### heatmap\n\n2D heatmap with color scale. Data format: `[x_index, y_index, value]`.\n\n```json\n{\n \"type\": \"heatmap\",\n \"title\": \"Activity by Day/Hour\",\n \"xCategories\": [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\"],\n \"yCategories\": [\"9am\", \"12pm\", \"3pm\", \"6pm\"],\n \"data\": [\n [0, 0, 10], [1, 0, 15], [2, 0, 20], [3, 0, 18], [4, 0, 12],\n [0, 1, 25], [1, 1, 30], [2, 1, 35], [3, 1, 28], [4, 1, 22]\n ]\n}\n```\n\n---\n\n### calendar\n\nCalendar heatmap (GitHub-style).\n\n```json\n{\n \"type\": \"calendar\",\n \"title\": \"Daily Activity\",\n \"year\": 2024,\n \"data\": [\n {\"date\": \"2024-01-15\", \"value\": 5},\n {\"date\": \"2024-01-16\", \"value\": 12},\n {\"date\": \"2024-01-17\", \"value\": 8}\n ]\n}\n```\n\n---\n\n### sparkline\n\nCompact inline chart for trends. Types: `line`, `bar`, `area`.\n\n```json\n{\n \"type\": \"sparkline\",\n \"title\": \"Revenue Trend\",\n \"sparkType\": \"line\",\n \"data\": [42, 48, 45, 52, 58, 55, 62, 68, 72, 78, 85, 92]\n}\n```\n\n---\n\n### combo\n\nCombined bar and line chart with optional dual axis.\n\n```json\n{\n \"type\": \"combo\",\n \"title\": \"Sales & Growth\",\n \"x\": \"quarter\",\n \"bar\": [\"sales\"],\n \"line\": [\"growth\"],\n \"dualAxis\": true,\n \"data\": [\n {\"quarter\": \"Q1\", \"sales\": 120000, \"growth\": 0.12},\n {\"quarter\": \"Q2\", \"sales\": 150000, \"growth\": 0.25},\n {\"quarter\": \"Q3\", \"sales\": 140000, \"growth\": -0.07},\n {\"quarter\": \"Q4\", \"sales\": 180000, \"growth\": 0.29}\n ]\n}\n```\n\n---\n\n### dumbbell\n\nBefore/after comparison with directional color-coding.\n\n- **Green** = improvement, **Red** = decline, **Gray** = no change\n- Set `higherIsBetter: false` for rankings (lower is better)\n\n**Revenue growth:**\n```json\n{\n \"type\": \"dumbbell\",\n \"title\": \"Revenue by Region\",\n \"category\": \"region\",\n \"start\": \"before\",\n \"end\": \"after\",\n \"startLabel\": \"2023\",\n \"endLabel\": \"2024\",\n \"data\": [\n {\"region\": \"East\", \"before\": 30, \"after\": 45},\n {\"region\": \"West\", \"before\": 25, \"after\": 60},\n {\"region\": \"North\", \"before\": 40, \"after\": 35}\n ]\n}\n```\n\n**Rankings (lower is better):**\n```json\n{\n \"type\": \"dumbbell\",\n \"title\": \"League Rankings\",\n \"category\": \"team\",\n \"start\": \"week1\",\n \"end\": \"week10\",\n \"higherIsBetter\": false,\n \"data\": [\n {\"team\": \"Team Alpha\", \"week1\": 5, \"week10\": 1},\n {\"team\": \"Team Beta\", \"week1\": 10, \"week10\": 3}\n ]\n}\n```\n\n---\n\n### mermaid\n\nRenders Mermaid diagram syntax to SVG or ASCII. Supports flowcharts, sequence diagrams, state diagrams, class diagrams, and ER diagrams.\n\nThe `code` field accepts a string or array of strings (joined with newlines). Array format is easier to read and edit:\n\n**Flowchart (array format - recommended):**\n```json\n{\n \"type\": \"mermaid\",\n \"title\": \"User Flow\",\n \"code\": [\n \"graph TD\",\n \" A[Start] --> B{Decision}\",\n \" B -->|Yes| C[Action]\",\n \" B -->|No| D[End]\"\n ]\n}\n```\n\n**Sequence diagram:**\n```json\n{\n \"type\": \"mermaid\",\n \"title\": \"API Call\",\n \"code\": [\n \"sequenceDiagram\",\n \" Client->>Server: Request\",\n \" Server-->>Client: Response\"\n ]\n}\n```\n\n**ER diagram:**\n```json\n{\n \"type\": \"mermaid\",\n \"title\": \"Data Model\",\n \"code\": [\n \"erDiagram\",\n \" CUSTOMER ||--o{ ORDER : places\",\n \" ORDER ||--|{ LINE_ITEM : contains\",\n \" PRODUCT ||--o{ LINE_ITEM : includes\"\n ]\n}\n```\n\n**ASCII rendering** (set `ascii: true` for text-based output):\n```json\n{\n \"type\": \"mermaid\",\n \"title\": \"Process Flow\",\n \"code\": [\"graph LR\", \" A[Input] --> B[Process] --> C[Output]\"],\n \"ascii\": true\n}\n```\n\n**Pure ASCII** (no Unicode box-drawing characters, add `useAscii: true`):\n```json\n{\n \"type\": \"mermaid\",\n \"title\": \"Simple Flow\",\n \"code\": [\"graph LR\", \" A --> B --> C\"],\n \"ascii\": true,\n \"useAscii\": true\n}\n```\n\n**Lint rules** (these cause validation errors):\n- No `\u003cbr/>` tags in labels - they render as literal text\n- No quoted labels like `A[\"text\"]` in flowcharts - quotes appear in output\n```\n\n---\n\n## UI Components\n\n### big_value\n\nLarge metric display for KPIs. Optional `comparison` shows change indicator.\n\n**Basic (label below number):**\n```json\n{\n \"type\": \"big_value\",\n \"value\": 1250000,\n \"label\": \"Total Revenue\",\n \"format\": \"currency\"\n}\n```\n\n**With title (you can use either `title` or `label` - both work the same when only one is provided):**\n```json\n{\n \"type\": \"big_value\",\n \"value\": 29.6,\n \"title\": \"Points Per Game\",\n \"format\": \"num1\"\n}\n```\n\n**With header AND label (title becomes H2 header above, label goes below):**\n```json\n{\n \"type\": \"big_value\",\n \"title\": \"Q4 Results\",\n \"value\": 1250000,\n \"label\": \"Total Revenue\",\n \"format\": \"currency\"\n}\n```\n\n**With comparison:**\n```json\n{\n \"type\": \"big_value\",\n \"value\": 1250000,\n \"label\": \"Total Revenue\",\n \"format\": \"currency\",\n \"comparison\": {\n \"value\": 0.15,\n \"label\": \"vs Last Month\",\n \"format\": \"pct\"\n }\n}\n```\n\n---\n\n### delta\n\nChange indicator with arrow. Green for positive (or red if `positiveIsGood: false`).\n\n**Basic (label below value):**\n```json\n{\n \"type\": \"delta\",\n \"value\": 0.15,\n \"label\": \"vs Last Month\",\n \"format\": \"pct\"\n}\n```\n\n**With title (you can use either `title` or `label` - both work the same when only one is provided):**\n```json\n{\n \"type\": \"delta\",\n \"value\": -0.0041,\n \"title\": \"Margin Gap\",\n \"format\": \"pct\"\n}\n```\n\n**With header AND label (title becomes header above, label goes below):**\n```json\n{\n \"type\": \"delta\",\n \"title\": \"Q4 Impact\",\n \"value\": -82769,\n \"label\": \"Late vs On Time\",\n \"format\": \"currency_auto\"\n}\n```\n\n**Inverted (lower is better):**\n```json\n{\n \"type\": \"delta\",\n \"value\": -25,\n \"label\": \"Bugs Fixed\",\n \"format\": \"num0\",\n \"positiveIsGood\": false\n}\n```\n\n---\n\n### alert\n\nColored notification banner. Types: `info`, `success`, `warning`, `error`. Supports `**bold**` and `*italic*`.\n\n```json\n{\n \"type\": \"alert\",\n \"message\": \"Q2 targets **exceeded** by 15%!\",\n \"alertType\": \"success\"\n}\n```\n\n---\n\n### note\n\nInformation callout box. Types: `default` (red), `warning` (yellow), `tip` (green).\n\n```json\n{\n \"type\": \"note\",\n \"label\": \"Pro Tip\",\n \"content\": \"Use **keyboard shortcuts** to navigate faster.\",\n \"noteType\": \"tip\"\n}\n```\n\n---\n\n### text\n\nStyled paragraph. Supports `**bold**` and `*italic*`.\n\n```json\n{\n \"type\": \"text\",\n \"content\": \"This dashboard shows **key performance metrics** for Q2 2024.\"\n}\n```\n\n---\n\n### textarea\n\nMarkdown-formatted text area. Supports headers, lists, code blocks, blockquotes, links.\n\n```json\n{\n \"type\": \"textarea\",\n \"title\": \"Summary\",\n \"content\": \"## Overview\\n\\nThis dashboard shows **key metrics** for Q2.\\n\\n- Revenue up 15%\\n- Customer growth steady\"\n}\n```\n\n---\n\n### empty_space\n\nInvisible grid spacer.\n\n```json\n{\"type\": \"empty_space\"}\n```\n\n---\n\n### table\n\nData table with formatting. Supports inline sparklines and heatmap columns.\n\n**Basic:**\n```json\n{\n \"type\": \"table\",\n \"title\": \"Sales Data\",\n \"columns\": [\n {\"id\": \"product\", \"title\": \"Product\"},\n {\"id\": \"sales\", \"title\": \"Sales\", \"align\": \"right\", \"fmt\": \"currency\"},\n {\"id\": \"margin\", \"title\": \"Margin\", \"align\": \"right\", \"fmt\": \"pct\"}\n ],\n \"data\": [\n {\"product\": \"Widget Pro\", \"sales\": 125000, \"margin\": 0.35},\n {\"product\": \"Gadget X\", \"sales\": 98000, \"margin\": 0.28}\n ]\n}\n```\n\n**With sparklines:**\n```json\n{\n \"type\": \"table\",\n \"columns\": [\n {\"id\": \"product\", \"title\": \"Product\"},\n {\"id\": \"sales\", \"title\": \"Sales\", \"fmt\": \"currency0k\"},\n {\"id\": \"trend\", \"title\": \"Trend\", \"type\": \"sparkline\", \"sparkType\": \"line\"}\n ],\n \"data\": [\n {\"product\": \"Widget\", \"sales\": 125000, \"trend\": [85, 92, 98, 108, 115, 125]}\n ]\n}\n```\n\n**Column types:**\n- `\"type\": \"sparkline\"` with `sparkType`: `line`, `bar`, `area`, `pct_bar`, `dumbbell`\n- `\"type\": \"heatmap\"` - color gradient from low to high values. Set `higherIsBetter: false` to invert the gradient (intense color = low values, useful for error rates, latency, etc.)\n\n**Dumbbell sparklines:** Data as `[start, end]` array. Set `higherIsBetter: false` for metrics where lower is better.\n\n```json\n{\"id\": \"change\", \"type\": \"sparkline\", \"sparkType\": \"dumbbell\", \"higherIsBetter\": true}\n```\n\n**Cell overrides:** Use `{\"value\": \"text\", \"bold\": true}` to override column styling per cell.\n\n**Sort & filter:**\n\n- `sortable` (default `true`) — click a column header to sort; cycles asc → desc → off. Numeric columns sort by raw value so formatted text (`$1.250m`, `15.0%`) doesn't break ordering.\n- `filter` (default `false`) — when `true`, render a \"Filter by any value…\" search input above the table. Rows filter live on case-insensitive contains match across all cells.\n\n```json\n{\n \"type\": \"table\",\n \"sortable\": true,\n \"filter\": true,\n \"columns\": [\n {\"id\": \"product\"},\n {\"id\": \"category\"},\n {\"id\": \"sales\", \"fmt\": \"currency\"}\n ],\n \"data\": [...]\n}\n```\n\nSet `sortable: false` to render a static table (useful for small reference tables or print-only reports).\n\n---\n\n## Format Options\n\n| Format | Example | Description |\n|--------|---------|-------------|\n| `auto` | 1.000m | Smart auto-format (default) |\n| `currency_auto` | $1.000m | Smart auto-format with currency symbol |\n| `currency` | $1,250,000 | Full currency |\n| `currency0k` | $125k | Compact thousands |\n| `currency0m` | $1.2m | Compact millions |\n| `pct` | 15.0% | Percentage with decimal |\n| `pct0` | 15% | Percentage integer |\n| `num0` | 1,250,000 | Number with commas |\n| `num0k` | 125k | Compact thousands |\n| `date` | 2026-02-24 | ISO date — strings pass through, numbers render as `YYYY-MM-DD`. Tables sort by epoch ms. |\n| `duration` | 1m7s | Duration — numeric seconds render as `1m7s` / `25m39s`, strings pass through. Tables sort by total seconds. |\n\n**Percentages:**\n\n- **Scalars** (`big_value`, `delta`) always multiply the value by 100. Pass `0.155` for `15.5%`. Pass `1.2` for `120%`. The linter warns when `|value| > 1` with a `pct*` format in case you meant `value/100`.\n- **Charts and tables** are series-aware: they check the column/series and skip the multiply when any value exceeds 1. `[15, 22, 18]` → `15%`, `22%`, `18%`. `[0.15, 0.22, 0.18]` → `15%`, `22%`, `18%`. `[0.8, 1.2]` → `80%`, `120%` (max > 1 means \"already percent\", so the fractional interpretation doesn't apply here — split values > 1 into their own scalar if that's not what you want).\n\n**Auto-detection:** Fields named `revenue`, `sales`, `price`, `cost` → `currency_auto`. Fields with `pct`, `percent`, `rate` → `pct`.\n\n---\n\n## Grid System\n\n16-column grid. Use `size=[cols,rows]` in code block headers.\n\n**Default sizes:**\n| Component | Size | | Component | Size |\n|-----------|------|-|-----------|------|\n| `big_value`, `delta` | [4,2] | | `bar`, `line`, `area` | [8,5] |\n| `table`, `textarea` | [16,4] | | `dumbbell` | [12,6] |\n| `mermaid` | [8,5] | | | |\n| `alert`, `note` | [16,1] | | `empty_space` | [4,2] |\n\n**Auto-sizing:** Use `size=auto` to calculate from data.\n\n---\n\n## Dashboard Markdown\n\n```markdown\n---\ntitle: My Dashboard\ntheme: light\ncontinuous: true\n---\n\n# Title\n\n## Section\n\n```big_value size=[4,2]\n{\"value\": 125000, \"label\": \"Revenue\", \"format\": \"currency0k\"}\n```\n```bar size=[12,6] file=data/sales.json\n```\n```\n\n**Frontmatter:** `title`, `theme` (light/dark), `continuous` (removes section breaks)\n\n**Breaks:** `---` = section break, `===` = page break (PDF)\n\n**Side-by-side:** Code blocks with no blank line between them share the same row.\n\n---\n\n## File References\n\n**JSON:** `file=data/sales.json` - complete spec in file\n\n**CSV:** `file=data.csv` with inline options - data from CSV, config inline\n\n```bash\nduckdb -csv -c \"SELECT quarter, revenue FROM sales\" > data/quarterly.csv\n```\n\n```markdown\n```bar file=data/quarterly.csv\n{\"title\": \"Quarterly Revenue\", \"x\": \"quarter\", \"y\": \"revenue\"}\n```\n```\n\n---\n\n## Color Palette\n\n| Color | Hex | Use |\n|-------|-----|-----|\n| Primary Blue | `#0777b3` | Primary series |\n| Secondary Orange | `#bd4e35` | Secondary/accent |\n| Positive Green | `#2d7a00` | Success |\n| Warning Amber | `#e18727` | Warnings |\n| Error Red | `#bc1200` | Errors |\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":18764,"content_sha256":"f9e447a8bd86a57fb6e905532cbf561b22b2136ac0da990f1c12d60e1cb9b079"},{"filename":"schema/mviz.schema.json","content":"{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://raw.githubusercontent.com/matsonj/mviz/main/schema/mviz.v1.schema.json\",\n \"title\": \"mviz Chart Specification\",\n \"description\": \"Schema for mviz data visualization specs (v1.x)\",\n \"version\": \"1.0.0\",\n \"oneOf\": [\n {\n \"$ref\": \"#/$defs/BarChart\"\n },\n {\n \"$ref\": \"#/$defs/LineChart\"\n },\n {\n \"$ref\": \"#/$defs/AreaChart\"\n },\n {\n \"$ref\": \"#/$defs/PieChart\"\n },\n {\n \"$ref\": \"#/$defs/ScatterChart\"\n },\n {\n \"$ref\": \"#/$defs/BubbleChart\"\n },\n {\n \"$ref\": \"#/$defs/BoxplotChart\"\n },\n {\n \"$ref\": \"#/$defs/HistogramChart\"\n },\n {\n \"$ref\": \"#/$defs/WaterfallChart\"\n },\n {\n \"$ref\": \"#/$defs/XmrChart\"\n },\n {\n \"$ref\": \"#/$defs/SankeyChart\"\n },\n {\n \"$ref\": \"#/$defs/FunnelChart\"\n },\n {\n \"$ref\": \"#/$defs/HeatmapChart\"\n },\n {\n \"$ref\": \"#/$defs/CalendarChart\"\n },\n {\n \"$ref\": \"#/$defs/SparklineChart\"\n },\n {\n \"$ref\": \"#/$defs/ComboChart\"\n },\n {\n \"$ref\": \"#/$defs/DumbbellChart\"\n },\n {\n \"$ref\": \"#/$defs/MermaidChart\"\n },\n {\n \"$ref\": \"#/$defs/BigValue\"\n },\n {\n \"$ref\": \"#/$defs/Delta\"\n },\n {\n \"$ref\": \"#/$defs/Alert\"\n },\n {\n \"$ref\": \"#/$defs/Note\"\n },\n {\n \"$ref\": \"#/$defs/Text\"\n },\n {\n \"$ref\": \"#/$defs/Textarea\"\n },\n {\n \"$ref\": \"#/$defs/EmptySpace\"\n },\n {\n \"$ref\": \"#/$defs/Table\"\n }\n ],\n \"$defs\": {\n \"Theme\": {\n \"enum\": [\n \"light\",\n \"dark\"\n ]\n },\n \"FormatOption\": {\n \"enum\": [\n \"auto\",\n \"currency_auto\",\n \"currency\",\n \"currency0k\",\n \"currency0m\",\n \"currency0b\",\n \"num0\",\n \"num1\",\n \"num0k\",\n \"num0m\",\n \"num0b\",\n \"pct\",\n \"pct0\",\n \"pct1\"\n ]\n },\n \"TableColumnFormatOption\": {\n \"description\": \"Format options for table columns. Extends FormatOption with 'date' and 'duration', which only affect table rendering and sort (not chart axes/labels).\",\n \"enum\": [\n \"auto\",\n \"currency_auto\",\n \"currency\",\n \"currency0k\",\n \"currency0m\",\n \"currency0b\",\n \"num0\",\n \"num1\",\n \"num0k\",\n \"num0m\",\n \"num0b\",\n \"pct\",\n \"pct0\",\n \"pct1\",\n \"date\",\n \"duration\"\n ]\n },\n \"CurrencyCode\": {\n \"type\": \"string\",\n \"description\": \"ISO 4217 currency code (e.g., USD, GBP, EUR)\"\n },\n \"YField\": {\n \"oneOf\": [\n {\n \"type\": \"string\"\n },\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"minItems\": 1\n }\n ]\n },\n \"DataArray\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\"\n }\n },\n \"ColumnarData\": {\n \"type\": \"object\",\n \"description\": \"Columnar data shape accepted as an alternative to `data` on DataArray-based chart types. The CLI normalizes this to `data` before rendering or linting.\",\n \"properties\": {\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n }\n }\n },\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n },\n \"BarChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"x\",\n \"y\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"bar\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"x\": {\n \"type\": \"string\"\n },\n \"y\": {\n \"$ref\": \"#/$defs/YField\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"horizontal\": {\n \"type\": \"boolean\"\n },\n \"stacked\": {\n \"type\": \"boolean\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"yMin\": {\n \"type\": \"number\"\n },\n \"yMax\": {\n \"type\": \"number\"\n },\n \"xAxisType\": {\n \"enum\": [\n \"category\",\n \"time\",\n \"value\"\n ]\n }\n },\n \"additionalProperties\": false\n },\n \"LineChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"x\",\n \"y\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"line\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"x\": {\n \"type\": \"string\"\n },\n \"y\": {\n \"$ref\": \"#/$defs/YField\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"smooth\": {\n \"type\": \"boolean\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"yMin\": {\n \"type\": \"number\"\n },\n \"yMax\": {\n \"type\": \"number\"\n },\n \"xAxisType\": {\n \"enum\": [\n \"category\",\n \"time\",\n \"value\"\n ]\n }\n },\n \"additionalProperties\": false\n },\n \"AreaChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"x\",\n \"y\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"area\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"x\": {\n \"type\": \"string\"\n },\n \"y\": {\n \"$ref\": \"#/$defs/YField\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"stacked\": {\n \"type\": \"boolean\"\n },\n \"smooth\": {\n \"type\": \"boolean\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"yMin\": {\n \"type\": \"number\"\n },\n \"yMax\": {\n \"type\": \"number\"\n },\n \"xAxisType\": {\n \"enum\": [\n \"category\",\n \"time\",\n \"value\"\n ]\n }\n },\n \"additionalProperties\": false\n },\n \"PieChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"pie\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"name\": {\n \"type\": \"string\",\n \"default\": \"name\"\n },\n \"value\": {\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"donut\": {\n \"type\": \"boolean\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"ScatterChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"x\",\n \"y\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"scatter\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"x\": {\n \"type\": \"string\"\n },\n \"y\": {\n \"type\": \"string\"\n },\n \"series\": {\n \"type\": \"string\",\n \"description\": \"Field name to group data points by (creates multiple colored series)\"\n },\n \"label\": {\n \"type\": \"string\",\n \"description\": \"Field name for point labels shown in tooltips\"\n },\n \"showLabels\": {\n \"type\": \"boolean\",\n \"description\": \"Show persistent labels next to points (requires label field)\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"xFormat\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"yFormat\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n }\n },\n \"additionalProperties\": false\n },\n \"BubbleChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"x\",\n \"y\",\n \"size\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"bubble\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"x\": {\n \"type\": \"string\"\n },\n \"y\": {\n \"type\": \"string\"\n },\n \"size\": {\n \"type\": \"string\"\n },\n \"series\": {\n \"type\": \"string\",\n \"description\": \"Field name to group data points by (creates multiple colored series)\"\n },\n \"label\": {\n \"type\": \"string\",\n \"description\": \"Field name for point labels shown in tooltips\"\n },\n \"showLabels\": {\n \"type\": \"boolean\",\n \"description\": \"Show persistent labels next to points (requires label field)\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"xFormat\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"yFormat\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n }\n },\n \"additionalProperties\": false\n },\n \"BoxplotChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"data\",\n \"categories\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"boxplot\"\n },\n \"data\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"number\"\n },\n \"minItems\": 5,\n \"maxItems\": 5,\n \"description\": \"[min, Q1, median, Q3, max]\"\n }\n },\n \"categories\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"HistogramChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"histogram\"\n },\n \"data\": {\n \"oneOf\": [\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"number\"\n }\n },\n {\n \"$ref\": \"#/$defs/DataArray\"\n }\n ]\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"value\": {\n \"type\": \"string\"\n },\n \"bins\": {\n \"type\": \"integer\",\n \"default\": 10\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"WaterfallChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"waterfall\"\n },\n \"data\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\n \"type\": \"string\"\n },\n \"value\": {\n \"type\": [\n \"number\",\n \"null\"\n ]\n },\n \"type\": {\n \"enum\": [\n \"total\",\n \"subtotal\"\n ],\n \"description\": \"Mark bar as total or subtotal (anchored at zero)\"\n },\n \"isTotal\": {\n \"type\": \"boolean\",\n \"description\": \"Alternative to type: 'total' - marks bar as total (anchored at zero)\"\n }\n }\n }\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"name\": {\n \"type\": \"string\",\n \"default\": \"name\",\n \"description\": \"Field name for category labels\"\n },\n \"value\": {\n \"type\": \"string\",\n \"default\": \"value\",\n \"description\": \"Field name for numeric values\"\n },\n \"x\": {\n \"type\": \"string\",\n \"description\": \"Alias for 'name' field (consistent with other chart types)\"\n },\n \"y\": {\n \"type\": \"string\",\n \"description\": \"Alias for 'value' field (consistent with other chart types)\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"XmrChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"xmr\"\n },\n \"data\": {\n \"oneOf\": [\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"number\"\n }\n },\n {\n \"$ref\": \"#/$defs/DataArray\"\n }\n ]\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"value\": {\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"label\": {\n \"type\": \"string\",\n \"default\": \"label\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"nelson_rules\": {\n \"type\": \"boolean\",\n \"default\": true\n },\n \"yMin\": {\n \"type\": \"number\"\n },\n \"yMax\": {\n \"type\": \"number\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"SankeyChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"sankey\"\n },\n \"data\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"required\": [\n \"source\",\n \"target\",\n \"value\"\n ],\n \"properties\": {\n \"source\": {\n \"type\": \"string\"\n },\n \"target\": {\n \"type\": \"string\"\n },\n \"value\": {\n \"type\": \"number\"\n }\n }\n }\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"nodes\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\"\n }\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"FunnelChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"funnel\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"name\": {\n \"type\": \"string\"\n },\n \"value\": {\n \"type\": \"string\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"HeatmapChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"xCategories\",\n \"yCategories\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"heatmap\"\n },\n \"data\": {\n \"type\": \"array\",\n \"items\": {\n \"oneOf\": [\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"number\"\n },\n \"minItems\": 3,\n \"maxItems\": 3\n },\n {\n \"type\": \"object\",\n \"properties\": {\n \"x\": {},\n \"y\": {},\n \"value\": {\n \"type\": \"number\"\n }\n }\n }\n ]\n }\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"xCategories\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"yCategories\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"CalendarChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"year\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"calendar\"\n },\n \"data\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"required\": [\n \"date\",\n \"value\"\n ],\n \"properties\": {\n \"date\": {\n \"type\": \"string\",\n \"pattern\": \"^\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n },\n \"value\": {\n \"type\": \"number\"\n }\n }\n }\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"year\": {\n \"type\": \"integer\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"SparklineChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"sparkline\"\n },\n \"data\": {\n \"oneOf\": [\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"number\"\n }\n },\n {\n \"$ref\": \"#/$defs/DataArray\"\n }\n ]\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"value\": {\n \"oneOf\": [\n {\n \"type\": \"number\"\n },\n {\n \"type\": \"string\"\n }\n ],\n \"description\": \"Numeric percentage for pct/pct_bar sparkType, or the data field name (string) whose values are plotted for line/bar/area/dumbbell sparkType.\"\n },\n \"sparkType\": {\n \"enum\": [\n \"line\",\n \"bar\",\n \"area\",\n \"pct\",\n \"pct_bar\"\n ],\n \"default\": \"line\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"showValue\": {\n \"type\": \"boolean\",\n \"default\": true,\n \"description\": \"Show the formatted value next to the bar (pct/pct_bar sparkType only)\"\n }\n },\n \"additionalProperties\": false\n },\n \"ComboChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"x\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"combo\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"x\": {\n \"type\": \"string\"\n },\n \"bar\": {\n \"oneOf\": [\n {\n \"type\": \"string\"\n },\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n }\n ]\n },\n \"line\": {\n \"oneOf\": [\n {\n \"type\": \"string\"\n },\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n }\n ]\n },\n \"dualAxis\": {\n \"type\": \"boolean\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"secondaryFormat\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"yMin\": {\n \"type\": \"number\"\n },\n \"yMax\": {\n \"type\": \"number\"\n }\n },\n \"additionalProperties\": false\n },\n \"DumbbellChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"category\",\n \"start\",\n \"end\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"dumbbell\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"category\": {\n \"type\": \"string\"\n },\n \"start\": {\n \"type\": \"string\"\n },\n \"end\": {\n \"type\": \"string\"\n },\n \"startLabel\": {\n \"type\": \"string\"\n },\n \"endLabel\": {\n \"type\": \"string\"\n },\n \"higherIsBetter\": {\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showValues\": {\n \"type\": \"boolean\",\n \"default\": true\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"xMin\": {\n \"type\": \"number\"\n },\n \"xMax\": {\n \"type\": \"number\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"MermaidChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"code\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"mermaid\"\n },\n \"code\": {\n \"oneOf\": [\n {\n \"type\": \"string\"\n },\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n }\n ],\n \"description\": \"Mermaid diagram syntax as string or array of lines (joined with newlines)\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"height\": {\n \"type\": \"number\",\n \"default\": 400\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"ascii\": {\n \"type\": \"boolean\",\n \"default\": false,\n \"description\": \"Render as ASCII/Unicode text art instead of SVG\"\n },\n \"useAscii\": {\n \"type\": \"boolean\",\n \"default\": false,\n \"description\": \"When ascii=true, use pure ASCII instead of Unicode box-drawing characters\"\n }\n },\n \"additionalProperties\": false\n },\n \"BigValue\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"value\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"big_value\"\n },\n \"value\": {\n \"type\": \"number\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"If only title is provided (no label), used as label below number. If both provided, title becomes H2 header above.\"\n },\n \"label\": {\n \"type\": \"string\",\n \"description\": \"Text displayed below the number\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"comparison\": {\n \"type\": \"object\",\n \"properties\": {\n \"value\": {\n \"type\": \"number\"\n },\n \"label\": {\n \"type\": \"string\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n }\n }\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"Delta\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"value\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"delta\"\n },\n \"value\": {\n \"type\": \"number\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"If only title is provided (no label), used as label below value. If both provided, title becomes header above.\"\n },\n \"label\": {\n \"type\": \"string\",\n \"description\": \"Text displayed below the delta value\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"positiveIsGood\": {\n \"type\": \"boolean\",\n \"default\": true\n },\n \"neutralIs\": {\n \"type\": \"number\",\n \"default\": 0,\n \"description\": \"Value treated as neutral (no change). When value === neutralIs, the indicator uses a right-arrow and muted color instead of green/red.\"\n },\n \"comparison\": {\n \"type\": \"object\",\n \"properties\": {\n \"value\": {\n \"type\": \"number\"\n },\n \"label\": {\n \"type\": \"string\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n }\n }\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"Alert\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"anyOf\": [\n {\n \"required\": [\n \"message\"\n ]\n },\n {\n \"required\": [\n \"content\"\n ]\n }\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"alert\"\n },\n \"message\": {\n \"type\": \"string\"\n },\n \"content\": {\n \"type\": \"string\",\n \"description\": \"Alias for message (deprecated, use message)\"\n },\n \"alertType\": {\n \"enum\": [\n \"info\",\n \"success\",\n \"warning\",\n \"error\"\n ],\n \"default\": \"info\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"title\": {\n \"type\": \"string\"\n }\n },\n \"additionalProperties\": false\n },\n \"Note\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"content\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"note\"\n },\n \"content\": {\n \"type\": \"string\"\n },\n \"label\": {\n \"type\": \"string\"\n },\n \"noteType\": {\n \"enum\": [\n \"default\",\n \"warning\",\n \"tip\"\n ],\n \"default\": \"default\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"Text\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"content\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"text\"\n },\n \"content\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"Textarea\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"content\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"textarea\"\n },\n \"content\": {\n \"type\": \"string\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"EmptySpace\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"empty_space\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"TableColumnDef\": {\n \"type\": \"object\",\n \"required\": [\n \"id\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"id\": {\n \"type\": \"string\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"align\": {\n \"enum\": [\n \"left\",\n \"center\",\n \"right\"\n ]\n },\n \"fmt\": {\n \"$ref\": \"#/$defs/TableColumnFormatOption\"\n },\n \"bold\": {\n \"type\": \"boolean\"\n },\n \"italic\": {\n \"type\": \"boolean\"\n },\n \"type\": {\n \"enum\": [\n \"sparkline\",\n \"heatmap\"\n ]\n },\n \"sparkType\": {\n \"enum\": [\n \"line\",\n \"bar\",\n \"area\",\n \"pct_bar\",\n \"dumbbell\"\n ]\n },\n \"width\": {\n \"type\": \"integer\"\n },\n \"higherIsBetter\": {\n \"type\": \"boolean\"\n }\n },\n \"additionalProperties\": false\n },\n \"Table\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"data\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"table\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/TableColumnDef\"\n }\n },\n \"striped\": {\n \"type\": \"boolean\",\n \"default\": true\n },\n \"compact\": {\n \"type\": \"boolean\"\n },\n \"sortable\": {\n \"type\": \"boolean\",\n \"default\": true,\n \"description\": \"Enable click-to-sort on column headers. Cycles asc \\u2192 desc \\u2192 none. Numeric columns sort by raw value.\"\n },\n \"filter\": {\n \"type\": \"boolean\",\n \"default\": false,\n \"description\": \"When true, render a global search input above the table that filters rows by case-insensitive contains match on any cell.\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n }\n }\n}\n","content_type":"application/json; charset=utf-8","language":"json","size":41794,"content_sha256":"3494bb7f8cc23e2cd5f447cf978d41ca072d7c63a7ad554f294d789cbd81c11d"},{"filename":"schema/mviz.v1.schema.json","content":"{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://raw.githubusercontent.com/matsonj/mviz/main/schema/mviz.v1.schema.json\",\n \"title\": \"mviz Chart Specification\",\n \"description\": \"Schema for mviz data visualization specs (v1.x)\",\n \"version\": \"1.0.0\",\n \"oneOf\": [\n {\n \"$ref\": \"#/$defs/BarChart\"\n },\n {\n \"$ref\": \"#/$defs/LineChart\"\n },\n {\n \"$ref\": \"#/$defs/AreaChart\"\n },\n {\n \"$ref\": \"#/$defs/PieChart\"\n },\n {\n \"$ref\": \"#/$defs/ScatterChart\"\n },\n {\n \"$ref\": \"#/$defs/BubbleChart\"\n },\n {\n \"$ref\": \"#/$defs/BoxplotChart\"\n },\n {\n \"$ref\": \"#/$defs/HistogramChart\"\n },\n {\n \"$ref\": \"#/$defs/WaterfallChart\"\n },\n {\n \"$ref\": \"#/$defs/XmrChart\"\n },\n {\n \"$ref\": \"#/$defs/SankeyChart\"\n },\n {\n \"$ref\": \"#/$defs/FunnelChart\"\n },\n {\n \"$ref\": \"#/$defs/HeatmapChart\"\n },\n {\n \"$ref\": \"#/$defs/CalendarChart\"\n },\n {\n \"$ref\": \"#/$defs/SparklineChart\"\n },\n {\n \"$ref\": \"#/$defs/ComboChart\"\n },\n {\n \"$ref\": \"#/$defs/DumbbellChart\"\n },\n {\n \"$ref\": \"#/$defs/MermaidChart\"\n },\n {\n \"$ref\": \"#/$defs/BigValue\"\n },\n {\n \"$ref\": \"#/$defs/Delta\"\n },\n {\n \"$ref\": \"#/$defs/Alert\"\n },\n {\n \"$ref\": \"#/$defs/Note\"\n },\n {\n \"$ref\": \"#/$defs/Text\"\n },\n {\n \"$ref\": \"#/$defs/Textarea\"\n },\n {\n \"$ref\": \"#/$defs/EmptySpace\"\n },\n {\n \"$ref\": \"#/$defs/Table\"\n }\n ],\n \"$defs\": {\n \"Theme\": {\n \"enum\": [\n \"light\",\n \"dark\"\n ]\n },\n \"FormatOption\": {\n \"enum\": [\n \"auto\",\n \"currency_auto\",\n \"currency\",\n \"currency0k\",\n \"currency0m\",\n \"currency0b\",\n \"num0\",\n \"num1\",\n \"num0k\",\n \"num0m\",\n \"num0b\",\n \"pct\",\n \"pct0\",\n \"pct1\"\n ]\n },\n \"TableColumnFormatOption\": {\n \"description\": \"Format options for table columns. Extends FormatOption with 'date' and 'duration', which only affect table rendering and sort (not chart axes/labels).\",\n \"enum\": [\n \"auto\",\n \"currency_auto\",\n \"currency\",\n \"currency0k\",\n \"currency0m\",\n \"currency0b\",\n \"num0\",\n \"num1\",\n \"num0k\",\n \"num0m\",\n \"num0b\",\n \"pct\",\n \"pct0\",\n \"pct1\",\n \"date\",\n \"duration\"\n ]\n },\n \"CurrencyCode\": {\n \"type\": \"string\",\n \"description\": \"ISO 4217 currency code (e.g., USD, GBP, EUR)\"\n },\n \"YField\": {\n \"oneOf\": [\n {\n \"type\": \"string\"\n },\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"minItems\": 1\n }\n ]\n },\n \"DataArray\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\"\n }\n },\n \"ColumnarData\": {\n \"type\": \"object\",\n \"description\": \"Columnar data shape accepted as an alternative to `data` on DataArray-based chart types. The CLI normalizes this to `data` before rendering or linting.\",\n \"properties\": {\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n }\n }\n },\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n },\n \"BarChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"x\",\n \"y\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"bar\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"x\": {\n \"type\": \"string\"\n },\n \"y\": {\n \"$ref\": \"#/$defs/YField\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"horizontal\": {\n \"type\": \"boolean\"\n },\n \"stacked\": {\n \"type\": \"boolean\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"yMin\": {\n \"type\": \"number\"\n },\n \"yMax\": {\n \"type\": \"number\"\n },\n \"xAxisType\": {\n \"enum\": [\n \"category\",\n \"time\",\n \"value\"\n ]\n }\n },\n \"additionalProperties\": false\n },\n \"LineChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"x\",\n \"y\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"line\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"x\": {\n \"type\": \"string\"\n },\n \"y\": {\n \"$ref\": \"#/$defs/YField\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"smooth\": {\n \"type\": \"boolean\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"yMin\": {\n \"type\": \"number\"\n },\n \"yMax\": {\n \"type\": \"number\"\n },\n \"xAxisType\": {\n \"enum\": [\n \"category\",\n \"time\",\n \"value\"\n ]\n }\n },\n \"additionalProperties\": false\n },\n \"AreaChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"x\",\n \"y\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"area\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"x\": {\n \"type\": \"string\"\n },\n \"y\": {\n \"$ref\": \"#/$defs/YField\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"stacked\": {\n \"type\": \"boolean\"\n },\n \"smooth\": {\n \"type\": \"boolean\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"yMin\": {\n \"type\": \"number\"\n },\n \"yMax\": {\n \"type\": \"number\"\n },\n \"xAxisType\": {\n \"enum\": [\n \"category\",\n \"time\",\n \"value\"\n ]\n }\n },\n \"additionalProperties\": false\n },\n \"PieChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"pie\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"name\": {\n \"type\": \"string\",\n \"default\": \"name\"\n },\n \"value\": {\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"donut\": {\n \"type\": \"boolean\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"ScatterChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"x\",\n \"y\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"scatter\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"x\": {\n \"type\": \"string\"\n },\n \"y\": {\n \"type\": \"string\"\n },\n \"series\": {\n \"type\": \"string\",\n \"description\": \"Field name to group data points by (creates multiple colored series)\"\n },\n \"label\": {\n \"type\": \"string\",\n \"description\": \"Field name for point labels shown in tooltips\"\n },\n \"showLabels\": {\n \"type\": \"boolean\",\n \"description\": \"Show persistent labels next to points (requires label field)\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"xFormat\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"yFormat\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n }\n },\n \"additionalProperties\": false\n },\n \"BubbleChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"x\",\n \"y\",\n \"size\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"bubble\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"x\": {\n \"type\": \"string\"\n },\n \"y\": {\n \"type\": \"string\"\n },\n \"size\": {\n \"type\": \"string\"\n },\n \"series\": {\n \"type\": \"string\",\n \"description\": \"Field name to group data points by (creates multiple colored series)\"\n },\n \"label\": {\n \"type\": \"string\",\n \"description\": \"Field name for point labels shown in tooltips\"\n },\n \"showLabels\": {\n \"type\": \"boolean\",\n \"description\": \"Show persistent labels next to points (requires label field)\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"xFormat\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"yFormat\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n }\n },\n \"additionalProperties\": false\n },\n \"BoxplotChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"data\",\n \"categories\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"boxplot\"\n },\n \"data\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"number\"\n },\n \"minItems\": 5,\n \"maxItems\": 5,\n \"description\": \"[min, Q1, median, Q3, max]\"\n }\n },\n \"categories\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"HistogramChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"histogram\"\n },\n \"data\": {\n \"oneOf\": [\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"number\"\n }\n },\n {\n \"$ref\": \"#/$defs/DataArray\"\n }\n ]\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"value\": {\n \"type\": \"string\"\n },\n \"bins\": {\n \"type\": \"integer\",\n \"default\": 10\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"WaterfallChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"waterfall\"\n },\n \"data\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\n \"type\": \"string\"\n },\n \"value\": {\n \"type\": [\n \"number\",\n \"null\"\n ]\n },\n \"type\": {\n \"enum\": [\n \"total\",\n \"subtotal\"\n ],\n \"description\": \"Mark bar as total or subtotal (anchored at zero)\"\n },\n \"isTotal\": {\n \"type\": \"boolean\",\n \"description\": \"Alternative to type: 'total' - marks bar as total (anchored at zero)\"\n }\n }\n }\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"name\": {\n \"type\": \"string\",\n \"default\": \"name\",\n \"description\": \"Field name for category labels\"\n },\n \"value\": {\n \"type\": \"string\",\n \"default\": \"value\",\n \"description\": \"Field name for numeric values\"\n },\n \"x\": {\n \"type\": \"string\",\n \"description\": \"Alias for 'name' field (consistent with other chart types)\"\n },\n \"y\": {\n \"type\": \"string\",\n \"description\": \"Alias for 'value' field (consistent with other chart types)\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"XmrChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"xmr\"\n },\n \"data\": {\n \"oneOf\": [\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"number\"\n }\n },\n {\n \"$ref\": \"#/$defs/DataArray\"\n }\n ]\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"value\": {\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"label\": {\n \"type\": \"string\",\n \"default\": \"label\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"nelson_rules\": {\n \"type\": \"boolean\",\n \"default\": true\n },\n \"yMin\": {\n \"type\": \"number\"\n },\n \"yMax\": {\n \"type\": \"number\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"SankeyChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"sankey\"\n },\n \"data\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"required\": [\n \"source\",\n \"target\",\n \"value\"\n ],\n \"properties\": {\n \"source\": {\n \"type\": \"string\"\n },\n \"target\": {\n \"type\": \"string\"\n },\n \"value\": {\n \"type\": \"number\"\n }\n }\n }\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"nodes\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\"\n }\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"FunnelChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"funnel\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"name\": {\n \"type\": \"string\"\n },\n \"value\": {\n \"type\": \"string\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"HeatmapChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"xCategories\",\n \"yCategories\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"heatmap\"\n },\n \"data\": {\n \"type\": \"array\",\n \"items\": {\n \"oneOf\": [\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"number\"\n },\n \"minItems\": 3,\n \"maxItems\": 3\n },\n {\n \"type\": \"object\",\n \"properties\": {\n \"x\": {},\n \"y\": {},\n \"value\": {\n \"type\": \"number\"\n }\n }\n }\n ]\n }\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"xCategories\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"yCategories\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"CalendarChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"year\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"calendar\"\n },\n \"data\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"required\": [\n \"date\",\n \"value\"\n ],\n \"properties\": {\n \"date\": {\n \"type\": \"string\",\n \"pattern\": \"^\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n },\n \"value\": {\n \"type\": \"number\"\n }\n }\n }\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"year\": {\n \"type\": \"integer\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"SparklineChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"sparkline\"\n },\n \"data\": {\n \"oneOf\": [\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"number\"\n }\n },\n {\n \"$ref\": \"#/$defs/DataArray\"\n }\n ]\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"value\": {\n \"oneOf\": [\n {\n \"type\": \"number\"\n },\n {\n \"type\": \"string\"\n }\n ],\n \"description\": \"Numeric percentage for pct/pct_bar sparkType, or the data field name (string) whose values are plotted for line/bar/area/dumbbell sparkType.\"\n },\n \"sparkType\": {\n \"enum\": [\n \"line\",\n \"bar\",\n \"area\",\n \"pct\",\n \"pct_bar\"\n ],\n \"default\": \"line\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"showValue\": {\n \"type\": \"boolean\",\n \"default\": true,\n \"description\": \"Show the formatted value next to the bar (pct/pct_bar sparkType only)\"\n }\n },\n \"additionalProperties\": false\n },\n \"ComboChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"x\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"combo\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"x\": {\n \"type\": \"string\"\n },\n \"bar\": {\n \"oneOf\": [\n {\n \"type\": \"string\"\n },\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n }\n ]\n },\n \"line\": {\n \"oneOf\": [\n {\n \"type\": \"string\"\n },\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n }\n ]\n },\n \"dualAxis\": {\n \"type\": \"boolean\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"secondaryFormat\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"yMin\": {\n \"type\": \"number\"\n },\n \"yMax\": {\n \"type\": \"number\"\n }\n },\n \"additionalProperties\": false\n },\n \"DumbbellChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"category\",\n \"start\",\n \"end\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"dumbbell\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Columnar form: column names. Use together with `rows` as an alternative to `data`.\"\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\"\n },\n \"description\": \"Columnar form: row values. Use together with `columns` as an alternative to `data`.\"\n },\n \"category\": {\n \"type\": \"string\"\n },\n \"start\": {\n \"type\": \"string\"\n },\n \"end\": {\n \"type\": \"string\"\n },\n \"startLabel\": {\n \"type\": \"string\"\n },\n \"endLabel\": {\n \"type\": \"string\"\n },\n \"higherIsBetter\": {\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showValues\": {\n \"type\": \"boolean\",\n \"default\": true\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"xMin\": {\n \"type\": \"number\"\n },\n \"xMax\": {\n \"type\": \"number\"\n }\n },\n \"additionalProperties\": false,\n \"anyOf\": [\n {\n \"required\": [\n \"data\"\n ]\n },\n {\n \"required\": [\n \"columns\",\n \"rows\"\n ]\n }\n ]\n },\n \"MermaidChart\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"code\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"mermaid\"\n },\n \"code\": {\n \"oneOf\": [\n {\n \"type\": \"string\"\n },\n {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n }\n ],\n \"description\": \"Mermaid diagram syntax as string or array of lines (joined with newlines)\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"height\": {\n \"type\": \"number\",\n \"default\": 400\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"ascii\": {\n \"type\": \"boolean\",\n \"default\": false,\n \"description\": \"Render as ASCII/Unicode text art instead of SVG\"\n },\n \"useAscii\": {\n \"type\": \"boolean\",\n \"default\": false,\n \"description\": \"When ascii=true, use pure ASCII instead of Unicode box-drawing characters\"\n }\n },\n \"additionalProperties\": false\n },\n \"BigValue\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"value\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"big_value\"\n },\n \"value\": {\n \"type\": \"number\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"If only title is provided (no label), used as label below number. If both provided, title becomes H2 header above.\"\n },\n \"label\": {\n \"type\": \"string\",\n \"description\": \"Text displayed below the number\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"comparison\": {\n \"type\": \"object\",\n \"properties\": {\n \"value\": {\n \"type\": \"number\"\n },\n \"label\": {\n \"type\": \"string\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n }\n }\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"Delta\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"value\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"delta\"\n },\n \"value\": {\n \"type\": \"number\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"If only title is provided (no label), used as label below value. If both provided, title becomes header above.\"\n },\n \"label\": {\n \"type\": \"string\",\n \"description\": \"Text displayed below the delta value\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"positiveIsGood\": {\n \"type\": \"boolean\",\n \"default\": true\n },\n \"neutralIs\": {\n \"type\": \"number\",\n \"default\": 0,\n \"description\": \"Value treated as neutral (no change). When value === neutralIs, the indicator uses a right-arrow and muted color instead of green/red.\"\n },\n \"comparison\": {\n \"type\": \"object\",\n \"properties\": {\n \"value\": {\n \"type\": \"number\"\n },\n \"label\": {\n \"type\": \"string\"\n },\n \"format\": {\n \"$ref\": \"#/$defs/FormatOption\"\n }\n }\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"Alert\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"anyOf\": [\n {\n \"required\": [\n \"message\"\n ]\n },\n {\n \"required\": [\n \"content\"\n ]\n }\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"alert\"\n },\n \"message\": {\n \"type\": \"string\"\n },\n \"content\": {\n \"type\": \"string\",\n \"description\": \"Alias for message (deprecated, use message)\"\n },\n \"alertType\": {\n \"enum\": [\n \"info\",\n \"success\",\n \"warning\",\n \"error\"\n ],\n \"default\": \"info\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n },\n \"title\": {\n \"type\": \"string\"\n }\n },\n \"additionalProperties\": false\n },\n \"Note\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"content\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"note\"\n },\n \"content\": {\n \"type\": \"string\"\n },\n \"label\": {\n \"type\": \"string\"\n },\n \"noteType\": {\n \"enum\": [\n \"default\",\n \"warning\",\n \"tip\"\n ],\n \"default\": \"default\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"Text\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"content\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"text\"\n },\n \"content\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"Textarea\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"content\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"textarea\"\n },\n \"content\": {\n \"type\": \"string\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"EmptySpace\": {\n \"type\": \"object\",\n \"required\": [\n \"type\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"empty_space\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n },\n \"TableColumnDef\": {\n \"type\": \"object\",\n \"required\": [\n \"id\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"id\": {\n \"type\": \"string\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"align\": {\n \"enum\": [\n \"left\",\n \"center\",\n \"right\"\n ]\n },\n \"fmt\": {\n \"$ref\": \"#/$defs/TableColumnFormatOption\"\n },\n \"bold\": {\n \"type\": \"boolean\"\n },\n \"italic\": {\n \"type\": \"boolean\"\n },\n \"type\": {\n \"enum\": [\n \"sparkline\",\n \"heatmap\"\n ]\n },\n \"sparkType\": {\n \"enum\": [\n \"line\",\n \"bar\",\n \"area\",\n \"pct_bar\",\n \"dumbbell\"\n ]\n },\n \"width\": {\n \"type\": \"integer\"\n },\n \"higherIsBetter\": {\n \"type\": \"boolean\"\n }\n },\n \"additionalProperties\": false\n },\n \"Table\": {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n \"data\"\n ],\n \"properties\": {\n \"$schema\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"type\": {\n \"const\": \"table\"\n },\n \"data\": {\n \"$ref\": \"#/$defs/DataArray\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"columns\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/TableColumnDef\"\n }\n },\n \"striped\": {\n \"type\": \"boolean\",\n \"default\": true\n },\n \"compact\": {\n \"type\": \"boolean\"\n },\n \"sortable\": {\n \"type\": \"boolean\",\n \"default\": true,\n \"description\": \"Enable click-to-sort on column headers. Cycles asc \\u2192 desc \\u2192 none. Numeric columns sort by raw value.\"\n },\n \"filter\": {\n \"type\": \"boolean\",\n \"default\": false,\n \"description\": \"When true, render a global search input above the table that filters rows by case-insensitive contains match on any cell.\"\n },\n \"currency\": {\n \"$ref\": \"#/$defs/CurrencyCode\"\n },\n \"theme\": {\n \"$ref\": \"#/$defs/Theme\"\n }\n },\n \"additionalProperties\": false\n }\n }\n}\n","content_type":"application/json; charset=utf-8","language":"json","size":41794,"content_sha256":"3494bb7f8cc23e2cd5f447cf978d41ca072d7c63a7ad554f294d789cbd81c11d"}],"content_json":{"type":"doc","content":[{"type":"paragraph","content":[{"text":"mviz v1.7.0","type":"text"}]},{"type":"heading","attrs":{"level":1},"content":[{"text":"mviz","type":"text"}]},{"type":"paragraph","content":[{"text":"Generate clean, data-focused charts and dashboards from compact JSON specs or markdown. Maximizes data-ink ratio with minimal chartjunk, gridlines, and decorative elements. Uses a 16-column grid layout system.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Setup","type":"text"}]},{"type":"paragraph","content":[{"text":"No installation required. Use ","type":"text"},{"text":"npx -y -q mviz","type":"text","marks":[{"type":"code_inline"}]},{"text":" which auto-downloads from npm. The ","type":"text"},{"text":"-q","type":"text","marks":[{"type":"code_inline"}]},{"text":" flag reduces npm output while still showing lint errors.","type":"text"}]},{"type":"paragraph","content":[{"text":"For faster repeated use, install globally: ","type":"text"},{"text":"npm install -g mviz","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"What This Skill Does","type":"text"}]},{"type":"paragraph","content":[{"text":"mviz is two things:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"A spec language","type":"text","marks":[{"type":"strong"}]},{"text":" — markdown with fenced code blocks describing components (charts, KPIs, tables, notes, etc.). One markdown file (or a single JSON spec) describes a whole visualization.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"A set of renderers","type":"text","marks":[{"type":"strong"}]},{"text":" that turn that spec into HTML for different display contexts. Today there are two:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"visualize:show_widget","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" (inline) — when you're in claude.ai with the visualization tool available, render the mviz spec as a ","type":"text"},{"text":"widget_code","type":"text","marks":[{"type":"code_inline"}]},{"text":" HTML fragment using claude.ai's design system (Chart.js, ","type":"text"},{"text":"var(--color-*)","type":"text","marks":[{"type":"code_inline"}]},{"text":" tokens). The widget appears inline in the conversation.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"mviz CLI","type":"text","marks":[{"type":"strong"}]},{"text":" (standalone) — ","type":"text"},{"text":"mviz dashboard.md -o report.html","type":"text","marks":[{"type":"code_inline"}]},{"text":" produces a complete HTML document with mviz's own design system (mdsinabox theme, ECharts). Suitable for downloads, iframe tiles, print/PDF.","type":"text"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"The mviz markdown spec is canonical; the renderer is chosen based on where the visualization will appear. Same spec, two renderings. A third-party could build a different renderer (Chart.js-based CLI, Slack-themed renderer, etc.) without changing the spec.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Picking a renderer","type":"text"}]},{"type":"paragraph","content":[{"text":"Always compose the mviz markdown spec first","type":"text","marks":[{"type":"strong"}]},{"text":", even when rendering inline. The spec is the canonical artifact — emit it in a fenced code block in your response so the user can copy / reuse / pipe-to-CLI later. Then render it with whichever renderer fits the request.","type":"text"}]},{"type":"paragraph","content":[{"text":"Before rendering, decide which renderer fits — and ","type":"text"},{"text":"state your inference","type":"text","marks":[{"type":"strong"}]},{"text":" so the user can correct you. Some signals:","type":"text"}]},{"type":"paragraph","content":[{"text":"Inline (","type":"text","marks":[{"type":"strong"}]},{"text":"visualize:show_widget","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":"):","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"show me X\" / \"what was X\" / \"how did X change\" / \"render\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"A small visualization (typically 1–4 components) that flows with the surrounding prose","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The user is exploring data, not preparing something to share","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"visualize:show_widget","type":"text","marks":[{"type":"code_inline"}]},{"text":" is available in this conversation","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Standalone (mviz CLI):","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"make a report\" / \"send me the file\" / \"export to PDF\" / \"save\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Multi-section composed view (5+ components, dividers, page breaks, full-width tables)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The user wants something to save, share, print, or embed in an external host (iframe tile, dashboard cell)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"visualize:show_widget","type":"text","marks":[{"type":"code_inline"}]},{"text":" is unavailable (no inline rendering surface)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Default to inline when ambiguous. State the inference explicitly:","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"\"I'll render this inline since you asked to see it in chat. If you'd rather a downloadable file, I can run the mviz CLI on the same spec — just say the word.\"","type":"text","marks":[{"type":"em"}]}]}]},{"type":"paragraph","content":[{"text":"If the user later asks for the file version after an inline render, the spec is already in the conversation — pipe it through ","type":"text"},{"text":"mviz ... -o out.html","type":"text","marks":[{"type":"code_inline"}]},{"text":" without re-composing.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Visual Style (mdsinabox theme — for the CLI renderer)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Font","type":"text","marks":[{"type":"strong"}]},{"text":": Helvetica Neue, Arial (clean sans-serif)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Signature","type":"text","marks":[{"type":"strong"}]},{"text":": Orange accent line at top of dashboards","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Palette","type":"text","marks":[{"type":"strong"}]},{"text":": Blue primary, orange secondary, semantic colors (green=positive, amber=warning, red=error)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Background","type":"text","marks":[{"type":"strong"}]},{"text":": Paper (","type":"text"},{"text":"#f8f8f8","type":"text","marks":[{"type":"code_inline"}]},{"text":" light) / Dark (","type":"text"},{"text":"#231f20","type":"text","marks":[{"type":"code_inline"}]},{"text":" dark)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Principles","type":"text","marks":[{"type":"strong"}]},{"text":": High data-ink ratio, no chartjunk, minimal gridlines, data speaks for itself","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Renderer: mviz CLI (standalone HTML)","type":"text"}]},{"type":"paragraph","content":[{"text":"The CLI renderer produces a complete HTML document styled in the mdsinabox theme above. Output is suitable for direct browser viewing, PDF export, iframe embedding, or sharing as a file.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Single Chart (JSON)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"echo '\u003cjson_spec>' | npx -y -q mviz -o chart.html","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Dashboard from Markdown","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"npx -y -q mviz dashboard.md -o dashboard.html","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Dashboard from Folder","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"npx -y -q mviz my-dashboard/ -o dashboard.html","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Iframe-embedded output (","type":"text"},{"text":"--embed","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"paragraph","content":[{"text":"When the rendered HTML will live inside another product's iframe (mdw-turbo Prism tiles, dashboard cells, etc.), pass ","type":"text"},{"text":"--embed","type":"text","marks":[{"type":"code_inline"}]},{"text":" to strip page chrome (red accent bar, title row, theme toggle) and apply a battery of post-processes that lean the body: CSS pruning, transparent backgrounds so the host's card chrome shows through, ECharts script dropped when no charts are present, marked dropped when no ","type":"text"},{"text":"marked.parse(...)","type":"text","marks":[{"type":"code_inline"}]},{"text":" caller, JS minify, etc.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"npx -y -q mviz dashboard.md --embed -o tile.html","type":"text"}]},{"type":"paragraph","content":[{"text":"Embed mode emits a complete HTML document (DOCTYPE + html + head + body intact). It's not the right tool for ","type":"text"},{"text":"visualize:show_widget","type":"text","marks":[{"type":"code_inline"}]},{"text":" — for that, use the other renderer (see below).","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"16-Column Grid System","type":"text"}]},{"type":"paragraph","content":[{"text":"Components are sized using ","type":"text"},{"text":"size=[cols,rows]","type":"text","marks":[{"type":"code_inline"}]},{"text":" syntax:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"```big_value size=[4,2]\n{\"value\": 1250000, \"label\": \"Revenue\", \"format\": \"currency0m\"}\n```\n```bar size=[8,6]\n{\"title\": \"Sales\", \"x\": \"month\", \"y\": \"sales\", \"file\": \"data/sales.json\"}\n```","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"16 columns","type":"text","marks":[{"type":"strong"}]},{"text":" total width (both portrait and landscape)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Row height","type":"text","marks":[{"type":"strong"}]},{"text":": ~32px per row unit (approximate - charts have padding)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Page capacity","type":"text","marks":[{"type":"strong"}]},{"text":": Portrait [16c × 30r], Landscape [16c × 22r]","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Components on same line share the row","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Empty line = new row","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Height Guidelines:","type":"text","marks":[{"type":"strong"}]}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Row Units","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Approximate Height","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Good For","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"2","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"~64px","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"KPIs, single-line notes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"4","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"~128px","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Small tables, text blocks","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"5-6","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"~160-192px","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Standard charts","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"8+","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"~256px+","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Dense tables, detailed charts","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"For charts with many categories (10+ bars, 10+ rows in dumbbell), increase row units to prevent compression.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Side-by-Side Layout","type":"text"}]},{"type":"paragraph","content":[{"text":"Critical:","type":"text","marks":[{"type":"strong"}]},{"text":" To place components side-by-side, their code blocks must have NO blank lines between them:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"```bar size=[8,5]\n{\"title\": \"Chart A\", ...}\n```\n```line size=[8,5]\n{\"title\": \"Chart B\", ...}\n```","type":"text"}]},{"type":"paragraph","content":[{"text":"This renders Chart A and Chart B on the same row. Adding a blank line between them would put them on separate rows.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Headings and Section Breaks","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Syntax","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Effect","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"# H1","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Page title","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"## H2","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Section title","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"### H3","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Light inline header (subtle, smaller text)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"---","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Visual divider line","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"===","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Page break for printing","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"empty_space","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Invisible grid cell spacer (default 4 cols × 2 rows)","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Heading Guidelines:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Set the page title with either the frontmatter ","type":"text"},{"text":"title:","type":"text","marks":[{"type":"code_inline"}]},{"text":" ","type":"text"},{"text":"or","type":"text","marks":[{"type":"em"}]},{"text":" a leading ","type":"text"},{"text":"# H1","type":"text","marks":[{"type":"code_inline"}]},{"text":", not both.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"## H2","type":"text","marks":[{"type":"code_inline"}]},{"text":" for section titles within a page (most common).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"### H3","type":"text","marks":[{"type":"code_inline"}]},{"text":" for lightweight subheadings that don't interrupt flow.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Section vs Page Breaks:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"---","type":"text","marks":[{"type":"code_inline"}]},{"text":" to separate logical sections visually. Content flows naturally to the next page when needed.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"===","type":"text","marks":[{"type":"code_inline"}]},{"text":" only when you explicitly want to force a new page (e.g., separating chapters or major report sections for PDF output).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never use ","type":"text"},{"text":"===","type":"text","marks":[{"type":"code_inline"}]},{"text":" by default. Only add page breaks when the user specifically requests them.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Default Sizes","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Component","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Default Size","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Notes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"big_value","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[4, 2]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fits 4 per row","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"delta","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[4, 2]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fits 4 per row","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"sparkline","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[4, 2]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Compact inline chart","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"bar","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"line","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"area","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[8, 5]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Half width","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pie","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"scatter","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"bubble","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[8, 5]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Half width","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"funnel","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"sankey","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"heatmap","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[8, 5]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Half width","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"histogram","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"boxplot","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"waterfall","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[8, 5]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Half width","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"combo","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[8, 5]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Half width","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"dumbbell","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[12, 6]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"3/4 width","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"table","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[16, 4]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Full width","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"textarea","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[16, 4]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Full width","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"calendar","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[16, 3]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Full width","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"xmr","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[16, 6]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Full width, tall","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"mermaid","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[8, 5]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Half width (use ","type":"text"},{"text":"ascii: true","type":"text","marks":[{"type":"code_inline"}]},{"text":" for text art)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"alert","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"note","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"text","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[16, 1]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Full width, single row","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"empty_space","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[4, 2]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Invisible spacer","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Recommended Size Pairings","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Layout Goal","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Components","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Sizes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"4 KPIs in a row","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"4× ","type":"text"},{"text":"big_value","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[4,2] each","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"5 KPIs in a row","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"4× ","type":"text"},{"text":"big_value","type":"text","marks":[{"type":"code_inline"}]},{"text":" + 1 wider","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[3,2] + [4,2]","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"KPI + context","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"big_value","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"textarea","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[3,2] + [13,2]","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"KPI + chart","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"big_value","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"bar","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[4,2] + [12,5]","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example: Dense KPI Row","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"```big_value size=[3,2]\n{\"value\": 1250000, \"label\": \"Revenue\", \"format\": \"currency0m\"}\n```\n```big_value size=[3,2]\n{\"value\": 8450, \"label\": \"Orders\", \"format\": \"num0k\"}\n```\n```big_value size=[3,2]\n{\"value\": 2400000000, \"label\": \"Queries\", \"format\": \"num0b\"}\n```\n```delta size=[3,2]\n{\"value\": 0.15, \"label\": \"MoM\", \"format\": \"pct0\"}\n```\n```delta size=[4,2]\n{\"value\": 0.08, \"label\": \"vs Target\", \"format\": \"pct0\"}\n```","type":"text"}]},{"type":"paragraph","content":[{"text":"This creates a row with 5 KPIs (3+3+3+3+4 = 16 columns).","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example: Two Charts Side by Side","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"```bar size=[8,6] file=data/region-sales.json\n```\n```line size=[8,6] file=data/monthly-trend.json\n```","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Supported Types","type":"text"}]},{"type":"paragraph","content":[{"text":"Charts:","type":"text","marks":[{"type":"strong"}]},{"text":" bar, line, area, pie, scatter, bubble, boxplot, histogram, waterfall, xmr, sankey, funnel, heatmap, calendar, sparkline, combo, dumbbell, mermaid","type":"text"}]},{"type":"paragraph","content":[{"text":"UI Components:","type":"text","marks":[{"type":"strong"}]},{"text":" big_value, delta, alert, note, text, textarea, empty_space, table","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Table Formatting","type":"text"}]},{"type":"paragraph","content":[{"text":"Tables support column-level and cell-level formatting:","type":"text"}]},{"type":"paragraph","content":[{"text":"Column options:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"bold","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"italic","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"type","type":"text","marks":[{"type":"code_inline"}]},{"text":" (\"sparkline\" or \"heatmap\")","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"type\": \"table\",\n \"columns\": [\n {\"id\": \"product\", \"title\": \"Product\", \"bold\": true},\n {\"id\": \"category\", \"title\": \"Category\", \"italic\": true},\n {\"id\": \"sales\", \"title\": \"Sales\", \"fmt\": \"currency\"},\n {\"id\": \"margin\", \"title\": \"Margin\", \"type\": \"heatmap\", \"fmt\": \"pct\"},\n {\"id\": \"trend\", \"title\": \"Trend\", \"type\": \"sparkline\", \"sparkType\": \"line\"}\n ],\n \"data\": [\n {\"product\": \"Widget\", \"category\": \"Electronics\", \"sales\": 125000, \"margin\": 0.85, \"trend\": [85, 92, 88, 95, 102, 125]}\n ]\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"Cell-level overrides:","type":"text","marks":[{"type":"strong"}]},{"text":" Use ","type":"text"},{"text":"{\"value\": \"text\", \"bold\": true}","type":"text","marks":[{"type":"code_inline"}]},{"text":" to override column defaults.","type":"text"}]},{"type":"paragraph","content":[{"text":"Heatmap:","type":"text","marks":[{"type":"strong"}]},{"text":" Applies color gradient from low to high values. Text auto-switches to white on dark backgrounds.","type":"text"}]},{"type":"paragraph","content":[{"text":"Sparkline types:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"line","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"bar","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"area","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pct_bar","type":"text","marks":[{"type":"code_inline"}]},{"text":" (progress bar), ","type":"text"},{"text":"dumbbell","type":"text","marks":[{"type":"code_inline"}]},{"text":" (before/after comparison)","type":"text"}]},{"type":"paragraph","content":[{"text":"Sort & filter:","type":"text","marks":[{"type":"strong"}]}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Field","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Values","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Default","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Effect","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"sortable","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"true","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"false","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"true","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Click a column header to sort. Cycles asc → desc → off. Numeric columns sort by raw value.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"filter","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"true","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"false","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"false","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"When true, render a \"Filter by any value…\" search input above the table. Rows filter live on case-insensitive contains match across all cells.","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Example: ","type":"text"},{"text":"{\"type\": \"table\", \"filter\": true, \"columns\": [...], \"data\": [...]}","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Note Types","type":"text"}]},{"type":"paragraph","content":[{"text":"Notes support three severity levels via ","type":"text"},{"text":"noteType","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Type","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Border Color","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use For","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"default","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Red","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Important notices (default)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"warning","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yellow","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cautions, preliminary data","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"tip","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Green","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Best practices, pro tips","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Notes also support an optional ","type":"text"},{"text":"label","type":"text","marks":[{"type":"code_inline"}]},{"text":" for bold prefix text:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\"type\": \"note\", \"label\": \"Pro Tip:\", \"content\": \"Use keyboard shortcuts for faster navigation.\", \"noteType\": \"tip\"}","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Specialized Chart Examples","type":"text"}]},{"type":"paragraph","content":[{"text":"big_value","type":"text","marks":[{"type":"strong"}]},{"text":" - Hero metrics with large display:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\"type\": \"big_value\", \"value\": 1250000, \"label\": \"Revenue\", \"format\": \"currency0m\"}","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Optional ","type":"text"},{"text":"comparison","type":"text","marks":[{"type":"code_inline"}]},{"text":" object: ","type":"text"},{"text":"{\"value\": 10300, \"format\": \"currency\", \"label\": \"vs last month\"}","type":"text","marks":[{"type":"code_inline"}]},{"text":" shows change with arrow","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"dumbbell","type":"text","marks":[{"type":"strong"}]},{"text":" - Before/after comparisons with directional coloring:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"type\": \"dumbbell\",\n \"title\": \"ELO Changes\",\n \"category\": \"team\",\n \"start\": \"before\",\n \"end\": \"after\",\n \"startLabel\": \"Week 1\",\n \"endLabel\": \"Week 2\",\n \"higherIsBetter\": true,\n \"data\": [\n {\"team\": \"Chiefs\", \"before\": 1650, \"after\": 1720},\n {\"team\": \"Bills\", \"before\": 1600, \"after\": 1550}\n ]\n}","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Green = improvement, Red = decline, Grey = no change","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"higherIsBetter: false","type":"text","marks":[{"type":"code_inline"}]},{"text":" for rankings (lower = better)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Labels auto-abbreviate large numbers (7450 → \"7k\")","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"delta","type":"text","marks":[{"type":"strong"}]},{"text":" - Change metrics with directional coloring:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\"type\": \"delta\", \"value\": 0.15, \"label\": \"MoM Growth\", \"format\": \"pct0\"}","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Positive values show green with ▲, negative show red with ▼","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Optional ","type":"text"},{"text":"comparison","type":"text","marks":[{"type":"code_inline"}]},{"text":" object: ","type":"text"},{"text":"{\"value\": 0.05, \"label\": \"vs Target\"}","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"area","type":"text","marks":[{"type":"strong"}]},{"text":" - Filled line chart for cumulative/volume data:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"type\": \"area\",\n \"title\": \"Daily Active Users\",\n \"x\": \"date\",\n \"y\": \"users\",\n \"data\": [{\"date\": \"Mon\", \"users\": 1200}, {\"date\": \"Tue\", \"users\": 1450}]\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"combo","type":"text","marks":[{"type":"strong"}]},{"text":" - Bar + line with dual Y-axis:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"type\": \"combo\",\n \"title\": \"Revenue vs Growth Rate\",\n \"x\": \"quarter\",\n \"y\": [\"revenue\", \"growth_rate\"],\n \"data\": [\n {\"quarter\": \"Q1\", \"revenue\": 1000000, \"growth_rate\": 0.15},\n {\"quarter\": \"Q2\", \"revenue\": 1200000, \"growth_rate\": 0.20}\n ]\n}","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"First y-field renders as bars, second as line","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dual Y-axes with independent scales","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"heatmap","type":"text","marks":[{"type":"strong"}]},{"text":" - 2D matrix visualization:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"type\": \"heatmap\",\n \"title\": \"Activity by Hour\",\n \"xCategories\": [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\"],\n \"yCategories\": [\"9am\", \"12pm\", \"3pm\", \"6pm\"],\n \"format\": \"num0\",\n \"data\": [[0, 0, 85], [1, 0, 90], [2, 0, 72]]\n}","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"format","type":"text","marks":[{"type":"code_inline"}]},{"text":" option applies to cell labels (e.g., ","type":"text"},{"text":"num0k","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"currency0k","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pct","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"funnel","type":"text","marks":[{"type":"strong"}]},{"text":" - Conversion or elimination flows:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"type\": \"funnel\",\n \"title\": \"Sales Pipeline\",\n \"format\": \"num0\",\n \"data\": [\n {\"stage\": \"Leads\", \"value\": 1000},\n {\"stage\": \"Qualified\", \"value\": 600},\n {\"stage\": \"Proposal\", \"value\": 300},\n {\"stage\": \"Closed\", \"value\": 100}\n ]\n}","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"format","type":"text","marks":[{"type":"code_inline"}]},{"text":" option applies to labels/tooltips (e.g., ","type":"text"},{"text":"currency_auto","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pct","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"num0","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"waterfall","type":"text","marks":[{"type":"strong"}]},{"text":" - Cumulative change visualization:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"type\": \"waterfall\",\n \"title\": \"Revenue Bridge\",\n \"x\": \"item\",\n \"y\": \"value\",\n \"data\": [\n {\"item\": \"Start\", \"value\": 1000, \"isTotal\": true},\n {\"item\": \"Growth\", \"value\": 200},\n {\"item\": \"Churn\", \"value\": -50},\n {\"item\": \"End\", \"value\": 1150, \"isTotal\": true}\n ]\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"bubble","type":"text","marks":[{"type":"strong"}]},{"text":" - Scatter with size dimension. Supports ","type":"text"},{"text":"series","type":"text","marks":[{"type":"code_inline"}]},{"text":" for color grouping and ","type":"text"},{"text":"showLabels","type":"text","marks":[{"type":"code_inline"}]},{"text":" for persistent labels:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"type\": \"bubble\",\n \"title\": \"Market Analysis\",\n \"x\": \"growth\",\n \"y\": \"profit\",\n \"size\": \"revenue\",\n \"series\": \"region\",\n \"label\": \"company\",\n \"data\": [\n {\"growth\": 5, \"profit\": 20, \"revenue\": 100, \"region\": \"US\", \"company\": \"Acme\"},\n {\"growth\": 10, \"profit\": 15, \"revenue\": 200, \"region\": \"EU\", \"company\": \"Beta\"}\n ]\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"sankey","type":"text","marks":[{"type":"strong"}]},{"text":" - Flow diagrams showing relationships:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"type\": \"sankey\",\n \"title\": \"Traffic Sources\",\n \"data\": [\n {\"source\": \"Organic\", \"target\": \"Landing\", \"value\": 500},\n {\"source\": \"Paid\", \"target\": \"Landing\", \"value\": 300},\n {\"source\": \"Landing\", \"target\": \"Signup\", \"value\": 400}\n ]\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"mermaid","type":"text","marks":[{"type":"strong"}]},{"text":" - Diagrams from Mermaid syntax (flowcharts, sequence, state, class, ER). Use array for multi-line code:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"type\": \"mermaid\",\n \"title\": \"User Flow\",\n \"code\": [\n \"graph TD\",\n \" A[Start] --> B{Decision}\",\n \" B -->|Yes| C[Action]\",\n \" B -->|No| D[End]\"\n ]\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"mermaid (ASCII)","type":"text","marks":[{"type":"strong"}]},{"text":" - ASCII/Unicode text-based diagrams (set ","type":"text"},{"text":"ascii: true","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"type\": \"mermaid\",\n \"title\": \"Process Flow\",\n \"code\": [\"graph LR\", \" A[Input] --> B[Process] --> C[Output]\"],\n \"ascii\": true\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"Mermaid lint rules","type":"text","marks":[{"type":"strong"}]},{"text":" (errors that will fail validation):","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No ","type":"text"},{"text":"\u003cbr/>","type":"text","marks":[{"type":"code_inline"}]},{"text":" tags in labels (render as literal text, not line breaks)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No quoted labels like ","type":"text"},{"text":"A[\"text\"]","type":"text","marks":[{"type":"code_inline"}]},{"text":" in flowcharts (quotes appear in output)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Number Format Options","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Format","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Example","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use For","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"auto","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1.000m, 10.00k","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Smart auto-format (recommended)","type":"text","marks":[{"type":"strong"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"currency_auto","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"$1.000m, $10.00k","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Smart auto-format with $ prefix","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"currency0m","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"$1.2m","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Millions","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"currency0b","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"$1.2b","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Billions","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"currency0k","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"$125k","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Thousands","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"currency","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"$1,250,000","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Detailed amounts","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"num0m","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1.2m","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Millions","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"num0b","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1.2b","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Billions","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"num0k","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"125k","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Thousands","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"num0","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1,250,000","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Detailed counts","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pct","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"15.0%","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Percentage with decimal","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pct0","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"15%","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Percentage integer","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pct1","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"15.0%","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Percentage with 1 decimal","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"date","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"2026-02-24","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Date column — string values pass through; numeric epoch ms render as ","type":"text"},{"text":"YYYY-MM-DD","type":"text","marks":[{"type":"code_inline"}]},{"text":". Tables sort by epoch ms.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"duration","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1m7s","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Duration column — numeric seconds render as ","type":"text"},{"text":"1m7s","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"25m39s","type":"text","marks":[{"type":"code_inline"}]},{"text":"; strings pass through. Tables sort by total seconds.","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Percentage formats:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"pct","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pct0","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pct1","type":"text","marks":[{"type":"code_inline"}]},{"text":" always multiply by 100 for scalar components (","type":"text"},{"text":"big_value","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"delta","type":"text","marks":[{"type":"code_inline"}]},{"text":") — pass ","type":"text"},{"text":"0.15","type":"text","marks":[{"type":"code_inline"}]},{"text":" for ","type":"text"},{"text":"15%","type":"text","marks":[{"type":"code_inline"}]},{"text":". Charts and tables are ","type":"text"},{"text":"series-aware","type":"text","marks":[{"type":"strong"}]},{"text":": they inspect the column/series and skip the multiply when any value exceeds 1, so ","type":"text"},{"text":"[15, 22, 18]","type":"text","marks":[{"type":"code_inline"}]},{"text":" renders as ","type":"text"},{"text":"15.0%","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"22.0%","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"18.0%","type":"text","marks":[{"type":"code_inline"}]},{"text":" (not ","type":"text"},{"text":"1500%+","type":"text","marks":[{"type":"code_inline"}]},{"text":") while ","type":"text"},{"text":"[0.15, 0.22, 0.18]","type":"text","marks":[{"type":"code_inline"}]},{"text":" still renders as ","type":"text"},{"text":"15.0%","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"22.0%","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"18.0%","type":"text","marks":[{"type":"code_inline"}]},{"text":". Pass ","type":"text"},{"text":"[0.8, 1.2]","type":"text","marks":[{"type":"code_inline"}]},{"text":" for a growth-rate series and it'll render as ","type":"text"},{"text":"80%","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"120%","type":"text","marks":[{"type":"code_inline"}]},{"text":". If ","type":"text"},{"text":"big_value","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"delta","type":"text","marks":[{"type":"code_inline"}]},{"text":" get a pct value > 1, the linter warns \"did you mean value/100?\".","type":"text"}]},{"type":"paragraph","content":[{"text":"Smart formatting (","type":"text","marks":[{"type":"strong"}]},{"text":"auto","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":"/","type":"text","marks":[{"type":"strong"}]},{"text":"currency_auto","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":") is recommended.","type":"text","marks":[{"type":"strong"}]},{"text":" The ","type":"text"},{"text":"format","type":"text","marks":[{"type":"code_inline"}]},{"text":" option applies to both axis labels and data labels on bar charts. It automatically picks the right suffix (k, m, b) based on magnitude and always shows 4 significant digits. Negative values are wrapped in parentheses: ","type":"text"},{"text":"(1.000m)","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"When no format is specified, smart formatting is used by default.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Auto-Detected Axis Formatting","type":"text"}]},{"type":"paragraph","content":[{"text":"Chart axes automatically detect the appropriate format based on field names:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Field Pattern","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Auto Format","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Example","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"revenue, sales, price, cost, profit, amount","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"currency_auto","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"$1.250m","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pct, percent, rate, ratio","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pct","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"15.0%","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"All other numeric fields","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"auto","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1.250m","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Override with an explicit ","type":"text"},{"text":"format","type":"text","marks":[{"type":"code_inline"}]},{"text":" field in the chart spec.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Columnar Data Format","type":"text"}]},{"type":"paragraph","content":[{"text":"The chart generator auto-detects columnar query results. Instead of manually converting ","type":"text"},{"text":"columns","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"rows","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"data","type":"text","marks":[{"type":"code_inline"}]},{"text":", pass the result directly:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"type\": \"bar\",\n \"title\": \"Sales by Region\",\n \"x\": \"region\",\n \"y\": \"sales\",\n \"columns\": [\"region\", \"sales\"],\n \"rows\": [[\"North\", 45000], [\"South\", 32000], [\"East\", 28000]]\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"This is automatically converted internally. No manual JSON reconstruction needed.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Axis Bounds (yMin/yMax)","type":"text"}]},{"type":"paragraph","content":[{"text":"For line, area, bar, and combo charts, control y-axis range with ","type":"text"},{"text":"yMin","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"yMax","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"type\": \"line\",\n \"title\": \"Elo Rating Trend\",\n \"x\": \"date\",\n \"y\": \"elo\",\n \"yMin\": 1400,\n \"data\": [{\"date\": \"Oct\", \"elo\": 1511}, {\"date\": \"Jan\", \"elo\": 1636}]\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"yMin","type":"text","marks":[{"type":"code_inline"}]},{"text":" when:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Data doesn't start at 0 (ratings, stock prices, temperatures)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"You want to emphasize relative changes over absolute values","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"yMax","type":"text","marks":[{"type":"code_inline"}]},{"text":" when:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Labels are being cut off at the top of the chart","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"You need headroom above the highest data point","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Validation & Lint Rules","type":"text"}]},{"type":"paragraph","content":[{"text":"The CLI validates specs automatically using built-in lint rules. Use ","type":"text"},{"text":"--lint","type":"text","marks":[{"type":"code_inline"}]},{"text":" flag for validation-only mode:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"npx -y -q mviz --lint dashboard.md # Validate without generating HTML","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Lint Rules","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rule","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Severity","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Trigger","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"required-fields","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"warning","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Missing required fields like ","type":"text"},{"text":"x","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"y","type":"text","marks":[{"type":"code_inline"}]},{"text":", or ","type":"text"},{"text":"data","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"unknown-field","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"warning","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Field not recognized for the chart type","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"time-series-sorted","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"error","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Time series data not in chronological order","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"sankey-wrong-keys","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"error","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Using ","type":"text"},{"text":"from","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"to","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead of ","type":"text"},{"text":"source","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"target","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"big-value-string","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"error","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Passing ","type":"text"},{"text":"\"62.5%\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" string instead of ","type":"text"},{"text":"0.625","type":"text","marks":[{"type":"code_inline"}]},{"text":" number","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"duplicate-x-values","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"warning","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Duplicate values on x-axis","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"mermaid-no-br-tags","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"error","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003cbr/>","type":"text","marks":[{"type":"code_inline"}]},{"text":" tags in mermaid code (render as literal text)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"mermaid-no-quoted-labels","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"error","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Quoted labels like ","type":"text"},{"text":"A[\"text\"]","type":"text","marks":[{"type":"code_inline"}]},{"text":" in flowcharts","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Errors","type":"text","marks":[{"type":"strong"}]},{"text":" exit with code 1. ","type":"text"},{"text":"Warnings","type":"text","marks":[{"type":"strong"}]},{"text":" log to stderr but don't fail.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Common Fixes","type":"text"}]},{"type":"paragraph","content":[{"text":"Time series error:","type":"text","marks":[{"type":"strong"}]},{"text":" Sort your data by date before passing to the chart.","type":"text"}]},{"type":"paragraph","content":[{"text":"Sankey wrong keys:","type":"text","marks":[{"type":"strong"}]},{"text":" Use ","type":"text"},{"text":"source","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"target","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"value","type":"text","marks":[{"type":"code_inline"}]},{"text":" in your data:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\"source\": \"A\", \"target\": \"B\", \"value\": 100}","type":"text"}]},{"type":"paragraph","content":[{"text":"big_value string:","type":"text","marks":[{"type":"strong"}]},{"text":" Pass numeric value with format option:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\"type\": \"big_value\", \"value\": 0.625, \"format\": \"pct0\", \"label\": \"Rate\"}","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Troubleshooting","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Warning Messages","type":"text"}]},{"type":"paragraph","content":[{"text":"The generator outputs helpful warnings to stderr when issues are detected:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Warning","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cause","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Solution","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Invalid JSON in 'bar' block","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Malformed JSON syntax","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Check JSON syntax, ensure proper quoting","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Unknown component type 'bars'","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Typo in chart type","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use suggested type (e.g., ","type":"text"},{"text":"bar","type":"text","marks":[{"type":"code_inline"}]},{"text":" not ","type":"text"},{"text":"bars","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cannot resolve 'file=...'","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"File reference without base directory","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use file path argument or inline JSON","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Row exceeds 16 columns","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Too many components in one row","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reduce component widths or split into rows","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Warnings include context like content previews, similar type suggestions, and section/row info.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Labels Cut Off at Chart Edges","type":"text"}]},{"type":"paragraph","content":[{"text":"If data labels on bar, line, or area charts are being cut off at the top:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Find the maximum value in your data","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Set ","type":"text"},{"text":"yMax","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ~10-15% higher than that value","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Example:","type":"text","marks":[{"type":"strong"}]},{"text":" If max value is 200, set ","type":"text"},{"text":"\"yMax\": 220","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"type\": \"bar\",\n \"title\": \"Sales\",\n \"x\": \"month\",\n \"y\": \"sales\",\n \"yMax\": 250,\n \"data\": [{\"month\": \"Jan\", \"sales\": 180}, {\"month\": \"Feb\", \"sales\": 220}]\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"This provides headroom for the label text above the bars.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Data Generation Best Practice","type":"text"}]},{"type":"paragraph","content":[{"text":"Use SQL to generate data files","type":"text","marks":[{"type":"strong"}]},{"text":" instead of manually authoring JSON. This reduces errors and ensures data accuracy:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"sql"},"content":[{"text":"-- Generate chart data file\nCOPY (\n SELECT month, SUM(sales) as sales, SUM(revenue) as revenue\n FROM orders\n GROUP BY month\n ORDER BY month\n) TO 'data/monthly-sales.json' (FORMAT JSON, ARRAY true);","type":"text"}]},{"type":"paragraph","content":[{"text":"Then reference the generated file:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"```bar file=data/monthly-sales.json\n{\"title\": \"Monthly Sales\", \"x\": \"month\", \"y\": \"sales\"}\n```","type":"text"}]},{"type":"paragraph","content":[{"text":"This approach:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ensures data accuracy (no manual transcription errors)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keeps data in sync with source systems","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reduces token usage (SQL is more compact than JSON arrays)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Makes updates easy (re-run query to refresh)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"File References (JSON and CSV)","type":"text"}]},{"type":"paragraph","content":[{"text":"Reference external data files to save tokens and enable data/visualization separation:","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"JSON Files","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"```bar size=[8,6] file=data/sales.json\n```","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"CSV Files (DuckDB Workflow)","type":"text"}]},{"type":"paragraph","content":[{"text":"CSV files work great with DuckDB for data exploration:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Export query results to CSV\nduckdb -csv -c \"SELECT quarter, revenue FROM sales\" > data/quarterly.csv","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"```bar file=data/quarterly.csv\n{\"title\": \"Quarterly Revenue\", \"x\": \"quarter\", \"y\": \"revenue\"}\n```","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CSV provides data","type":"text","marks":[{"type":"strong"}]},{"text":", inline JSON provides chart options (title, x, y, format)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Auto-detection","type":"text","marks":[{"type":"strong"}]},{"text":": If no inline options, first column = x, second column = y","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Type conversion","type":"text","marks":[{"type":"strong"}]},{"text":": Numeric strings auto-convert to int/float","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Benefits of File References","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Approach","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Best For","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Inline JSON","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Small, static specs","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"JSON files","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reusable chart configs","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CSV files","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DuckDB workflows, frequently updated data","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Dashboard Markdown Format","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"---\ntheme: light\ntitle: My Dashboard\n---\n\n# Page Title\n\n## Section Name\n\n```big_value size=[4,2]\n{\"value\": 125000, \"label\": \"Revenue\", \"format\": \"currency0k\"}\n```\n```bar size=[12,6] file=data/sales.json\n```","type":"text"}]},{"type":"paragraph","content":[{"text":"Rules:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"# Title","type":"text","marks":[{"type":"code_inline"}]},{"text":" sets the page title (first occurrence only)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"## Section","type":"text","marks":[{"type":"code_inline"}]},{"text":" creates a new section with divider (border, spacing)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"### Header","type":"text","marks":[{"type":"code_inline"}]},{"text":" creates a soft header within the current section (no divider)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"---","type":"text","marks":[{"type":"code_inline"}]},{"text":" creates a section break (untitled, visual divider only)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"===","type":"text","marks":[{"type":"code_inline"}]},{"text":" creates a page break (forces new page when printing to PDF)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"size=[cols,rows]","type":"text","marks":[{"type":"code_inline"}]},{"text":" controls layout (16-column grid)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"size=auto","type":"text","marks":[{"type":"code_inline"}]},{"text":" auto-calculates size from data","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"file=path","type":"text","marks":[{"type":"code_inline"}]},{"text":" references external JSON","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Empty lines = new rows","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Theme Toggle","type":"text"}]},{"type":"paragraph","content":[{"text":"Dashboards include a theme toggle button (top right) that switches between light and dark modes. All charts dynamically update when the theme changes.","type":"text"}]},{"type":"paragraph","content":[{"text":"Set the default theme in frontmatter:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"---\ntitle: My Dashboard\ntheme: dark\norientation: landscape\nprint: true\n---","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Option","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"title","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Dashboard title displayed at top","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"theme","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"light","type":"text","marks":[{"type":"code_inline"}]},{"text":" (default) or ","type":"text"},{"text":"dark","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"orientation","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"portrait","type":"text","marks":[{"type":"code_inline"}]},{"text":" (default) or ","type":"text"},{"text":"landscape","type":"text","marks":[{"type":"code_inline"}]},{"text":" for print layout","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"print","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"When ","type":"text"},{"text":"true","type":"text","marks":[{"type":"code_inline"}]},{"text":", requires explicit ","type":"text"},{"text":"size=[cols,rows]","type":"text","marks":[{"type":"code_inline"}]},{"text":" on all components","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"continuous","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"When ","type":"text"},{"text":"true","type":"text","marks":[{"type":"code_inline"}]},{"text":", removes section breaks between ","type":"text"},{"text":"#","type":"text","marks":[{"type":"code_inline"}]},{"text":" headers for flowing layout","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"embed","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"When ","type":"text"},{"text":"true","type":"text","marks":[{"type":"code_inline"}]},{"text":", strips page chrome (red accent bar, title row, theme toggle) so the output can be tucked inside another host's frame (iframe tile, dashboard cell). Also available as the ","type":"text"},{"text":"--embed","type":"text","marks":[{"type":"code_inline"}]},{"text":" CLI flag.","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Page capacity:","type":"text","marks":[{"type":"strong"}]},{"text":" Portrait fits 30 row units, landscape fits 22 row units (Letter paper, 0.5\" margins).","type":"text"}]},{"type":"paragraph","content":[{"text":"The theme toggle affects all charts globally - individual chart ","type":"text"},{"text":"theme","type":"text","marks":[{"type":"code_inline"}]},{"text":" settings are ignored in favor of the global toggle.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Renderer: ","type":"text"},{"text":"visualize:show_widget","type":"text","marks":[{"type":"code_inline"}]},{"text":" (inline)","type":"text"}]},{"type":"paragraph","content":[{"text":"The show_widget renderer lives inside Claude. Claude reads the mviz spec, translates it to a ","type":"text"},{"text":"widget_code","type":"text","marks":[{"type":"code_inline"}]},{"text":" HTML fragment using claude.ai's design system (Chart.js + ","type":"text"},{"text":"var(--color-*)","type":"text","marks":[{"type":"code_inline"}]},{"text":" tokens), and passes it to ","type":"text"},{"text":"visualize:show_widget","type":"text","marks":[{"type":"code_inline"}]},{"text":". The widget appears inline in the conversation.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Workflow","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Compose the mviz markdown spec","type":"text","marks":[{"type":"strong"}]},{"text":" and emit it visibly in your response, in a fenced code block. The spec is the canonical artifact — even when the user only wants an inline render, write the spec down so it's available for later (copy / reuse / pipe-to-CLI). Then render below it.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"```mviz\n---\ntitle: Q3 sales\n---\n```big_value\n{\"value\": 1250000, \"label\": \"Q3 Revenue\", \"format\": \"currency_auto\"}","type":"text"}]},{"type":"paragraph","content":[{"text":"...","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""}}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"State your renderer inference","type":"text","marks":[{"type":"strong"}]},{"text":", one short sentence. Example: ","type":"text"},{"text":"\"Rendering inline since you asked to see it in chat — say the word if you want the file too.\"","type":"text","marks":[{"type":"em"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Translate the spec to ","type":"text","marks":[{"type":"strong"}]},{"text":"widget_code","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" following the mapping tables below. Use claude.ai's design tokens (","type":"text"},{"text":"var(--color-*)","type":"text","marks":[{"type":"code_inline"}]},{"text":"), Chart.js for charts, metric cards for KPIs.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Call ","type":"text","marks":[{"type":"strong"}]},{"text":"visualize:show_widget","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" with the ","type":"text"},{"text":"widget_code","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Write the surrounding explanation as normal response text","type":"text","marks":[{"type":"strong"}]},{"text":", outside the tool call. The widget is the visual; your prose carries the meaning.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"When this renderer is the right pick","type":"text"}]},{"type":"paragraph","content":[{"text":"Trigger inline rendering when ","type":"text"},{"text":"all","type":"text","marks":[{"type":"strong"}]},{"text":" of these hold:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The user is in claude.ai web/desktop (not Claude Code, not the API).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"visualize:show_widget","type":"text","marks":[{"type":"code_inline"}]},{"text":" is in your available tools.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The visualization is composed of mviz component types that have chat-native equivalents (see mapping below).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The visualization is small enough to emit naturally as ","type":"text"},{"text":"widget_code","type":"text","marks":[{"type":"code_inline"}]},{"text":" — model emit time dominates latency, so 6–8 components is comfortable, 15+ starts to feel slow.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If any of those fails — mviz-only chart types like sankey/heatmap, very large dashboards, no ","type":"text"},{"text":"show_widget","type":"text","marks":[{"type":"code_inline"}]},{"text":" — render through the mviz CLI instead and offer the file as a follow-up.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"What the show_widget renderer is NOT","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Not the mviz CLI.","type":"text","marks":[{"type":"strong"}]},{"text":" Don't invoke ","type":"text"},{"text":"npx mviz","type":"text","marks":[{"type":"code_inline"}]},{"text":" to produce HTML for ","type":"text"},{"text":"show_widget","type":"text","marks":[{"type":"code_inline"}]},{"text":". The CLI's mdsinabox-styled output doesn't match the chat host, and the subprocess + larger emit costs latency. The CLI is a ","type":"text"},{"text":"peer renderer","type":"text","marks":[{"type":"em"}]},{"text":" used for standalone files.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Not a paste-mviz-output-into-widget_code path.","type":"text","marks":[{"type":"strong"}]},{"text":" mviz CLI emits a complete document with its own design system; that wouldn't render correctly inside the widget sandbox even if ","type":"text"},{"text":"show_widget","type":"text","marks":[{"type":"code_inline"}]},{"text":" accepted documents (it doesn't — it requires fragments).","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"The translation: mviz spec → claude.ai-native HTML","type":"text"}]},{"type":"paragraph","content":[{"text":"mviz markdown describes structure. Read the spec, then write a ","type":"text"},{"text":"widget_code","type":"text","marks":[{"type":"code_inline"}]},{"text":" HTML fragment using claude.ai's design system.","type":"text"}]},{"type":"paragraph","content":[{"text":"Component type mapping","type":"text","marks":[{"type":"strong"}]}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"mviz type","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"claude.ai-native rendering","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"big_value","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Metric card: surface card with ","type":"text"},{"text":"--color-text-secondary","type":"text","marks":[{"type":"code_inline"}]},{"text":" 13px label above, 24px / weight 500 number below","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"delta","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Metric card variant with colored sign (","type":"text"},{"text":"--color-success","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"--color-danger","type":"text","marks":[{"type":"code_inline"}]},{"text":") + arrow glyph","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"bar","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Chart.js ","type":"text"},{"text":"type: 'bar'","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"line","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Chart.js ","type":"text"},{"text":"type: 'line'","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"area","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Chart.js ","type":"text"},{"text":"type: 'line'","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"fill: true","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pie","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Chart.js ","type":"text"},{"text":"type: 'pie'","type":"text","marks":[{"type":"code_inline"}]},{"text":". If the mviz spec sets ","type":"text"},{"text":"donut: true","type":"text","marks":[{"type":"code_inline"}]},{"text":", render as Chart.js ","type":"text"},{"text":"type: 'doughnut'","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead (mviz exposes donut as a ","type":"text"},{"text":"flag","type":"text","marks":[{"type":"em"}]},{"text":" on the pie type, not a separate component).","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scatter","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Chart.js ","type":"text"},{"text":"type: 'scatter'","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"bubble","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Chart.js ","type":"text"},{"text":"type: 'bubble'","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"histogram","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Chart.js ","type":"text"},{"text":"type: 'bar'","type":"text","marks":[{"type":"code_inline"}]},{"text":" over pre-bucketed data","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"sparkline","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Inline ","type":"text"},{"text":"\u003csvg>","type":"text","marks":[{"type":"code_inline"}]},{"text":" (3–5 line-width strokes) — too small to justify Chart.js","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"table","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"HTML ","type":"text"},{"text":"\u003ctable>","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"border-collapse: collapse","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"0.5px solid var(--color-border-tertiary)","type":"text","marks":[{"type":"code_inline"}]},{"text":", header row in ","type":"text"},{"text":"--color-background-secondary","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"note","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"alert","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Bordered callout: ","type":"text"},{"text":"border-left: 3px solid var(--color-{info,warning,success,danger})","type":"text","marks":[{"type":"code_inline"}]},{"text":", padding 8px 12px, label + body text","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"text","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"textarea","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Render the markdown content as prose ","type":"text"},{"text":"in your response text outside the tool call","type":"text","marks":[{"type":"strong"}]},{"text":" — not inside the widget. The ","type":"text"},{"text":"visualize:read_me","type":"text","marks":[{"type":"code_inline"}]},{"text":" design guide is explicit: text goes in the response, visuals go in the tool.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"empty_space","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"An empty grid cell — produce ","type":"text"},{"text":"\u003cdiv>\u003c/div>","type":"text","marks":[{"type":"code_inline"}]},{"text":" with the right ","type":"text"},{"text":"grid-column: span N","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"boxplot","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"waterfall","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"xmr","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"sankey","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"funnel","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"heatmap","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"calendar","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"combo","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"dumbbell","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"mermaid","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No clean Chart.js equivalent. If the visualization centers on one of these, fall back to a standalone ","type":"text"},{"text":"mviz","type":"text","marks":[{"type":"code_inline"}]},{"text":" export and don't render inline.","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Layout mapping","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"mviz uses a 16-col grid with explicit ","type":"text"},{"text":"size=[cols, rows]","type":"text","marks":[{"type":"code_inline"}]},{"text":" directives. Inside a 680px-wide ","type":"text"},{"text":"show_widget","type":"text","marks":[{"type":"code_inline"}]},{"text":" container, that translates to:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"css"},"content":[{"text":".mviz-grid {\n display: grid;\n grid-template-columns: repeat(16, 1fr);\n gap: 8px;\n}\n.mviz-grid > .item-12 { grid-column: span 12; }\n.mviz-grid > .item-8 { grid-column: span 8; }\n.mviz-grid > .item-4 { grid-column: span 4; }","type":"text"}]},{"type":"paragraph","content":[{"text":"For simple 2–3 component widgets you can also use claude.ai's recommended responsive pattern:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"css"},"content":[{"text":"display: grid;\ngrid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\ngap: 12px;","type":"text"}]},{"type":"paragraph","content":[{"text":"This auto-fits to the host's chosen width, which is the right choice when the exact column count is not load-bearing.","type":"text"}]},{"type":"paragraph","content":[{"text":"Format hint mapping","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"mviz format strings → JS formatters used inside Chart.js label/tooltip callbacks and metric-card text:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"mviz ","type":"text"},{"text":"fmt","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"JS expression (assume ","type":"text"},{"text":"v","type":"text","marks":[{"type":"code_inline"}]},{"text":" is the number)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"currency_auto","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"auto","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use the helper below — the smart-format contract has magnitude tiers that don't fit on one line","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"currency","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"'

mviz v1.7.0 mviz Generate clean, data-focused charts and dashboards from compact JSON specs or markdown. Maximizes data-ink ratio with minimal chartjunk, gridlines, and decorative elements. Uses a 16-column grid layout system. Setup No installation required. Use which auto-downloads from npm. The flag reduces npm output while still showing lint errors. For faster repeated use, install globally: What This Skill Does mviz is two things: 1. A spec language — markdown with fenced code blocks describing components (charts, KPIs, tables, notes, etc.). One markdown file (or a single JSON spec) descr…

+ v.toLocaleString('en-US')","type":"text","marks":[{"type":"code_inline"}]},{"text":" (e.g. ","type":"text"},{"text":"1250000","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"$1,250,000","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"currency0k","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"'

mviz v1.7.0 mviz Generate clean, data-focused charts and dashboards from compact JSON specs or markdown. Maximizes data-ink ratio with minimal chartjunk, gridlines, and decorative elements. Uses a 16-column grid layout system. Setup No installation required. Use which auto-downloads from npm. The flag reduces npm output while still showing lint errors. For faster repeated use, install globally: What This Skill Does mviz is two things: 1. A spec language — markdown with fenced code blocks describing components (charts, KPIs, tables, notes, etc.). One markdown file (or a single JSON spec) descr…

+ Math.round(v/1000) + 'k'","type":"text","marks":[{"type":"code_inline"}]},{"text":" (e.g. ","type":"text"},{"text":"125000","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"$125k","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"currency0m","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"'

mviz v1.7.0 mviz Generate clean, data-focused charts and dashboards from compact JSON specs or markdown. Maximizes data-ink ratio with minimal chartjunk, gridlines, and decorative elements. Uses a 16-column grid layout system. Setup No installation required. Use which auto-downloads from npm. The flag reduces npm output while still showing lint errors. For faster repeated use, install globally: What This Skill Does mviz is two things: 1. A spec language — markdown with fenced code blocks describing components (charts, KPIs, tables, notes, etc.). One markdown file (or a single JSON spec) descr…

+ (v/1e6).toFixed(1) + 'm'","type":"text","marks":[{"type":"code_inline"}]},{"text":" (e.g. ","type":"text"},{"text":"1250000","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"$1.2m","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pct","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pct1","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"(v * 100).toFixed(1) + '%'","type":"text","marks":[{"type":"code_inline"}]},{"text":" — mviz multiplies pct by 100, so pass the decimal (","type":"text"},{"text":"0.15","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"15.0%","type":"text","marks":[{"type":"code_inline"}]},{"text":"). For chart series mviz auto-detects whether values are already in pct units; mirror that if the data is ambiguous.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pct0","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Math.round(v * 100) + '%'","type":"text","marks":[{"type":"code_inline"}]},{"text":" (e.g. ","type":"text"},{"text":"0.15","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"15%","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"num0","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"v.toLocaleString('en-US')","type":"text","marks":[{"type":"code_inline"}]},{"text":" (e.g. ","type":"text"},{"text":"1250","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"1,250","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"num1","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"v.toFixed(1)","type":"text","marks":[{"type":"code_inline"}]},{"text":" (e.g. ","type":"text"},{"text":"1.234","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"1.2","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"num0k","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Math.round(v/1000) + 'k'","type":"text","marks":[{"type":"code_inline"}]},{"text":" (e.g. ","type":"text"},{"text":"1250","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"1k","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"The ","type":"text"},{"text":"currency_auto","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"auto","type":"text","marks":[{"type":"code_inline"}]},{"text":" helper, mirroring mviz's ","type":"text"},{"text":"smartFormatNumber","type":"text","marks":[{"type":"code_inline"}]},{"text":". ~4 significant digits, magnitude tier with suffix, negatives in parentheses — match these exact thresholds so the inline render and the standalone report show identical numbers:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"js"},"content":[{"text":"function fmtAuto(v, sym = '') {\n const neg = v \u003c 0, abs = Math.abs(v);\n let s;\n if (abs >= 1e9) {\n const x = abs / 1e9;\n s = sym + (x >= 100 ? x.toFixed(1) : x >= 10 ? x.toFixed(2) : x.toFixed(3)) + 'b';\n } else if (abs >= 1e6) {\n const x = abs / 1e6;\n s = sym + (x >= 100 ? x.toFixed(1) : x >= 10 ? x.toFixed(2) : x.toFixed(3)) + 'm';\n } else if (abs >= 1e4) {\n const x = abs / 1e3;\n s = sym + (x >= 100 ? x.toFixed(1) : x.toFixed(2)) + 'k';\n } else if (abs >= 1e3) {\n s = sym + abs.toLocaleString('en-US', { maximumFractionDigits: 0 });\n } else {\n s = sym + (abs === Math.floor(abs) ? abs : abs.toFixed(2));\n }\n return neg ? '(' + s + ')' : s;\n}\n// currency_auto: fmtAuto(v, '

mviz v1.7.0 mviz Generate clean, data-focused charts and dashboards from compact JSON specs or markdown. Maximizes data-ink ratio with minimal chartjunk, gridlines, and decorative elements. Uses a 16-column grid layout system. Setup No installation required. Use which auto-downloads from npm. The flag reduces npm output while still showing lint errors. For faster repeated use, install globally: What This Skill Does mviz is two things: 1. A spec language — markdown with fenced code blocks describing components (charts, KPIs, tables, notes, etc.). One markdown file (or a single JSON spec) descr…

) → 1_250_000 → \"$1.250m\", 12_500 → \"$12.50k\", 1_250 → \"$1,250\"\n// auto: fmtAuto(v) → 1_250_000 → \"1.250m\", 12_500 → \"12.50k\", 1_250 → \"1,250\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Always ","type":"text"},{"text":"Math.round","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":".toFixed(n)","type":"text","marks":[{"type":"code_inline"}]},{"text":" displayed numbers — ","type":"text"},{"text":"0.1 + 0.2","type":"text","marks":[{"type":"code_inline"}]},{"text":" is ","type":"text"},{"text":"0.30000000000000004","type":"text","marks":[{"type":"code_inline"}]},{"text":" and that artifact will leak into the widget if you don't.","type":"text"}]},{"type":"paragraph","content":[{"text":"Color mapping","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"Chart.js renders to ","type":"text"},{"text":"\u003ccanvas>","type":"text","marks":[{"type":"code_inline"}]},{"text":" and cannot resolve CSS variables. Use hardcoded hex from claude.ai's color ramps (the ","type":"text"},{"text":"c-{ramp}","type":"text","marks":[{"type":"code_inline"}]},{"text":" stops listed in the ","type":"text"},{"text":"visualize:read_me","type":"text","marks":[{"type":"code_inline"}]},{"text":" design module). A safe default palette for mviz series:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Primary (mviz blue ","type":"text"},{"text":"#0777b3","type":"text","marks":[{"type":"code_inline"}]},{"text":") → ","type":"text"},{"text":"c-blue","type":"text","marks":[{"type":"code_inline"}]},{"text":" 600 ","type":"text"},{"text":"#185FA5","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Secondary (mviz orange ","type":"text"},{"text":"#bd4e35","type":"text","marks":[{"type":"code_inline"}]},{"text":") → ","type":"text"},{"text":"c-coral","type":"text","marks":[{"type":"code_inline"}]},{"text":" 600 ","type":"text"},{"text":"#993C1D","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Positive (mviz green ","type":"text"},{"text":"#2d7a00","type":"text","marks":[{"type":"code_inline"}]},{"text":") → ","type":"text"},{"text":"c-green","type":"text","marks":[{"type":"code_inline"}]},{"text":" 600 ","type":"text"},{"text":"#3B6D11","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Warning (mviz amber ","type":"text"},{"text":"#e18727","type":"text","marks":[{"type":"code_inline"}]},{"text":") → ","type":"text"},{"text":"c-amber","type":"text","marks":[{"type":"code_inline"}]},{"text":" 600 ","type":"text"},{"text":"#854F0B","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Error (mviz red ","type":"text"},{"text":"#bc1200","type":"text","marks":[{"type":"code_inline"}]},{"text":") → ","type":"text"},{"text":"c-red","type":"text","marks":[{"type":"code_inline"}]},{"text":" 600 ","type":"text"},{"text":"#A32D2D","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Tertiary / neutral → ","type":"text"},{"text":"c-gray","type":"text","marks":[{"type":"code_inline"}]},{"text":" 600 ","type":"text"},{"text":"#5F5E5A","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"For HTML elements (cards, table borders, text), use ","type":"text"},{"text":"var(--color-text-primary)","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"var(--color-background-secondary)","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"var(--color-border-tertiary)","type":"text","marks":[{"type":"code_inline"}]},{"text":" and friends — they auto-adapt to dark mode.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Chart.js setup (canonical patterns from ","type":"text"},{"text":"visualize:read_me","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"html"},"content":[{"text":"\u003cscript src=\"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js\">\u003c/script>","type":"text"}]},{"type":"paragraph","content":[{"text":"Wrap every canvas in a sized div:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"html"},"content":[{"text":"\u003cdiv style=\"position: relative; width: 100%; height: 240px;\">\n \u003ccanvas id=\"c1\" role=\"img\" aria-label=\"Sales by region — NA $540k, EU $380k, APAC $220k, LATAM $110k\">\n Sales by region (bar chart). NA 540, EU 380, APAC 220, LATAM 110.\n \u003c/canvas>\n\u003c/div>","type":"text"}]},{"type":"paragraph","content":[{"text":"Constraints to honor:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"responsive: true, maintainAspectRatio: false","type":"text","marks":[{"type":"code_inline"}]},{"text":" — height goes on the wrapper div only, never on the canvas itself.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Horizontal bar charts: wrapper height ≥ ","type":"text"},{"text":"(bars * 40) + 80","type":"text","marks":[{"type":"code_inline"}]},{"text":" px.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"≤12 categories where every label must be visible: ","type":"text"},{"text":"scales.x.ticks: { autoSkip: false, maxRotation: 45 }","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Bubble/scatter: pad ","type":"text"},{"text":"scales.{x,y}.{min,max}","type":"text","marks":[{"type":"code_inline"}]},{"text":" ~10 % beyond data range so radii don't clip.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Negative currency: ","type":"text"},{"text":"-$5M","type":"text","marks":[{"type":"code_inline"}]},{"text":" not ","type":"text"},{"text":"$-5M","type":"text","marks":[{"type":"code_inline"}]},{"text":" — sign before symbol. Use a formatter ","type":"text"},{"text":"(v) => (v \u003c 0 ? '-' : '') + '

mviz v1.7.0 mviz Generate clean, data-focused charts and dashboards from compact JSON specs or markdown. Maximizes data-ink ratio with minimal chartjunk, gridlines, and decorative elements. Uses a 16-column grid layout system. Setup No installation required. Use which auto-downloads from npm. The flag reduces npm output while still showing lint errors. For faster repeated use, install globally: What This Skill Does mviz is two things: 1. A spec language — markdown with fenced code blocks describing components (charts, KPIs, tables, notes, etc.). One markdown file (or a single JSON spec) descr…

+ Math.abs(v) + 'M'","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Default Chart.js legends use round dots and no values — disable them and emit your own legend as inline HTML (small color squares, tight spacing, include the value/percentage when categorical):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"js"},"content":[{"text":"plugins: { legend: { display: false } }","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"html"},"content":[{"text":"\u003cdiv style=\"display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 8px;\n font-size: 12px; color: var(--color-text-secondary);\">\n \u003cspan style=\"display: flex; align-items: center; gap: 4px;\">\n \u003cspan style=\"width: 10px; height: 10px; border-radius: 2px; background: #185FA5;\">\u003c/span>NA $540k\n \u003c/span>\n \u003c!-- repeat -->\n\u003c/div>","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Metric card (for ","type":"text"},{"text":"big_value","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"delta","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"html"},"content":[{"text":"\u003cdiv style=\"background: var(--color-background-secondary); border-radius: var(--border-radius-md);\n padding: 1rem;\">\n \u003cdiv style=\"font-size: 13px; color: var(--color-text-secondary);\">Q3 Revenue\u003c/div>\n \u003cdiv style=\"font-size: 24px; font-weight: 500; color: var(--color-text-primary);\n line-height: 1; margin-top: 4px;\">$1.250m\u003c/div>\n\u003c/div>","type":"text"}]},{"type":"paragraph","content":[{"text":"Use in grids of 2–4 with ","type":"text"},{"text":"gap: 12px","type":"text","marks":[{"type":"code_inline"}]},{"text":". For ","type":"text"},{"text":"delta","type":"text","marks":[{"type":"code_inline"}]},{"text":", add a small directional indicator with ","type":"text"},{"text":"color: var(--color-success)","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"var(--color-danger)","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Worked example","type":"text"}]},{"type":"paragraph","content":[{"text":"Spec (mviz markdown):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"---\ntitle: Q3 sales\n---\n\n```big_value size=[4,2]\n{\"value\": 1250000, \"label\": \"Q3 Revenue\", \"format\": \"currency_auto\"}\n```\n```big_value size=[4,2]\n{\"value\": 0.184, \"label\": \"Growth\", \"format\": \"pct1\"}\n```\n\n```bar size=[16,5]\n{\"title\": \"Sales by region\", \"x\": \"region\", \"y\": \"sales\", \"format\": \"currency0k\",\n \"data\": [{\"region\":\"NA\",\"sales\":540000},{\"region\":\"EU\",\"sales\":380000},\n {\"region\":\"APAC\",\"sales\":220000},{\"region\":\"LATAM\",\"sales\":110000}]}\n```","type":"text"}]},{"type":"paragraph","content":[{"text":"Inline render (the ","type":"text"},{"text":"widget_code","type":"text","marks":[{"type":"code_inline"}]},{"text":" you pass to ","type":"text"},{"text":"visualize:show_widget","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"html"},"content":[{"text":"\u003ch2 class=\"sr-only\">Q3 sales overview: $1.250m revenue, 18.4% growth, regional breakdown.\u003c/h2>\n\u003cdiv style=\"display: grid; grid-template-columns: repeat(16, 1fr); gap: 12px;\">\n \u003cdiv style=\"grid-column: span 4; background: var(--color-background-secondary);\n border-radius: var(--border-radius-md); padding: 1rem;\">\n \u003cdiv style=\"font-size: 13px; color: var(--color-text-secondary);\">Q3 Revenue\u003c/div>\n \u003cdiv style=\"font-size: 24px; font-weight: 500; margin-top: 4px;\">$1.250m\u003c/div>\n \u003c/div>\n \u003cdiv style=\"grid-column: span 4; background: var(--color-background-secondary);\n border-radius: var(--border-radius-md); padding: 1rem;\">\n \u003cdiv style=\"font-size: 13px; color: var(--color-text-secondary);\">Growth\u003c/div>\n \u003cdiv style=\"font-size: 24px; font-weight: 500; margin-top: 4px;\n color: var(--color-success);\">+18.4%\u003c/div>\n \u003c/div>\n \u003cdiv style=\"grid-column: span 16;\">\n \u003cdiv style=\"font-size: 14px; font-weight: 500; margin-bottom: 8px;\">Sales by region\u003c/div>\n \u003cdiv style=\"position: relative; width: 100%; height: 240px;\">\n \u003ccanvas id=\"mviz-bar-1\" role=\"img\"\n aria-label=\"Sales by region — NA $540k, EU $380k, APAC $220k, LATAM $110k\">\n NA 540, EU 380, APAC 220, LATAM 110.\n \u003c/canvas>\n \u003c/div>\n \u003c/div>\n\u003c/div>\n\u003cscript src=\"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js\">\u003c/script>\n\u003cscript>\n new Chart(document.getElementById('mviz-bar-1'), {\n type: 'bar',\n data: {\n labels: ['NA', 'EU', 'APAC', 'LATAM'],\n datasets: [{ label: 'Sales', data: [540000, 380000, 220000, 110000], backgroundColor: '#185FA5' }],\n },\n options: {\n responsive: true, maintainAspectRatio: false,\n plugins: { legend: { display: false } },\n scales: {\n y: { ticks: { callback: (v) => '

mviz v1.7.0 mviz Generate clean, data-focused charts and dashboards from compact JSON specs or markdown. Maximizes data-ink ratio with minimal chartjunk, gridlines, and decorative elements. Uses a 16-column grid layout system. Setup No installation required. Use which auto-downloads from npm. The flag reduces npm output while still showing lint errors. For faster repeated use, install globally: What This Skill Does mviz is two things: 1. A spec language — markdown with fenced code blocks describing components (charts, KPIs, tables, notes, etc.). One markdown file (or a single JSON spec) descr…

+ Math.round(v/1000) + 'k' } },\n x: { ticks: { autoSkip: false, maxRotation: 45 } },\n },\n },\n });\n\u003c/script>","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Constraints + things you must not do","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not invoke the ","type":"text","marks":[{"type":"strong"}]},{"text":"mviz","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" CLI for ","type":"text","marks":[{"type":"strong"}]},{"text":"show_widget","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" output.","type":"text","marks":[{"type":"strong"}]},{"text":" This path renders without it. The CLI is only for standalone HTML reports.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not paste mviz's own HTML into ","type":"text","marks":[{"type":"strong"}]},{"text":"show_widget","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":".","type":"text","marks":[{"type":"strong"}]},{"text":" mviz emits its own styled fragments — they will not match the chat host and the bytes are wasted.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No ","type":"text"},{"text":"\u003c!DOCTYPE>","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"\u003chtml>","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"\u003chead>","type":"text","marks":[{"type":"code_inline"}]},{"text":", or ","type":"text"},{"text":"\u003cbody>","type":"text","marks":[{"type":"code_inline"}]},{"text":" in the widget — ","type":"text"},{"text":"show_widget","type":"text","marks":[{"type":"code_inline"}]},{"text":" rejects documents.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No ","type":"text"},{"text":"position: fixed","type":"text","marks":[{"type":"code_inline"}]},{"text":". It collapses the iframe's auto-sized viewport.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Only load from the allowlist: ","type":"text"},{"text":"cdnjs.cloudflare.com","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"esm.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"cdn.jsdelivr.net","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"unpkg.com","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"fonts.googleapis.com","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"fonts.gstatic.com","type":"text","marks":[{"type":"code_inline"}]},{"text":". Chart.js from ","type":"text"},{"text":"cdnjs.cloudflare.com","type":"text","marks":[{"type":"code_inline"}]},{"text":" is the canonical source.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Tables with more than ~3 columns or any prose-y content: render as markdown in your response text instead of inside the widget. The ","type":"text"},{"text":"visualize:read_me","type":"text","marks":[{"type":"code_inline"}]},{"text":" design guide is explicit about this.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Falling back to the CLI renderer","type":"text"}]},{"type":"paragraph","content":[{"text":"When the inline renderer isn't a fit, the spec is still useful — pipe it through the CLI:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Chart type with no Chart.js equivalent","type":"text","marks":[{"type":"strong"}]},{"text":" (sankey, heatmap, calendar, etc.) — render via the CLI, offer the file. Mention that the mviz design system kicks in for the standalone output.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"visualize:show_widget","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" is not available","type":"text","marks":[{"type":"strong"}]},{"text":" — render via the CLI and present as an artifact.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User is in Claude Code","type":"text","marks":[{"type":"strong"}]},{"text":" — render via the CLI, write the file path, they'll open it in a browser. No inline widget surface in the TUI.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Visualization exceeds the inline size budget","type":"text","marks":[{"type":"strong"}]},{"text":" (15+ components, dense tables with sparklines, multi-section reports) — render via the CLI. Offer inline as a follow-up if they want a smaller summary view.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Single tiny visual","type":"text","marks":[{"type":"strong"}]},{"text":" (one sparkline, one 3-cell metric) — consider hand-rolling SVG directly via ","type":"text"},{"text":"show_widget","type":"text","marks":[{"type":"code_inline"}]},{"text":" without going through the mviz spec at all. mviz earns its keep when composing multiple components; for a one-off icon it's overkill.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Custom Themes","type":"text"}]},{"type":"paragraph","content":[{"text":"Load custom brand colors and fonts from a YAML file:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"npx -y -q mviz --theme my_theme.yaml dashboard.md -o dashboard.html","type":"text"}]},{"type":"paragraph","content":[{"text":"Example theme file:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"name: brand-colors\nextends: light\n\ncolors:\n primary: \"#1a73e8\"\n secondary: \"#ea4335\"\n\npalette:\n - \"#1a73e8\"\n - \"#ea4335\"\n - \"#fbbc04\"\n\nfonts:\n family: \"'Roboto', sans-serif\"\n import: \"https://fonts.googleapis.com/css2?family=Roboto&display=swap\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Custom themes merge with defaults - only specify what you want to override.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Print and PDF Support","type":"text"}]},{"type":"paragraph","content":[{"text":"Charts are optimized for printing to PDF:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"High-Quality Rendering","type":"text","marks":[{"type":"strong"}]},{"text":": Uses SVG renderer for crisp vector graphics at any zoom level","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No Page Breaks","type":"text","marks":[{"type":"strong"}]},{"text":": CSS prevents charts and tables from being split across pages","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"All Labels Visible","type":"text","marks":[{"type":"strong"}]},{"text":": Category labels always shown with 45° rotation to fit","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"When printing dashboards to PDF, all content stays intact without being cut off mid-chart.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"JSON Formatting for Editability","type":"text"}]},{"type":"paragraph","content":[{"text":"Use formatted (multi-line) JSON","type":"text","marks":[{"type":"strong"}]},{"text":" when data may need editing. This enables smaller, more precise edits:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"```bar size=[8,5]\n{\n \"title\": \"Monthly Sales\",\n \"x\": \"month\",\n \"y\": \"sales\",\n \"data\": [\n {\"month\": \"Jan\", \"sales\": 120},\n {\"month\": \"Feb\", \"sales\": 150},\n {\"month\": \"Mar\", \"sales\": 180}\n ]\n}\n```","type":"text"}]},{"type":"paragraph","content":[{"text":"Benefits:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Each data point on its own line enables targeted edits","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Changing one value: ~30 chars vs ~200+ chars with compact JSON","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Easier to review diffs in version control","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"When to use compact JSON:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Very small specs (\u003c 100 chars)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Data that won't change","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Single-line values like ","type":"text"},{"text":"{\"value\": 1250000, \"label\": \"Revenue\"}","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"JSON Schema","type":"text"}]},{"type":"paragraph","content":[{"text":"mviz specs can be validated using the JSON Schema at:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"https://raw.githubusercontent.com/matsonj/mviz/main/schema/mviz.schema.json","type":"text"}]},{"type":"paragraph","content":[{"text":"Add ","type":"text"},{"text":"$schema","type":"text","marks":[{"type":"code_inline"}]},{"text":" to enable editor autocomplete and validation:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"$schema\": \"https://raw.githubusercontent.com/matsonj/mviz/main/schema/mviz.schema.json\",\n \"type\": \"bar\",\n \"title\": \"Sales\",\n ...\n}","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Color Palette (mdsinabox theme)","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Color","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Hex","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Primary Blue","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#0777b3","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Primary series","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Secondary Orange","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#bd4e35","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Secondary series, accent","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Info Blue","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#638CAD","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Tertiary, informational","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Positive Green","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#2d7a00","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Success, positive values","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Warning Amber","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#e18727","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Warnings","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Error Red","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#bc1200","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Errors, negative emphasis","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"reference/chart-types.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for complete documentation.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Your Role","type":"text"}]},{"type":"paragraph","content":[{"text":"You are an analytics assistant helping a human who has decision-making context that you lack. Your job is to present data clearly and surface patterns worth investigating—not to draw conclusions or make recommendations.","type":"text"}]},{"type":"paragraph","content":[{"text":"Key principles:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use a matter-of-fact tone. State what the data shows, not what it means.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Design analysis that invites further questions, not analysis that closes them.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Surface anomalies and patterns without assuming their cause or significance.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Let the human add context and make decisions.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"For additional guidance on creating effective data visualizations—including Tufte-inspired principles, anti-patterns to avoid, and layout examples—see ","type":"text"},{"text":"Best_practices.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Feedback","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Having issues with mviz? Ask Claude to create a friction log documenting the problem, then open it as an issue at https://github.com/matsonj/mviz/issues","type":"text"}]}]},"metadata":{"date":"2026-06-05","name":"mviz","author":"@skillopedia","source":{"stars":215,"repo_name":"mviz","origin_url":"https://github.com/matsonj/mviz/blob/HEAD/SKILL.md","repo_owner":"matsonj","body_sha256":"52937ad2a9737a65bf613ecec1165e848fae85554af350bb1a7549ad934692d2","cluster_key":"24bb6f7255853467a814ac29744bbfef18fab4ef35d3c337d98ef27e93159f45","clean_bundle":{"format":"clean-skill-bundle-v1","source":"matsonj/mviz/SKILL.md","attachments":[{"id":"d2e8de7c-95af-558c-b483-5b49ccef6aa9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d2e8de7c-95af-558c-b483-5b49ccef6aa9/attachment.md","path":"Best_practices.md","size":8072,"sha256":"0f18c1941ad9fd361422bd436735546bf10363114a34f3ef732e5d9b88fdc43f","contentType":"text/markdown; charset=utf-8"},{"id":"73c33f87-bb28-52b8-9f8c-d777159bd137","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/73c33f87-bb28-52b8-9f8c-d777159bd137/attachment.png","path":"DarkMode.png","size":167725,"sha256":"212ed361364726b84df35adbb19ebc5c47175ffe30c06c19cef9fb4e43562818","contentType":"image/png"},{"id":"df2aa76b-5e8e-5099-8941-5d27344e9160","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/df2aa76b-5e8e-5099-8941-5d27344e9160/attachment.png","path":"LightMode.png","size":161158,"sha256":"85ec3b8708e7936cda0a98fd56ba33b49925ac0e927bdd8bb169a531742b51f5","contentType":"image/png"},{"id":"64769d3e-7bcb-580d-89a8-ad30d91f70f9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/64769d3e-7bcb-580d-89a8-ad30d91f70f9/attachment.md","path":"README.md","size":14430,"sha256":"b595a2344ab3898edb9603abbe7bba77d03ddce3b01195653256c7c82336db10","contentType":"text/markdown; charset=utf-8"},{"id":"8d99de32-8559-5ef7-b926-75c4b1e0147c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8d99de32-8559-5ef7-b926-75c4b1e0147c/attachment.pdf","path":"Sample Report.pdf","size":272569,"sha256":"30bd3d52e355b096f2718e4c3a409fcf35ec26c18b61b118bf9f81ec82c88134","contentType":"application/pdf"},{"id":"1fc84f2c-b478-5dad-a7a0-f633b61932da","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1fc84f2c-b478-5dad-a7a0-f633b61932da/attachment.pdf","path":"The Bo Nix Injury_ Quantifying the Damage.pdf","size":203328,"sha256":"7a4871d134d28cfc26d181e7af892f27729a35be8a1a70e17b496d9daed9c322","contentType":"application/pdf"},{"id":"620fb82a-e64b-5fe4-a32e-2dce6832b20c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/620fb82a-e64b-5fe4-a32e-2dce6832b20c/attachment.md","path":"reference/chart-types.md","size":18764,"sha256":"f9e447a8bd86a57fb6e905532cbf561b22b2136ac0da990f1c12d60e1cb9b079","contentType":"text/markdown; charset=utf-8"},{"id":"7653dde0-bfc5-5c94-9c7c-7199d7eded47","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7653dde0-bfc5-5c94-9c7c-7199d7eded47/attachment.json","path":"schema/mviz.schema.json","size":41794,"sha256":"3494bb7f8cc23e2cd5f447cf978d41ca072d7c63a7ad554f294d789cbd81c11d","contentType":"application/json; charset=utf-8"},{"id":"6267058a-8784-5fc0-85cc-cee7070bd1fc","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6267058a-8784-5fc0-85cc-cee7070bd1fc/attachment.json","path":"schema/mviz.v1.schema.json","size":41794,"sha256":"3494bb7f8cc23e2cd5f447cf978d41ca072d7c63a7ad554f294d789cbd81c11d","contentType":"application/json; charset=utf-8"}],"bundle_sha256":"d4842f777eb45f1f3f4b35069d53468eccb8def873b6d6c2069063e52fd21cbb","attachment_count":9,"text_attachments":5,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":4,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"web-development","category_label":"Web"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"web-development","import_tag":"clean-skills-v1","description":"A chart & report builder for AI. Use when the user asks to show, display, render, or visualize data as a chart (bar, line, pie, scatter, area, bubble, histogram, etc.), a table, KPI, big number, dashboard, report, sparkline, heatmap, sankey, funnel, calendar, waterfall, or any composed data visualization. Generates compact markdown specs that render either inline (via visualize:show_widget) or as standalone HTML files (via the mviz CLI)."}},"renderedAt":1782980686562}

mviz v1.7.0 mviz Generate clean, data-focused charts and dashboards from compact JSON specs or markdown. Maximizes data-ink ratio with minimal chartjunk, gridlines, and decorative elements. Uses a 16-column grid layout system. Setup No installation required. Use which auto-downloads from npm. The flag reduces npm output while still showing lint errors. For faster repeated use, install globally: What This Skill Does mviz is two things: 1. A spec language — markdown with fenced code blocks describing components (charts, KPIs, tables, notes, etc.). One markdown file (or a single JSON spec) descr…