Skip to content
Open

Dev #97

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- ECharts categorical legends (and their title graphics) are pinned with
`legend.right` instead of a design-canvas `left` pixel. Hosts that size the
container independently of `_width` and call `chart.resize()` keep the
reserved gutter instead of overlapping the plot or clipping the legend
([#98](https://github.com/microsoft/flint-chart/issues/98)).
- Visible units now require an explicit `unit` in the field's semantic
annotation. Conventional compact units may accompany values, while lexical
units such as `years` are stated once as part of the field title. Bar Tables
also no longer repeat their value column as annotations on the bars.
- A raw sum-stacked chart whose total lands exactly on a clean axis tick now
keeps that edge flush instead of adding an empty interval above it, including
machine-scale residue from calculated shares. Totals meaningfully beyond the
clean endpoint still advance to the next tick; the rule is derived from the
plotted stack and does not special-case percentages or 100.
- Series-end labels now use a bounded screen-space packing pass when endpoints
form one readable column. Small adjustments keep labels attached by proximity;
crowded or horizontally staggered sets fall back together to the next legend
placement instead of leaving a partial or overlapping direct-label system.

## [0.5.1] - 2026-08-13

### Added
Expand Down
15 changes: 14 additions & 1 deletion agent-skills/flint-chart-author/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,20 @@ understates what you know:
}
```

- `unit` — the unit or currency code: `"USD"`, `"°C"`, `"kg"`.
- `unit` — an optional assertion that authorizes Flint to display a unit. Add
it only when the data or surrounding context establishes the measurement
and seeing it materially changes how a reader interprets the number. A type
such as `Duration`, a field name such as `life_expectancy`, or values that
merely look plausible are not enough evidence by themselves.
- Prefer canonical codes: `"USD"`, `"°C"`, `"kg"`, `"km/h"`, `"min"`.
- Conventional compact units are normalized and may appear beside values
(`USD` → `$`, `hours` → `hr`).
- Lexical units such as `"years"` are stated once beside the field name as
`field (years)`, not repeated after every value.
- Do not put explanatory phrases in `unit`. Put qualifications such as
`"per working-age resident"` or `"constant 2024 prices"` in the subtitle.
- Omit `unit` when its meaning, scale, or denominator is uncertain. Flint
does not infer a visible unit from the semantic type or field name.
- `intrinsicDomain` — the field's own bounds, for bounded scales only: `[1, 5]`
for a five-star rating, `[0, 100]` for a percentage score. Not for
open-ended measures.
Expand Down
4 changes: 3 additions & 1 deletion agent-skills/flint-theme-author/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ the authored blocks and their jobs:
| `layout` | Density, target width, title block, and band step |
| `chartDefaults` | Optional defaults keyed by registered chart type or `*`; caller values still win |
| `compileDefaults` | Preferred base size, canvas size, and supported assemble options |
| `interaction` | Tooltip format |
| `interaction` | Tooltip format and semantic selection-boundary paint |
| `variants` | Conditional policy adaptations; variants may not change `ink` or `type` |

### High-value nested shapes
Expand All @@ -154,6 +154,8 @@ the authored blocks and their jobs:
}
```

`interaction.selectionBoundary` accepts `color`, `width`, `opacity`, `haloColor`, `haloWidth`, and `haloOpacity`. Omitted paint is grounded from the theme: foreground from `ink.accent` then primary text, and halo from the plot or canvas surface. Use explicit values only when the house has a distinct interaction treatment.

This is a shape example, not a palette recommendation. Derive actual values
from the user's references.

Expand Down
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,8 @@ channels. This decouples user/AI intent from rendering specifics.
First-class channel for grouped bar charts. The analysis stage resolves
its semantics (type, color scheme) without any VL knowledge. The grouping
axis is auto-detected: whichever of `x`/`y` is discrete gets subdivided.
For a Grouped Bar Chart, `color` is an equivalent alias for `group`; when only
`color` is supplied, Flint canonicalizes it to `group` before analysis.

During instantiation, `buildVLEncodings()` translates:
- `group` → VL `color` encoding (for coloring)
Expand Down
2 changes: 1 addition & 1 deletion docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ interface AssembleOptions {
maxStretchX?: number; // per-dimension width cap (derived from canvasSize)
maxStretchY?: number; // per-dimension height cap (derived from canvasSize)
facetElasticity?: number; // facet stretch (default 0.3)
minStep?: number; // min px per discrete item (default 6)
minStep?: number; // min px per discrete item (default 8)
minSubplotSize?: number; // min facet subplot px (default 60)
maxColorValues?: number; // color cardinality before truncation (default 24)
stepPadding?: number; // band inner padding fraction (default 0.1)
Expand Down
63 changes: 63 additions & 0 deletions docs/community-backends.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Community backends

Community backends extend Flint to additional renderers and delivery surfaces.
They use the same `ChartAssemblyInput`, but may have different chart coverage,
release cadence, and gallery, editor, MCP, or ThemeSpec integration from Flint's
core backends.

## Image-Charts

> Originally contributed by
> [François-Guillaume Ribreau](https://github.com/FGRibreau).

The Image-Charts backend compiles a Flint input into an unsigned URL for the
third-party [Image-Charts](https://www.image-charts.com/) service. It is useful
when the output must work as an ordinary image URL, including email, generated
documents, chat messages, and other no-JavaScript environments.

```ts
import {
assembleImageCharts,
isImageChartsSupported,
} from 'flint-chart/image-charts';

if (isImageChartsSupported(input.chart_spec.chartType)) {
const artifact = assembleImageCharts(input);
// { type: 'image-charts', url: 'https://image-charts.com/chart?...' }
}
```

Assembly is pure: it creates the URL without making a network request. Loading
the returned URL sends the encoded chart data to Image-Charts, so do not use it
with confidential data unless sending that data to the service is acceptable
under your privacy and deployment requirements.

### Supported charts

- Bar Chart, Grouped Bar Chart, and Stacked Bar Chart
- Line Chart, Sparkline, and Area Chart
- Scatter Plot
- Pie Chart and Donut Chart
- Radar Chart

Unsupported chart types and faceted inputs throw an error rather than silently
falling back to another representation.

### Current scope

- Output is an unsigned `https://image-charts.com/chart?...` GET URL. Account
identifiers, HMAC signatures, and secrets are outside this pure compiler.
- Width and height are clamped to 999 pixels, and total area is clamped to
998,001 pixels, matching the service's documented chart-size limits.
- Data, labels, legends, colors, and titles are carried in the query string.
Large or label-heavy charts can produce long URLs; Flint does not currently
convert them to Image-Charts POST requests or enforce a maximum URL length.
- Banded bar charts use Flint's overflow filtering before URL serialization.
- The backend uses a fixed categorical palette. ThemeSpec and most
`chartProperties` are not applied.
- Flint does not currently render this artifact in its gallery, editor, or MCP
server. Availability, caching, retention, quotas, and subscription behavior
are controlled by Image-Charts.

See the [Image-Charts API documentation](https://documentation.image-charts.com/)
for the hosted service's current request grammar and limits.
5 changes: 4 additions & 1 deletion docs/design-semantics.md
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,10 @@ Only override native formatting when semantic context adds value: prefix/suffix,
| **Sentiment / Correlation** | `+` + data-driven | — | — | — | Signed decimal |
| **Latitude / Longitude** | — (empty) | — | — | — | VL native |

Unit/currency priority is `annotation.unit` > column-name heuristics > data-value scanning > type defaults.
Visible unit text requires `annotation.unit`; semantic types, column names, and
data values do not authorize display by themselves. Conventional compact units
such as `$`, `%`, `°C`, `kg`, or `min` may accompany values. Lexical units such
as `years` are stated once with the field title (`field (years)`).

**Parsing** is the compiler's job, guided by semantic type rather than stored on context:

Expand Down
8 changes: 4 additions & 4 deletions docs/design-stretch-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,12 +283,12 @@ The layout balances two directions:
| $L_{\max}$ | Maximum axis length | `base × β` (β from `maxStretch` or `canvasSize`) | 800 px |
| $N$ | Number of banded items | Field cardinality | data-dependent |
| $\ell_0$ | Natural (base) size per band | `defaultBandSize` | ~20 px |
| $\ell_{\min}$ | Minimum size per band | `minStep` option | 6 px |
| $\ell_{\min}$ | Minimum size per band | `minStep` option | 8 px |
| $\ell_{\max}$ | Maximum size per band | `maxBandSize` option | = $\ell_0$ |
| $\alpha$ | Elasticity exponent | `elasticity` option | 0.5 |
| $\beta$ | Maximum stretch multiplier | `maxStretch`, or derived from `canvasSize` | 1.5 |

> **Code defaults:** `elasticity: 0.5`, `minStep: 6`, and `maxStretch: 1.5` when no `canvasSize` ceiling is set. $\ell_0$ and $\ell_{\max}$ are given at a 300 px reference canvas and scaled with size: `round(bandSize × max(1, sizeRatio))`.
> **Code defaults:** `elasticity: 0.5`, `minStep: 8`, and `maxStretch: 1.5` when no `canvasSize` ceiling is set. $\ell_0$ and $\ell_{\max}$ are given at a 300 px reference canvas and scaled with size: `round(bandSize × max(1, sizeRatio))`.

### §2.2.1 Band size bounds — min, base, max

Expand Down Expand Up @@ -422,7 +422,7 @@ Grouped items, such as a grouped bar chart with $m$ sub-bars per group, are trea
| Parameter | Simple discrete | Grouped bar ($m$ sub-bars) |
|---|---|---|
| $\ell_0$ (natural) | `defaultStepSize` | $m \times$ `defaultStepSize` |
| $\ell_{\min}$ (solid) | `minStep` (6 px) | $2m$ px (2 px per sub-bar) |
| $\ell_{\min}$ (solid) | `minStep` (8 px) | $2m$ px (2 px per sub-bar) |
| $N$ (item count) | Field cardinality | Number of **groups** |

The elastic budget formula is unchanged — only the parameter values change.
Expand Down Expand Up @@ -530,7 +530,7 @@ The minimum subplot size ($S_{\min}$) is axis-aware:
|---|---|---|
| $N$ | Number of discrete items | data-dependent |
| $\ell_0$ | Natural step size | ~20 px |
| $\ell_{\min}$ | Minimum step size | 6 px |
| $\ell_{\min}$ | Minimum step size | 8 px |
| $\alpha$ | Elasticity exponent | 0.5 |
| $\beta$ | Maximum stretch | 1.5 |

Expand Down
9 changes: 7 additions & 2 deletions docs/reference-vegalite.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,10 @@ The **Availability** column shows whether a parameter is `always` available or `

### ![](chart-icon-column-grouped.svg) Grouped Bar Chart

**Encoding channels:** `x`, `y`, `group`, `column`, `row`
**Encoding channels:** `x`, `y`, `group`, `color`, `column`, `row`

For grouped bars, `color` is an alias for `group`; either channel creates the
colored side-by-side subdivision.

| Parameter | Control | Domain | Default | Availability | Description |
|---|---|---|---|---|---|
Expand Down Expand Up @@ -415,7 +418,9 @@ The **Availability** column shows whether a parameter is `always` available or `

**Encoding channels:** `x`, `color`

_No template-specific parameters._
| Parameter | Control | Domain | Default | Availability | Description |
|---|---|---|---|---|---|
| `cornerRadius` | number | 0 – 8 (step 1) | `2` | always | Corner radius for supported marks. |

### ![](chart-icon-bar-table.svg) Bar Table

Expand Down
20 changes: 20 additions & 0 deletions docs/theme-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,31 @@ Every field is optional. Start with the decisions that matter to your product, t
| `annotation` | Units, axis titles, number formats, point emphasis, and statistics |
| `layout`, `facets` | Density, title spacing, band steps, panel spacing, and shared scales |
| `chartDefaults`, `compileDefaults` | House defaults for chart controls, base size, canvas size, and layout limits |
| `interaction` | Tooltip formatting and semantic selection-boundary paint |
| `furniture` | Rules, tabs, and other recurring chart chrome |
| `variants` | Semantic conditions that adapt policy to a chart's role, density, or shape |

Theme rules are semantic. For example, `structure.grid.measure` controls the grid used to read values, whichever physical axis carries the measure. `legend.placement` gives the compiler an ordered set of acceptable positions rather than fixed coordinates. This is what lets one theme generalize across different chart types, data, and canvas sizes.

Selection boundaries are inferred from the theme unless explicitly stated. Their foreground defaults to `ink.accent`, then `ink.text.primary`; their halo defaults to the plot or canvas surface. This gives a continuous-color grid an outline that belongs to the house while remaining legible across both ends of its ramp. A theme can override the treatment:

```json
{
"interaction": {
"selectionBoundary": {
"color": "#b54a20",
"width": 1.5,
"opacity": 1,
"haloColor": "#ffffff",
"haloWidth": 3,
"haloOpacity": 0.8
}
}
}
```

This block controls paint only. The ChartDef still decides whether a representation needs a boundary and the renderer still computes its contiguous geometry.

### 3. Inherit and override

Use `extends` when a preset is close to your brand:
Expand Down
2 changes: 1 addition & 1 deletion docs/zh-CN/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ interface AssembleOptions {
maxStretchX?: number; // per-dimension width cap (derived from canvasSize)
maxStretchY?: number; // per-dimension height cap (derived from canvasSize)
facetElasticity?: number; // facet stretch (default 0.3)
minStep?: number; // min px per discrete item (default 6)
minStep?: number; // min px per discrete item (default 8)
minSubplotSize?: number; // min facet subplot px (default 60)
maxColorValues?: number; // color cardinality before truncation (default 24)
stepPadding?: number; // band inner padding fraction (default 0.1)
Expand Down
8 changes: 4 additions & 4 deletions docs/zh-CN/design-stretch-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,11 +263,11 @@ continuousWidth = stepSize × (N + 1)
| $L_{\max}$ | Maximum axis length | `base × β`(β 来自 `maxStretch` 或 `canvasSize`) | 800 px |
| $N$ | Number of banded items | Field cardinality | data-dependent |
| $\ell_0$ | Natural length per item | `defaultStepSize` | ~20 px |
| $\ell_{\min}$ | Minimum length per item | `minStep` option | 6 px |
| $\ell_{\min}$ | Minimum length per item | `minStep` option | 8 px |
| $\alpha$ | Elasticity exponent | `elasticity` option | 0.5 |
| $\beta$ | Maximum stretch multiplier | `maxStretch`,或从 `canvasSize` 推导 | 1.5 |

> **Code defaults:** 未设置 `canvasSize` 上限时,`elasticity: 0.5`、`minStep: 6`、`maxStretch: 1.5`。`defaultStepSize` 根据画布尺寸动态计算:`round(20 × max(1, sizeRatio) × defaultStepMultiplier)`。
> **Code defaults:** 未设置 `canvasSize` 上限时,`elasticity: 0.5`、`minStep: 8`、`maxStretch: 1.5`。`defaultStepSize` 根据画布尺寸动态计算:`round(20 × max(1, sizeRatio) × defaultStepMultiplier)`。

## §2.3 三种状态

Expand Down Expand Up @@ -357,7 +357,7 @@ $$\boxed{\ell = \frac{\kappa \cdot \ell_0 + L_0 / N}{1 + \kappa}}$$
| Parameter | Simple discrete | Grouped bar ($m$ sub-bars) |
|---|---|---|
| $\ell_0$ (natural) | `defaultStepSize` | $m \times$ `defaultStepSize` |
| $\ell_{\min}$ (solid) | `minStep` (6 px) | $2m$ px(每子 bar 2 px) |
| $\ell_{\min}$ (solid) | `minStep` (8 px) | $2m$ px(每子 bar 2 px) |
| $N$ (item count) | Field cardinality | **组**数量 |

elastic budget 公式不变 — 仅参数值变化。
Expand Down Expand Up @@ -465,7 +465,7 @@ gas pressure 模型(§3)在每个子图内运行,容器为 $W_{\text{sub}}
|---|---|---|
| $N$ | Number of discrete items | data-dependent |
| $\ell_0$ | Natural step size | ~20 px |
| $\ell_{\min}$ | Minimum step size | 6 px |
| $\ell_{\min}$ | Minimum step size | 8 px |
| $\alpha$ | Elasticity exponent | 0.5 |
| $\beta$ | Maximum stretch | 1.5 |

Expand Down
Loading
Loading