Semiotic v3.8.0
Added
-
StreamPhysicsFrameand physics process runtime. A deterministic,
streaming physics frame for data stories where motion is the mechanism:
sampling, lateness, queues, routing, collision, threshold crossing, and
accumulation. It includes resident worker execution; running/paused/settled
lifecycle; chart-derived colliders, regions, portals, absorption, forces,
annotations, selection, observations, semantic interaction, and an imperative
control surface; optional Matter and Rapier adapters; capacity queues, finite
service-resource pools, service-level and dependency-gate controllers,
journey ledgers, reference envelopes, and trace comparison; accessible
settled projections and tables; deterministic SVG/evidence output and
server-stepped animated GIF frames; plus EventDrop, Galton board, gauntlet,
pile, collision, physical-flow, process-flow, and custom physics HOCs. -
ChartContainer notifications. New
notificationsprop surfaces
chart-level notices that have no single mark to anchor to (a data-pitfalls
or accessibility-audit finding about the whole chart, an unplaceable
data-quality result) and custom user-authored notes. They collapse into a
single severity-colored toolbar bell with a count badge — the bell
adopts the icon and color of the most severe visible notice — and clicking
it opens a popover with the dismissible cards, so a notice arriving or
being dismissed never reflows the chart body. EachChartNotificationis
{ id?, level?, title?, message, source?, dismissible? }; levels (info|
success|warning|error|neutral) resolve through the theme's
semantic role CSS variables, a screen-reader-onlyaria-live="polite"
region announces the current count + most-severe level so notices arriving
while streaming are still voiced with the popover collapsed, and dismissal
is tracked internally byidwith anonNotificationDismisscallback for
host stores/telemetry. Semantic class hooks:
semiotic-chart-notifications(wrapper),-toggle(bell),-badge,
-popover, andsemiotic-chart-notification+--{level}/
-source/-title/-message/-dismiss(cards). Types exported from
semioticandsemiotic/ai; demonstrated on the Chart Container feature
page and the Chart Clinic example. -
Worker-based force layout. ForceDirectedGraph gains
layoutExecution
("auto"default |"worker"|"sync"),layoutLoadingContent, and
onLayoutStateChange; inautomode, layouts whose estimated cost
(iterations × (nodes + edges)) exceeds a threshold settle in a short-lived
module Web Worker and fall back to the synchronous path when workers are
unavailable. NewforceLayoutAsync()(Promise sibling of theforceLayout
recipe, identical deterministic output) anduseForceLayout()(React wrapper;
SSR and first hydration stay synchronous for markup parity, and settled
positions are memoized by graph identity + options so remounts don't re-enter
a loading state). Ships a packagedforceLayoutWorker.jsasset — CSP
deployments needworker-src 'self'. -
x-bandannotation. Full-height vertical shaded region (x0,x1,
fill,fillOpacity,label) for marking eras/phases, rendered by both the
canvas annotation rules and static SSR output; annotations with a missing
bound are skipped in both. -
intervalLanesLayoutminBarWidth(default 2): zero- and short-duration
intervals stay visible and hoverable on long domains. -
Custom-layout readback:
ref.current.getCustomLayout(). All four custom
chart HOCs (XY/Ordinal/Network/Geo) expose the most recentlayout(ctx)
result on their ref, besidegetData()/getScales()— hosts that need the
computed placement (stats readouts, inspectors, validation) no longer re-run
the layout function themselves.nullbefore the first layout or on
built-in chart types. -
unwrapDatumalso unwraps.datumnesting (some interaction payloads
nest the raw object there rather than under.data), and is now documented
as the single unwrap path for bothonObservationand
frameProps.tooltipContentvalues.
Changed
- Force layout model overhauled (degree-aware charge, degree-normalized link
strength, radius-aware collision, weaker centering; theforceLayoutrecipe is
now backed by d3-force). Layouts settle to better-spread positions, but the
same seed produces different geometry than earlier 3.x betas — regenerate
position-pinned snapshots.forceStrengthdocumentation was corrected to its
actual link-attraction semantics, and its response curve changed with the new
model, so hand-tuned values may need revisiting. intervalLanesLayoutpacks in rendered-pixel space, so theminBarWidth
floor and inclusiveend + unitextension can no longer overlap a same-track
neighbor (previously visible as darker doubled fills when one interval ended
the same unit the next began).- DataPitfalls and GoFish adapters remain experimental. The DataPitfalls
bridge is exposed fromsemiotic/experimentalas
unstable_toDataPitfallsChain/unstable_buildDataPitfallsBridge, with
return-path helpers for DataPitfalls PR #35 reports:
unstable_toDataPitfallsNotificationsfor chart-level findings and
unstable_toDataPitfallsAnnotationsfor host-anchored Semiotic v3
annotations. The GoFish DisplayList adapter remainsunstable_fromGofishIR
on the same endpoint. Neither adapter is part of the stable API surface yet. - Force layout worker session.
runForceLayoutWorkerreuses a long-lived
ForceLayoutWorkerSession(request IDs, no spawn/terminate per layout),
matching the physics worker lifecycle. Abort cancels the pending request
without killing the shared worker. - Growing-window default cap.
windowMode: "growing"now defaults
maxCapacityto 100_000 (was 1_000_000). Dev builds log a one-time
warning when the buffer first crosses 50_000 points. world-atlasis an optional peer. Built-in reference geographies still
work when the package is installed; consumers who never call
resolveReferenceGeographyno longer take it as a hard dependency.
(Still a devDependency for tests/docs.)- Library build target raised from
es2015toes2020. - CI: full gate suite runs once on Node 22; other engines run a smoke
matrix (install, dist, vitest, typecheck). - size-limit budgets added for
physics,server,ai,recipes,
utils, andvalueentry points. - Stream frames memoized:
StreamXYFrame,StreamOrdinalFrame,
StreamNetworkFrame,StreamGeoFrame, andStreamPhysicsFrameare
React.memo-wrapped so parent re-renders with stable props skip the
frame body. - Interaction canvas idle skip on XY and Geo (one clear when hover ends).
- Shared
frameThemeColorsmodule for version-cached theme/background
resolution (StreamXYFrame uses it; other frames can adopt the same helper). RingBuffer.resizeusesslicefor shrink (avoids O(n²)shiftloops
when a growing window hits its cap).pipelineIdentityOpsshared helpers for id-keyed remove/update paths.- Shared
paintCanvasBackgroundhelper used by XY / Ordinal / Network / Geo
frames (CSS-var-safe fill, transparent + backgroundGraphics opt-outs). - Network dirty-canvas path: full clear/redraw only when data, transition,
particles, encodings, or continuous animation need it — annotation-only rAF
retries no longer thrash the data canvas. - Root entry diet: physics HOCs (
GaltonBoardChart,GauntletChart,
PhysicsPileChart, etc.) are no longer exported fromsemiotic. Import
fromsemiotic/physics(orsemiotic/aifor tooling). Measured full
package entry ~233 KB gz (was ~296 KB gz). - XY decay/pulse share a version-cached datum→index map
(buildDatumIndexMap) so continuous frames do not rebuild the map twice. - Geo dirty-canvas path: hover-only repaints skip the data canvas and
only update the interaction layer. - Shared
paintNeedshelpers (needsDataCanvasPaint/
needsInteractionCanvasPaint) used by Network/Geo/XY paint gates. - Test typecheck is clean: baseline emptied (~228 → 0 known errors).
Cleared keyboardNav (82), ordinalSceneBuilders (45), recipes, heatmap,
hydration suites, networkColoring, and the remaining debt files.
Deprecated
GuantletCharttypo alias ofGauntletChart— useGauntletChart.
Removal planned for the next major.
Docs
CONTRIBUTING.md/DEVELOPMENT.mdupdated for the tsup + Vite toolchain
(removed stale Rollup/Parcel/src/processingguidance).