@lastbrain/ai-ui-react 1.0.25 → 1.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/components/AiChipLabel.d.ts +12 -0
  2. package/dist/components/AiChipLabel.d.ts.map +1 -1
  3. package/dist/components/AiChipLabel.js +129 -1
  4. package/dist/components/AiContextButton.d.ts +18 -0
  5. package/dist/components/AiContextButton.d.ts.map +1 -0
  6. package/dist/components/AiContextButton.js +369 -0
  7. package/dist/components/AiImageButton.d.ts +12 -3
  8. package/dist/components/AiImageButton.d.ts.map +1 -1
  9. package/dist/components/AiImageButton.js +218 -8
  10. package/dist/components/AiPromptPanel.d.ts +1 -1
  11. package/dist/components/AiPromptPanel.d.ts.map +1 -1
  12. package/dist/components/AiPromptPanel.js +11 -1
  13. package/dist/components/AiStatusButton.d.ts.map +1 -1
  14. package/dist/components/AiStatusButton.js +1 -1
  15. package/dist/components/UsageToast.d.ts.map +1 -1
  16. package/dist/components/UsageToast.js +5 -3
  17. package/dist/examples/AiChipInputExample.d.ts +2 -0
  18. package/dist/examples/AiChipInputExample.d.ts.map +1 -0
  19. package/dist/examples/AiChipInputExample.js +14 -0
  20. package/dist/examples/AiContextButtonExample.d.ts +2 -0
  21. package/dist/examples/AiContextButtonExample.d.ts.map +1 -0
  22. package/dist/examples/AiContextButtonExample.js +88 -0
  23. package/dist/examples/AiImageButtonExample.d.ts +2 -0
  24. package/dist/examples/AiImageButtonExample.d.ts.map +1 -0
  25. package/dist/examples/AiImageButtonExample.js +26 -0
  26. package/dist/hooks/useAiCallImage.d.ts.map +1 -1
  27. package/dist/hooks/useAiCallImage.js +107 -1
  28. package/dist/hooks/useAiCallText.d.ts.map +1 -1
  29. package/dist/hooks/useAiCallText.js +25 -1
  30. package/dist/index.d.ts +4 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +4 -0
  33. package/dist/styles/inline.d.ts.map +1 -1
  34. package/dist/styles/inline.js +3 -1
  35. package/package.json +2 -2
  36. package/src/components/AiChipLabel.tsx +218 -1
  37. package/src/components/AiContextButton.tsx +585 -0
  38. package/src/components/AiImageButton.tsx +386 -38
  39. package/src/components/AiPromptPanel.tsx +18 -1
  40. package/src/components/AiStatusButton.tsx +7 -3
  41. package/src/components/UsageToast.tsx +5 -3
  42. package/src/examples/AiChipInputExample.tsx +81 -0
  43. package/src/examples/AiContextButtonExample.tsx +338 -0
  44. package/src/examples/AiImageButtonExample.tsx +72 -0
  45. package/src/hooks/useAiCallImage.ts +149 -1
  46. package/src/hooks/useAiCallText.ts +30 -1
  47. package/src/index.ts +4 -0
  48. package/src/styles/inline.ts +3 -1
@@ -0,0 +1,338 @@
1
+ import { AiContextButton } from "@lastbrain/ai-ui-react";
2
+ import { useState } from "react";
3
+
4
+ // Données d'exemple pour le tableau
5
+ const sampleTableData = [
6
+ {
7
+ id: 1,
8
+ user: "Alice Martin",
9
+ product: "Laptop Pro X1",
10
+ sales: 15420,
11
+ commission: 1542,
12
+ region: "Nord",
13
+ month: "Janvier 2026",
14
+ },
15
+ {
16
+ id: 2,
17
+ user: "Bob Durant",
18
+ product: "Smartphone Z10",
19
+ sales: 8750,
20
+ commission: 875,
21
+ region: "Sud",
22
+ month: "Janvier 2026",
23
+ },
24
+ {
25
+ id: 3,
26
+ user: "Claire Dubois",
27
+ product: "Tablet Air 5",
28
+ sales: 12300,
29
+ commission: 1230,
30
+ region: "Est",
31
+ month: "Janvier 2026",
32
+ },
33
+ {
34
+ id: 4,
35
+ user: "David Moreau",
36
+ product: "Laptop Pro X1",
37
+ sales: 18900,
38
+ commission: 1890,
39
+ region: "Ouest",
40
+ month: "Janvier 2026",
41
+ },
42
+ {
43
+ id: 5,
44
+ user: "Emma Leroy",
45
+ product: "Smartphone Z10",
46
+ sales: 6420,
47
+ commission: 642,
48
+ region: "Centre",
49
+ month: "Janvier 2026",
50
+ },
51
+ ];
52
+
53
+ // Données de produits pour un autre exemple
54
+ const productAnalytics = {
55
+ period: "Q1 2026",
56
+ totalRevenue: 2450000,
57
+ products: [
58
+ { name: "Laptop Pro X1", revenue: 980000, units: 320, avgPrice: 3062.5 },
59
+ { name: "Smartphone Z10", revenue: 750000, units: 500, avgPrice: 1500 },
60
+ { name: "Tablet Air 5", revenue: 720000, units: 400, avgPrice: 1800 },
61
+ ],
62
+ regions: {
63
+ Nord: { revenue: 650000, growth: "+12%" },
64
+ Sud: { revenue: 580000, growth: "+8%" },
65
+ Est: { revenue: 520000, growth: "+15%" },
66
+ Ouest: { revenue: 700000, growth: "+20%" },
67
+ },
68
+ };
69
+
70
+ export function AiContextButtonExample() {
71
+ const [analysisResults, setAnalysisResults] = useState<string[]>([]);
72
+
73
+ const handleSalesAnalysis = (
74
+ result: string,
75
+ metadata?: { requestId: string; tokens: number }
76
+ ) => {
77
+ console.log("Analyse des ventes:", { result, metadata });
78
+ setAnalysisResults((prev) => [
79
+ ...prev,
80
+ `Ventes: ${result.substring(0, 100)}...`,
81
+ ]);
82
+ };
83
+
84
+ const handleProductAnalysis = (
85
+ result: string,
86
+ metadata?: { requestId: string; tokens: number }
87
+ ) => {
88
+ console.log("Analyse produits:", { result, metadata });
89
+ setAnalysisResults((prev) => [
90
+ ...prev,
91
+ `Produits: ${result.substring(0, 100)}...`,
92
+ ]);
93
+ };
94
+
95
+ const handleToast = (data: {
96
+ type: "success" | "error";
97
+ message: string;
98
+ }) => {
99
+ console.log(`${data.type}: ${data.message}`);
100
+ };
101
+
102
+ return (
103
+ <div className="p-6 max-w-6xl mx-auto">
104
+ <h1 className="text-3xl font-bold mb-6">AI Context Button - Exemples</h1>
105
+
106
+ {/* Exemple 1: Analyse de tableau de ventes */}
107
+ <div className="mb-8">
108
+ <h2 className="text-xl font-semibold mb-4">
109
+ 1. Analyse de données de ventes
110
+ </h2>
111
+
112
+ {/* Tableau des ventes */}
113
+ <div className="mb-4 overflow-hidden rounded-lg border border-gray-200">
114
+ <table className="min-w-full divide-y divide-gray-200">
115
+ <thead className="bg-gray-50">
116
+ <tr>
117
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
118
+ Vendeur
119
+ </th>
120
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
121
+ Produit
122
+ </th>
123
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
124
+ Ventes (€)
125
+ </th>
126
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
127
+ Commission (€)
128
+ </th>
129
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
130
+ Région
131
+ </th>
132
+ </tr>
133
+ </thead>
134
+ <tbody className="bg-white divide-y divide-gray-200">
135
+ {sampleTableData.map((row) => (
136
+ <tr key={row.id} className="hover:bg-gray-50">
137
+ <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
138
+ {row.user}
139
+ </td>
140
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
141
+ {row.product}
142
+ </td>
143
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
144
+ {row.sales.toLocaleString()} €
145
+ </td>
146
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
147
+ {row.commission.toLocaleString()} €
148
+ </td>
149
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
150
+ {row.region}
151
+ </td>
152
+ </tr>
153
+ ))}
154
+ </tbody>
155
+ </table>
156
+ </div>
157
+
158
+ <div className="flex gap-3 flex-wrap">
159
+ <AiContextButton
160
+ contextData={sampleTableData}
161
+ contextDescription="Données de ventes par vendeur pour Janvier 2026"
162
+ onResult={handleSalesAnalysis}
163
+ onToast={handleToast}
164
+ resultModalTitle="Analyse des Ventes - Janvier 2026"
165
+ >
166
+ Analyser les ventes
167
+ </AiContextButton>
168
+
169
+ <AiContextButton
170
+ contextData={sampleTableData}
171
+ contextDescription="Performance des vendeurs"
172
+ onResult={handleSalesAnalysis}
173
+ onToast={handleToast}
174
+ resultModalTitle="Top Performers"
175
+ >
176
+ Top 3 vendeurs
177
+ </AiContextButton>
178
+ </div>
179
+ </div>
180
+
181
+ {/* Exemple 2: Analyse de données produits */}
182
+ <div className="mb-8">
183
+ <h2 className="text-xl font-semibold mb-4">
184
+ 2. Analyse de performance produits
185
+ </h2>
186
+
187
+ {/* Affichage des analytics produits */}
188
+ <div className="mb-4 p-6 bg-gray-50 rounded-lg">
189
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
190
+ <div className="bg-white p-4 rounded-lg shadow-sm">
191
+ <h3 className="text-sm font-medium text-gray-500">
192
+ Chiffre d'affaires total
193
+ </h3>
194
+ <p className="text-2xl font-bold text-gray-900">
195
+ {productAnalytics.totalRevenue.toLocaleString()} €
196
+ </p>
197
+ </div>
198
+ <div className="bg-white p-4 rounded-lg shadow-sm">
199
+ <h3 className="text-sm font-medium text-gray-500">Période</h3>
200
+ <p className="text-2xl font-bold text-gray-900">
201
+ {productAnalytics.period}
202
+ </p>
203
+ </div>
204
+ <div className="bg-white p-4 rounded-lg shadow-sm">
205
+ <h3 className="text-sm font-medium text-gray-500">Produits</h3>
206
+ <p className="text-2xl font-bold text-gray-900">
207
+ {productAnalytics.products.length}
208
+ </p>
209
+ </div>
210
+ <div className="bg-white p-4 rounded-lg shadow-sm">
211
+ <h3 className="text-sm font-medium text-gray-500">Régions</h3>
212
+ <p className="text-2xl font-bold text-gray-900">
213
+ {Object.keys(productAnalytics.regions).length}
214
+ </p>
215
+ </div>
216
+ </div>
217
+
218
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
219
+ {/* Produits */}
220
+ <div>
221
+ <h4 className="font-semibold mb-3">Performance par produit</h4>
222
+ <div className="space-y-2">
223
+ {productAnalytics.products.map((product, index) => (
224
+ <div
225
+ key={index}
226
+ className="flex justify-between items-center p-3 bg-white rounded"
227
+ >
228
+ <span className="font-medium">{product.name}</span>
229
+ <div className="text-right">
230
+ <div className="text-sm text-gray-600">
231
+ {product.units} unités
232
+ </div>
233
+ <div className="font-semibold">
234
+ {product.revenue.toLocaleString()} €
235
+ </div>
236
+ </div>
237
+ </div>
238
+ ))}
239
+ </div>
240
+ </div>
241
+
242
+ {/* Régions */}
243
+ <div>
244
+ <h4 className="font-semibold mb-3">Performance par région</h4>
245
+ <div className="space-y-2">
246
+ {Object.entries(productAnalytics.regions).map(
247
+ ([region, data]) => (
248
+ <div
249
+ key={region}
250
+ className="flex justify-between items-center p-3 bg-white rounded"
251
+ >
252
+ <span className="font-medium">{region}</span>
253
+ <div className="text-right">
254
+ <div className="text-sm text-green-600">
255
+ {data.growth}
256
+ </div>
257
+ <div className="font-semibold">
258
+ {data.revenue.toLocaleString()} €
259
+ </div>
260
+ </div>
261
+ </div>
262
+ )
263
+ )}
264
+ </div>
265
+ </div>
266
+ </div>
267
+ </div>
268
+
269
+ <div className="flex gap-3 flex-wrap">
270
+ <AiContextButton
271
+ contextData={productAnalytics}
272
+ contextDescription="Analytics de performance produits Q1 2026"
273
+ onResult={handleProductAnalysis}
274
+ onToast={handleToast}
275
+ resultModalTitle="Analyse de Performance - Q1 2026"
276
+ >
277
+ Analyser les performances
278
+ </AiContextButton>
279
+
280
+ <AiContextButton
281
+ contextData={productAnalytics}
282
+ contextDescription="Données produits et régions"
283
+ onResult={handleProductAnalysis}
284
+ onToast={handleToast}
285
+ resultModalTitle="Recommandations Stratégiques"
286
+ >
287
+ Recommandations
288
+ </AiContextButton>
289
+ </div>
290
+ </div>
291
+
292
+ {/* Exemple 3: Contexte textuel */}
293
+ <div className="mb-8">
294
+ <h2 className="text-xl font-semibold mb-4">
295
+ 3. Analyse de texte libre
296
+ </h2>
297
+
298
+ <div className="mb-4 p-4 bg-gray-50 rounded-lg">
299
+ <p className="text-sm text-gray-700 mb-2">
300
+ <strong>Feedback client :</strong>
301
+ </p>
302
+ <p className="italic">
303
+ "Le produit est excellent en terme de qualité mais le prix me semble
304
+ un peu élevé. La livraison a été rapide et le service client très
305
+ réactif quand j'ai eu un problème avec ma commande. Je recommande ce
306
+ produit mais j'espère que les prix baisseront à l'avenir."
307
+ </p>
308
+ </div>
309
+
310
+ <AiContextButton
311
+ contextData="Le produit est excellent en terme de qualité mais le prix me semble un peu élevé. La livraison a été rapide et le service client très réactif quand j'ai eu un problème avec ma commande. Je recommande ce produit mais j'espère que les prix baisseront à l'avenir."
312
+ contextDescription="Feedback client sur un produit"
313
+ onResult={(result) => console.log("Analyse sentiment:", result)}
314
+ onToast={handleToast}
315
+ resultModalTitle="Analyse de Sentiment Client"
316
+ >
317
+ Analyser le sentiment
318
+ </AiContextButton>
319
+ </div>
320
+
321
+ {/* Historique des analyses */}
322
+ {analysisResults.length > 0 && (
323
+ <div className="mt-8">
324
+ <h2 className="text-xl font-semibold mb-4">
325
+ Historique des analyses
326
+ </h2>
327
+ <div className="space-y-2">
328
+ {analysisResults.map((result, index) => (
329
+ <div key={index} className="p-3 bg-blue-50 rounded-lg text-sm">
330
+ {result}
331
+ </div>
332
+ ))}
333
+ </div>
334
+ </div>
335
+ )}
336
+ </div>
337
+ );
338
+ }
@@ -0,0 +1,72 @@
1
+ import { AiImageButton } from "@lastbrain/ai-ui-react";
2
+ import { useState } from "react";
3
+
4
+ export function ExampleUsage() {
5
+ const [savedImages, setSavedImages] = useState<string[]>([]);
6
+
7
+ const handleImageGenerated = (
8
+ url: string,
9
+ metadata?: { requestId: string; tokens: number }
10
+ ) => {
11
+ console.log("Image générée:", { url, metadata });
12
+ // L'image est automatiquement affichée dans la card
13
+ };
14
+
15
+ const handleImageSave = async (url: string) => {
16
+ // Simulation d'une sauvegarde en base
17
+ await new Promise((resolve) => setTimeout(resolve, 1000));
18
+
19
+ // Ici vous pouvez faire l'appel API pour sauvegarder en base
20
+ // const response = await fetch('/api/images', {
21
+ // method: 'POST',
22
+ // body: JSON.stringify({ url }),
23
+ // headers: { 'Content-Type': 'application/json' }
24
+ // });
25
+
26
+ setSavedImages((prev) => [...prev, url]);
27
+ };
28
+
29
+ const handleToast = (data: {
30
+ type: "success" | "error";
31
+ message: string;
32
+ }) => {
33
+ // Vous pouvez utiliser votre système de toast préféré ici
34
+ console.log(`${data.type}: ${data.message}`);
35
+ };
36
+
37
+ return (
38
+ <div className="p-6">
39
+ <h1 className="text-2xl font-bold mb-4">Générateur d'Images AI</h1>
40
+
41
+ <AiImageButton
42
+ onImage={handleImageGenerated}
43
+ onImageSave={handleImageSave}
44
+ onToast={handleToast}
45
+ showImageCard={true}
46
+ className="mb-4"
47
+ >
48
+ Créer une image
49
+ </AiImageButton>
50
+
51
+ {/* Liste des images sauvegardées */}
52
+ {savedImages.length > 0 && (
53
+ <div className="mt-8">
54
+ <h2 className="text-lg font-semibold mb-2">
55
+ Images sauvegardées ({savedImages.length})
56
+ </h2>
57
+ <div className="grid grid-cols-2 md:grid-cols-3 gap-4">
58
+ {savedImages.map((url, index) => (
59
+ <div key={index} className="border rounded-lg p-2">
60
+ <img
61
+ src={url}
62
+ alt={`Saved image ${index + 1}`}
63
+ className="w-full h-32 object-cover rounded"
64
+ />
65
+ </div>
66
+ ))}
67
+ </div>
68
+ </div>
69
+ )}
70
+ </div>
71
+ );
72
+ }
@@ -4,6 +4,129 @@ import { useState, useCallback } from "react";
4
4
  import type { AiImageRequest, AiImageResponse } from "@lastbrain/ai-ui-core";
5
5
  import { useAiClient } from "./useAiClient";
6
6
 
7
+ /**
8
+ * Génère une image Canvas pour l'environnement de développement
9
+ */
10
+ async function generateDevImage(
11
+ prompt: string,
12
+ width: number,
13
+ height: number
14
+ ): Promise<string> {
15
+ // Créer un canvas
16
+ const canvas = document.createElement("canvas");
17
+ const ctx = canvas.getContext("2d");
18
+
19
+ if (!ctx) {
20
+ throw new Error("Could not create canvas context");
21
+ }
22
+
23
+ canvas.width = width;
24
+ canvas.height = height;
25
+
26
+ // Générer des couleurs basées sur le prompt
27
+ const promptHash = prompt
28
+ .split("")
29
+ .reduce((hash, char) => (hash * 31 + char.charCodeAt(0)) % 255, 0);
30
+
31
+ // Couleurs de base dégradées
32
+ const colors = [
33
+ `hsl(${(promptHash * 7) % 360}, 70%, 60%)`,
34
+ `hsl(${(promptHash * 11) % 360}, 60%, 70%)`,
35
+ `hsl(${(promptHash * 13) % 360}, 50%, 80%)`,
36
+ ];
37
+
38
+ // Créer un dégradé radial
39
+ const gradient = ctx.createRadialGradient(
40
+ width / 2,
41
+ height / 2,
42
+ 0,
43
+ width / 2,
44
+ height / 2,
45
+ Math.max(width, height) / 2
46
+ );
47
+
48
+ gradient.addColorStop(0, colors[0]);
49
+ gradient.addColorStop(0.5, colors[1]);
50
+ gradient.addColorStop(1, colors[2]);
51
+
52
+ // Remplir le fond
53
+ ctx.fillStyle = gradient;
54
+ ctx.fillRect(0, 0, width, height);
55
+
56
+ // Ajouter des formes géométriques basées sur le prompt
57
+ const shapes = prompt.split(" ").slice(0, 5);
58
+
59
+ shapes.forEach((word, index) => {
60
+ const wordHash = word
61
+ .split("")
62
+ .reduce((hash, char) => (hash * 31 + char.charCodeAt(0)) % 1000, 0);
63
+
64
+ ctx.save();
65
+ ctx.globalAlpha = 0.3 + index * 0.1;
66
+ ctx.fillStyle = `hsl(${(wordHash * 17) % 360}, 80%, 50%)`;
67
+
68
+ // Formes différentes selon l'index
69
+ const x = (wordHash % (width - 100)) + 50;
70
+ const y = ((wordHash * 3) % (height - 100)) + 50;
71
+ const size = 30 + (wordHash % 50);
72
+
73
+ switch (index % 4) {
74
+ case 0: // Cercle
75
+ ctx.beginPath();
76
+ ctx.arc(x, y, size, 0, Math.PI * 2);
77
+ ctx.fill();
78
+ break;
79
+ case 1: // Carré
80
+ ctx.fillRect(x - size / 2, y - size / 2, size, size);
81
+ break;
82
+ case 2: // Triangle
83
+ ctx.beginPath();
84
+ ctx.moveTo(x, y - size / 2);
85
+ ctx.lineTo(x - size / 2, y + size / 2);
86
+ ctx.lineTo(x + size / 2, y + size / 2);
87
+ ctx.closePath();
88
+ ctx.fill();
89
+ break;
90
+ case 3: // Losange
91
+ ctx.save();
92
+ ctx.translate(x, y);
93
+ ctx.rotate((wordHash / 100) % (Math.PI * 2));
94
+ ctx.fillRect(-size / 2, -size / 2, size, size);
95
+ ctx.restore();
96
+ break;
97
+ }
98
+ ctx.restore();
99
+ });
100
+
101
+ // Ajouter le texte du prompt en overlay
102
+ ctx.save();
103
+ ctx.fillStyle = "rgba(255, 255, 255, 0.9)";
104
+ ctx.font = `bold ${Math.min(width, height) / 30}px system-ui, -apple-system, sans-serif`;
105
+ ctx.textAlign = "center";
106
+ ctx.textBaseline = "middle";
107
+
108
+ // Fond semi-transparent pour le texte
109
+ const textMetrics = ctx.measureText(prompt);
110
+ const textWidth = textMetrics.width;
111
+ const textHeight = parseInt(ctx.font);
112
+
113
+ ctx.fillStyle = "rgba(0, 0, 0, 0.6)";
114
+ ctx.fillRect(
115
+ width / 2 - textWidth / 2 - 20,
116
+ height / 2 - textHeight / 2 - 10,
117
+ textWidth + 40,
118
+ textHeight + 20
119
+ );
120
+
121
+ // Texte du prompt
122
+ ctx.fillStyle = "white";
123
+ ctx.fillText(prompt, width / 2, height / 2);
124
+ ctx.restore();
125
+
126
+ // Convertir en data URL
127
+ return canvas.toDataURL("image/png", 0.8);
128
+ }
129
+
7
130
  export interface UseAiCallImageOptions {
8
131
  baseUrl?: string;
9
132
  apiKeyId?: string;
@@ -27,6 +150,31 @@ export function useAiCallImage(
27
150
  setLoading(true);
28
151
  setError(null);
29
152
  try {
153
+ // Vérifier si on est en mode dev (API key contient "dev")
154
+ if (options?.apiKeyId?.includes("dev")) {
155
+ // Simulation complète pour l'environnement dev
156
+ await new Promise((resolve) => setTimeout(resolve, 2000)); // Délai réaliste pour simuler l'API
157
+
158
+ // Parse size format "1024x1024" or default to "1024x1024"
159
+ const sizeMatch = request.size?.match(/(\d+)x(\d+)/);
160
+ const width = sizeMatch ? parseInt(sizeMatch[1]) : 1024;
161
+ const height = sizeMatch ? parseInt(sizeMatch[2]) : 1024;
162
+
163
+ // Créer une image générée avec Canvas
164
+ const imageUrl = await generateDevImage(
165
+ request.prompt,
166
+ width,
167
+ height
168
+ );
169
+
170
+ return {
171
+ requestId: `dev-img-${Date.now()}`,
172
+ url: imageUrl,
173
+ debitTokens: 30, // Coût simulé
174
+ };
175
+ }
176
+
177
+ // Mode production : appel API normal
30
178
  const result = await client.generateImage(request);
31
179
  return result;
32
180
  } catch (err) {
@@ -38,7 +186,7 @@ export function useAiCallImage(
38
186
  setLoading(false);
39
187
  }
40
188
  },
41
- [client]
189
+ [client, options?.apiKeyId]
42
190
  );
43
191
 
44
192
  return {
@@ -27,6 +27,35 @@ export function useAiCallText(
27
27
  setLoading(true);
28
28
  setError(null);
29
29
  try {
30
+ // Vérifier si on est en mode dev (API key contient "dev")
31
+ if (options?.apiKeyId?.includes("dev")) {
32
+ // Simulation pour l'environnement dev
33
+ await new Promise((resolve) => setTimeout(resolve, 1000)); // Délai pour simuler l'API
34
+
35
+ let simulatedResult = "";
36
+
37
+ // Si le prompt contient des mots-clés pour les chips
38
+ if (
39
+ request.prompt.toLowerCase().includes("tags") ||
40
+ request.prompt.toLowerCase().includes("chip") ||
41
+ request.prompt.toLowerCase().includes("virgule")
42
+ ) {
43
+ simulatedResult =
44
+ "react, typescript, javascript, frontend, backend, api, development, web, mobile, database";
45
+ } else {
46
+ // Lorem ipsum pour les autres cas
47
+ simulatedResult =
48
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.";
49
+ }
50
+
51
+ return {
52
+ requestId: `dev-${Date.now()}`,
53
+ text: simulatedResult,
54
+ debitTokens: 150,
55
+ };
56
+ }
57
+
58
+ // Mode production : appel API normal
30
59
  const result = await client.generateText(request);
31
60
  return result;
32
61
  } catch (err) {
@@ -38,7 +67,7 @@ export function useAiCallText(
38
67
  setLoading(false);
39
68
  }
40
69
  },
41
- [client]
70
+ [client, options?.apiKeyId]
42
71
  );
43
72
 
44
73
  return {
package/src/index.ts CHANGED
@@ -21,6 +21,7 @@ export * from "./components/AiTextarea";
21
21
  export * from "./components/AiSelect";
22
22
  export * from "./components/AiChipLabel";
23
23
  export * from "./components/AiImageButton";
24
+ export * from "./components/AiContextButton";
24
25
  export * from "./components/AiSettingsButton";
25
26
  export * from "./components/AiStatusButton";
26
27
 
@@ -31,3 +32,6 @@ export * from "./utils/cache";
31
32
  // Examples
32
33
  export * from "./examples/AiImageGenerator";
33
34
  export * from "./examples/AiPromptPanelAdvanced";
35
+ export * from "./examples/AiChipInputExample";
36
+ export * from "./examples/AiImageButtonExample";
37
+ export * from "./examples/AiContextButtonExample";
@@ -278,7 +278,9 @@ export const aiStyles = {
278
278
  lineHeight: "1.5",
279
279
  color: themeVars.text,
280
280
  background: themeVars.bg,
281
- border: `1px solid ${themeVars.border}`,
281
+ borderWidth: "1px",
282
+ borderStyle: "solid",
283
+ borderColor: themeVars.border,
282
284
  borderRadius: "8px",
283
285
  outline: "none",
284
286
  transition: "all 0.2s",