畫布卡片
Agent 可以「畫」一張圖表或版面給使用者看:一份自成一體的 HTML/SVG 片段,SDK 把它渲染成一張
畫布卡片。內建 <Chatbot> 會自動處理,不必接線。
這段 markup 是不可信的
畫布內容是模型逐次對話生成的,可能含 <style> 與 <script>。直接注入宿主頁面等於把該頁面的
origin——它的 cookie、storage、DOM——交給不是你寫的內容。
一定要放在允許執行 script、但拒絕 same-origin 存取的獨立瀏覽環境裡。瀏覽器上就是一個
沒有 allow-same-origin 的 sandboxed iframe,用 srcdoc 餵進去。
這層隔離同時也把片段擋在網路之外——這是預期行為,畫布本來就被要求自成一體。
SDK 內建的 CanvasTemplate 已經照這個規則做好了。只有在完全繞過內建渲染時才需要自己處理。
型別
interface CanvasMessageTemplate {
type: "CANVAS";
title?: string; // 卡片標題;渲染端只顯示這個
canvas: { html: string };
}
title 是唯一的標題來源——不要從 markup 裡面撈標題。
摺進對話串之後是這個形狀:
type ConversationCanvasMessage = {
type: "canvas";
messageId: string;
html: string;
title?: string;
isDrawing: boolean; // true = 還在畫;false = 權威版本已抵達
traceId?: string;
};
串流:delta 只是為了讓它「看起來在畫」
畫布走 asgard.message.canvas.start / .delta / .complete 三個事件。
complete 帶的 html 是權威版本。 中途的 delta 純粹是為了讓卡片能被看到逐步成形;一個完全
忽略所有 delta 的客戶端,最後從這個欄位渲染出來的文件一模一樣。
isDrawing 為 true 時,html 是還在長的前綴,不是完整文件。
主題
畫布在 iframe 裡,拿不到宿主的 CSS。SDK 用 resolveCanvasTheme() 解析出七個具體值交給它:
| 欄位 | 用途 |
|---|---|
fg / bg | 前景/背景 |
accent | 強調:資料本身(長條、折線、標亮)與任何可點的東西 |
muted | 弱化文字:標籤、說明、座標軸 |
border | 細線、卡片外框、分隔線 |
padding / selection | 內距與選取色 |
前五個名字是與後端 show_canvas 工具描述的契約,不要自行改名。
resolveCanvasTheme() 取的是真實元素的 computed color / background-color,而不是
--fg / --surface 之類的自訂屬性。
原因是踩過坑:讀變數的版本在一套把主要文字色叫 --text-primary 的設計系統上完全對不到,文字色
退回接近白色,而 --surface 在淺色模式解析成 #ffffff——白底白字,每個像素都在,什麼都看不到,
只有在深色宿主上碰巧可讀。
computed 值沒有這些問題:它一定是解析後的 rgb(...)、一定反映當下生效的主題,也不需要知道宿主
把 token 叫什麼、寫成 hex 還是 hsl 還是 oklch。背景還會往上找祖先——卡片本身可能是透明的,而沒有
指定背景的 iframe 是白色的。
要覆寫的話,CanvasTemplate 收 theme?: Partial<ResolvedCanvasTheme>。