Thank you for being patient! We're working hard on resolving the issue
Every canonical (id, role) in the catalog maps to one class
per platform, all implementing a shared contract. New platforms
drop in by supplying their surface type + one class per id.
BUILTIN_RENDER_PLUGIN_SPECS is the canonical 45-entry list:
weather, timezone, data, task,
task-list, calendar, calendar-list, calendar-allday)block, group, header, column, grid,
canvas, timeline[row], timeline[column],
timeline-legend, sheet-footer)column.text, column.number,
column.task, timeline[row].event, grid.task,
canvas.bar, …)All three lists are exposed as role-grouped arrays:
import {
ALIAS_PER_TYPE_SPECS,
LAYOUT_PER_TYPE_SPECS,
RENDERER_PER_TYPE_SPECS,
PER_TYPE_SPECS, // alias + layout + renderer, in canonical order
BUILTIN_RENDER_PLUGIN_SPECS,
} from "@tento-lona/sheets";
interface PerTypeSpec extends RenderPluginSpec {
id: string;
role: "alias" | "layout" | "renderer" | "source";
aliases?: readonly string[];
legacyIds?: readonly string[];
dataProvider?: PluginDataProvider;
cellValueFormatter?: (value: CellValue | null, ctx: { rowType: string; pluginId: string }) => string;
}
Slots are shared across platforms — a cellValueFormatter on
COLUMN_NUMBER_SPEC runs in CLI and DOM alike, so a snapshot
test on CLI catches a corresponding DOM regression.
Every platform defines one class implementing this for each spec:
interface PerTypeAdapter<TSurface, TContext> {
readonly spec: PerTypeSpec;
render(ctx: TContext): TSurface;
onCellRecycled?(surface: TSurface): void;
onRowBound?(ctx: TContext): void;
}
TSurface is the platform's render output type:
HTMLElementCliPerTypeSurface (label + tags)TuiCellFormat (value + glyph + alignment)TContext is the platform's per-render context, also platform-defined.
Each platform mirrors the rows-side directory layout:
tento/tento-lona-js/sheets/src/plugins/
├── alias/ ← 8 specs
├── layout/ ← 10 specs
└── renderer/ ← 27 specs
tento/tento-lona-js/sheets-ui/src/plugins/{alias,layout,renderer}/...
← DOM classes, one per spec
tento/tento-lona-js/cli/plugins/{alias,layout,renderer}/...
← CliPerTypePlugin instances, one per spec
The cross-platform parity test
(tento/tento-lona-js/sheets/tests/plugins/cross-platform-parity.deno.test.ts)
asserts each platform's manifest mirrors BUILTIN_RENDER_PLUGIN_SPECS
exactly (same order, same (id, role) pairs).
Given a SheetRow, the binding resolvers find the plugin that
owns its cell / row / chrome surface:
import {
RenderPluginRegistry,
resolveCellRenderBinding,
resolveRowRenderBinding,
resolveChromeRenderBinding,
} from "@tento-lona/sheets";
const registry = new RenderPluginRegistry();
for (const spec of PER_TYPE_SPECS) registry.plugin(spec);
const cellBinding = resolveCellRenderBinding(row, registry);
const plugin = registry.get(cellBinding.pluginId, cellBinding.pluginRole);
tento/tento-lona-js/sheets/src/plugins/<role>/<id>.ts exporting
<NAME>_SPEC: PerTypeSpec.tento/tento-lona-js/sheets/src/plugins/index.ts in the appropriate
role-grouped array.tento/tento-lona-js/sheets-ui/src/plugins/<role>/<id>.ts (DOM impl).tento/tento-lona-js/cli/plugins/<role>/<id>.ts (CLI impl).cross-platform-parity.deno.test.ts — it asserts
every manifest stays in sync.__design_docs/lona-rows/rowkey/per-type-plugin-plan.md