開箱即用的 json-render 應用程式除錯面板。當使用者想要除錯生成式 UI、檢視 spec 樹狀結構、在執行階段編輯狀態、查看已發送的 action、即時追蹤串流 patch、瀏覽 catalog,或是透過點選 DOM 元素來尋找其 spec key 時使用。觸發語句包含「新增 devtools」、「除錯 json-render」、「檢視 spec」、「為什麼這個元素沒有渲染」、「查看執行階段狀態」,或要求為 `@json-render/devtools` 擷取串流 / 記錄 action 紀錄等。
@json-render/devtools
專為 json-render 應用程式打造的浮動式除錯面板。提供跨框架的核心庫,並針對不同框架(React、Vue、Svelte、Solid)提供相應的 Adapter。
生產環境安全:當 NODE_ENV === "production" 時,此元件會直接渲染為 null。
安裝
安裝核心套件以及與主應用程式渲染器匹配的 Adapter。
# React
npm install @json-render/devtools @json-render/devtools-react
# Vue
npm install @json-render/devtools @json-render/devtools-vue
# Svelte
npm install @json-render/devtools @json-render/devtools-svelte
# Solid
npm install @json-render/devtools @json-render/devtools-solid
開箱即用
將 <JsonRenderDevtools /> 放置在現有 <JSONUIProvider>(或相應框架的等效 Provider)內部的任何位置即可,無需額外配置。
React
import { JsonRenderDevtools } from "@json-render/devtools-react";
<JSONUIProvider registry={registry} handlers={handlers}>
<Renderer spec={spec} registry={registry} />
<JsonRenderDevtools spec={spec} catalog={catalog} messages={messages} />
</JSONUIProvider>;
Vue
<script setup>
import { JsonRenderDevtools } from "@json-render/devtools-vue";
</script>
<template>
<JSONUIProvider :registry="registry">
<Renderer :spec="spec" :registry="registry" />
<JsonRenderDevtools :spec="spec" :catalog="catalog" :messages="messages" />
</JSONUIProvider>
</template>
Svelte
<script>
import { JsonRenderDevtools } from "@json-render/devtools-svelte";
</script>
<JSONUIProvider {registry}>
<Renderer {spec} {registry} />
<JsonRenderDevtools {spec} {catalog} {messages} />
</JSONUIProvider>
Solid
import { JsonRenderDevtools } from "@json-render/devtools-solid";
<JSONUIProvider registry={registry}>
<Renderer spec={spec()} registry={registry} />
<JsonRenderDevtools
spec={spec()}
catalog={catalog}
messages={messages()}
/>
</JSONUIProvider>;
操作控制
- 右下角顯示浮動切換按鈕。
- 快捷鍵:
Ctrl/Cmd+Shift+J(可透過hotkeyprop 自訂)。 - 抽屜式面板支援拉伸調整大小,且高度設定會自動儲存至
localStorage。
Props
spec(Spec | null) — 當前 spec。catalog(Catalog | null) — catalog 定義;Catalog 面板所必需的屬性。messages(UIMessage[]) — AI SDKuseChat的訊息陣列;會自動掃描其中的 spec 資料片段。initialOpen(boolean) — 預設是否展開面板。position("bottom-right" | "bottom-left" | "right") — 面板停靠位置與切換按鈕角落。"bottom-*"停靠於底部;"right"則以全高停靠於右側邊緣(推薦用於已使用100vh或固定底部列的 App Shell 布局)。hotkey(string | false) — 預設為"mod+shift+j"。bufferSize(number) — 事件環形緩衝區上限,預設為 500。reserveSpace(boolean, 預設為true) — 為true時,面板會透過在body上施加padding-bottom/padding-right來推開主應用程式內容。設為false則將面板作為純粹的浮層 (overlay)。allowDockToggle(boolean, 預設為true) — 在工具列顯示按鈕,允許使用者在底部停靠與右側停靠之間切換。使用者的選擇會持久化儲存於localStorage,並在後續掛載時覆蓋position設定。傳入false則可鎖定面板停靠於position指定的位置。onEvent((DevtoolsEvent) => void) — 可選的事件監聽/攔截回調函式。
面板功能
- Spec — 以
spec.root為根節點的元素樹;顯示 props、可見性 (visibility)、事件 (events) 與監聽器 (watchers) 的詳細資訊;整合validateSpec警告訊息。 - State — 條列所有 JSON Pointer 路徑,並支援透過
store.set進行行內 (inline) 編輯。 - Actions — 已發送 action 的時間軸(包含名稱、參數、結果/錯誤、執行耗時)。
- Stream — 按生成批次分組顯示 spec patch、文本區塊、token 用量及生命週期標記。
- Catalog — 顯示在 catalog 中聲明的元件與 action,並附帶 prop 標籤。
元素選擇器 (Picker 工具列)
元素選擇器是面板頂欄中的一個工具列按鈕(類似 Chrome DevTools 的設計),而非獨立分頁。點擊按鈕即可啟動選取模式,接著點擊頁面中任何已渲染的元素,畫面就會自動跳轉至 Spec 分頁並聚焦該元素。按下 Esc 即可取消選取。
空間預留與面板停靠
面板可停靠於頁面底部或右側邊緣。預設情況下,使用者可以透過工具列按鈕在兩者之間自由切換(該選擇會儲存於 localStorage)。如果主應用程式僅相容單一停靠模式,可設定 allowDockToggle={false},按鈕將被隱藏,並固定停靠於 position 設定的位置。
根據您的頁面布局選擇合適的初始停靠位置:
- 底部停靠(預設) — 最適合文件 / 行銷 / 內容流網站,以及透過
height: 100%鏈結建構的 App Shell(html { height: 100% }→body { height: 100% }→.app { height: 100% })。面板會將其高度寫入--jr-devtools-offset-bottom,並在body上套用對應的padding-bottom,讓非固定定位的內容自然空出空間。 - 右側停靠 (
position="right") — 推薦用於採用100vh或position: fixed; bottom: 0的 App Shell 布局。右側停靠可完全避開底部邊緣,並改為將面板寬度寫入--jr-devtools-offset-right。
使用 100vh、position: fixed 或 position: sticky 的應用程式,可以使用套件釋出的 CSS 自訂屬性來為特定元素手動對齊:
.composer { bottom: var(--jr-devtools-offset-bottom, 0); }
.sidebar { right: var(--jr-devtools-offset-right, 0); }
.app-shell { height: calc(100vh - var(--jr-devtools-offset-bottom, 0)); }
若自動設定的 body padding 對特定布局造成干擾,可傳入 reserveSpace={false} 將面板轉為純覆蓋層 (overlay) — 此時 CSS 自訂屬性仍會持續發布,以便您進行手動空間預留。
(保留 --jr-devtools-offset 作為當前有效邊緣的向後相容別名。)
單頁面多重渲染器(例如聊天介面)
單一 <JsonRenderDevtools /> 實例即可同時檢視多個 <Renderer /> 實例 — 例如每個 AI 訊息各渲染自身 spec 的聊天介面,或是由多個獨立 Widget 組成的儀表板。配置方式如下:
- 單一最上層
<JSONUIProvider>:確保所有渲染器共享同一個 state store 與 action dispatcher。Devtools 位於此 Provider 內部,藉此掌握整體全貌。 - 各渲染器獨立 spec、共享 state:每個 AI 訊息直接渲染
<Renderer spec={msgSpec} registry={registry} />,無需包裹在獨立的StateProvider中。請注意不同訊息的 state 路徑切勿發生碰撞衝突。 - 為每輪對話加上 State 命名空間:當資料來源為 AI 串流時,傳遞獨立的
messageId給 Agent,並要求所有元素 key(例如<id>-root)與 state 路徑(例如/<id>/count)皆必須加上該 ID 作為前綴。 - 傳入
spec={latest}+messages={all}:spec用於驅動 Spec 面板(通常傳入最新訊息的 spec),而messages則為 Stream 面板提供來自每一輪對話的 patch 資料。 - Action 與 Picker 本身即具備全域特性:
registerActionObserver能擷取元件樹中任何ActionProvider所發送的 action,且data-jr-key是由渲染器本身寫入的,因此不論元素是由哪一條訊息生成,Picker 功能都能完美運作。
參閱 examples/devtools 可查看依照此方式配置的完整 AI 聊天實例。
命令式 API(僅限 React)
import { useJsonRenderDevtools } from "@json-render/devtools-react";
const devtools = useJsonRenderDevtools();
devtools?.open();
devtools?.toggle();
devtools?.recordEvent({ kind: "stream-text", at: Date.now(), text: "hi" });
在生產環境或元件掛載完成之前會傳回 null。
伺服器端串流攔截 (Stream tap)
在 API 路由擷取 spec patch,使事件得以持久化儲存於伺服器端,或串接至您自訂的遙測系統 (telemetry)。
import { tapJsonRenderStream, createEventStore } from "@json-render/devtools";
import { pipeJsonRender } from "@json-render/core";
const events = createEventStore({ bufferSize: 1000 });
const tapped = tapJsonRenderStream(result.toUIMessageStream(), events);
writer.merge(pipeJsonRender(tapped));
YAML 等效 API:tapYamlStream。
底層運作原理
- Shadow DOM 樣式隔離面板 — 面板的樣式絕不會滲漏至主應用程式中,反之亦然。
- 環形緩衝事件儲存區 (Ring-buffered event store) — 設有數量上限的 devtools 事件日誌(記錄狀態變更、action 發送、串流 patch 等)。
- Action 觀察者註冊表 — 各框架的
ActionProvider會透過@json-render/core中的notifyActionDispatch/notifyActionSettle發送通告;Devtools 則透過registerActionObserver進行訂閱。 - Picker 元素標籤化 — 當 Devtools 掛載時,
ElementRenderer會將每個渲染出的元素包裹在<span data-jr-key="..." style="display:contents">中,藉此讓 Picker 能精確完成 DOM → spec key 的映射,且完全不影響版面布局。






