flow: structured dtypes, and the widgets that read them

A series, record or list message declares its shape instead of riding
DType.JSON, so a widget binds a shape rather than some JSON and a wrong
binding is refused before anything runs. A list declares its item type,
which is what keeps list[float] expressible for a pipeline.

On top of that: an agenda over a list, a notification over a record, and
a dashboard alert channel that publishes engine faults as one — so a
panel can show what went wrong without a flow wiring it by hand.

Also: only None means a node published nothing, a falsy value of the
wrong shape is now the named error it always should have been; and the
gauge's readout says its size is viewBox geometry rather than type scale.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-17 15:06:45 +02:00
co-authored by Claude Opus 5
parent 18837e8880
commit 413501c6ce
16 changed files with 751 additions and 43 deletions
+4 -1
View File
@@ -57,9 +57,12 @@ const FIELDS: Record<Channel["kind"], [string, string, string][]> = {
],
smtp: [["to", "Send to", "someone@example.com"]],
webhook: [["url", "URL", "https://example.com/hook"]],
// The message has to be one a flow declares, like anything a dashboard
// writes to. A notification widget bound to it is what shows the alert.
dashboard: [["message", "Message", "house.notice"]],
}
const KINDS: Channel["kind"][] = ["ntfy", "smtp", "webhook"]
const KINDS: Channel["kind"][] = ["ntfy", "smtp", "webhook", "dashboard"]
/** A setting may hold a `{"$secret": "name"}` reference rather than a literal,
* so text that parses as JSON is stored as JSON and survives a round trip. */