Thank you for being patient! We're working hard on resolving the issue
Sheets are the core data structure in Lona. Each sheet contains a tree of rows, where each row has a type, optional label, and date-indexed cell data.
Package note. The
Sheetvalue type — its stores, invalidation, canonical cell cache — lives in@tento-lona/sheets. The SDK layer (@tento-lona) exposes it asSheetHandle(a type alias) and wraps it with aSheetsAccessorthat owns the shared canonical-cell store across every open sheet. Most app code works withSheetHandle; reach for@tento-lona/sheetsAPIs (SheetsAccessor,CanonicalBackend,RangeCellHandle) when writing library or plugin code.
const sheets = await client.sheets.list();
for (const sheet of sheets) {
console.log(`${sheet.id} — ${sheet.title}`);
}
client.open(key) fetches the sheet and returns a SheetHandle:
const sheet = await client.open(sheetKey);
console.log(sheet.id); // sheet UUID
console.log(sheet.title); // sheet title or null
const created = await client.sheets.create({
title: "My Sheet",
rows: [
{ type: "number", label: "Weight" },
{ type: "text", label: "Notes" },
],
});
Update sheet metadata with client.sheets.update(). Updates are
fire-and-forget — the SDK syncs to the server without blocking the caller:
// Update title
client.sheets.update(sheetKey, { title: "New Title" });
// Update description
client.sheets.update(sheetKey, { description: "Weekly tracker" });
// Update frozen rows (pinned at top)
client.sheets.update(sheetKey, { numFrozenRows: 2 });
// Update sheet width (undefined = default)
client.sheets.update(sheetKey, { width: 1200 });
The local cache is updated immediately. The SDK emits
SheetMetadataChanged so the UI can refresh. Changing numFrozenRows or
width also emits FrozenRowsChanged / SheetWidthChanged so layout
effects rerun. See Invalidation.
Sheets are ordered by fractional positions. To reorder, compute a new position between two neighbors and send it to the server:
import { Rational } from "@tento-core/rational";
// Position between two sheets
const position = Rational.midpoint(
sheetAbove?.position, // upper bound (or undefined for first)
sheetBelow?.position, // lower bound (or undefined for last)
);
const result = await client.sheets.reorder(sheetKey, { position });
Repeated insertions between items cause fractions to grow (e.g., inserting
between 1/3 and 1/2 produces 2/5, then 3/8, 5/13, ...). When
numerator or denominator exceeds a threshold, the server
recanonicalized all positions — reassigning them as 1/n, 2/n, ..., (n-1)/n to reset the fractions to small integers while preserving order.
const result = await client.sheets.reorder(sheetKey, { position });
if (result.recanonicalized) {
// Server reset all sheet positions — update the full list
updateSheetCache(result.sheets);
} else {
// Only the moved sheet changed position
updateSingleSheet(sheetKey, position);
}
Recanonicalization is transparent — the server handles it automatically. Clients just need to check whether the response includes updated sheets and refresh their cache accordingly.
Delete calls feedback.confirmDelete() before proceeding:
await client.sheets.delete(sheetKey);
The SDK orchestrates: confirm → delete on server → remove from cache →
emit SheetListChanged.
Share sheets with other users:
// Grant access
await client.sheets.grantAccess(sheetKey, {
userId: "other-user-id",
role: "editor",
});
// List who has access
const acl = await client.sheets.getAcl(sheetKey);
// Revoke access
await client.sheets.revokeAccess(sheetKey, "other-user-id");
client.open() returns a SheetHandle — the primary interface for working
with a sheet's rows, cells, and tree structure.
| Property | Type | Description |
|---|---|---|
sheet.id | Uuid | Sheet UUID |
sheet.title | string | null | Sheet title |
sheet.numFrozenRows | number | Rows pinned at the top |
sheet.width | number | undefined | Explicit width override (px); undefined = viewport picks |
sheet.rows | RowsAccessor | Row lookup and operations |
sheet.cells | CellAccessor | Cell fetch and cache |
sheet.editable | boolean | Whether current user can edit |
sheet.cells.fetch(range) loads cells for all rows in a date range:
import { NaiveDate } from "@tento-chrono";
const today = NaiveDate.today();
const start = today.addDays(-60);
await sheet.cells.fetch(new NaiveDate.Range(start, today));
For a single row with progressive-load callbacks (loading shimmer → partial
data → final data), use RangeCellHandle — see Cells.
Access the row tree to navigate parent/child relationships:
const tree = sheet.tree();
for (const node of tree.nodes) {
const row = sheet.row(node.id);
console.log(`${row?.label} (${row?.type})`);
}
node.id is a RowLocalId — pass it directly to sheet.row(id); no
string conversion needed.
LonaSdk.Client setup