Thank you for being patient! We're working hard on resolving the issue
The calendar template renders a calendar surface — timed events on a
day/week grid, the connected calendar list as a sidebar, and a secondary
timezone strip when the user has one configured.
It's the largest builtin template and demonstrates three patterns together: nested aliases, layout switching, and app-layer dependencies.
{
"type": "alias",
"label": "Calendar",
"attributes": { "template": "calendar" }
}
The calendar template takes no props — one alias row produces one
calendar surface for the active user.
The template's activeLayout rule synthesizes a layout row at hydrate
time — timeline[column] for day/week, grid for month. That row's
DOM-side plugin (TimelineColumnLayout / CalendarMonthPlugin) owns
the time-grid (or month-grid) surface. The calendar-list legend and
secondary-timezone bar nest under the synthesized layout row via the
template's layoutChildren field — those aliases get subsumed by the
layout's activeLayout and absorbed into the surface's slot machinery
instead of rendering as standalone rows.
alias template=calendar
├─ data-source preset=calendar ← TRANSITIONAL ⚠
│ │ attrs={dtype:"obj", startHr:0, endHr:24}
│ └─ data-renderer type=calendar ← column.calendar painter
└─ timeline[column] ← synthesized by activeLayout(timeScale)
"timeline[column]" for day/week,
"grid" for month
├─ alias template=calendar-list
└─ alias template=preferences-secondary-timezones
The data-source and the synthesized layout row are siblings under
the alias. The alias's static children rule emits only the
data-source; layoutChildren declares the calendar-list +
secondary-timezones aliases that the hydrator places under the
synthesized layout row. When the user switches timescale,
Sheet.setTimeScale(s) re-runs hydration so the layout row swaps type
(timeline[column] ↔ grid).
⚠ Transitional shape. The
data-renderer (type=calendar)is the painter today — it resolves to thecolumn.calendarplugin (CalendarSheetPlugin) which paints the time-grid surface (hourly divisions, timed-event positioning along a vertical hour axis) and walks the descendants for legend / timezone data.
TimelineColumnLayout(the new layout-role plugin fortimeline[column]) takes the same painting logic but binds at the synthesized layout row, walking up to the alias and then sideways to the sibling data-source for theCalendarRenderertyped accessor. Once the live calendar is verified to paint correctly throughTimelineColumnLayout, the transitionaldata-renderer (type=calendar)child drops out, the data-source itself can retire, andCalendarSheetPluginretires alongside it.
| Identifier | Role | Notes |
|---|---|---|
calendar | alias template, row type, column.calendar renderer | One per connected calendar; the transitional renderer paints the time grid today |
calendar-list | alias template, row type, aggregator | Subsumes multiple per-account calendars; provides the unified events facade |
calendar-allday | alias template | Wraps calendar-list under timeline-row for the all-day strip |
timeline[row] / timeline[column] / grid | layout row types | Synthesized by activeLayout at hydrate time; timeline[row] (all-day strip), timeline[column] (timed grid for day/week), grid (month) |
TimelineRowLayout / TimelineColumnLayout | DOM layout plugins | One class per layout id; timeline-plugin.ts + calendar-plugin.ts are transitional and retire once the new classes paint end-to-end |
timeline[*].event | renderer suffix | Singular event by convention; event-list is not a thing in this codebase |
EventsIndex | client query API | row.index(EventsIndex) returns event queries |
EVENTS_FACADE | rows-side facade key | Established; do not rename |
The two nested aliases recursively expand. calendar-list is itself
dynamic-tail driven — one source row per connected provider account:
alias template=calendar-list (app-layer)
├─ data-source lookupKey=:~google:auth_a:calendar:cal_x
│ ├─ data-renderer type=calendar
│ ├─ data-renderer type=event, slot=timeline[row]
│ ├─ data-renderer type=event, slot=timeline[column]
│ └─ data-renderer type=event, slot=grid
├─ data-source lookupKey=:~google:auth_a:calendar:cal_y
│ └─ ...
└─ data-source lookupKey=:~apple:auth_b:calendar:cal_z
└─ ...
preferences-secondary-timezones produces one nested timezone alias per
configured tz:
alias template=preferences-secondary-timezones
├─ alias template=timezone, props.tz=Pacific/Auckland
│ └─ data-source ... (see timezone template page)
└─ alias template=timezone, props.tz=Europe/London
└─ data-source ...
The calendar template declares an activeLayout rule:
activeLayout: (timeScale) =>
timeScale === "month" ? "grid" : "timeline[column]"
This means the same alias row picks a different layout plugin at the
month time scale — the FE binds the grid layout (month grid) instead
of the timeline[column] layout (timed grid) without rewriting the
tree. No mutation; the layout is selected per-render from the sheet's
active time scale. Children whose subsumedBy list contains the active
layout id (e.g. calendar-list's subsumedBy: ["timeline[column]","grid","timeline"]) get hidden and absorbed into
the parent's slot machinery instead of rendering as standalone rows.
The all-day strip is not nested inside the calendar template. It's
a separate top-level alias (calendar-allday) that the system auto-pairs
with each calendar row via the header binding mechanism — users see
them paired visually without having to add the all-day row themselves.
The two are peers at the row tree level:
[Sheet root]
├─ alias template=calendar
│ └─ data-source (preset=calendar)
│ └─ ...
└─ alias template=calendar-allday ← peer, auto-linked in header
└─ timeline-row label="All-day"
└─ alias template=calendar-list
└─ ... (one data-source per connected calendar)
The all-day strip's events come from the same calendar-list aggregate
the timed grid uses (just filtered to all-day cells via
calendar-allday-events's fullDayEventsInRange filter). The visual
pairing reflects the shared data flow.
calendar-list and preferences-secondary-timezones aren't pure-static
factories — they each need a closure:
| Template | Dependency | Source |
|---|---|---|
calendar-list | CalendarConfigProvider | tento/tento-lona-js/src/preferences/calendar-aliases.ts |
preferences-secondary-timezones | prefs LiveData | tento/tento-lona-js/src/preferences/preferences-aliases.ts |
Apps register them at SheetsAccessor construction by passing
calendarListAliasFactory(provider) and secondaryTimezonesAliasFactory(prefs)
alongside defaultBuiltinAliasFactories(). Tests that don't exercise the
calendar-list dynamic tail can register stub factories that return empty
children.
Events themselves are not a template — they're cells on the calendar data-source. See Events for the events surface, time-range shape, and provider routing.