跳至主要内容

畫布卡片

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 工具描述的契約,不要自行改名。

它讀的是實際算出來的顏色,不是 CSS 變數

resolveCanvasTheme() 取的是真實元素的 computed color / background-color,而不是 --fg / --surface 之類的自訂屬性。

原因是踩過坑:讀變數的版本在一套把主要文字色叫 --text-primary 的設計系統上完全對不到,文字色 退回接近白色,而 --surface 在淺色模式解析成 #ffffff——白底白字,每個像素都在,什麼都看不到, 只有在深色宿主上碰巧可讀。

computed 值沒有這些問題:它一定是解析後的 rgb(...)、一定反映當下生效的主題,也不需要知道宿主 把 token 叫什麼、寫成 hex 還是 hsl 還是 oklch。背景還會往上找祖先——卡片本身可能是透明的,而沒有 指定背景的 iframe 是白色的。

要覆寫的話,CanvasTemplatetheme?: Partial<ResolvedCanvasTheme>

也看看