Draw the simulation's Stock trajectories with uPlot (ADR-0005): a real time axis, hover-to-read values, and a playhead-marker hook the canvas animation drives. SimChart owns the imperative uPlot lifecycle behind a small prop API.
3.0 KiB
uPlot draws the behaviour-over-time chart
Part of meadows · see DESIGN.md.
The Results panel (ADR-0004's phase 2) traces each Stock's value over time. It began as a hand-built SVG — a deliberate lean-substrate choice, no charting dependency. But a polyline is all that substrate could draw: no time axis, no gridlines, and a legend that could only show a Stock's final value, never the value under the cursor. For a tool whose whole point is reading behaviour over time, "what is this Stock at t = 40?" is the question you most want to answer by pointing at the curve.
Decision
Adopt uPlot (uplot, ~15 KB gzip) — a small, fast canvas time-series
library — for the behaviour-over-time chart. It earns the dependency with the
three things the SVG could not give: a real time axis with ticks and
gridlines, and a live legend that reads each Stock's value at the hovered
time.
uPlot is imperative and canvas-based, so it is wrapped in SimChart.vue,
which owns its lifecycle (build on mount, setData on recompute, rebuild on a
track-set change, ResizeObserver for width, destroy on unmount). ResultsPanel
stays declarative: it assembles plain aligned data (Stocks only — the system's
memory, per ADR-0004) and hands it down.
Considered Options
- Keep the hand-built SVG (zero deps) — rejected: re-implementing axes, tick placement, a hover cursor, and hit-testing is rebuilding a charting library, badly. The lean-substrate note was a code comment, not an ADR; nothing prior is contradicted by spending one dependency where it clearly pays.
@gouvfr/dsfr-chart(the original prompt) — rejected: it requires the entire DSFR design system (CSS + JS API) and embeds its own Vue and Chart.js, so we would ship Vue twice and a second design language clashing with DaisyUI. It is built for French-government dashboards (region maps, gauges), not generic time series.- Chart.js / vue-chartjs — viable and same engine class, but heavier; uPlot is lighter and faster for many series and live-updating data, which is exactly the simulation's shape.
- uPlot (chosen) — lightest credible option that gives the axis + hover.
Consequences
- This is the project's first runtime charting dependency. The price, as with
Vue Flow (ADR-0002), is a thin wrapper layer:
SimChart.vuetranslates between reactive props and uPlot's imperative API. - The x scale runs with
time: false— x is simulation time (start…stop), not wall-clock, which uPlot would otherwise format as calendar dates. - Track colours are read from the DaisyUI theme variables. The app ships a
single
lighttheme, so they are resolved once at build time; a future dark theme would need the plot re-initialised on theme change. - Swapping charting libraries later means rewriting only
SimChart.vue—ResultsPanelpasses plain[times, ...tracks]aligned data and knows nothing of uPlot.