Thank you for being patient! We're working hard on resolving the issue
There are two weather templates with the same expanded shape but
different sourcing for locationId:
| Template | locationId source | When to use |
|---|---|---|
weather | props.locationId (per-row) | Sheet-specific rows ("Perth at the office") |
preferences-default-weather | prefs.defaultWeatherLocation (per-user) | The user's default weather row |
Both expand to the same data-source + data-renderer + logs triple.
The preferences-default-weather template wires an onAttach listener so
every alias instance fans out when the user changes their default location
preference; the weather template is purely static and only re-expands
when its own props mutate.
weather{
"type": "alias",
"label": "Perth",
"attributes": {
"template": "weather",
"props": { "locationId": "2063523" }
}
}
| Prop | Type | Required | Meaning |
|---|---|---|---|
locationId | string | yes | OpenWeatherMap city id (e.g. 2063523 for Perth) |
preferences-default-weather{
"type": "alias",
"label": "Weather",
"attributes": { "template": "preferences-default-weather" }
}
This template takes no props. The locationId is read from
CalendarPreferences.defaultWeatherLocation — a LiveData<string> backed
by localStorage under the lona-pref-default-weather-location key.
Updating the preference fans out via sheet.reexpandAliasesByTemplate(...)
to every sheet that has a preferences-default-weather alias attached, so
all default-weather rows pick up the new location simultaneously.
The canonical builtin :~lona:weather resolves server-side to the
preferences-default-weather template — linkRow(sheet, ":~lona:weather")
is the one-liner that adds a default-weather row.
:~lona:weatherThe canonical builtin :~lona:weather resolves to the
preferences-default-weather template. The wire form is one row with no
props; hydration reads the user's preferred location from
CalendarPreferences.defaultWeatherLocation (default Honolulu, 2550002)
and expands to the standard triple:
:~lona:weather ← wire row, type=alias
│ template=preferences-default-weather
│ (no props; locationId from prefs)
├─ data-source lookupKey=:~weather:2550002 ← from prefs
│ │ attrs={dtype:"obj", lid:2550002}
│ └─ data-renderer type=weather ← column.weather plugin
└─ logs lookupKey=:~weather:2550002:logs
attrs.logType=weather
When the user changes their default location preference, the alias
re-expands in place — both the data-source and logs lookup keys flip,
the renderer child's identity is preserved, and every sheet's default
weather row updates simultaneously. There is no per-row setProp for
this template.
When a user picks a city in the location modal, the alias carries an
explicit locationId (here Perth, 2063523):
alias template=weather
│ label="Perth"
│ props.locationId=2063523
├─ data-source lookupKey=:~weather:2063523
│ │ attrs={dtype:"obj", lid:2063523}
│ └─ data-renderer type=weather
└─ logs lookupKey=:~weather:2063523:logs
attrs.logType=weather
The shape is identical to the default — only the locationId prop and
the derived lookup keys differ.
data-source lookup key follows :~weather:{lid}. The server's
weather provider syncs into that key; cells encode as weather.v1.data-renderer is a data-renderer of type weather — the
FE's column.weather plugin paints daily forecast tiles in a week-span
layout.logs row is keyed :~weather:{lid}:logs and carries
attributes.logType = "weather" so the logs viewer filters to weather
sync events only.If you bypassed the template entirely and shipped the equivalent tree as plain rows, you'd write four rows (the alias replaces this with one):
data-source lookupKey=:~weather:2063523
│ attrs={dtype:"obj", lid:2063523}
│ label="Perth"
└─ data-renderer type=weather
storage=parent
logs lookupKey=:~weather:2063523:logs
attrs.logType=weather
label="Sync Logs"
(The two trees are peers — logs is a top-level row, not a child of
data-source. The alias bundles them as siblings under itself; the
hand-built form makes that explicit.)
Two reasons we don't do this:
Mutation cost. Switching cities is a one-prop edit on the alias versus three coordinated row edits (source key, source attrs, logs key). Templates make the canonical mutation atomic.
Schema versioning. Renaming column.weather → column.weather.v2
or adding a new sibling renderer is a template-factory edit; with hand-
built rows you'd migrate every persisted row.
The hand-built shape is still legal — it's how the renderers actually load. Templates are sugar that produces this tree on hydrate.
The data-renderer child is storage: "parent" — it lives on the
data-source, not on the alias. That matters for two reasons:
(applies to the weather template only — preferences-default-weather
rows update via CalendarPreferences.defaultWeatherLocation; see the
Default alias section.)
const row = sheet.row({ rowLabel: "Perth" });
row?.alias?.setProp("locationId", "2193733"); // → Auckland
This re-expands both child rows with the new lookup keys
(:~weather:2193733 and :~weather:2193733:logs) without dropping the
renderer child or the row label. The new lookup keys then drive the
canonical-cell stream through SheetsAccessor.
(applies to the weather template only — preferences-default-weather
takes no props.)
The weather template may carry an explicit lookupKey prop in addition
to locationId. When both are present, lookupKey wins for the source
key and the logs key derives ${lookupKey}:logs. This is how migrations
stage custom keys; in normal use, you only set locationId.
If neither prop is present, hydration fails with
Alias template "weather" requires non-empty string prop "locationId".
linkRow → server dispatches via the rowkey schema (:~weather
provider) → upserts a canonical weather SheetRowDb row → ships it back
in the sheet payload. The FE creates the alias row in
insertTopLevelRow, which mirrors it into the rows-side Sheet so
hydration runs immediately — no full-sheet refetch required.
weather.v1 canonical encoding