@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.
- package/dist/components/AiChipLabel.d.ts +12 -0
- package/dist/components/AiChipLabel.d.ts.map +1 -1
- package/dist/components/AiChipLabel.js +129 -1
- package/dist/components/AiContextButton.d.ts +18 -0
- package/dist/components/AiContextButton.d.ts.map +1 -0
- package/dist/components/AiContextButton.js +369 -0
- package/dist/components/AiImageButton.d.ts +12 -3
- package/dist/components/AiImageButton.d.ts.map +1 -1
- package/dist/components/AiImageButton.js +218 -8
- package/dist/components/AiPromptPanel.d.ts +1 -1
- package/dist/components/AiPromptPanel.d.ts.map +1 -1
- package/dist/components/AiPromptPanel.js +11 -1
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +1 -1
- package/dist/components/UsageToast.d.ts.map +1 -1
- package/dist/components/UsageToast.js +5 -3
- package/dist/examples/AiChipInputExample.d.ts +2 -0
- package/dist/examples/AiChipInputExample.d.ts.map +1 -0
- package/dist/examples/AiChipInputExample.js +14 -0
- package/dist/examples/AiContextButtonExample.d.ts +2 -0
- package/dist/examples/AiContextButtonExample.d.ts.map +1 -0
- package/dist/examples/AiContextButtonExample.js +88 -0
- package/dist/examples/AiImageButtonExample.d.ts +2 -0
- package/dist/examples/AiImageButtonExample.d.ts.map +1 -0
- package/dist/examples/AiImageButtonExample.js +26 -0
- package/dist/hooks/useAiCallImage.d.ts.map +1 -1
- package/dist/hooks/useAiCallImage.js +107 -1
- package/dist/hooks/useAiCallText.d.ts.map +1 -1
- package/dist/hooks/useAiCallText.js +25 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/styles/inline.d.ts.map +1 -1
- package/dist/styles/inline.js +3 -1
- package/package.json +2 -2
- package/src/components/AiChipLabel.tsx +218 -1
- package/src/components/AiContextButton.tsx +585 -0
- package/src/components/AiImageButton.tsx +386 -38
- package/src/components/AiPromptPanel.tsx +18 -1
- package/src/components/AiStatusButton.tsx +7 -3
- package/src/components/UsageToast.tsx +5 -3
- package/src/examples/AiChipInputExample.tsx +81 -0
- package/src/examples/AiContextButtonExample.tsx +338 -0
- package/src/examples/AiImageButtonExample.tsx +72 -0
- package/src/hooks/useAiCallImage.ts +149 -1
- package/src/hooks/useAiCallText.ts +30 -1
- package/src/index.ts +4 -0
- 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";
|
package/src/styles/inline.ts
CHANGED
|
@@ -278,7 +278,9 @@ export const aiStyles = {
|
|
|
278
278
|
lineHeight: "1.5",
|
|
279
279
|
color: themeVars.text,
|
|
280
280
|
background: themeVars.bg,
|
|
281
|
-
|
|
281
|
+
borderWidth: "1px",
|
|
282
|
+
borderStyle: "solid",
|
|
283
|
+
borderColor: themeVars.border,
|
|
282
284
|
borderRadius: "8px",
|
|
283
285
|
outline: "none",
|
|
284
286
|
transition: "all 0.2s",
|