Repository navigation
Releases
v0.0.1
[0.0.1] - 2026-11-12
The tool contract moves from 1.0.0 to 1.1.0 (CONTRACT_VERSION); the change is additive. Every 1.0.0 call keeps its
meaning, apart from the refusals under Security and Fixed that correct defects, and new warnings. The chart document
introduced here is version 1.
Security
- Interactive HTML could run script from an ECharts tooltip. In 0.0.0, and in the npm releases 0.0.2 and 0.0.3
before it, the page's CSP allowed'unsafe-inline'scripts, and ECharts writes a tooltip formatter string into the page as HTML, so a formatter such as
<img src=x onerror=…>in an option (the option can come from data or a model) ran its handler when the tooltip
opened. Three layers now stop it: the page'sscript-srcis the SHA-256 hashes of exactly its own two scripts
(withbase-uri,form-actionandobject-src'none'), so no inline handler,javascript:URL or injected
script runs; every ECharts tooltip in an option is checked against an allow-list of attribute-free tags (<br>,
<b>,<span>,<table>…), and anything else, an attribute, a comment or an encoded<is refused with
ECHARTS_MARKUP_REFUSEDat its JSON path; and the same check runs inside the page before ECharts starts, so an
edited page fails to its static chart rather than inserting markup.{b}<br/>{c}still draws two lines. Regenerate
any interactive HTML made with 0.0.0, 0.0.2 or 0.0.3 from untrusted options.
Added
- Chart documents (
ChartDocumentV1, version 1): a typed recipe or a raw engine spec, its data, and its
appearance, layout and interactions, checked strictly with a JSON path for every problem.charts_quicktakes
document(the recommended path for the 14 standard types);charts_renderandcharts_validatetake
documentor a saveddocumentPath(with an optionalexpectedRevision);documentis a new output format. A
call takes exactly one input arm (INPUT_CONFLICTotherwise). Seedocs/documents.md. - 14 typed recipes (
bar,horizontal_bar,grouped_bar,stacked_bar,line,area,stacked_area,
scatter,bubble,pie,donut,histogram,box_plot,heatmap) from one registry that owns each recipe's
schemas, compiler, examples, data requirements and interaction and layout support. Aggregation is explicit:
repeated groups withoutoptions.aggregateareAGGREGATION_REQUIRED,countcounts rows and takes no measure,
and a missing measure is left out and reported (VALUE_MISSING), never read as zero. - The saved artifact
<name>.chart.json(opencode-charts-artifact, version 1): the canonical document with its
rows captured before the transform, the captured theme base, defaults, custom-font digests and colour domain, and a
sha256:revision. Replaying it uses what it captured, not today's defaults. A result also carries a render
fingerprint, and never the captured rows. charts_update: a presentation patch (title, alt text, theme and token overrides, category colours, layout,
interactions) to a saved artifact at a known revision, published as a new immutable generation. Data, bindings,
aggregation, sorting, transforms, locale, currency and seed cannot change. A stale or edited revision is
DOCUMENT_REVISION_MISMATCH, and a custom font whose bytes changed isDOCUMENT_DEPENDENCY_CHANGED, before
anything is drawn. It is anexporttool on every surface: registration, tools RPC, Jelly (ask), permissions and
docs/tool-parity.md.- Immutable generations: a document call's files are staged whole and published as one folder
<outDir>/<name>-<digest>/named by its file manifest; an identical existing folder is verified and reused, a
different one is refused, and a failure publishes nothing. - Recipe layout:
widthandheightare the outer canvas, exact in SVG and in PNG pixels × scale. Five targets
(inline720 × 420 responsive,analysisandreport900 × 540,presentation1280 × 720 with 18 px labels,
paper1000 × 650). A pure three-candidate planner, shared with the HTML page, wraps or turns labels, moves the
legend and thins ticks, and never drops data or shrinks text below the floor:LAYOUT_UNSATISFIABLElists the
conflicts instead. Engine documents keep native layout (LAYOUT_UNSUPPORTEDotherwise). - Interaction presets in HTML:
tooltip(with a keyboard-reachable values table),legend: "highlight",
zoom: "x"(temporal line and area) andbrush: "interval"(scatter and bubble), with the documented combinations,
accessible Clear/Reset controls, and responsive re-planning at the container's width. An unsupported preset or
combination isINTERACTION_UNSUPPORTED; an explicit format list withouthtmlfor an explicit interaction is
INTERACTION_FORMAT_MISMATCH. - Typed appearance:
appearance.overridesandcharts_theme_save'sthemeare strict projections of the
canonical theme tokens (an unknown token is refused at its path). Recipe charts get a stable colour domain by typed
category identity (1and"1"differ), andcategoryColorspins colours by category. - Interactive HTML status:
<html data-chart-status>isinitializing,readyorfailed; the live chart starts
off-screen after the embedded fonts load and replaces the static SVG only once it started. A failure keeps the
static chart with a visible notice and anHTML_RUNTIME_FAILEDdiagnostic. - Discovery from the registry:
charts_capabilitiesreports document versions, the recipe contracts, the layout
profiles and the interaction presets;charts_cataloguemarks the recipe types and returns each one's contract and
exactcharts_quickdocument calls;charts_recommendadds a document draft on the caller's own columns, with an
explicit medium as its layout target, and withholds it with a warning when the aggregate would have to be guessed. SIZE_OVERRIDE_IGNORED: a raw spec's ownwidthorheightbeats the tool's, as before, and now says so, naming
both values and the path.- New codes:
INPUT_CONFLICT,DOCUMENT_INVALID,DOCUMENT_VERSION_UNSUPPORTED,DOCUMENT_REVISION_MISMATCH,
DOCUMENT_DEPENDENCY_CHANGED,ARTIFACT_INVALID,AGGREGATION_REQUIRED,POSITION_MISSING,LAYOUT_UNSUPPORTED,
LAYOUT_UNSATISFIABLE,INTERACTION_UNSUPPORTED,INTERACTION_FORMAT_MISMATCH,VALUE_MISSING,
CATEGORY_COLOR_UNUSED,SIZE_OVERRIDE_IGNORED,ECHARTS_COMPONENT_UNSUPPORTED,ECHARTS_MARKUP_REFUSED,
HTML_RUNTIME_FAILED,OUTPUT_WRITE_FAILED. The README's anddocs/configuration.md's tables list them. - A
browsercheck stage: playwright-core's headless Chromium (pinned in the lockfile) opens pages made by the
built plugin, under their CSP, with violation, console and request listeners, and exercises every advertised
interaction. A missing browser fails the stage. docs/documents.md; the cookbook skill now leads with a generated recipe-document section, and
test/generated.test.tsfails when the cookbook or the recipe table indocs/documents.mdis stale.
Fixed
- Catalogue examples that could not be read. The funnel's labels sat inside segments narrower than the label, some
at under 4.5:1 contrast, and printed10000: they now sit outside the segments with grouped counts (10,000). The
waterfall hid four of its six step names and laid two change labels over their bars: it is wider, names every step
and puts each change above its bar, with the total labelled. The radial chart had no month labels or legend: each
wedge now names its month and value, beside a titled colour legend. The world choropleth drew only the countries
with data: every country is now drawn, those without data in a no-data fill with its own legend entry. Smaller
fixes: human axis and legend titles (lollipop, dumbbell, brushable scatter, line with band), padded scales (dumbbell,
dot plot), a dumbbell row with two distinct points,0%rather than+0%(diverging bar), year labels (connected
scatter), no gridlines through the end labels (slope), and labelled ends on the calendar heatmap's legend. These
change the examples only, not how any chart type renders a call. - Vega and Vega-Lite HTML did not start in a browser. Vega's default expression evaluator builds functions from
strings, which the page's CSP (nounsafe-eval) refuses. Pages now parse with{ast: true}and run with
vega-interpreter 2.3.2; the network-denying loader stays. charts_catalogue's example call left out the example'swidth,heightandlocale(the calendar heatmap is
820 × 220). One function now builds the exact tested call for the catalogue, the cookbook, the gallery, the goldens
and the evals.- The compiler's fit and autosize warnings were filtered out; they now reach the result as
VEGA_LITE_WARNING. - ECharts options that used
dataZoom,brush,toolbox,timeline,matrixorthumbnailwere drawn without
them; they are refused withECHARTS_COMPONENT_UNSUPPORTEDat their path. One module table feeds the server and
browser runtimes and the capability report. - Partial output: every requested file and the preview are now made before anything is written, an invalid PNG
scale is a finding up front, and legacy flat files are staged and moved into place with a best-effort rollback
(OUTPUT_WRITE_FAILEDnames any file it could not restore).charts_validatecreates no folder. - Custom fonts in exports: a theme's own
.ttfor.otfis embedded in HTML and embedded-font SVG with its own
data URI andformat(), from the same approved resource list as metrics and the PNG. - A label containing markup (
<img onerror=…>) in a drawn SVG is data, not a refused event handler. - Layout fitting is monotonic in size: a recipe chart that fits at one size fits at every larger one. Axes keep
room for every label (Vega's defaultmaxExtenthad capped it), turned l...
v0.0.0
[0.0.0] - 2026-11-10
Added
- One tool registry (
src/registry.ts): each tool is defined once, and the plugin's tool registration, the Jelly
declaration, the tools RPC,charts_capabilitiesanddocs/tool-parity.mdare projections of it.
test/registry.test.tsfails when they drift. charts_capabilities: exactly what this build can do, computed rather than promised — working output formats,
engines and the families each draws, themes and their palette sizes, bundled fonts and the scripts they cover, data
formats, locales, atlases, limits, every tool with its class, and what is known not to work.charts_recommend: which chart fits a question, its data and its medium. A deterministic local ranking over the
53-type catalogue with a reason for each pick, the encoding to use, and a readycharts_rendercall on the caller's
own columns. It abstains with a reason rather than guessing, and it never renders.- The figure tree:
charts_renderandcharts_validatereturn the drawing as text — every label with a stable id,
its box, and which labels collide or run past the edge — and the preview PNG numbers the same ids (marks). - The tools RPC v2 at
charts.tools: every tool is a method with its class, so a script draws or checks a chart
with no model.docs/scripting.mdhas the classes, the rule that decides a call and the saved-approval note. FONT_GLYPH_MISSING: a label using a code point no bundled face draws is named, with the characters and the fix,
instead of silently becoming a blank box.docs/tool-parity.md,docs/status.md.- One typed code table:
ChartsCodeinsrc/errors.tsis every failure and every issue code the plugin can
return, and the README's anddocs/configuration.md's error tables now list them all — the validation and lint
issue codes included.test/codes.test.tsfails when a code is raised that is not in the table, or either table
differs from it. - The tools
charts_render,charts_quick,charts_validate,charts_catalogue,charts_themesandcharts_theme_save. - Engines: Vega-Lite and Vega (rendered in a worker with time and memory limits) and ECharts server-side SVG. PNG through resvg (wasm). Offline interactive HTML.
- Validation with did-you-mean and JSON paths; lint; layout checks (label collisions, clipping, duplicate ticks); PNG previews for the model.
- A cookbook skill generated from the 53-type catalogue.
- Bundled fonts: Inter and JetBrains Mono.
Fixed
-
Every tool declares its output schema, and it is a real one: the registry's, projected to the model path and to the
tools RPC, which validates each script's result against it. This keeps 0.0.3's fix — OpenCode 2.2.5 fails a result
from a tool that declares no output — and makes the declaration checkable, with nopattern(the shared module's
rule for an owned output schema). A host case on 2.2.3, 2.2.4, 2.2.5 and 2.2.6 calls all eight tools and asserts every
result is accepted. -
The TUI part (
dist/tui.js) loads from a local-path install again. It is built unminified: OpenCode could not resolve the minified form of itssolid-jsimport, so the status dialog never started. The build fails if the file is minified.
Changed
-
The Code Mode catalogue no longer pins
charts_quickorcharts_render. Their signatures are large; the charts skill holds the workflow, andsearch({ namespace: "charts", query })finds a tool. -
Every tool is allowed by default. The plugin inserts one
allowrule forcharts_*and one for each of the
eight tools after every agent's base rule, so a render writes chart files into the workspace and a theme is saved
with no prompt, and a tool a later release adds runs at once. An agent's own rules, yourpermissionsand a saved
approval still win. -
The
accessoption restricts that in one line:"all"(the default),"ask-changes"(the five reads run, the
three writers ask) and"read-only"(the five reads run, the three writers are denied). The README prints the block
for each value, and/charts-statusshows the one in force. -
A categorical palette that never runs out. A theme's published colours stay first; past them, hues are generated
in OKLCH at the theme's own lightness and chroma, held to a minimum perceptual distance, so 12 series inlightno
longer repeat a colour.printandhigh-contrastare not extended: their constraint is what they are for. -
The word cloud's golden changed: its thirteenth colour is no longer a repeat of its second.
-
The built-in themes are neutral:
light(the default),dark,high-contrast,printandpresentation. The two brand themes and their font are no longer in the plugin. Save a brand theme withcharts_theme_save, or set it in the plugin's options, and use it by name.
Notes
- The design borrows from MCP Clearsight (
cesar-alves/mcp-clearsight, MIT OR Apache-2.0) and Rune
(anomalyco/rune, MIT). No source file was copied;NOTICErecords what was adapted, from which commit, and under
which licence. - The shared tools RPC module is v2.1 (
57377eb3…), andscripts/check-copies.mjspins that hash.
Known limits
- No PDF output, and no Plotly or Mermaid input. docs/status.md says what does not work and why, and
charts_capabilitiesreports what this build supports. - Bundled fonts cover Latin, Greek and Cyrillic. A label beyond them gets
FONT_GLYPH_MISSING; the fix is a font file withtheme.font.files. - Built-in themes are neutral. Brand themes are not in the plugin. Save one with
charts_theme_save, or set it in the plugin's options, and use it by name. - Nothing is loaded from the network. A remote data file or image is not fetched.
- PNG and HTML need the packaged runtime. A missing runtime is reported as
PNG_UNAVAILABLEorHTML_UNAVAILABLE; reinstalling the plugin restores it. - Package size. The package is about 8 MB unpacked, and
dist/index.jsis 2.3 MB: Vega, ECharts and the PNG rasteriser are bundled, so a render needs no network. - Host range. OpenCode
>=2.2.3 <2.3.0, tested on 2.2.3, 2.2.4, 2.2.5 and 2.2.6. - Updates are not automatic for a Git install, and
opencode plugin updateandopencode plugin checkdo not work on one: to move to a newer release, remove the old entry and add the new tag.