Communitygithub.com

AgentProof – Widgets interactifs pour agents IA

Bonnes pratiques pour lier des outils à des widgets frontend interactifs avec @Widget et le SDK @nitrostack/widgets (synchronisation d'état, appels d'outils, modes d'affichage, requêtes média et actions de chat).

Qu'est-ce que AgentProof – Widgets interactifs pour agents IA ?

Cette compétence est essentielle pour les développeurs qui conçoivent et intègrent des widgets React interactifs au sein d'interfaces d'agents IA telles que Claude Code, Cursor ou NitroStudio. Elle explique en détail comment utiliser le décorateur @Widget pour associer une méthode d'outil backend à un composant frontend React, comment recevoir les données de l'outil via le hook useWidgetSDK, et comment gérer l'état local du widget avec useWidgetState pour une persistance transparente. La compétence couvre également des fonctionnalités avancées comme l'appel direct d'autres outils depuis le widget (callTool), le contrôle dynamique de la mise en page (plein écran, incrusté, picture-in-picture), la navigation dans le chat (ouverture de liens externes, envoi de messages au LLM) et les requêtes d'accessibilité et de média (préférence de mouvement réduit, détection tactile). En suivant ces directives, les développeurs peuvent créer des widgets riches, réactifs et accessibles qui améliorent considérablement l'expérience utilisateur dans les environnements d'agents IA.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Quand l'utiliser

Utilisez cette compétence lorsque vous concevez, construisez ou modifiez des widgets d'interface utilisateur interactifs qui affichent du contenu React personnalisé dans des clients IA ou NitroStudio.


1. Définition backend (@Widget)

Pour afficher un widget basé sur React pour la sortie d'un outil, décorez la méthode de l'outil avec @Widget.

Options :

  • Route chaîne : Une simple chaîne représentant l'identifiant de la route dans l'application React frontend (ex. 'product-card').
  • Route objet : Objet comprenant :
    • route (obligatoire) : Le chemin de la route.
    • domain (optionnel) : Domaine de sandbox autorisé.
    • csp (optionnel) : Directives de politique de sécurité du contenu.

Exemple :

import { Tool, Widget, z } from '@nitrostack/core';

export class CatalogTools {
  @Tool({
    name: 'fetch_product',
    description: 'Obtenir les informations d'un produit par code-barres.',
    inputSchema: z.object({ barcode: z.string() }),
  })
  @Widget('product-details') // Mappe au composant frontend "product-details"
  async fetchProduct(input: { barcode: string }) {
    return {
      name: 'Super Nitro Energy Drink',
      price: 2.99,
      sku: input.barcode,
    };
  }
}

2. Widget React frontend (@nitrostack/widgets)

Dans votre application de widgets React frontend (typiquement un composant client Next.js), utilisez le hook useWidgetSDK pour recevoir les données d'entrée de l'hôte client.

Exemple de composant 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">Connexion à l'hôte...</div>;
  }

  if (!data) {
    return <div className="error">Aucune donnée produit reçue.</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. Gestion et synchronisation d'état (useWidgetState)

Utilisez useWidgetState pour gérer et persister l'état côté client du widget (ex. onglets sélectionnés, valeurs de filtre, états de saisie). Cet état se synchronise automatiquement avec le contexte de l'application hôte, persistant à travers les re-rendus de page.

Exemple :

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' })}>
        Voir les alertes
      </button>
      <p>Onglet actuel : {state?.selectedTab}</p>
    </div>
  );
}

4. Appel d'outils principaux depuis les widgets (callTool)

Vous pouvez invoquer d'autres outils MCP backend directement depuis le widget frontend à l'aide de callTool. C'est utile pour le chaînage d'outils ou le déclenchement d'audits détaillés.

Exemple :

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('Échec de l\'exécution du diagnostic.');
    } finally {
      setIsRunning(false);
    }
  };

  return (
    <button onClick={runDiagnostic} disabled={isRunning}>
      {isRunning ? 'Exécution...' : 'Lancer le diagnostic'}
    </button>
  );
}

5. Contrôles de mise en page et d'affichage

Les widgets peuvent demander dynamiquement des changements de mode de taille (plein écran, incrusté, picture-in-picture) et adapter les mises en page aux zones de sécurité (dynamic islands/notches) ou aux contraintes de hauteur maximale.

Méthodes clés :

  • requestFullscreen() : Passe l'affichage du widget hôte en plein écran.
  • requestInline() : Passe l'affichage du widget hôte en mode incrusté.
  • requestPip() : Fait flotter le widget en Picture-in-Picture.
  • requestClose() : Ferme complètement le widget.

Exemple :

import React from 'react';
import { useWidgetSDK } from '@nitrostack/widgets';

export default function StatusBoard() {
  const { 
    requestFullscreen, 
    requestInline, 
    requestClose,
    displayMode,   // Propriété réactive ('fullscreen' | 'inline' | 'pip')
    maxHeight,     // Contrainte réactive de hauteur maximale (en pixels)
    getSafeArea    // Données d'encart : { top, right, bottom, left }
  } = useWidgetSDK();

  const safeArea = getSafeArea() || { top: 0, bottom: 0 };

  return (
    <div style={{ maxHeight: maxHeight || 400, paddingTop: safeArea.top }}>
      <h3>Mode : {displayMode}</h3>
      <button onClick={requestFullscreen}>Plein écran</button>
      <button onClick={requestInline}>Réduire</button>
      <button onClick={requestClose}>Fermer le widget</button>
    </div>
  );
}

6. Navigation et actions dans le chat

Les widgets peuvent interagir avec le panneau de chat hôte à l'aide de liens externes et d'invites de suivi.

Méthodes clés :

  • openExternal(url) : Ouvre l'URL cible en toute sécurité dans le navigateur externe principal de l'utilisateur.
  • sendFollowUpMessage(prompt) : Insère un message dans le flux de chat, le soumettant automatiquement à l'agent LLM.

Exemple :

import React from 'react';
import { useWidgetSDK } from '@nitrostack/widgets';

export default function MissionControl() {
  const { openExternal, sendFollowUpMessage } = useWidgetSDK();

  return (
    <div>
      {/* Ouvrir la documentation externe */}
      <button onClick={() => openExternal('https://docs.nitrostack.dev')}>
        Ouvrir le manuel
      </button>

      {/* Interroger directement l'agent LLM depuis le widget */}
      <button onClick={() => sendFollowUpMessage('Analyser les échecs de mission de la semaine dernière')}>
        Demander à l'agent d'analyser
      </button>
    </div>
  );
}

7. Requêtes média et d'accessibilité

Le SDK fournit des utilitaires pour interroger les capacités du client cible à des fins de style ou d'accessibilité.

Utilitaires clés :

  • prefersReducedMotion() : Renvoie true si les paramètres du client spécifient un mouvement réduit. Désactiver les animations.
  • isPrimarilyTouchDevice() : Renvoie true si le dispositif possède un pointeur grossier (ex. tactile/mobile). Augmenter la taille des cibles des boutons.
  • isHoverAvailable() : Renvoie true si le pointeur prend en charge les états de survol.
  • prefersDarkColorScheme() : Renvoie true si le thème système est sombre.

Exemple :

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' }}>
        Bouton interactif
      </button>
    </div>
  );
}

8. Tester les widgets

  • Ouvrez votre projet dans NitroStudio pour un aperçu visuel.
  • Invoquez l'outil depuis le chat IA ou le panneau de test pour vérifier que le widget se met à jour instantanément avec la structure JSON renvoyée.

Skills associés