Windows & blocks
app.open(title) returns a Window. Fill it through win.blocks(); each method appends a block and returns a typed handle you keep for later updates.
Block types
| Method | Handle | Use for |
|---|---|---|
fields([(label, value), …]) | FieldsHandle | Labelled key/value state — forms, detail panes |
table(columns, rows) | TableHandle | Uniform records |
list(items) / ordered_list | ListHandle | Sequences |
text(s) | TextHandle | Prose, summaries |
notice(severity, text) | NoticeHandle | Validation and status messages |
media(mime, url) | MediaHandle | Images, charts, screenshots |
custom(payload) | BlockHandle | Anything Serialize that fits no other kind |
let win = app.open("Inbox");
win.set_summary("12 unread");
let stats = win.blocks().fields([("Unread", 12), ("Total", 340)]);
let mail = win.blocks().table(["From", "Subject"], vec![vec!["ops", "Deploy done"]]);
let banner = win.blocks().notice(Severity::Info, "Syncing…");Keeping the projection current
Every handle method is a projection update: version bumps and client notifications are automatic. Call them from your UI's change events.
fn on_recipient_changed(form: &FieldsHandle, win: &Window, value: &str) {
form.set("Recipient", value); // any Serialize value; auto version++ + notify
win.set_summary(if value.is_empty() { "…recipient empty" } else { "…ready" });
}The one-sync habit
For anything more than a single scalar, write one method that mirrors current state and call it after every mutation. This is what makes drift impossible:
impl Todos {
/// The only place the projection is written.
fn sync(&self) {
self.table.clear_rows();
for (i, t) in self.items.iter().enumerate() {
self.table.push_row([
(i + 1).to_string(),
t.text.clone(),
if t.done { "done" } else { "open" }.to_string(),
]);
}
}
/// Mutate, then sync — the one path buttons and agent actions share.
fn mutate(&mut self, f: impl FnOnce(&mut Self)) {
f(self);
self.sync();
}
}Fields upsert
FieldsHandle::set appends a field when the label is unknown, rather than erroring. That lets a projection grow fields dynamically — but it also means a typo silently adds a field instead of failing.
Large collections
Do not project thousands of rows. Mirror one page and declare the window with table_truncated / list_truncated, so the agent knows it is seeing a slice:
// rows[offset .. offset+PAGE] out of `total` overall
let log = win.blocks().table_truncated(["Seq", "Kind"], page_rows, offset, total);
// Later, after the underlying data changed:
log.set_truncation(new_offset, new_total);Careful
push_row does not adjust total. On a truncated block, restate it with set_truncation whenever the real count changes.
Sensitive values
Mark a field once; from then on you write it like any other, and Purview redacts it on the way out:
let f = win.blocks().fields([("Account", "")]);
f.set_sensitive("Account", true);
f.set("Account", "12345678"); // mirrored, but never exposed in app://windowsOne-way
Clearing the flag does not restore the plaintext — set a fresh value afterwards.