Thank you for being patient! We're working hard on resolving the issue
Rows are the main building blocks of a sheet. Some rows hold data, some organize other rows, and some provide specialized views such as calendars, graphs, or generated reports.
Most SDK code works with rows through a SheetHandle:
for (const row of sheet.rows.list) {
console.log(row.type, row.label);
}
Every row has a few common concepts, even when the type is different:
| Field | Meaning |
|---|---|
id | The row's RowLocalId — in-memory identity |
descriptor | Immutable role + dtype definition (see below) |
type | The row's wire type ("calendar", "number", …) |
label | Human-readable display name |
lookupKey | Optional stable name for programmatic access |
timeScale | How the row maps onto dates and periods |
attributes | Type-specific configuration bag |
Some rows primarily store cells. Others primarily provide structure or derived views.
const byId = sheet.row("r_0123456789abcdef0123456789abcdef");
const byLabel = sheet.row({ label: "Weight" });
const byLookupKey = sheet.row({ lookupKey: ":revenue" });
const byType = sheet.row({ type: "number" });
Use labels when the query is user-facing. Use lookup keys when the reference needs to survive renames.
Rows live in a tree, not just a flat list. Group rows, timeline rows, and other container-style rows can own children.
const tree = sheet.tree();
for (const node of tree.nodes) {
console.log(node.id, node.children.length);
}
This is the important public model:
Every row has a role on its descriptor — the governing axis of its behavior. Four roles, four concrete base classes:
| Role | Base class | What it does |
|---|---|---|
source | SourceRow | Owns or computes data (user edits, provider pushes, formulas) |
layout | LayoutRow | Rendering container — timeline, canvas, grid, list, column |
renderer | RendererRow | Fills a single slot inside a layout (column.text, canvas.hrule, …) |
alias | AliasRow | Figma-style template instance with props + overrides |
import { SourceRow, LayoutRow, RendererRow, AliasRow } from "@tento-lona/sheets";
const row = sheet.row({ label: "Weight" });
if (row instanceof SourceRow) {
// Has cells(), setCell(), facades, etc.
}
Most app code never needs to check the role directly — use facades (see below) or the row's type string when branching.
The exact row type list evolves over time, but most rows fit into a few broad families:
| Family | Examples | What they do |
|---|---|---|
| Basic data rows | text, number, checkmark, formula | Store or compute scalar values |
| Calendar and time rows | calendar, calendar-list, timezone | Show time-based data and events |
| Structural rows | group, block, timeline, calendar-month | Organize or present child rows |
| Data views | graph, data, data-canvas, data-series, data-renderer | Visualize or transform data |
| Specialized rows | weather, logs, ai-report | Integrations or generated content |
For most applications, the useful question is not "what is the full internal row architecture?" but "what kind of row am I working with, and what should the user be able to do with it?"
Three row concepts are easy to mix up:
label is for displaylookupKey is a stable named reference (e.g. :revenue or :~weather)RowLocalId is the in-memory identity used by trees and UI stateIf you need a durable reference, prefer a lookup key or row key over a label.
If you need to key a Map or work with sheet.tree(), use the RowLocalId
you already have.
See Row Keys and Row Identity.
Not every visible row is a normal user-created row.
virtual(r_...|calendar-list) under a calendar row)system(header), system(footer))These rows are still part of the tree and still have identifiers, but they usually behave more like structure than user-authored content.
For rows whose cells carry object data — calendar events, tasks, weather
observations, scheduling links — use facades. A facade is obtained via
row.as(FACADE_KEY) and gives you a typed API over the row's cells without
hand-parsing attributes or CellEntry unions.
import {
EVENTS_FACADE,
TASKS_FACADE,
WEATHER_FACADE,
DATA_FACADE,
SCHEDULING_LINK_FACADE,
} from "@tento-lona/sheets";
import { NaiveDate } from "@tento-chrono";
// Calendar events — partial-day + full-day queries
const calendarRow = sheet.row({ type: "calendar" });
const events = calendarRow?.as(EVENTS_FACADE);
if (events) {
const today = NaiveDate.today();
for (const event of events.eventsOn(today)) {
console.log(event.summary, event.start, event.end);
}
}
// Tasks — timed + due + slot queries
const tasksRow = sheet.row({ type: "tasks" });
const tasks = tasksRow?.as(TASKS_FACADE);
if (tasks) {
for (const task of tasks.tasksInRange(range)) {
console.log(task.title, task.status);
}
}
// Weather — daily observations
const weatherRow = sheet.row({ lookupKey: ":~weather" });
const weather = weatherRow?.as(WEATHER_FACADE);
const observation = weather?.observationOn(today);
// Data-series — graph props, series, custom cells
const dataRow = sheet.row({ type: "data" });
const data = dataRow?.as(DATA_FACADE);
if (data) {
for (const [key, series] of data.getAllSeries()) {
console.log(key, series.points.length);
}
}
Facades return null when the row isn't shape-compatible, so the optional-
chain pattern above is safe. See
Row Roles & Facades for the full list and the
registration mechanism.
For configuration that isn't cell data — calendar time bounds, formula style, graph scale — read the row's typed attribute bag:
const calendarRow = sheet.row({ type: "calendar" });
const attrs = calendarRow?.attributes<{
startHr?: number;
endHr?: number;
calendarType?: string;
timezone?: string;
}>();
console.log(attrs?.startHr, attrs?.endHr, attrs?.timezone);
const formulaRow = sheet.row({ type: "formula" });
const formulaAttrs = formulaRow?.attributes<{ style?: string; size?: string }>();
console.log(formulaAttrs?.style, formulaAttrs?.size);
The attributes<A>() generic tells the compiler the shape you expect; the
runtime returns {} when the row has no attribute bag.
RowKey, RowLocalId, RowId