Skip to content

[ui] add the Chart layer: typed authoring spec lowering to SVG#1665

Open
fwbrasil wants to merge 5 commits into
mainfrom
charts-pr-clean
Open

[ui] add the Chart layer: typed authoring spec lowering to SVG#1665
fwbrasil wants to merge 5 commits into
mainfrom
charts-pr-clean

Conversation

@fwbrasil

@fwbrasil fwbrasil commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Problem

kyo-ui can render typed SVG, but there is no way to author a data visualization from a dataset. Today that means hand-computing scales, axes, layout, and legends and emitting raw Svg.* shapes, with no type-driven mapping of data fields to visual marks and no reactive or animated updates.

Solution

Add Chart, a pure immutable authoring spec (not a UI node) that lowers to an Svg.Root. The dataset fixes the row type, so field accessors like x = _.month infer with no annotations, and a Plottable typeclass derives the right scale per field (band scales for enums, linear for numbers, time for instants). Marks (bar, line, area, point, text, error bar, rule) are the visual vocabulary; axes, legends, layout, stacking, theming, scales, interaction, and transitions are applied by refinement methods that return a refined Chart. Because Svg.Root is itself HtmlContent, a lowered chart drops into any HTML container, and a chart bound to reactive data animates between states via SMIL.

The public surface is Chart.scala; the scale inference, axis, layout, legend, mark, interaction, and transition machinery lives in kyo.internal, so the reviewable API is small relative to the diff.

Notes

  • SVG layer: Animate gains calcMode/keyTimes/keySplines so chart transitions can drive spline-interpolated keyframes.
  • Renderer correctness: the session id and base path are interpolated into a <script> JS string but were escaped with the HTML-attribute escaper, the wrong context. They now use a JS-string escaper. Both values are server-controlled today (a random UUID and a configured base path), so this is hardening, not a vulnerability fix; the escaping is simply now correct for the context.
  • Demos renamed to the *Demo.scala convention, matching kyo-browser.
  • Test cleanup: removed compile-assertions that only restate language guarantees (a sealed type can't be extended, a private member isn't callable); every genuine designed-rejection contract test is kept.
  • Svg.Paint's Style.Color conversion moves from a given Conversion to a plain implicit def, dropping the now-unneeded import scala.language.implicitConversions across the module.

fwbrasil and others added 5 commits June 9, 2026 18:14
## Problem

kyo-ui can render typed SVG, but there is no way to author a data
visualization from a dataset. Today that means hand-computing scales,
axes, layout, and legends and emitting raw `Svg.*` shapes, with no
type-driven mapping of data fields to visual marks and no reactive or
animated updates.

## Solution

Add `Chart`, a pure immutable authoring spec (not a `UI` node) that
lowers to an `Svg.Root`. The dataset fixes the row type, so field
accessors like `x = _.month` infer with no annotations, and a
`Plottable` typeclass derives the right scale per field (band scales
for enums, linear for numbers, time for instants). Marks (bar, line,
area, point, text, error bar, rule) are the visual vocabulary; axes,
legends, layout, stacking, theming, scales, interaction, and
transitions are applied by refinement methods that return a refined
`Chart`. Because `Svg.Root` is itself `HtmlContent`, a lowered chart
drops into any HTML container, and a chart bound to reactive data
animates between states via SMIL.

The public surface is `Chart.scala`; the scale inference, axis,
layout, legend, mark, interaction, and transition machinery lives in
`kyo.internal`, so the reviewable API is small relative to the diff.

## Notes

- SVG layer: `Animate` gains `calcMode`/`keyTimes`/`keySplines` so
  chart transitions can drive spline-interpolated keyframes.
- Renderer correctness: the session id and base path are interpolated
  into a `<script>` JS string but were escaped with the HTML-attribute
  escaper, the wrong context. They now use a JS-string escaper. Both
  values are server-controlled today (a random UUID and a configured
  base path), so this is hardening, not a vulnerability fix; the
  escaping is simply now correct for the context.
- Demos renamed to the `*Demo.scala` convention, matching kyo-browser.
- Test cleanup: removed compile-assertions that only restate language
  guarantees (a `sealed` type can't be extended, a `private` member
  isn't callable); every genuine designed-rejection contract test is
  kept.
- `Svg.Paint`'s `Style.Color` conversion moves from a `given
  Conversion` to a plain `implicit def`, dropping the now-unneeded
  `import scala.language.implicitConversions` across the module.
Resolves conflicts introduced by #1664 (connection-bound reactive sessions):
- HtmlRenderer: clientJs(jsStr(basePath)), combining #1664's dropped sessionId
  (the WebSocket owns the session) with #1665's jsStr JS-string escaping.
- HtmlRendererTest: keep both added blocks (charts' JS-escaping tests + main's
  clientJs transport tests), adapting charts' tests to the 4-arg renderPage.
- SvgReactiveTest: import scala.language.implicitConversions (UISession removed
  upstream; UIExchange unused in the merged body).
- README: charts' demo list (matches the actual XDemo.scala files), with the
  stale "over SSE" corrected to "over the WebSocket".
- ChartMorphTest: 4-arg renderPage (drop the sessionId argument).

Validated: kyo-ui Test/compile clean; affected tests 116 passed, 0 failed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant