使用時機
當設計、建置或修改在 AI 用戶端或 NitroStudio 中顯示自訂 React 內容的互動式使用者介面小工具時,請使用此技能。
1. 後端定義 (@Widget)
若要為工具的輸出顯示一個基於 React 的小工具,請在工具方法上加上 @Widget 裝飾器。
選項:
- 字串路由 (String Route):一個代表前端 React 應用程式中路由識別碼的簡單字串(例如
'product-card')。 - 物件路由 (Object Route):包含以下項目的物件:
route(必要):路由路徑。domain(選填):允許的沙箱網域。csp(選填):內容安全政策指南。
範例:
import { Tool, Widget, z } from '@nitrostack/core';
export class CatalogTools {
@Tool({
name: 'fetch_product',
description: '根據條碼取得產品資訊。',
inputSchema: z.object({ barcode: z.string() }),
})
@Widget('product-details') // 對應到前端 "product-details" 元件
async fetchProduct(input: { barcode: string }) {
return {
name: 'Super Nitro Energy Drink',
price: 2.99,
sku: input.barcode,
};
}
}
2. 前端 React 小工具 (@nitrostack/widgets)
在您的 React 小工具前端應用程式中(通常是一個 Next.js 用戶端元件),使用 useWidgetSDK hook 從用戶端主機接收輸入資料。
React 元件範例:
'use client';
import React from 'react';
import { useWidgetSDK } from '@nitrostack/widgets';
interface ProductData {
name: string;
price: number;
sku: string;
}
export default function ProductDetailsWidget() {
const { isReady, getToolOutput, theme } = useWidgetSDK();
const data = getToolOutput<ProductData>();
if (!isReady) {
return <div className="loading">正在連接到主機...</div>;
}
if (!data) {
return <div className="error">未收到產品資料。</div>;
}
return (
<div className={`product-card ${theme === 'dark' ? 'dark' : 'light'}`}>
<h3>{data.name}</h3>
<p className="price">${data.price.toFixed(2)}</p>
<span className="sku">SKU: {data.sku}</span>
</div>
);
}
3. 狀態管理與同步 (useWidgetState)
使用 useWidgetState 來管理並持久化用戶端小工具的狀態(例如選取的分頁、篩選值、輸入狀態)。此狀態會自動與主機應用程式上下文同步,並在頁面重新渲染之間保持持久。
範例:
import React from 'react';
import { useWidgetState } from '@nitrostack/widgets';
export default function StationPanelWidget() {
const [state, setState] = useWidgetState(() => ({
selectedTab: 'overview',
showExtendedInfo: false,
}));
return (
<div>
<button onClick={() => setState({ ...state, selectedTab: 'alerts' })}>
檢視警示
</button>
<p>目前分頁: {state?.selectedTab}</p>
</div>
);
}
4. 從小工具呼叫核心工具 (callTool)
您可以使用 callTool 直接從前端小工具呼叫其他後端 MCP 工具。這對於工具鏈結或觸發詳細稽核非常有用。
範例:
import React, { useState } from 'react';
import { useWidgetSDK } from '@nitrostack/widgets';
export default function SystemDiagnostics() {
const { callTool, isReady } = useWidgetSDK();
const [isRunning, setIsRunning] = useState(false);
const [result, setResult] = useState<string | null>(null);
const runDiagnostic = async () => {
if (!isReady) return;
setIsRunning(true);
try {
const response = await callTool('run_diagnostic', { system: 'oxygen_scrubber' });
setResult(response.result as string);
} catch (err) {
setResult('診斷執行失敗。');
} finally {
setIsRunning(false);
}
};
return (
<button onClick={runDiagnostic} disabled={isRunning}>
{isRunning ? '執行中...' : '執行診斷'}
</button>
);
}
5. 佈局與顯示控制
小工具可以動態請求尺寸模式變更(全螢幕、內嵌、子母畫面),並根據安全區域(動態島/瀏海)或最大高度限制調整佈局。
主要方法:
requestFullscreen():將主機小工具顯示切換為全螢幕。requestInline():將主機小工具顯示切換回內嵌。requestPip():將小工具浮動顯示為子母畫面。requestClose():完全關閉小工具。
範例:
import React from 'react';
import { useWidgetSDK } from '@nitrostack/widgets';
export default function StatusBoard() {
const {
requestFullscreen,
requestInline,
requestClose,
displayMode, // 響應式屬性 ('fullscreen' | 'inline' | 'pip')
maxHeight, // 響應式 maxHeight 限制(像素)
getSafeArea // 邊距資料: { top, right, bottom, left }
} = useWidgetSDK();
const safeArea = getSafeArea() || { top: 0, bottom: 0 };
return (
<div style={{ maxHeight: maxHeight || 400, paddingTop: safeArea.top }}>
<h3>模式: {displayMode}</h3>
<button onClick={requestFullscreen}>全螢幕</button>
<button onClick={requestInline}>收起</button>
<button onClick={requestClose}>關閉小工具</button>
</div>
);
}
6. 聊天導航與動作
小工具可以使用外部瀏覽器連結和後續提示與主機聊天面板互動。
主要方法:
openExternal(url):安全地在使用者的主要外部瀏覽器中開啟目標 URL。sendFollowUpMessage(prompt):將訊息插入聊天流程中,並自動提交給 LLM 代理。
範例:
import React from 'react';
import { useWidgetSDK } from '@nitrostack/widgets';
export default function MissionControl() {
const { openExternal, sendFollowUpMessage } = useWidgetSDK();
return (
<div>
{/* 開啟外部文件 */}
<button onClick={() => openExternal('https://docs.nitrostack.dev')}>
開啟手冊
</button>
{/* 從小工具直接詢問 LLM 代理 */}
<button onClick={() => sendFollowUpMessage('分析上週的任務失敗原因')}>
請代理分析
</button>
</div>
);
}
7. 媒體與無障礙查詢
SDK 提供輔助工具來查詢目標用戶端的功能,以便進行樣式或無障礙設計。
主要工具:
prefersReducedMotion():若用戶端設定指定減少動畫,則回傳true。請停用動畫效果。isPrimarilyTouchDevice():若裝置具有粗糙指針(例如觸控/手機),則回傳true。請增加按鈕的目標尺寸。isHoverAvailable():若指針支援懸浮狀態,則回傳true。prefersDarkColorScheme():若系統主題為深色,則回傳true。
範例:
import React from 'react';
import { isPrimarilyTouchDevice, prefersReducedMotion } from '@nitrostack/widgets';
export default function AccessiblePanel() {
const isTouch = isPrimarilyTouchDevice();
const reducedMotion = prefersReducedMotion();
return (
<div className={reducedMotion ? 'no-animations' : 'smooth-transitions'}>
<button style={{ padding: isTouch ? '16px' : '8px' }}>
互動按鈕
</button>
</div>
);
}
8. 測試小工具
- 在 NitroStudio 中開啟您的專案以進行視覺預覽。
- 從 AI 聊天或測試面板中呼叫工具,以驗證小工具是否會立即使用回傳的 JSON 結構進行更新。