Widgets : interfaces dans ChatGPT
Créer des interfaces visuelles dans la conversation
Les Widgets transforment ChatGPT d’un assistant textuel en une plateforme d’applications interactives. Au lieu de décrire un graphique en mots, vous l’affichez. Au lieu de lister des options, vous présentez des boutons. Cette leçon couvre en détail le système de widgets du Apps SDK.
Le système de composants
Le SDK fournit une bibliothèque de composants que vous assemblez pour créer vos interfaces. Chaque widget est déclaratif : vous décrivez ce que vous voulez afficher, pas comment le rendre.
import { defineWidget } from "@openai/apps-sdk";
export const productCard = defineWidget({
name: "productCard",
description: "Affiche les détails d'un produit avec son prix et un bouton d'achat",
render: (data) => ({
type: "card",
title: data.name,
subtitle: data.category,
content: [
{ type: "image", src: data.imageUrl, alt: data.name },
{ type: "text", value: data.description },
{
type: "grid",
columns: 2,
items: [
{ type: "metric", label: "Prix", value: `${data.price} €` },
{ type: "metric", label: "Stock", value: `${data.stock} unités` },
],
},
{
type: "button",
label: "Ajouter au panier",
action: "addToCart",
params: { productId: data.id },
variant: "primary",
},
],
}),
});
Types de composants disponibles
Composants de mise en page
// Card — conteneur principal
{ type: "card", title: "Titre", subtitle: "Sous-titre", content: [...] }
// Grid — grille responsive
{ type: "grid", columns: 3, gap: "md", items: [...] }
// Stack — empilement vertical ou horizontal
{ type: "stack", direction: "vertical", spacing: "sm", items: [...] }
// Tabs — onglets
{ type: "tabs", items: [
{ label: "Aperçu", content: [...] },
{ label: "Détails", content: [...] },
]}
// Accordion — sections dépliables
{ type: "accordion", items: [
{ title: "Section 1", content: [...] },
]}
Composants de données
// Table — tableau de données
{
type: "table",
columns: [
{ key: "name", label: "Nom" },
{ key: "price", label: "Prix", align: "right" },
{ key: "status", label: "Statut" },
],
rows: data.products,
sortable: true,
paginated: true,
pageSize: 10,
}
// Chart — graphiques
{
type: "chart",
chartType: "bar", // "line", "pie", "area"
data: {
labels: ["Jan", "Fév", "Mar", "Avr"],
datasets: [{ label: "Ventes", values: [120, 190, 300, 250] }],
},
}
// Metric — indicateur chiffré
{ type: "metric", label: "Chiffre d'affaires", value: "12 450 €", trend: "+15%" }
Composants interactifs
// Button — bouton d'action
{
type: "button",
label: "Confirmer",
action: "confirmOrder",
variant: "primary", // "secondary", "danger"
}
// Form — formulaire complet
{
type: "form",
fields: [
{ name: "email", label: "Email", inputType: "email", required: true },
{ name: "message", label: "Message", inputType: "textarea" },
{
name: "priority",
label: "Priorité",
inputType: "select",
options: [
{ value: "low", label: "Basse" },
{ value: "high", label: "Haute" },
],
},
],
submitAction: "sendMessage",
submitLabel: "Envoyer",
}
Gérer les interactions
Quand l’utilisateur clique un bouton ou soumet un formulaire, le SDK déclenche l’action associée :
// Le bouton dans le widget
{ type: "button", label: "Supprimer", action: "deleteItem", params: { id: "123" } }
// L'action correspondante
app.action("deleteItem", {
description: "Supprime un élément par son identifiant",
parameters: {
id: { type: "string", required: true },
},
handler: async ({ id }) => {
await db.items.delete(id);
return { success: true, message: "Élément supprimé" };
},
});
Mettre à jour un widget
Après une interaction, vous pouvez mettre à jour le widget affiché :
app.action("toggleFavorite", {
description: "Ajoute ou retire un produit des favoris",
parameters: { productId: { type: "string", required: true } },
handler: async ({ productId }, context) => {
const isFav = await toggleFavorite(context.user.id, productId);
return {
_widget: "productCard", // Re-render le widget avec les nouvelles données
...await getProduct(productId),
isFavorite: isFav,
};
},
});
Widgets dynamiques et conditionnels
Vous pouvez adapter l’affichage en fonction des données :
export const orderStatus = defineWidget({
name: "orderStatus",
render: (data) => ({
type: "card",
title: `Commande #${data.orderId}`,
content: [
{
type: "status",
value: data.status,
color: data.status === "delivered" ? "green" :
data.status === "shipped" ? "blue" : "orange",
},
// Afficher le suivi seulement si expédié
...(data.trackingUrl ? [{
type: "button",
label: "Suivre le colis",
action: "openUrl",
params: { url: data.trackingUrl },
}] : []),
// Afficher les articles
{
type: "table",
columns: [
{ key: "name", label: "Article" },
{ key: "qty", label: "Qté" },
{ key: "price", label: "Prix" },
],
rows: data.items,
},
],
}),
});
Mise en pratique
Créez un widget userDashboard qui affiche :
- Un en-tête avec le nom et l’avatar de l’utilisateur
- Une grille de 3 métriques (commandes, dépenses, points fidélité)
- Un tableau des 5 dernières commandes
- Un bouton « Voir toutes les commandes »
Points clés à retenir
- Les widgets sont déclaratifs : vous décrivez la structure, le SDK gère le rendu
- Les composants couvrent la mise en page, les données et les interactions
- Les boutons et formulaires déclenchent des actions côté serveur
- Un widget peut être mis à jour dynamiquement après une interaction
- L’affichage conditionnel permet d’adapter l’interface aux données