Thank you for being patient! We're working hard on resolving the issue
Data rows are the SDK's charting entry point. They let you read synced source data, extract series, inspect rendering presets, and, when needed, compute or query regression curves.
For engineering work, the important split is:
DataVariant on a data row gives you a DataIndexDataSeriesVariant on a data-series child row gives you a RegressionIndexkernelSmooth() and impute() are the low-level math utilitiesData rows usually appear with virtual children:
├── [data] "Garmin Stress" (:~garmin:stress)
│ ├── [data-canvas]
│ │ ├── [data-renderer] ← points / curve / band / rules
│ ├── [data-series] ← one named series
│ ├── [data-series] ← another named series
│ └── [data-json]
What each piece does:
| Row type | Purpose |
|---|---|
data | Owns the synced source cells and exposes DataIndex |
data-series | Declares one named series and can expose RegressionIndex |
data-canvas | Declares chart-level config such as axes and render layers |
data-renderer | Declares visual layers such as points, rules, and regression curves |
data-json | Raw JSON/debug view |
If you want chart data and regression data available through row variants, register the factories when creating the client:
import { DataIndex, LonaSdk, RegressionIndex } from "@tento-lona";
const client = new LonaSdk.Client({
...config,
rowIndexFactories: [
DataIndex.factory(),
RegressionIndex.factory(),
],
});
DataIndex.factory() attaches to data rows.
RegressionIndex.factory() attaches to data-series rows whose
seriesType is "regression".
Start from the data row itself:
import { DataVariant } from "@tento-lona";
const dataRow = sheet.row({ type: "data" });
if (!dataRow) throw new Error("no data row");
const variant = dataRow.variant as DataVariant;
const dataIndex = variant.dataIndex;
if (!dataIndex) throw new Error("DataIndex not available");
If you started from a child row such as data-canvas or data-series, walk to
its parent first:
const parent = childRow.parent();
if (!parent?.is("data")) throw new Error("expected a data row parent");
const dataIndex = (parent.variant as DataVariant).dataIndex;
DataIndexDataIndex is the main read API for extracted chart series.
const series = dataIndex.getSeries("default");
if (!series) throw new Error("missing series");
console.log(series.key); // "default"
console.log(series.type); // "regression" | "default"
console.log(series.label); // optional display label
console.log(series.points.length); // extracted [x, y] points
console.log(series.yRange); // optional axis override
console.log(series.convHalfWidth); // optional smoothing hint
console.log(series.maxGap); // optional segmentation hint
console.log(series.minPoints); // optional regression threshold
console.log(series.minCoverage); // optional regression threshold
Enumerate every series:
for (const [key, series] of dataIndex.getAllSeries()) {
console.log(key, series.type, series.points.length);
}
Read only the points in an x-range:
const points = dataIndex.getPointsInRange("default", xStart, xEnd);
DataFrameFor rendering a visible column window, build a frame from the index:
import { NaiveDate, TimezoneRegion } from "@tento-chrono";
const range = new NaiveDate.Range(
NaiveDate.fromYmd1(2026, 3, 10).exp(),
NaiveDate.fromYmd1(2026, 3, 17).exp(),
);
const frame = dataIndex.getDataFrame(range, TimezoneRegion.UTC);
if (!frame) throw new Error("no frame");
console.log(frame.xRange); // { start, end }
console.log(frame.yAxis); // optional { min, max, center }
console.log(frame.rendererSpecs); // declarative render layers
Each frame contains:
| Field | Type | Meaning |
|---|---|---|
xRange | { start, end } | Numeric x-range for the requested date window |
yAxis | Option<{ min, max, center }> | Y-axis config from the preset |
series | ReadonlyMap<string, DataFrameSeries> | Windowed series data |
rendererSpecs | RendererSpec[] | Declarative render layers |
Each DataFrameSeries contains:
| Field | Type | Meaning |
|---|---|---|
key | string | Series key |
points | readonly [number, number][] | Raw points in the requested window |
yRange | Option<{ min, max, center }> | Series-specific axis override |
Important: DataFrameSeries does not include regression output. Regression
curves come from RegressionIndex, described below.
Regression data lives on data-series rows, not on DataFrameSeries.
import { DataSeriesVariant } from "@tento-lona";
const seriesRow = dataRow.children().find((row) => row.is("data-series"));
if (!seriesRow) throw new Error("no data-series child");
const seriesVariant = seriesRow.variant as DataSeriesVariant;
const regressionIndex = seriesVariant.regressionIndex;
if (!regressionIndex) throw new Error("RegressionIndex not available");
console.log(seriesVariant.seriesKey);
console.log(seriesVariant.seriesType);
console.log(seriesVariant.color);
console.log(regressionIndex.seriesKey);
console.log(regressionIndex.points.length);
Query regression output in a visible range:
const frame = dataIndex.getDataFrame(range, TimezoneRegion.UTC);
if (!frame) throw new Error("no frame");
const rawPoints = regressionIndex.getPointsInRange(
frame.xRange.start,
frame.xRange.end,
);
const curve = regressionIndex.getRegressionInRange(
frame.xRange.start,
frame.xRange.end,
);
for (const point of curve) {
console.log(point.x, point.y, point.confidence, point.density, point.trust);
}
Each regression point contains:
| Field | Meaning |
|---|---|
x | Evaluation coordinate |
y | Smoothed value |
confidence | Confidence band width |
density | Data density within the kernel window |
trust | Overall support signal for rendering decisions |
frame.rendererSpecs describes what the preset expects to be drawn. These are
declarative layer descriptions, not rendered objects.
for (const spec of frame.rendererSpecs) {
switch (spec.type) {
case "points":
case "regression-curve":
case "regression-band":
case "hrule":
case "hrule-all":
case "bar-chart-with-width":
case "buckets":
console.log(spec.id, spec.type, spec.key);
break;
}
}
Common spec fields:
| Field | Meaning |
|---|---|
id | Stable renderer identifier |
type | Layer type |
key | Optional series key |
color | Optional color |
radius | Point radius |
offset / offsetType | Rule placement |
center | Center value for rule families |
barColor, topColor, bottomColor | Layer-specific colors |
Common renderer types in built-in presets:
| Type | Meaning |
|---|---|
points | Raw data points |
regression-curve | Smoothed trend line |
regression-band | Confidence band fill |
hrule | Single horizontal rule |
hrule-all | Multiple reference rules |
bar-chart-with-width | Width-aware bars |
buckets | Bucketed aggregates |
If you want to run smoothing yourself, use the exported math helpers:
import { impute, kernelSmooth } from "@tento-lona";
const filled = impute(
[
[0, 70],
[2, 74],
[5, 73],
],
1,
);
const result = kernelSmooth(filled, 2);
for (const point of result.curve) {
console.log(point.x, point.y, point.confidence, point.density, point.trust);
}
Use impute() when you want to fill gaps before smoothing. Use
kernelSmooth() when you already have sorted [x, y] points and want a
smoothed curve with confidence metadata.
LinearYScalerIf you need a scaler for rendering, LinearYScaler is the public low-level
utility for converting data values to canvas coordinates:
import { castData, LinearYScaler } from "@tento-lona/sheets";
if (!frame.yAxis) throw new Error("missing y-axis");
const yScaler = new LinearYScaler({
range: frame.yAxis,
});
const canvasY = yScaler.d2c(castData(75), height, padding);
const dataY = yScaler.c2d(canvasY, height, padding);
const ticks = yScaler.getTicks();
row.variant