Add the dashboard settings channel, wired for theme and lock
A dashboard could only ever receive as a set of tiles. This adds the dashboard itself as a receiver: `settings` maps a name to a value plus an optional binding. Unbound, the setting is simply its value — a wall panel that is always dark costs no flow. Bound, a flow drives it live and the value is the fallback. Two settings are wired: `theme` (system/light/dark) and `locked` (read-only). There is no schedule field on purpose — a node publishing to the bound message on a cron is what a schedule is here, which is the point of a channel. - `messages_for()` now walks a dashboard's bound settings as well as its widgets' bindings. Without this a paired screen is refused its own theme message, on the one surface the setting exists for; it bounds the socket too. - `locked` is gated in `usePublish`, so every control inherits it, and each control also draws itself disabled — a dead button reads as broken otherwise. The panel surface says Read-only in the corner. - The theme is a class on the dashboard's own surface, never the root: inside the app shell it must not flip the chrome. `.light` gains the tokens `.dark` already had (mirrored in the index repo) so both directions work on a subtree. - Settings bindings are type-checked from the document alone, the rule widget bindings follow, and mirrored on the server. - A bound setting is drawn on the flow canvas as a dashboard-level endpoint. - The demo's house flow now publishes `home.panel_theme`, which the demo dashboard's theme binds to: the panel goes dark after sunset, at no tile cost. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tULRZJUkZsw7rMJ3h4xvu
This commit is contained in:
@@ -86,6 +86,44 @@ it, so swapping the store is a change to one flow and nothing else. The answer
|
||||
also states what it was computed for, so an answer to a different question is
|
||||
ignored rather than two charts overwriting each other's picture.
|
||||
|
||||
## Dashboard settings
|
||||
|
||||
Most of what a dashboard carries is a widget: a tile bound to a message. Two
|
||||
things are not, because they belong to the whole surface rather than to any
|
||||
tile on it — and a screen bolted to a wall has nobody standing at it to set
|
||||
them.
|
||||
|
||||
| Setting | Is | Driven by |
|
||||
|---|---|---|
|
||||
| **Theme** | `System`, `Light` or `Dark` | a `str` message |
|
||||
| **Lock** | read-only on or off | a `bool` message |
|
||||
|
||||
Both work the same way, and both halves are optional:
|
||||
|
||||
- **Just a value.** Set Theme to `Dark` and that dashboard is dark wherever it
|
||||
is shown, whatever the device or the browser prefers. This costs no flow at
|
||||
all, and it is what most wall panels want.
|
||||
- **Driven by a message.** Pick one in **Driven by** and a flow takes the
|
||||
setting over, exactly as it drives a tile. The value you set stays the
|
||||
fallback: what the dashboard uses before the first message arrives, and
|
||||
whenever the message is silent.
|
||||
|
||||
A bound setting is type-checked like a widget binding — a Theme pointed at a
|
||||
`float` is refused by the editor and by the server — and the panel's credential
|
||||
is extended to it, so a paired screen may read its own theme message and
|
||||
nothing further.
|
||||
|
||||
There is no schedule field, on purpose. **A schedule is a node publishing to
|
||||
the bound message**: an `inject` with a cron expression, feeding a `change`
|
||||
node that maps the hour onto `"dark"` or `"light"`, is the whole of "dark after
|
||||
sunset" — and the same channel then serves anything else you want to drive,
|
||||
including locking a panel down remotely.
|
||||
|
||||
Lock is a read-only *surface*, not a permission. The controls stay visible,
|
||||
stop publishing and read as disabled, and the panel says **Read-only** in the
|
||||
corner. What a paired screen is allowed to reach is still decided by its own
|
||||
credential, below.
|
||||
|
||||
## Showing one
|
||||
|
||||
- `/view/{name}` — a browser tab pointed at one dashboard. Needs an ordinary
|
||||
@@ -110,9 +148,12 @@ What the screen holds is not a login. It reaches that panel's published
|
||||
dashboards and the messages its own widgets read or publish, and nothing else —
|
||||
a message no tile on it draws is refused in both directions.
|
||||
|
||||
It cannot be made strictly read-only, and that is honest rather than an
|
||||
oversight: a querying chart publishes its request, and a control on a panel is
|
||||
the reason you put one there.
|
||||
The *credential* cannot be made strictly read-only, and that is honest rather
|
||||
than an oversight: a querying chart publishes its request, and a control on a
|
||||
panel is the reason you put one there. The dashboard's own **Lock** setting
|
||||
above stops its controls publishing and can be driven by a flow, which is how
|
||||
you quieten a screen remotely — but that is the surface behaving, not the
|
||||
credential being narrowed.
|
||||
|
||||
Deleting the panel revokes the credential and the assignment together, which is
|
||||
how you retire a device and what it showed. Unpairing revokes only the
|
||||
|
||||
Reference in New Issue
Block a user