Communitygithub.com

joshi-chinmay-016/AgentProof

@Widget および @nitrostack/widgets SDK を使用して、インタラクティブなフロントエンドウィジェットにツールをリンクするためのベストプラクティス(状態同期、ツール呼び出し、表示モード、メディアクエリ、チャットアクションを含む)。

AgentProof とは?

このスキルは、AIクライアントまたはNitroStudio内でカスタムReactコンテンツを表示するインタラクティブなUIウィジェットの設計、構築、修正を支援します。@Widget デコレータを使用してバックエンドツールの出力をReactコンポーネントにマッピングし、@nitrostack/widgets ライブラリの useWidgetSDK フックを使用して入力データを受信します。また、useWidgetState によるクライアント状態の管理と永続化、callTool によるバックエンドツールの直接呼び出し、フルスクリーンやインラインなどの表示モード制御、安全な外部リンク開封やフォローアッププロンプト送信などのチャットインタラクション、そしてメディアクエリやアクセシビリティユーティリティによる端末適応もサポートします。これにより、開発者はAI駆動アプリケーションにおいて、リッチでリアクティブなウィジェットエクスペリエンスを容易に構築できます。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/joshi-chinmay-016/AgentProof/tree/HEAD/agentproof/.agents/skills/ui-widgets

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

使用するタイミング

AIクライアントまたはNitroStudio内でカスタムReactコンテンツを表示するインタラクティブなUIウィジェットを設計、構築、または修正する場合にこのスキルを使用します。


1. バックエンド定義(@Widget)

ツールの出力にReactベースのウィジェットを表示するには、ツールメソッドを @Widget でデコレートします。

オプション:

  • 文字列ルート:フロントエンドReactアプリのルート識別子を表す単純な文字列(例:'product-card')。
  • オブジェクトルート:以下を含むオブジェクト:
    • 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: 'スーパーニトロエナジードリンク',
      price: 2.99,
      sku: input.barcode,
    };
  }
}

2. フロントエンドReactウィジェット(@nitrostack/widgets)

Reactウィジェットのフロントエンドアプリケーション(通常はNext.jsのクライアントコンポーネント)で、useWidgetSDK フックを使用してクライアントホストから入力データを受信します。

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,     // リアクティブな最大高さ制約(ピクセル単位)
    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構造でウィジェットが即座に更新されることを確認します。

関連スキル