視窗與內容區塊
app.open(title) 會回傳一個 Window。透過 win.blocks() 填充它;每個方法都會附加一個內容區塊,並回傳一個具型別的控制柄,供你保留以便日後更新。
內容區塊類型
| 方法 | 控制柄 | 適用於 |
|---|---|---|
fields([(label, value), …]) | FieldsHandle | 帶標籤的鍵/值狀態——表單、詳情面板 |
table(columns, rows) | TableHandle | 結構一致的記錄 |
list(items) / ordered_list | ListHandle | 序列 |
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單向操作
清除標記並不會還原明文——之後請重新設定一個新值。