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:
@@ -85,11 +85,15 @@ def find(panel_id: str) -> PanelDef | None:
|
||||
|
||||
|
||||
def messages_for(panel_id: str, store: DashboardStore) -> set[str]:
|
||||
"""Every message the widgets of this panel's dashboards read or write.
|
||||
"""Every message this panel's dashboards read or write.
|
||||
|
||||
What a screen is entitled to see, as its own dashboards define it. Read
|
||||
from the published documents, since that is what a panel draws, and empty
|
||||
for a panel that is gone — which is the same answer as "nothing".
|
||||
|
||||
A dashboard's own bound settings count, not only its widgets': the theme a
|
||||
panel is driven to is a message no tile on it draws, and a wall panel
|
||||
refused its own theme message is the one surface the setting exists for.
|
||||
"""
|
||||
panel = find(panel_id)
|
||||
if panel is None:
|
||||
@@ -100,6 +104,7 @@ def messages_for(panel_id: str, store: DashboardStore) -> set[str]:
|
||||
defn = store.read(dashboard)
|
||||
except DashboardNotFound:
|
||||
continue
|
||||
names.update(defn.setting_messages)
|
||||
for widget in defn.widgets:
|
||||
names.update(widget.messages)
|
||||
if widget.target:
|
||||
|
||||
Reference in New Issue
Block a user