Semiotic 3.5.2
Shared HOC hooks, ProcessSankey + SankeyDiagram particle unification, regression sugar on five more charts, FlowMap push API, and a capability matrix the MCP server can filter against.
AI-Generated·
3.5.2 is mostly a "factor and extend" release. The long-running HOC/Frame architecture audit got two more steps closer to landing, the realtime family picked up auseStreamStatus hook for outside-world push observability, andProcessSankey finally inheritedSankeyDiagram's canvas particle pipeline so the two charts now share ribbon geometry and particle rendering end-to-end. Highlights below; full release notes are onGitHub.
Hooks & factoring
useSeriesFeatures— forecast + anomaly props are now first-class on AreaChart, Scatterplot, and ConnectedScatterplot (LineChart had them before; the others were carrying ~85 LOC of synthetic-key + lazy-load boilerplate each). Capability tags surface throughchartSpecs.ts / ai/capabilities.json for AI agent discovery.useEncodingDomain— generic [min, max] tracker over bounded data + push-mode values, extracted from BubbleChart's sizeBy logic. Fixes a latent Scatterplot bug wheresizeBy radii returned the raw data value as the pixel radius in push mode. String-field accessors hitting numeric-string values now coerce cleanly instead of leaking strings into the math.useStreamStatus— user-facing observer for push-API charts. Wraps a ref, intercepts push /pushMany, and exposes a reactive status enum ("idle" | "active" | "stale") plus lastPushTime. Surfaced viasemiotic/utils and semiotic/realtime. Wrap-once symbol guard prevents StrictMode double-wrap.useXYLineStyle hook (Phase 2 step 5 of the HOC/Frame audit)— the line-side analogue of useXYPointStyle. LineChart, MultiAxisLineChart, and MinimapChart (both main + overview lines) collapse to a single hook call covering base stroke width → color resolution → optional group-aware fill → mergeShapeStyleoverlay → wrapStyleWithSelection. Net ~65 LOC removed across three HOCs.
ProcessSankey unified with SankeyDiagram particles
Particles now ride the canvas + ParticlePool path that SankeyDiagram uses. The HOC writes pre-computed cubic bezier control points onto each ribbon spec;NetworkPipelineStore's particle-pool gate broadened fromchartType === "sankey" to also accept customNetworkLayout. SVG particle overlay deleted (~80 LOC, including the per-particle <circle>allocation per frame). Prop surface aligned: showParticles +particleStyle. Individual particleRadius /particleDuration / etc. props removed.
Side benefit: ribbon geometry itself is now a single source of truth. SankeyDiagram and ProcessSankey both call buildRibbonGeometry for the M-C-L-C-Z path emission. SankeyDiagram passes the d3-sankey S-curve control points; ProcessSankey passes lane-aware single-point bends. Same code path; identical shapes when the inputs match.
regression prop on more charts
Scatterplot, BubbleChart, ConnectedScatterplot, BarChart, and DotPlot now accept aregression prop: true | method string ("linear" | "polynomial" | "loess") | full RegressionConfig. Sugar over the existing trend annotation. Ordinal charts treat categories as integer indices and project the regression line through the band scale (with linear interpolation between band centers for LOESS fractional indices).
FlowMap push API
FlowMap joined the realtime-capable HOC family. The frame gained a geo-lines variant on useFrameImperativeHandle pluspushLine / pushManyLines / removeLine /getLines / lineIdAccessor on GeoPipelineStore.supportsPush: true in capabilities; docs streaming demo flipped fromsetState(flows) to ref.current.push(flow).
Capability matrix
ai/capabilities.json indexes all 44 charts across 5 categories withrenderModes / supportsPush / supportsSSR /supportsLegend / supportsSelection /supportsLinkedHover / colorModel / layoutMode /specialFeatures fields. Generated alongside docs/capabilities.mdby npm run docs:capabilities; locked against chartSpecs.ts bycheck:capabilities. suggestCharts({ capabilities }) accepts push/linkedHover/ssr/selection/legend constraints and surfaces a filteredOutlist with reasons. Interactive filterable matrix at/features/capabilities.
Other fixes worth mentioning
- Bundle-size truth source — a new
scripts/sync-bundle-sizes.mjs reads package.json#exports, gzips each entry point, and upserts the marker-block bundle-size tables in README.md, CLAUDE.md, and the AI system prompt. CI fails when a dependency bump nudges a bundle past its rounded boundary and the docs haven't been regenerated. - Edge value preservation through bounded ingestion —
Number.isFinite(numValue) ? numValue : 1 instead ofNumber(v) || 1, so a legitimate value: 0 edge survives end-to-end. algorithm.js → algorithm.ts— the last .js file in the chart source tree migrated to TypeScript. Types inlined as the canonical source.- Keyboard nav skips invisible scene nodes —
extractNetworkNavPoints skips r <= 0 circles andw <= 0 || h <= 0 rects. Keyboard focus on ProcessSankey now lands on a real band/ribbon instead of an off-canvas color-binding placeholder.
Upgrade notes
Most of 3.5.2 is additive. Three small breaking changes worth flagging:
- ProcessSankey particle props consolidated — if you were setting
particleRadius / particleDuration / particleDensity/ particleMaxPerEdge directly, fold them into a singleparticleStyle object (matches SankeyDiagram's shape). Number(v) || 1 → Number.isFinite(numValue) ? numValue : 1for network edge values — if you were relying on value: 0 being silently swapped to 1, that no longer happens. (You almost certainly weren't.)- Strings as edge values now coerce instead of being dropped — bounded ingestion accepts numeric strings (
"5" → 5) instead of silently dropping them. Same for sizeBy string fields. Almost always what you want; flagged because the previous behavior was permissive.