@lastbrain/ai-ui-react 1.0.68 → 1.0.69
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 +8 -3
- package/dist/components/AiChipLabel.d.ts.map +1 -1
- package/dist/components/AiChipLabel.js +21 -70
- package/dist/components/AiContextButton.d.ts +5 -1
- package/dist/components/AiContextButton.d.ts.map +1 -1
- package/dist/components/AiContextButton.js +67 -291
- package/dist/components/AiImageButton.d.ts +5 -1
- package/dist/components/AiImageButton.d.ts.map +1 -1
- package/dist/components/AiImageButton.js +6 -142
- package/dist/components/AiInput.d.ts +5 -3
- package/dist/components/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +13 -25
- package/dist/components/AiPromptPanel.d.ts.map +1 -1
- package/dist/components/AiPromptPanel.js +58 -212
- package/dist/components/AiSelect.d.ts +5 -3
- package/dist/components/AiSelect.d.ts.map +1 -1
- package/dist/components/AiSelect.js +21 -30
- package/dist/components/AiStatusButton.d.ts +4 -1
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +198 -668
- package/dist/components/AiTextarea.d.ts +4 -2
- package/dist/components/AiTextarea.d.ts.map +1 -1
- package/dist/components/AiTextarea.js +14 -26
- package/dist/components/LBApiKeySelector.d.ts.map +1 -1
- package/dist/components/LBApiKeySelector.js +5 -166
- package/dist/components/LBConnectButton.d.ts +4 -7
- package/dist/components/LBConnectButton.d.ts.map +1 -1
- package/dist/components/LBConnectButton.js +17 -86
- package/dist/components/LBSigninModal.d.ts +1 -1
- package/dist/components/LBSigninModal.d.ts.map +1 -1
- package/dist/components/LBSigninModal.js +42 -320
- package/dist/examples/AiUiPremiumShowcase.d.ts +2 -0
- package/dist/examples/AiUiPremiumShowcase.d.ts.map +1 -0
- package/dist/examples/AiUiPremiumShowcase.js +15 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/styles/inline.d.ts +1 -0
- package/dist/styles/inline.d.ts.map +1 -1
- package/dist/styles/inline.js +25 -129
- package/dist/styles.css +1268 -369
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/AiChipLabel.tsx +64 -101
- package/src/components/AiContextButton.tsx +138 -430
- package/src/components/AiImageButton.tsx +29 -190
- package/src/components/AiInput.tsx +49 -74
- package/src/components/AiPromptPanel.tsx +71 -254
- package/src/components/AiSelect.tsx +61 -69
- package/src/components/AiStatusButton.tsx +477 -1313
- package/src/components/AiTextarea.tsx +49 -64
- package/src/components/LBApiKeySelector.tsx +86 -274
- package/src/components/LBConnectButton.tsx +46 -334
- package/src/components/LBSigninModal.tsx +140 -481
- package/src/examples/AiUiPremiumShowcase.tsx +91 -0
- package/src/index.ts +3 -0
- package/src/styles/inline.ts +27 -148
- package/src/styles.css +1268 -369
- package/src/types.ts +3 -0
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useState, type ButtonHTMLAttributes } from "react";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
Download,
|
|
6
|
+
FileText,
|
|
7
|
+
Loader2,
|
|
8
|
+
Lock,
|
|
9
|
+
Sparkles,
|
|
10
|
+
X,
|
|
11
|
+
} from "lucide-react";
|
|
5
12
|
import type { BaseAiProps } from "../types";
|
|
13
|
+
import type { AiRadius, AiSize, AiVariant } from "../types";
|
|
6
14
|
import { useAiCallText } from "../hooks/useAiCallText";
|
|
7
15
|
import { AiPromptPanel } from "./AiPromptPanel";
|
|
8
16
|
import { useUsageToast } from "./UsageToast";
|
|
9
17
|
import { useErrorToast, ErrorToast } from "./ErrorToast";
|
|
10
|
-
import { aiStyles } from "../styles/inline";
|
|
11
18
|
import { useAiContext } from "../context/AiProvider";
|
|
12
19
|
import { handleAIError } from "../utils/errorHandler";
|
|
13
20
|
import { useLB } from "../context/LBAuthProvider";
|
|
14
21
|
import { LBSigninModal } from "./LBSigninModal";
|
|
15
22
|
|
|
16
23
|
export interface AiContextButtonProps
|
|
17
|
-
extends
|
|
18
|
-
Omit<BaseAiProps, "onValue" | "type">,
|
|
24
|
+
extends Omit<BaseAiProps, "onValue" | "type">,
|
|
19
25
|
Omit<ButtonHTMLAttributes<HTMLButtonElement>, "baseUrl" | "apiKeyId"> {
|
|
20
|
-
// Données de contexte à analyser
|
|
21
26
|
contextData: any;
|
|
22
27
|
contextDescription?: string;
|
|
23
28
|
onResult?: (
|
|
@@ -26,11 +31,13 @@ export interface AiContextButtonProps
|
|
|
26
31
|
) => void;
|
|
27
32
|
uiMode?: "modal" | "drawer";
|
|
28
33
|
resultModalTitle?: string;
|
|
29
|
-
storeOutputs?: boolean;
|
|
30
|
-
artifactTitle?: string;
|
|
31
|
-
// Props optionnelles pour override du contexte
|
|
34
|
+
storeOutputs?: boolean;
|
|
35
|
+
artifactTitle?: string;
|
|
32
36
|
baseUrl?: string;
|
|
33
37
|
apiKeyId?: string;
|
|
38
|
+
size?: AiSize;
|
|
39
|
+
radius?: AiRadius;
|
|
40
|
+
variant?: AiVariant;
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
export function AiContextButton({
|
|
@@ -45,11 +52,10 @@ export function AiContextButton({
|
|
|
45
52
|
className,
|
|
46
53
|
children,
|
|
47
54
|
resultModalTitle = "Résultat de l'analyse",
|
|
48
|
-
|
|
49
|
-
|
|
55
|
+
size = "md",
|
|
56
|
+
radius = "full",
|
|
57
|
+
variant = "default",
|
|
50
58
|
context: _context,
|
|
51
|
-
model: _model,
|
|
52
|
-
prompt: _prompt,
|
|
53
59
|
...buttonProps
|
|
54
60
|
}: AiContextButtonProps) {
|
|
55
61
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -62,20 +68,18 @@ export function AiContextButton({
|
|
|
62
68
|
tokens: number;
|
|
63
69
|
cost: number;
|
|
64
70
|
} | null>(null);
|
|
65
|
-
|
|
71
|
+
|
|
72
|
+
const { showUsageToast } = useUsageToast();
|
|
66
73
|
const { showErrorToast, errorData, errorKey, clearError } = useErrorToast();
|
|
67
74
|
|
|
68
|
-
// Rendre l'authentification optionnelle
|
|
69
75
|
let lbStatus: string | undefined;
|
|
70
76
|
try {
|
|
71
77
|
const lbContext = useLB();
|
|
72
78
|
lbStatus = lbContext.status;
|
|
73
79
|
} catch {
|
|
74
|
-
// LBProvider n'est pas disponible, ignorer
|
|
75
80
|
lbStatus = undefined;
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
// Récupérer le contexte AiProvider avec fallback sur les props
|
|
79
83
|
const aiContext = useAiContext();
|
|
80
84
|
const baseUrl = propBaseUrl ?? aiContext.baseUrl;
|
|
81
85
|
const apiKeyId = propApiKeyId ?? aiContext.apiKeyId;
|
|
@@ -95,99 +99,20 @@ export function AiContextButton({
|
|
|
95
99
|
setIsOpen(true);
|
|
96
100
|
};
|
|
97
101
|
|
|
98
|
-
const handleClosePanel = () => {
|
|
99
|
-
setIsOpen(false);
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const handleCloseResult = () => {
|
|
103
|
-
setIsResultOpen(false);
|
|
104
|
-
setAnalysisResult(null);
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
const saveToFile = () => {
|
|
108
|
-
if (!analysisResult) return;
|
|
109
|
-
|
|
110
|
-
const currentDate = new Date()
|
|
111
|
-
.toLocaleDateString("fr-FR")
|
|
112
|
-
.replace(/\//g, "-");
|
|
113
|
-
const defaultName = `analyse-${currentDate}.txt`;
|
|
114
|
-
const fileName = prompt("Nom du fichier :", defaultName) || defaultName;
|
|
115
|
-
|
|
116
|
-
const content = `ANALYSE DES DONNÉES - ${new Date().toLocaleString("fr-FR")}
|
|
117
|
-
|
|
118
|
-
PROMPT UTILISÉ :
|
|
119
|
-
${analysisResult.prompt}
|
|
120
|
-
|
|
121
|
-
RÉSULTAT DE L'ANALYSE :
|
|
122
|
-
${analysisResult.content}
|
|
123
|
-
|
|
124
|
-
--- MÉTADONNÉES ---
|
|
125
|
-
Tokens utilisés: ${analysisResult.tokens.toLocaleString()}
|
|
126
|
-
Coût: $${(apiKeyId?.includes("dev") ? 0 : analysisResult.cost).toFixed(6)}
|
|
127
|
-
ID de requête: ${analysisResult.requestId || "N/A"}
|
|
128
|
-
Date: ${new Date().toLocaleString("fr-FR")}`;
|
|
129
|
-
|
|
130
|
-
const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
|
|
131
|
-
const url = URL.createObjectURL(blob);
|
|
132
|
-
const a = document.createElement("a");
|
|
133
|
-
a.href = url;
|
|
134
|
-
a.download = fileName.endsWith(".txt") ? fileName : `${fileName}.txt`;
|
|
135
|
-
document.body.appendChild(a);
|
|
136
|
-
a.click();
|
|
137
|
-
document.body.removeChild(a);
|
|
138
|
-
URL.revokeObjectURL(url);
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
// Styles selon le thème
|
|
142
|
-
const getThemeStyles = () => {
|
|
143
|
-
const isDark =
|
|
144
|
-
typeof document !== "undefined" &&
|
|
145
|
-
(document.documentElement.classList.contains("dark") ||
|
|
146
|
-
(!document.documentElement.classList.contains("light") &&
|
|
147
|
-
window.matchMedia("(prefers-color-scheme: dark)").matches));
|
|
148
|
-
|
|
149
|
-
return {
|
|
150
|
-
modal: {
|
|
151
|
-
backgroundColor: isDark ? "#1f2937" : "white",
|
|
152
|
-
border: `1px solid ${isDark ? "#374151" : "#e5e7eb"}`,
|
|
153
|
-
color: isDark ? "#f3f4f6" : "#374151",
|
|
154
|
-
},
|
|
155
|
-
header: {
|
|
156
|
-
color: isDark ? "#f9fafb" : "#1f2937",
|
|
157
|
-
borderBottom: `1px solid ${isDark ? "#374151" : "#e5e7eb"}`,
|
|
158
|
-
},
|
|
159
|
-
content: {
|
|
160
|
-
backgroundColor: isDark ? "#111827" : "#f9fafb",
|
|
161
|
-
border: `1px solid ${isDark ? "#374151" : "#e5e7eb"}`,
|
|
162
|
-
},
|
|
163
|
-
closeButton: {
|
|
164
|
-
color: isDark ? "#9ca3af" : "#6b7280",
|
|
165
|
-
hoverColor: isDark ? "#d1d5db" : "#374151",
|
|
166
|
-
},
|
|
167
|
-
};
|
|
168
|
-
};
|
|
169
|
-
|
|
170
102
|
const formatContextData = (data: any): string => {
|
|
171
|
-
if (typeof data === "string")
|
|
103
|
+
if (typeof data === "string") {
|
|
104
|
+
return data;
|
|
105
|
+
}
|
|
172
106
|
if (typeof data === "object" && data !== null) {
|
|
173
107
|
return JSON.stringify(data, null, 2);
|
|
174
108
|
}
|
|
175
109
|
return String(data);
|
|
176
110
|
};
|
|
177
111
|
|
|
178
|
-
const handleSubmit = async (
|
|
179
|
-
selectedModel: string,
|
|
180
|
-
selectedPrompt: string
|
|
181
|
-
) => {
|
|
112
|
+
const handleSubmit = async (selectedModel: string, selectedPrompt: string) => {
|
|
182
113
|
try {
|
|
183
|
-
// Construire le prompt avec le contexte
|
|
184
114
|
const contextString = formatContextData(contextData);
|
|
185
|
-
const fullPrompt = `${selectedPrompt}
|
|
186
|
-
|
|
187
|
-
CONTEXTE (${contextDescription}):
|
|
188
|
-
${contextString}
|
|
189
|
-
|
|
190
|
-
Analyse ces données et réponds de manière structurée et claire.`;
|
|
115
|
+
const fullPrompt = `${selectedPrompt}\n\nCONTEXTE (${contextDescription}):\n${contextString}\n\nAnalyse ces données et réponds de manière structurée et claire.`;
|
|
191
116
|
|
|
192
117
|
const result = await callText({
|
|
193
118
|
prompt: fullPrompt,
|
|
@@ -197,48 +122,46 @@ Analyse ces données et réponds de manière structurée et claire.`;
|
|
|
197
122
|
temperature: 0.7,
|
|
198
123
|
});
|
|
199
124
|
|
|
200
|
-
if (result.text) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const totalTokens =
|
|
204
|
-
(resultAny.inputTokens || 0) + (resultAny.outputTokens || 0) ||
|
|
205
|
-
result.debitTokens ||
|
|
206
|
-
0;
|
|
207
|
-
const actualCost = resultAny.cost || 0;
|
|
208
|
-
|
|
209
|
-
const resultData = {
|
|
210
|
-
content: result.text,
|
|
211
|
-
prompt: selectedPrompt,
|
|
212
|
-
requestId: result.requestId,
|
|
213
|
-
tokens: totalTokens,
|
|
214
|
-
cost: actualCost,
|
|
215
|
-
};
|
|
125
|
+
if (!result.text) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
216
128
|
|
|
217
|
-
|
|
218
|
-
|
|
129
|
+
const resultAny = result as any;
|
|
130
|
+
const totalTokens =
|
|
131
|
+
(resultAny.inputTokens || 0) + (resultAny.outputTokens || 0) ||
|
|
132
|
+
result.debitTokens ||
|
|
133
|
+
0;
|
|
134
|
+
const actualCost = resultAny.cost || 0;
|
|
135
|
+
|
|
136
|
+
setAnalysisResult({
|
|
137
|
+
content: result.text,
|
|
138
|
+
prompt: selectedPrompt,
|
|
139
|
+
requestId: result.requestId,
|
|
140
|
+
tokens: totalTokens,
|
|
141
|
+
cost: actualCost,
|
|
142
|
+
});
|
|
143
|
+
setIsResultOpen(true);
|
|
219
144
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
145
|
+
onResult?.(result.text, {
|
|
146
|
+
requestId: result.requestId,
|
|
147
|
+
tokens: result.debitTokens || 0,
|
|
148
|
+
});
|
|
224
149
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
150
|
+
onToast?.({
|
|
151
|
+
type: "success",
|
|
152
|
+
message: `Analyse terminée - Coût: $${(apiKeyId?.includes("dev") ? 0 : actualCost).toFixed(6)}`,
|
|
153
|
+
});
|
|
229
154
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
});
|
|
241
|
-
}
|
|
155
|
+
showUsageToast({
|
|
156
|
+
requestId: result.requestId,
|
|
157
|
+
debitTokens: totalTokens,
|
|
158
|
+
usage: {
|
|
159
|
+
total_tokens: totalTokens,
|
|
160
|
+
prompt_tokens: resultAny.inputTokens || 0,
|
|
161
|
+
completion_tokens: resultAny.outputTokens || 0,
|
|
162
|
+
},
|
|
163
|
+
cost: apiKeyId?.includes("dev") ? 0 : actualCost,
|
|
164
|
+
});
|
|
242
165
|
} catch (error) {
|
|
243
166
|
console.error("AiContextButton error:", error);
|
|
244
167
|
handleAIError(error, onToast, showErrorToast);
|
|
@@ -247,352 +170,137 @@ Analyse ces données et réponds de manière structurée et claire.`;
|
|
|
247
170
|
}
|
|
248
171
|
};
|
|
249
172
|
|
|
173
|
+
const saveToFile = () => {
|
|
174
|
+
if (!analysisResult) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const currentDate = new Date().toLocaleDateString("fr-FR").replace(/\//g, "-");
|
|
179
|
+
const defaultName = `analyse-${currentDate}.txt`;
|
|
180
|
+
const fileName = prompt("Nom du fichier :", defaultName) || defaultName;
|
|
181
|
+
|
|
182
|
+
const content = `ANALYSE DES DONNÉES - ${new Date().toLocaleString("fr-FR")}\n\nPROMPT UTILISÉ :\n${analysisResult.prompt}\n\nRÉSULTAT DE L'ANALYSE :\n${analysisResult.content}\n\n--- MÉTADONNÉES ---\nTokens utilisés: ${analysisResult.tokens.toLocaleString()}\nCoût: $${(
|
|
183
|
+
apiKeyId?.includes("dev") ? 0 : analysisResult.cost
|
|
184
|
+
).toFixed(6)}\nID de requête: ${analysisResult.requestId || "N/A"}`;
|
|
185
|
+
|
|
186
|
+
const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
|
|
187
|
+
const url = URL.createObjectURL(blob);
|
|
188
|
+
const anchor = document.createElement("a");
|
|
189
|
+
anchor.href = url;
|
|
190
|
+
anchor.download = fileName.endsWith(".txt") ? fileName : `${fileName}.txt`;
|
|
191
|
+
document.body.appendChild(anchor);
|
|
192
|
+
anchor.click();
|
|
193
|
+
document.body.removeChild(anchor);
|
|
194
|
+
URL.revokeObjectURL(url);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const sizeClass = `ai-size-${size}`;
|
|
198
|
+
const radiusClass = `ai-radius-${radius}`;
|
|
199
|
+
const variantClass = variant === "light" ? "ai-btn--light" : "";
|
|
200
|
+
|
|
250
201
|
return (
|
|
251
202
|
<>
|
|
252
|
-
<div
|
|
203
|
+
<div className="relative inline-block ai-glow">
|
|
253
204
|
<button
|
|
254
205
|
{...buttonProps}
|
|
255
206
|
onClick={handleOpenPanel}
|
|
256
207
|
disabled={disabled || loading || !isAuthReady}
|
|
257
|
-
className={className}
|
|
258
|
-
|
|
259
|
-
...aiStyles.button,
|
|
260
|
-
display: "flex",
|
|
261
|
-
alignItems: "center",
|
|
262
|
-
gap: "8px",
|
|
263
|
-
cursor:
|
|
264
|
-
disabled || loading || !isAuthReady ? "not-allowed" : "pointer",
|
|
265
|
-
opacity: disabled || loading || !isAuthReady ? 0.6 : 1,
|
|
266
|
-
backgroundColor: loading
|
|
267
|
-
? "#8b5cf6"
|
|
268
|
-
: !isAuthReady
|
|
269
|
-
? "#94a3b8"
|
|
270
|
-
: "#7c3aed",
|
|
271
|
-
color: "white",
|
|
272
|
-
border: "none",
|
|
273
|
-
borderRadius: "12px",
|
|
274
|
-
// padding: "12px 20px",
|
|
275
|
-
|
|
276
|
-
fontSize: "14px",
|
|
277
|
-
fontWeight: "600",
|
|
278
|
-
minWidth: "20px",
|
|
279
|
-
height: "44px",
|
|
280
|
-
transition: "all 0.2s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
281
|
-
boxShadow: loading
|
|
282
|
-
? "0 4px 12px rgba(139, 92, 246, 0.3)"
|
|
283
|
-
: "0 2px 8px rgba(124, 58, 237, 0.2)",
|
|
284
|
-
transform: "scale(1)",
|
|
285
|
-
...(loading && {
|
|
286
|
-
background: "linear-gradient(135deg, #7c3aed, #8b5cf6)",
|
|
287
|
-
animation: "pulse 2s ease-in-out infinite",
|
|
288
|
-
}),
|
|
289
|
-
...buttonProps.style,
|
|
290
|
-
}}
|
|
291
|
-
onMouseEnter={(e) => {
|
|
292
|
-
if (!disabled && !loading && isAuthReady) {
|
|
293
|
-
e.currentTarget.style.transform = "scale(1.02)";
|
|
294
|
-
e.currentTarget.style.boxShadow =
|
|
295
|
-
"0 6px 16px rgba(124, 58, 237, 0.3)";
|
|
296
|
-
}
|
|
297
|
-
}}
|
|
298
|
-
onMouseLeave={(e) => {
|
|
299
|
-
if (!disabled && !loading && isAuthReady) {
|
|
300
|
-
e.currentTarget.style.transform = "scale(1)";
|
|
301
|
-
e.currentTarget.style.boxShadow = loading
|
|
302
|
-
? "0 4px 12px rgba(139, 92, 246, 0.3)"
|
|
303
|
-
: "0 2px 8px rgba(124, 58, 237, 0.2)";
|
|
304
|
-
}
|
|
305
|
-
}}
|
|
306
|
-
onMouseDown={(e) => {
|
|
307
|
-
if (!disabled && !loading && isAuthReady) {
|
|
308
|
-
e.currentTarget.style.transform = "scale(0.98)";
|
|
309
|
-
}
|
|
310
|
-
}}
|
|
311
|
-
onMouseUp={(e) => {
|
|
312
|
-
if (!disabled && !loading && isAuthReady) {
|
|
313
|
-
e.currentTarget.style.transform = "scale(1.02)";
|
|
314
|
-
}
|
|
315
|
-
}}
|
|
316
|
-
data-ai-context-button
|
|
317
|
-
title={
|
|
318
|
-
!isAuthReady ? "Authentication required" : "Analyser avec l'IA"
|
|
319
|
-
}
|
|
208
|
+
className={`ai-btn ai-context-btn ${variantClass} ${sizeClass} ${radiusClass} ${className || ""}`}
|
|
209
|
+
title={!isAuthReady ? "Authentication required" : "Analyser avec l'IA"}
|
|
320
210
|
>
|
|
321
211
|
{loading ? (
|
|
322
212
|
<>
|
|
323
|
-
<Loader2
|
|
324
|
-
|
|
325
|
-
className="animate-spin"
|
|
326
|
-
style={{
|
|
327
|
-
color: "white",
|
|
328
|
-
filter: "drop-shadow(0 0 2px rgba(255,255,255,0.3))",
|
|
329
|
-
}}
|
|
330
|
-
/>
|
|
331
|
-
<span style={{ letterSpacing: "0.025em" }}>Analyse...</span>
|
|
213
|
+
<Loader2 size={16} className="ai-spinner" />
|
|
214
|
+
<span>Analyse...</span>
|
|
332
215
|
</>
|
|
333
216
|
) : !isAuthReady ? (
|
|
334
217
|
<>
|
|
335
|
-
<Lock
|
|
336
|
-
size={18}
|
|
337
|
-
style={{
|
|
338
|
-
color: "white",
|
|
339
|
-
filter: "drop-shadow(0 0 2px rgba(255,255,255,0.2))",
|
|
340
|
-
}}
|
|
341
|
-
/>
|
|
218
|
+
<Lock size={16} />
|
|
342
219
|
{children || <span>Connexion requise</span>}
|
|
343
220
|
</>
|
|
344
221
|
) : (
|
|
345
222
|
<>
|
|
346
|
-
<
|
|
347
|
-
|
|
348
|
-
style={{
|
|
349
|
-
color: "white",
|
|
350
|
-
filter: "drop-shadow(0 0 2px rgba(255,255,255,0.2))",
|
|
351
|
-
}}
|
|
352
|
-
/>
|
|
353
|
-
{/* <span style={{ letterSpacing: "0.025em" }}>{children || ""}</span> */}
|
|
223
|
+
<Sparkles size={16} />
|
|
224
|
+
{children || <span>Analyser</span>}
|
|
354
225
|
</>
|
|
355
226
|
)}
|
|
356
227
|
</button>
|
|
357
228
|
|
|
358
|
-
{isOpen
|
|
229
|
+
{isOpen ? (
|
|
359
230
|
<AiPromptPanel
|
|
360
231
|
isOpen={isOpen}
|
|
361
|
-
onClose={
|
|
232
|
+
onClose={() => setIsOpen(false)}
|
|
362
233
|
onSubmit={handleSubmit}
|
|
363
234
|
uiMode={uiMode}
|
|
364
235
|
models={[]}
|
|
365
|
-
enableModelManagement
|
|
236
|
+
enableModelManagement
|
|
366
237
|
modelCategory="text"
|
|
367
238
|
baseUrl={baseUrl}
|
|
368
239
|
apiKey={apiKeyId}
|
|
369
240
|
/>
|
|
370
|
-
)}
|
|
241
|
+
) : null}
|
|
371
242
|
</div>
|
|
372
243
|
|
|
373
|
-
{
|
|
374
|
-
{isResultOpen && analysisResult && (
|
|
244
|
+
{isResultOpen && analysisResult ? (
|
|
375
245
|
<div
|
|
376
|
-
|
|
377
|
-
position: "fixed",
|
|
378
|
-
top: 0,
|
|
379
|
-
left: 0,
|
|
380
|
-
right: 0,
|
|
381
|
-
bottom: 0,
|
|
382
|
-
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
383
|
-
backdropFilter: "blur(8px)",
|
|
384
|
-
display: "flex",
|
|
385
|
-
alignItems: "center",
|
|
386
|
-
justifyContent: "center",
|
|
387
|
-
zIndex: 1000,
|
|
388
|
-
padding: "20px",
|
|
389
|
-
}}
|
|
246
|
+
className="ai-signin-overlay ai-overlay-panel"
|
|
390
247
|
onClick={(e) => {
|
|
391
248
|
if (e.target === e.currentTarget) {
|
|
392
|
-
|
|
249
|
+
setIsResultOpen(false);
|
|
250
|
+
setAnalysisResult(null);
|
|
393
251
|
}
|
|
394
252
|
}}
|
|
395
253
|
>
|
|
396
|
-
<div
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
maxHeight: "90vh",
|
|
402
|
-
borderRadius: "16px",
|
|
403
|
-
boxShadow: "0 20px 25px -5px rgba(0, 0, 0, 0.1)",
|
|
404
|
-
overflow: "hidden",
|
|
405
|
-
...getThemeStyles().modal,
|
|
406
|
-
}}
|
|
407
|
-
>
|
|
408
|
-
{/* Header */}
|
|
409
|
-
<div
|
|
410
|
-
style={{
|
|
411
|
-
padding: "20px 24px 16px",
|
|
412
|
-
marginBottom: "12px",
|
|
413
|
-
display: "flex",
|
|
414
|
-
alignItems: "center",
|
|
415
|
-
gap: "12px",
|
|
416
|
-
flexDirection: "column",
|
|
417
|
-
justifyContent: "space-between",
|
|
418
|
-
...getThemeStyles().header,
|
|
419
|
-
}}
|
|
420
|
-
>
|
|
421
|
-
<div
|
|
422
|
-
style={{
|
|
423
|
-
display: "flex",
|
|
424
|
-
|
|
425
|
-
alignItems: "center",
|
|
426
|
-
gap: "12px",
|
|
427
|
-
}}
|
|
428
|
-
>
|
|
429
|
-
<FileText size={20} />
|
|
430
|
-
<h2
|
|
431
|
-
style={{
|
|
432
|
-
fontSize: "18px",
|
|
433
|
-
fontWeight: "600",
|
|
434
|
-
margin: 0,
|
|
435
|
-
}}
|
|
436
|
-
>
|
|
437
|
-
{resultModalTitle}
|
|
438
|
-
</h2>
|
|
254
|
+
<div className="ai-popover ai-result-modal" onClick={(e) => e.stopPropagation()}>
|
|
255
|
+
<div className="ai-result-header">
|
|
256
|
+
<div className="ai-row">
|
|
257
|
+
<FileText size={18} />
|
|
258
|
+
<h2 className="ai-result-title">{resultModalTitle}</h2>
|
|
439
259
|
</div>
|
|
440
|
-
<div
|
|
441
|
-
<button
|
|
442
|
-
onClick={saveToFile}
|
|
443
|
-
style={{
|
|
444
|
-
padding: "8px 12px",
|
|
445
|
-
borderRadius: "6px",
|
|
446
|
-
backgroundColor: "transparent",
|
|
447
|
-
border: `1px solid ${getThemeStyles().closeButton.color}30`,
|
|
448
|
-
cursor: "pointer",
|
|
449
|
-
color: getThemeStyles().closeButton.color,
|
|
450
|
-
transition: "all 0.2s",
|
|
451
|
-
display: "flex",
|
|
452
|
-
alignItems: "center",
|
|
453
|
-
gap: "6px",
|
|
454
|
-
fontSize: "12px",
|
|
455
|
-
}}
|
|
456
|
-
onMouseEnter={(e) => {
|
|
457
|
-
e.currentTarget.style.backgroundColor =
|
|
458
|
-
getThemeStyles().closeButton.hoverColor + "10";
|
|
459
|
-
e.currentTarget.style.color =
|
|
460
|
-
getThemeStyles().closeButton.hoverColor;
|
|
461
|
-
}}
|
|
462
|
-
onMouseLeave={(e) => {
|
|
463
|
-
e.currentTarget.style.backgroundColor = "transparent";
|
|
464
|
-
e.currentTarget.style.color =
|
|
465
|
-
getThemeStyles().closeButton.color;
|
|
466
|
-
}}
|
|
467
|
-
title="Télécharger l'analyse"
|
|
468
|
-
>
|
|
260
|
+
<div className="ai-row">
|
|
261
|
+
<button type="button" className={`ai-btn ai-btn--ghost ai-btn--compact ${sizeClass} ${radiusClass}`} onClick={saveToFile}>
|
|
469
262
|
<Download size={14} />
|
|
470
263
|
Sauvegarder
|
|
471
264
|
</button>
|
|
472
265
|
<button
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
padding: "4px",
|
|
479
|
-
borderRadius: "6px",
|
|
480
|
-
backgroundColor: "transparent",
|
|
481
|
-
border: "none",
|
|
482
|
-
cursor: "pointer",
|
|
483
|
-
color: getThemeStyles().closeButton.color,
|
|
484
|
-
transition: "color 0.2s",
|
|
485
|
-
}}
|
|
486
|
-
onMouseEnter={(e) => {
|
|
487
|
-
e.currentTarget.style.color =
|
|
488
|
-
getThemeStyles().closeButton.hoverColor;
|
|
489
|
-
}}
|
|
490
|
-
onMouseLeave={(e) => {
|
|
491
|
-
e.currentTarget.style.color =
|
|
492
|
-
getThemeStyles().closeButton.color;
|
|
266
|
+
type="button"
|
|
267
|
+
className="ai-icon-btn"
|
|
268
|
+
onClick={() => {
|
|
269
|
+
setIsResultOpen(false);
|
|
270
|
+
setAnalysisResult(null);
|
|
493
271
|
}}
|
|
272
|
+
aria-label="Fermer"
|
|
494
273
|
>
|
|
495
|
-
<X size={
|
|
274
|
+
<X size={16} />
|
|
496
275
|
</button>
|
|
497
276
|
</div>
|
|
277
|
+
</div>
|
|
498
278
|
|
|
499
|
-
|
|
500
|
-
<div
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
maxHeight: "calc(90vh - 80px)",
|
|
505
|
-
}}
|
|
506
|
-
>
|
|
507
|
-
{/* Prompt utilisé */}
|
|
508
|
-
<div style={{ marginBottom: "20px" }}>
|
|
509
|
-
<h3
|
|
510
|
-
style={{
|
|
511
|
-
fontSize: "14px",
|
|
512
|
-
fontWeight: "600",
|
|
513
|
-
marginBottom: "8px",
|
|
514
|
-
color: getThemeStyles().modal.color,
|
|
515
|
-
}}
|
|
516
|
-
>
|
|
517
|
-
Prompt utilisé :
|
|
518
|
-
</h3>
|
|
519
|
-
<div
|
|
520
|
-
style={{
|
|
521
|
-
padding: "12px",
|
|
522
|
-
borderRadius: "8px",
|
|
523
|
-
fontSize: "13px",
|
|
524
|
-
fontFamily: "monospace",
|
|
525
|
-
...getThemeStyles().content,
|
|
526
|
-
}}
|
|
527
|
-
>
|
|
528
|
-
{analysisResult.prompt}
|
|
529
|
-
</div>
|
|
530
|
-
</div>
|
|
531
|
-
|
|
532
|
-
{/* Résultat */}
|
|
533
|
-
<div>
|
|
534
|
-
<h3
|
|
535
|
-
style={{
|
|
536
|
-
fontSize: "14px",
|
|
537
|
-
fontWeight: "600",
|
|
538
|
-
marginBottom: "12px",
|
|
539
|
-
color: getThemeStyles().modal.color,
|
|
540
|
-
}}
|
|
541
|
-
>
|
|
542
|
-
Résultat de l'analyse :
|
|
543
|
-
</h3>
|
|
544
|
-
<div
|
|
545
|
-
style={{
|
|
546
|
-
padding: "16px",
|
|
547
|
-
borderRadius: "8px",
|
|
548
|
-
lineHeight: "1.6",
|
|
549
|
-
fontSize: "14px",
|
|
550
|
-
whiteSpace: "pre-wrap",
|
|
551
|
-
...getThemeStyles().content,
|
|
552
|
-
}}
|
|
553
|
-
>
|
|
554
|
-
{analysisResult.content}
|
|
555
|
-
</div>
|
|
556
|
-
</div>
|
|
279
|
+
<div className="ai-result-body">
|
|
280
|
+
<div className="ai-result-block">
|
|
281
|
+
<h3 className="ai-result-subtitle">Prompt utilisé</h3>
|
|
282
|
+
<pre className="ai-result-code">{analysisResult.prompt}</pre>
|
|
283
|
+
</div>
|
|
557
284
|
|
|
558
|
-
|
|
559
|
-
<
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
marginTop: "20px",
|
|
563
|
-
padding: "12px",
|
|
564
|
-
fontSize: "12px",
|
|
565
|
-
borderRadius: "8px",
|
|
566
|
-
display: "flex",
|
|
285
|
+
<div className="ai-result-block">
|
|
286
|
+
<h3 className="ai-result-subtitle">Résultat</h3>
|
|
287
|
+
<div className="ai-result-content">{analysisResult.content}</div>
|
|
288
|
+
</div>
|
|
567
289
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
? 0
|
|
576
|
-
: analysisResult.cost
|
|
577
|
-
).toFixed(6)}
|
|
578
|
-
</span>
|
|
579
|
-
<span>
|
|
580
|
-
ID: {analysisResult.requestId?.slice(-8) || "N/A"}
|
|
581
|
-
</span>
|
|
582
|
-
</div>
|
|
583
|
-
</div>
|
|
290
|
+
<div className="ai-result-meta ai-between">
|
|
291
|
+
<span>
|
|
292
|
+
Coût: ${
|
|
293
|
+
(apiKeyId?.includes("dev") ? 0 : analysisResult.cost).toFixed(6)
|
|
294
|
+
}
|
|
295
|
+
</span>
|
|
296
|
+
<span>ID: {analysisResult.requestId?.slice(-8) || "N/A"}</span>
|
|
584
297
|
</div>
|
|
585
298
|
</div>
|
|
586
299
|
</div>
|
|
587
300
|
</div>
|
|
588
|
-
)}
|
|
589
|
-
|
|
590
|
-
<LBSigninModal
|
|
591
|
-
isOpen={showAuthModal}
|
|
592
|
-
onClose={() => setShowAuthModal(false)}
|
|
593
|
-
/>
|
|
301
|
+
) : null}
|
|
594
302
|
|
|
595
|
-
{
|
|
303
|
+
<LBSigninModal isOpen={showAuthModal} onClose={() => setShowAuthModal(false)} />
|
|
596
304
|
<ErrorToast key={errorKey} error={errorData} onComplete={clearError} />
|
|
597
305
|
</>
|
|
598
306
|
);
|