Skip to content

窗口与内容块

app.open(title) 返回一个 Window。通过 win.blocks() 填充它;每个方法都会追加一个内容块,并返回一个类型化句柄,供你日后更新。

内容块类型

方法句柄适用于
fields([(label, value), …])FieldsHandle带标签的键值状态——表单、详情面板
table(columns, rows)TableHandle结构一致的记录
list(items) / ordered_listListHandle序列
text(s)TextHandle说明文字、概述
notice(severity, text)NoticeHandle校验与状态提示
media(mime, url)MediaHandle图片、图表、截图
custom(payload)BlockHandle任何实现了 Serialize 且不属于其他类型的数据
rust
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…");

让投影保持最新

句柄的每个方法都是一次投影更新:版本号递增和客户端通知都是自动完成的。在你的 UI 变更事件里调用它们即可。

rust
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" });
}

单一同步入口的习惯

只要不是单个标量那么简单,就写一个方法把当前状态整体镜像过去,并在每次变更后调用它。这正是让失真无从发生的关键:

rust
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();
    }
}

字段是 upsert 语义

当标签不存在时,FieldsHandle::set 会追加一个新字段,而不是报错。这让投影可以动态地增加字段——但也意味着写错标签时只会悄悄多出一个字段,而不会失败。

大型集合

不要把上千行数据全部投影出去。只镜像其中一页,并用 table_truncated / list_truncated 声明该窗口,好让 Agent 知道自己看到的只是一个切片:

rust
// 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);

注意

push_row 不会自动调整 total。对于已截断的内容块,只要真实总数发生变化,就要用 set_truncation 重新声明它。

敏感数据

只需标记一次;此后你像写普通字段一样写它,Purview 会在输出时自动脱敏:

rust
let f = win.blocks().fields([("Account", "")]);
f.set_sensitive("Account", true);
f.set("Account", "12345678"); // mirrored, but never exposed in app://windows

不可逆

清除该标记并不会恢复原文——之后需要重新写入一个新值。