feat(interactions): admission per chart type, one owner per trigger - #127
Merged
Chenglong Wang (Chenglong-MS) merged 20 commits intoSep 20, 2026
Merged
Chenglong Wang (Chenglong-MS) merged 20 commits into
Chenglong Wang (Chenglong-MS) merged 20 commits into
Conversation
…s as a list Eight chart capabilities (elements, cartesian-region, angular-region, navigation, reorder, legend, discrete-axis, index) move to core as INTERACTION_CAPABILITIES, and ChartInteractionSupport describes what a chart type offers; ChartTemplateDef gains an optional interactions block for it. Each registry entry lists the capabilities its preset needs in requires, and every wrapper stamps preset on the definition it returns so a definition made in code can be checked against the same list as a spec entry. Nothing reads the declarations yet.
…pport The interactions block on ChartTemplateDef replaces the navigation and reorder fields and the supportedRegionGestures entry of semanticInteractions. All 36 chart types declare what they offer: marks that resolve to data, the drag regions, the navigable and reorderable axes, the legend, the discrete axis labels, and the index axis. The assembler reads the block for navigation axes, reorder axes, and region gestures, so the compiled facts are unchanged; a test asserts that every template carries the block. The design doc records the table with the source of each cell and the judgment calls open for review.
… the chart's capabilities The assembler confirms the declared capabilities against the bound encodings and writes the active list, with the chart type, into _interactionSemantics. Admission replaces its four inferred checks with one rule: every capability in the preset's requires list must be present, or a spec entry drops with a message that names the chart type and the missing property, and a code definition throws. A plan the assembler did not annotate is read the way the compile step read it, so hand-built plans keep their behaviour. The region capability covers either kind of drag region, because brush-x on a polar chart is honoured as an angular brush; only brush-angle needs the angular one. A survey of both lab tabs showed 787 cards with no difference between code and spec; the KPI card lost the region presets, and the scatter family gained the index declaration its lab cases relied on.
…t types against presets INTERACTION_PRESET_REQUIREMENTS in core is the single table of what each preset needs; the registry entries read it. declaredInteractionCapabilities() and supportedInteractionPresets() turn a template's interactions block into the presets it can honour by declaration, so hosts and docs can list them without the runtime. The Interactions lab gains a Coverage tab: all 36 Vega-Lite chart types against all 20 presets, with the representative test case assembled per row to show which presets are active for that data, which the chart type supports but the data does not confirm, and which it never offers.
…ver interaction support validateChart() checks interaction_spec the way the mount does: a malformed spec is an invalid_interaction_spec error, an entry the assembled chart cannot honour is the same unsupported_interaction warning the surface reports, and a backend that runs no interactions reports the spec as ignored. The MCP tool schema gains interaction_spec with the preset names as an enum, toAssemblyInput passes it through, validate_chart reports the drops, and list_chart_types returns the interaction presets each chart type supports from the template declaration.
… chart type supports A new Using interactions page (docs/interaction-spec.md, with a zh-CN mirror, registered in the site catalog) explains the spec shape, the twenty presets with what each needs and its default reset, the reset gestures, how a chart type declares support, the warnings and where to read them, and the equivalence of a spec entry and a factory call. The API reference gains interaction_spec on ChartAssemblyInput with a section of its own. The chart-author skill gains an Interactions section with the authoring rules. The generated Vega-Lite reference prints an Interactions line per chart type from the same declaration the mount enforces.
…m interaction_spec The MCP chart view mounts buildInteractiveChart() with the CSP-safe expression interpreter when the input lists interactions, on the same preview input the static render sizes; the static render keeps running for the PNG export, and the surface warnings join the assembler's. The site gains one spec-aware Vega-Lite component, InteractiveVegaLiteView, used by the editor and by TripleChart whenever the input carries interaction entries; a TestCase may carry an interactionSpec, and the editor ships an Interactive bar example. The index chart stage and the chart-to-external lab, which used presets only, now ask for them in interaction_spec.
xavier (xavier-shaw)
marked this pull request as draft
September 14, 2026 17:36
Review of Stages A to C with the maintainer: - the template block is interactionSupport, not interactions - the drag-region capability is cartesian-region, and the four polar templates declare both regions, because a rectangle or lasso resolves their arcs and brush-x is honoured as a sector - the assembler is the one authority on a chart's capabilities: it derives the confirmed list from declaredInteractionCapabilities and the bound encodings, writes _interactionSemantics for every Vega-Lite chart, and capabilities is a required field of the admission plan; the gates that replayed the old inferred rules are gone, a definition made by hand needs nothing - preset requirements live in INTERACTION_PRESET_REQUIREMENTS only; the registry entries and the definition type no longer carry a copy, and admission no longer imports the registry - one phrase table for the capabilities, one generator pass shared by the two lab tabs, no wrapper around the preview sizing, no dedupe of disjoint warning lists - the coverage tab pins its header, marks unsupported cells with a cross, and slants the preset names - docs: stages B and C, this review, the future steps set aside, and the changelog
xavier (xavier-shaw)
force-pushed
the
feat/interaction-admission
branch
from
September 15, 2026 23:50
adec4df to
4b09ad7
Compare
…r the geometry permits reorder and discreteAxis on every cartesian template except Violin and Range Area, which refused reorder before, and legend on every template that returns legendFields. The assembler still confirms each one against the bound encodings, so a chart without a discrete axis or a discrete legend drops the preset as before; what changes is that a categorical scatter reorders again and a violin split by shift toggles its legend again.
docs/design-interactions.md, with a zh-CN mirror, explains the interaction model the way design-semantics.md explains semantic types: the three documents a chart is made of, presets and definitions, the spec and its resolver, the chart-semantics dictionary, the eight capabilities with what the type declares and what the data confirms, the requirement table, the match and the conflict rules, reset, the pipeline, hosts and discovery, and how to extend the model. The appendix table of all 36 declarations is generated by gen:reference between markers, so it cannot go stale. The dated decision log docs/design-interaction-spec.md is removed; its history lives in the commits and the two pull requests. The Vega-Lite reference is regenerated for the templates that gained declarations.
xavier (xavier-shaw)
added this pull request to stack #128
September 16, 2026 00:56
xavier (xavier-shaw)
marked this pull request as ready for review
September 16, 2026 00:58
The runtime mounts one navigation, one region drag, and one element drag. A second spec entry in a slot now yields with a conflicting_interactions warning, the way a second navigate did; before, the runtime kept the first and said nothing.
A definition's affordances become a required map from the kind of hit to its cursor and hover. The runtime sends a hit only to the interactions that afford its kind, on every path: the click and hover lists, and the keyboard, context, long-press, and double-click paths that had no gate. The two claim flags and the hand-made target checks in seven handlers go, because a key says the same thing once. clickHighlight() gains withoutAffordances(), a copy that affords fewer targets and keeps its preset and reset.
A trigger is one gesture on one kind of hit. triggersOf(definition) reads the triggers a definition takes from its event source, affordance keys, state group, and reset list. For each trigger two admitted definitions share, the one that can give it up and keep the rest does so through withoutAffordances with an info warning; otherwise the later entry yields whole, a spec entry always yields to code, and two code definitions throw. The three pair rules and the slot loop become rows of this one loop. Fixes the legend click that both click-highlight and legend-toggle answered: the series hid and every other bar dimmed.
Ten cases, each a pair of presets that share a trigger, with the live chart, the spec, the warning the surface reports, and what to try.
The mount filtered the authored list through an identity set of the admitted definitions. A definition that admission replaced with a narrower copy was neither in the set nor in the authored list, so it was never mounted: click-highlight vanished from every chart where it yielded a trigger. The mounted list is now the admitted one, in the author's order, matched by id, with external definitions passed through.
The render loop sent an axis target to the label painter only and skipped the render keys the axis resolver attached to its element, so a click on a discrete axis label through click-highlight or axis-highlight changed nothing. The element now also falls through to the mark key loop: the category's marks emphasise, the rest mute, and click-none clears it.
Two charts per row, a title, the warning under the chart, and the spec in a collapsed panel; the story and the try-it text are gone.
The scale navigation controller honours the transition option the geo
controller already had: the domain tweens from the frame on screen to the
target, the centre linearly and the span geometrically in the axis's
transformed space, and a render during the tween keeps its frame.
navigate() flies home over 400 ms unless resetTransition says otherwise;
brushZoom() tweens into the brushed region on commit and flies home on
reset, from a gesture or the reset control. { duration: 0 } turns one off.
Panning and wheel zooming still follow the pointer.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #124. Design doc:
docs/design-interactions.md(en, zh-CN).Summary
Interaction admission: Each chart type now declares the interaction capabilities it supports, and each preset declares the capabilities it requires. Admission matches the two.
validateChart(), the MCP tools, and the docs read the same declaration. The MCP chart view, the site editor, and the gallery mount frominteraction_spec.Interaction comfliction: Two interactions that share a trigger now get one owner.
affordancesis a map from hit kind to cursor and hover, and it is the only dispatch gate. For each shared trigger, admission either narrows one entry with aninfowarning or drops the later entry.Why
The old rules admitted every preset on every chart type, even the KPI card and the pie chart. The "element semantics" check was always true, and the registry's
requireswas never read.A legend click could also reach both
click-highlightandlegend-toggle: the series hid and the other bars dimmed. Target checks lived in three places, and four dispatch paths read none of them.What changed
ChartInteractionSupport,INTERACTION_PRESET_REQUIREMENTS,supportedInteractionPresets().interactionSupport.navigation,reorder, andsupportedRegionGesturesmove into it.triggersOf()resolves shared triggers.affordancesis a required map keyed bymark,legend-item,axis-label,plot.affordsTarget()gates every path, keyboard and long-press included. An axis label click now emphasises the category's marks.validateChart()reports the warnings. The MCP schema gainsinteraction_spec.list_chart_typesreturnsinteractionsper chart type.InteractiveVegaLiteViewfor the editor andTripleChart, an Interactive bar editor example.docs/interaction-spec.md, API reference, chart-author skill, generated reference.Key changes
ChartTemplateDef.navigationand.reordermove intointeractionSupport.listInteractionPresets().requiresis a list. The capabilityelement-semanticsis nowelements.affordancesis required.claimsLegendActivationandclaimsAxisActivationare removed.Verification
🤖 Generated with Claude Code