Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
33d7575
feat(interactions): chart capabilities named once, preset requirement…
xavier-shaw Sep 13, 2026
a56c6c6
feat(templates): every Vega-Lite template declares its interaction su…
xavier-shaw Sep 13, 2026
26291dc
feat(interactions): admission matches a preset's requirements against…
xavier-shaw Sep 13, 2026
668ac1e
feat(interactions): one requirement table, and a coverage tab of char…
xavier-shaw Sep 13, 2026
31faee2
feat(interactions): validateChart and the MCP server report and disco…
xavier-shaw Sep 13, 2026
7025d53
docs(interactions): a guide to interaction_spec, and the presets each…
xavier-shaw Sep 13, 2026
6482990
feat(hosts): the MCP chart view, the editor and the gallery mount fro…
xavier-shaw Sep 13, 2026
4b09ad7
code review fix
xavier-shaw Sep 15, 2026
95fb2aa
feat(templates): the data-dependent capabilities are declared whereve…
xavier-shaw Sep 16, 2026
af554e4
docs(interactions): a design document in place of the decision log
xavier-shaw Sep 16, 2026
33b3578
feat(interactions): one interaction per gesture slot
xavier-shaw Sep 18, 2026
b486813
refactor(interactions): affordances are the dispatch gate
xavier-shaw Sep 18, 2026
f8c3963
docs(interactions): affordances as the gate, and the gesture slots
xavier-shaw Sep 18, 2026
d0e1770
feat(interactions): one trigger, one owner
xavier-shaw Sep 18, 2026
c6d2f40
site(playground): a Conflict cases lab under Interactions
xavier-shaw Sep 18, 2026
16bc643
fix(interactions): mount the admitted copy, matched by id
xavier-shaw Sep 18, 2026
4e8328d
fix(interactions): an axis label click emphasises the category's marks
xavier-shaw Sep 18, 2026
192781e
site(playground): the Conflict cases page uses the Test cases card
xavier-shaw Sep 18, 2026
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
40 changes: 40 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- `interaction_spec`, a third document beside `chart_spec` and `theme_spec`: a
list of interaction presets by `type`, each with its `options`.
`buildInteractiveChart()`, the MCP chart view, and the site editor mount from
it. Guide: `docs/interaction-spec.md`.
- Admission per chart type. Each Vega-Lite template declares its capabilities
in `ChartTemplateDef.interactionSupport`; each preset declares its needs in
`INTERACTION_PRESET_REQUIREMENTS`. A spec entry the chart cannot honour is
dropped with an `unsupported_interaction` warning; a code definition throws.
`validateChart()` and the MCP `validate_chart` report the same warnings;
`list_chart_types` and the Vega-Lite reference list the supported presets.
One trigger, one owner: `triggersOf(definition)` lists the triggers a
definition takes (the navigation, region drag, and element drag slots, the
plot drag, the double-click, and the legend, axis, and retained-focus mark
clicks); when two admitted definitions share one, the one that can give it
up and keep the rest does so with an `info` warning (`click-highlight`
through `withoutAffordances`), otherwise the later entry drops with a
`conflicting_interactions` warning, a spec entry always yields to code, and
two code definitions throw. Before, only three pairs were checked, and two
code definitions on one slot were kept with the runtime using the first.
- Chart validation is now part of the core package. `validateChart(input,
backend)` returns `{ valid, warnings, errors, computedSize }` without
throwing, alongside `validateChartInput`, `validateSemanticTypes`,
Expand All @@ -24,8 +43,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`isRegistered` / `getRegisteredTypes` are exported from `flint-chart/core`
([#104](https://github.com/microsoft/flint-chart/issues/104)).

### Changed

- `CanvasInteractionDef.affordances` is now a required map from the kind of
hit (`mark`, `legend-item`, `axis-label`, `plot`) to its cursor and hover,
and it is the only dispatch gate: the runtime sends a hit to an interaction
only when the interaction affords its kind, on every path including
keyboard, context, long press, and double-click. The flags
`claimsLegendActivation` and `claimsAxisActivation` are gone; a key says the
same thing. `clickHighlight()` gains `withoutAffordances(drop)`, a copy that
affords fewer targets. A definition built by hand must declare
`affordances`; `affordsTarget(interaction, target)` reads the gate. A long
press, a right-click, or a double-click on a legend item no longer reaches a
preset that affords marks only.

### Fixed

- A legend click with both `click-highlight` and `legend-toggle` mounted hid the
series and dimmed every other bar, because both presets answered the click.
`click-highlight` now yields the legend click at admission.
- A click on a discrete axis label through `click-highlight` or
`axis-highlight` changed nothing on the chart. The renderer routed an axis
target to the label painter only and skipped the render keys of its marks.
The category's marks now emphasise and the rest mute, like a mark click.
- Keyboard targeting now navigates and emits `focus-element` through the
`keyboard-targeting` interaction ID without requiring a click preset. Enter
and Space still invoke configured click presets when present.
Expand Down
48 changes: 48 additions & 0 deletions agent-skills/flint-chart-author/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ or `assembleChartjs` to get a backend spec.

- **DO** emit `chart_spec` (chart type, channel→field mapping, properties)
and `semantic_types` (field → semantic type).
- **DO** add `interaction_spec` when the user asks for behaviour (highlight,
legend toggle, pan and zoom, brush). List presets by name; see
"Interactions".
- **Reference columns by name.** How `data` itself gets bound depends on
the situation — a URL, a host-side variable, or embedded rows (see "How
data gets bound"). Embedding is fine for small tables; just don't
Expand Down Expand Up @@ -242,6 +245,51 @@ chart input. ThemeSpec currently affects Vega-Lite only.
Full reference:
https://microsoft.github.io/flint-chart/#/documentation/theme-spec

## Interactions (`interaction_spec`)

Add `interaction_spec` beside `chart_spec` only when the user asks for
behaviour: highlight on click, a legend that hides series, pan and zoom, a
brush, an annotation on click. A static image never needs it.

```json
{
"chart_spec": { "chartType": "Bar Chart", "encodings": { "x": "country", "y": "gdp", "color": "region" } },
"interaction_spec": {
"interactions": [
{ "type": "click-highlight" },
{ "type": "legend-toggle" },
{ "type": "navigate", "options": { "axes": "y", "pan": false, "reset": ["double-click", "escape"] } }
]
}
}
```

Rules:

- **Presets only.** Every entry is `{ "type": <preset>, "options": { ... } }`.
Take the preset names for the chosen chart type from `list_chart_types`
(`chartTypes[].interactions`); a KPI card supports no brush, a pie chart no
`navigate`. Never invent a type.
- **Options nest under `options`.** An option beside `type` is rejected.
`id` is optional and sits on the entry, never inside `options`.
- **`reset`** is a list of `"click-none"`, `"double-click"`, `"escape"` on any
preset that keeps state. Leave it out to accept the preset's default.
- **The data decides too.** `legend-toggle` needs a colour field with a
discrete legend; `navigate` needs a continuous axis; `drag-reorder` needs a
discrete axis. An entry the chart cannot honour is dropped with a warning
and the chart still renders. Run `validate_chart` to read those warnings
before you show the chart.
- **Vega-Lite only.** Other backends ignore the spec.

Common presets: `click-highlight` (focus a mark), `click-group-focus`
(focus its group, `groupBy`), `legend-toggle`, `navigate` (`axes`, `pan`),
`brush-x` / `brush-y` / `select` (drag to focus an interval or area),
`click-annotate`, `inspect` and `inspect-index` (read values on hover),
`drag-reorder` (reorder categories).

Full guide:
https://microsoft.github.io/flint-chart/#/documentation/interaction-spec

## Step 1 — pick `chartType`

Use one of the registered names **exactly**. Vega-Lite is the default and
Expand Down
3 changes: 2 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
> For the semantic type system, see
> [design-semantics.md](design-semantics.md). For the axis layout
> compression models, see
> [design-stretch-model.md](design-stretch-model.md).
> [design-stretch-model.md](design-stretch-model.md). For the interaction
> model, see [design-interactions.md](design-interactions.md).

---

Expand Down
7 changes: 7 additions & 0 deletions docs/adding-a-chart-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ export const dotPlotDef: ChartTemplateDef = {
chart: 'Dot Plot',
template: { mark: 'circle', encoding: {} },
channels: ['x', 'y', 'color', 'size', 'column', 'row'],
interactionSupport: {
elements: true, // marks resolve to data rows
region: ['cartesian'], // rectangle and lasso drags resolve marks
navigation: {}, // continuous x and y pan and zoom
legend: true, // a discrete colour legend can be toggled
},
markCognitiveChannel: 'position',

declareLayoutMode: (channelSemantics, table, chartProperties) => {
Expand All @@ -69,6 +75,7 @@ export const dotPlotDef: ChartTemplateDef = {
2. **`markCognitiveChannel`** — tells the compiler how readers decode value (affects zero baseline and [Auto Layout Algorithm](/documentation/layout-model) compression).
3. **`instantiate`** — receives a **deep clone** of `template` plus `InstantiateContext` (resolved encodings, `ChannelSemantics`, `LayoutResult`, data table, canvas size).
4. **No semantic branching** — read `ctx.channelSemantics[channel].format`, `.type`, `.zero`, etc.; do not switch on raw field names or storage types.
5. **`interactionSupport`** — what the chart type offers to interaction presets (`ChartInteractionSupport` in `core/interaction-spec.ts`). Declare only what the chart can honour: `elements`, `region`, `navigation`, `reorder`, `legend`, `discreteAxis`, `index`. An absent key means "never"; the assembler confirms the data-dependent ones against the bound encodings. Admission drops or rejects a preset whose `requires` list names a capability the chart lacks, and `list_chart_types` reports the supported presets from the same block.

Optional hooks: `postProcess` (after layout), `encodingActions` (shelf quick actions).

Expand Down
20 changes: 20 additions & 0 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ interface ChartAssemblyInput {
canvasSize?: { width: number; height: number }; // optional hard ceiling on stretch
chartProperties?: Record<string, unknown>;
};
theme_spec?: ThemeSpec | string; // presentation, Vega-Lite only
interaction_spec?: InteractionSpec; // behaviour, Vega-Lite interactive surface only
options?: AssembleOptions;
field_display_names?: Record<string, string>;
}
Expand Down Expand Up @@ -156,6 +158,24 @@ legend headers. Keep encodings bound to the original field names:
}
```

### `interaction_spec`

How the chart behaves. Lists interaction presets by `type`, each with its own
`options`, plus the surface policies `assistedTargeting` and `keyboardTargeting`:

```ts
interface InteractionSpec {
interactions: { type: InteractionPresetType; id?: string; options?: Record<string, any> }[];
assistedTargeting?: boolean | AssistedTargetingOptions;
keyboardTargeting?: boolean;
}
```

`buildInteractiveChart()` reads it; the assemblers ignore it. An entry the chart type
cannot honour is dropped with an `unsupported_interaction` warning, and `validateChart`
reports the same warnings before anything renders. `supportedInteractionPresets(def.interactionSupport)`
lists the presets a template supports by declaration. See [Using interactions](/documentation/interaction-spec).

### `chart_spec`

| Field | Description |
Expand Down
Loading
Loading