@lastbrain/ai-ui-react 1.0.68 → 1.0.70
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 +23 -70
- package/dist/components/AiContextButton.d.ts +10 -2
- package/dist/components/AiContextButton.d.ts.map +1 -1
- package/dist/components/AiContextButton.js +73 -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 +64 -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 +211 -676
- 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/context/LBAuthProvider.d.ts +35 -3
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +2 -0
- 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/hooks/useAiModels.d.ts.map +1 -1
- package/dist/hooks/useModelManagement.d.ts.map +1 -1
- 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/dist/utils/errorHandler.d.ts +2 -2
- package/dist/utils/errorHandler.d.ts.map +1 -1
- package/dist/utils/errorHandler.js +8 -1
- package/dist/utils/modelManagement.d.ts +13 -10
- package/dist/utils/modelManagement.d.ts.map +1 -1
- package/dist/utils/modelManagement.js +19 -2
- package/package.json +2 -2
- package/src/components/AiChipLabel.tsx +68 -101
- package/src/components/AiContextButton.tsx +142 -413
- package/src/components/AiImageButton.tsx +29 -190
- package/src/components/AiInput.tsx +49 -74
- package/src/components/AiPromptPanel.tsx +81 -260
- package/src/components/AiSelect.tsx +61 -69
- package/src/components/AiStatusButton.tsx +496 -1327
- package/src/components/AiTextarea.tsx +50 -63
- package/src/components/LBApiKeySelector.tsx +93 -271
- package/src/components/LBConnectButton.tsx +39 -336
- package/src/components/LBSigninModal.tsx +141 -472
- package/src/context/LBAuthProvider.tsx +45 -6
- package/src/examples/AiUiPremiumShowcase.tsx +94 -0
- package/src/hooks/useAiModels.ts +2 -1
- package/src/hooks/useModelManagement.ts +2 -1
- 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
- package/src/utils/errorHandler.ts +16 -3
- package/src/utils/modelManagement.ts +53 -15
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useState, type ButtonHTMLAttributes } from "react";
|
|
4
|
-
import {
|
|
4
|
+
import { Download, FileText, Loader2, Lock, Sparkles, X } from "lucide-react";
|
|
5
5
|
import type { BaseAiProps } from "../types";
|
|
6
|
+
import type { AiRadius, AiSize, AiVariant } from "../types";
|
|
6
7
|
import { useAiCallText } from "../hooks/useAiCallText";
|
|
7
8
|
import { AiPromptPanel } from "./AiPromptPanel";
|
|
8
9
|
import { useUsageToast } from "./UsageToast";
|
|
9
10
|
import { useErrorToast, ErrorToast } from "./ErrorToast";
|
|
10
|
-
import { aiStyles } from "../styles/inline";
|
|
11
11
|
import { useAiContext } from "../context/AiProvider";
|
|
12
12
|
import { handleAIError } from "../utils/errorHandler";
|
|
13
13
|
import { useLB } from "../context/LBAuthProvider";
|
|
14
14
|
import { LBSigninModal } from "./LBSigninModal";
|
|
15
15
|
|
|
16
|
+
// Types pour les données de contexte
|
|
17
|
+
type ContextData = string | number | boolean | object | unknown[] | { [key: string]: unknown };
|
|
18
|
+
|
|
16
19
|
export interface AiContextButtonProps
|
|
17
20
|
extends
|
|
18
21
|
Omit<BaseAiProps, "onValue" | "type">,
|
|
19
22
|
Omit<ButtonHTMLAttributes<HTMLButtonElement>, "baseUrl" | "apiKeyId"> {
|
|
20
|
-
|
|
21
|
-
contextData: any;
|
|
23
|
+
contextData: ContextData;
|
|
22
24
|
contextDescription?: string;
|
|
23
25
|
onResult?: (
|
|
24
26
|
result: string,
|
|
@@ -26,11 +28,13 @@ export interface AiContextButtonProps
|
|
|
26
28
|
) => void;
|
|
27
29
|
uiMode?: "modal" | "drawer";
|
|
28
30
|
resultModalTitle?: string;
|
|
29
|
-
storeOutputs?: boolean;
|
|
30
|
-
artifactTitle?: string;
|
|
31
|
-
// Props optionnelles pour override du contexte
|
|
31
|
+
storeOutputs?: boolean;
|
|
32
|
+
artifactTitle?: string;
|
|
32
33
|
baseUrl?: string;
|
|
33
34
|
apiKeyId?: string;
|
|
35
|
+
size?: AiSize;
|
|
36
|
+
radius?: AiRadius;
|
|
37
|
+
variant?: AiVariant;
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
export function AiContextButton({
|
|
@@ -45,11 +49,10 @@ export function AiContextButton({
|
|
|
45
49
|
className,
|
|
46
50
|
children,
|
|
47
51
|
resultModalTitle = "Résultat de l'analyse",
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
size = "md",
|
|
53
|
+
radius = "full",
|
|
54
|
+
variant = "default",
|
|
50
55
|
context: _context,
|
|
51
|
-
model: _model,
|
|
52
|
-
prompt: _prompt,
|
|
53
56
|
...buttonProps
|
|
54
57
|
}: AiContextButtonProps) {
|
|
55
58
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -62,20 +65,18 @@ export function AiContextButton({
|
|
|
62
65
|
tokens: number;
|
|
63
66
|
cost: number;
|
|
64
67
|
} | null>(null);
|
|
65
|
-
|
|
68
|
+
|
|
69
|
+
const { showUsageToast } = useUsageToast();
|
|
66
70
|
const { showErrorToast, errorData, errorKey, clearError } = useErrorToast();
|
|
67
71
|
|
|
68
|
-
// Rendre l'authentification optionnelle
|
|
69
72
|
let lbStatus: string | undefined;
|
|
70
73
|
try {
|
|
71
74
|
const lbContext = useLB();
|
|
72
75
|
lbStatus = lbContext.status;
|
|
73
76
|
} catch {
|
|
74
|
-
// LBProvider n'est pas disponible, ignorer
|
|
75
77
|
lbStatus = undefined;
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
// Récupérer le contexte AiProvider avec fallback sur les props
|
|
79
80
|
const aiContext = useAiContext();
|
|
80
81
|
const baseUrl = propBaseUrl ?? aiContext.baseUrl;
|
|
81
82
|
const apiKeyId = propApiKeyId ?? aiContext.apiKeyId;
|
|
@@ -95,80 +96,10 @@ export function AiContextButton({
|
|
|
95
96
|
setIsOpen(true);
|
|
96
97
|
};
|
|
97
98
|
|
|
98
|
-
const
|
|
99
|
-
|
|
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
|
-
const formatContextData = (data: any): string => {
|
|
171
|
-
if (typeof data === "string") return data;
|
|
99
|
+
const formatContextData = (data: ContextData): string => {
|
|
100
|
+
if (typeof data === "string") {
|
|
101
|
+
return data;
|
|
102
|
+
}
|
|
172
103
|
if (typeof data === "object" && data !== null) {
|
|
173
104
|
return JSON.stringify(data, null, 2);
|
|
174
105
|
}
|
|
@@ -180,14 +111,8 @@ Date: ${new Date().toLocaleString("fr-FR")}`;
|
|
|
180
111
|
selectedPrompt: string
|
|
181
112
|
) => {
|
|
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,158 @@ 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()
|
|
179
|
+
.toLocaleDateString("fr-FR")
|
|
180
|
+
.replace(/\//g, "-");
|
|
181
|
+
const defaultName = `analyse-${currentDate}.txt`;
|
|
182
|
+
const fileName = prompt("Nom du fichier :", defaultName) || defaultName;
|
|
183
|
+
|
|
184
|
+
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: $${(apiKeyId?.includes(
|
|
185
|
+
"dev"
|
|
186
|
+
)
|
|
187
|
+
? 0
|
|
188
|
+
: analysisResult.cost
|
|
189
|
+
).toFixed(6)}\nID de requête: ${analysisResult.requestId || "N/A"}`;
|
|
190
|
+
|
|
191
|
+
const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
|
|
192
|
+
const url = URL.createObjectURL(blob);
|
|
193
|
+
const anchor = document.createElement("a");
|
|
194
|
+
anchor.href = url;
|
|
195
|
+
anchor.download = fileName.endsWith(".txt") ? fileName : `${fileName}.txt`;
|
|
196
|
+
document.body.appendChild(anchor);
|
|
197
|
+
anchor.click();
|
|
198
|
+
document.body.removeChild(anchor);
|
|
199
|
+
URL.revokeObjectURL(url);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const sizeClass = `ai-size-${size}`;
|
|
203
|
+
const radiusClass = `ai-radius-${radius}`;
|
|
204
|
+
const variantClass = variant === "light" ? "ai-btn--light" : "";
|
|
205
|
+
|
|
250
206
|
return (
|
|
251
207
|
<>
|
|
252
|
-
<div
|
|
208
|
+
<div className="relative inline-block ai-glow">
|
|
253
209
|
<button
|
|
254
210
|
{...buttonProps}
|
|
255
211
|
onClick={handleOpenPanel}
|
|
256
212
|
disabled={disabled || loading || !isAuthReady}
|
|
257
|
-
className={className}
|
|
258
|
-
style={{
|
|
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
|
|
213
|
+
className={`ai-btn ai-context-btn ${variantClass} ${sizeClass} ${radiusClass} ${className || ""}`}
|
|
317
214
|
title={
|
|
318
215
|
!isAuthReady ? "Authentication required" : "Analyser avec l'IA"
|
|
319
216
|
}
|
|
320
217
|
>
|
|
321
218
|
{loading ? (
|
|
322
219
|
<>
|
|
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>
|
|
220
|
+
<Loader2 size={16} className="ai-spinner" />
|
|
221
|
+
<span>Analyse...</span>
|
|
332
222
|
</>
|
|
333
223
|
) : !isAuthReady ? (
|
|
334
224
|
<>
|
|
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
|
-
/>
|
|
225
|
+
<Lock size={16} />
|
|
342
226
|
{children || <span>Connexion requise</span>}
|
|
343
227
|
</>
|
|
344
228
|
) : (
|
|
345
229
|
<>
|
|
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> */}
|
|
230
|
+
<Sparkles size={16} />
|
|
231
|
+
{children || <span>Analyser</span>}
|
|
354
232
|
</>
|
|
355
233
|
)}
|
|
356
234
|
</button>
|
|
357
235
|
|
|
358
|
-
{isOpen
|
|
236
|
+
{isOpen ? (
|
|
359
237
|
<AiPromptPanel
|
|
360
238
|
isOpen={isOpen}
|
|
361
|
-
onClose={
|
|
239
|
+
onClose={() => setIsOpen(false)}
|
|
362
240
|
onSubmit={handleSubmit}
|
|
363
241
|
uiMode={uiMode}
|
|
364
242
|
models={[]}
|
|
365
|
-
enableModelManagement
|
|
243
|
+
enableModelManagement
|
|
366
244
|
modelCategory="text"
|
|
367
245
|
baseUrl={baseUrl}
|
|
368
246
|
apiKey={apiKeyId}
|
|
369
247
|
/>
|
|
370
|
-
)}
|
|
248
|
+
) : null}
|
|
371
249
|
</div>
|
|
372
250
|
|
|
373
|
-
{
|
|
374
|
-
{isResultOpen && analysisResult && (
|
|
251
|
+
{isResultOpen && analysisResult ? (
|
|
375
252
|
<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
|
-
}}
|
|
253
|
+
className="ai-signin-overlay ai-overlay-panel"
|
|
390
254
|
onClick={(e) => {
|
|
391
255
|
if (e.target === e.currentTarget) {
|
|
392
|
-
|
|
256
|
+
setIsResultOpen(false);
|
|
257
|
+
setAnalysisResult(null);
|
|
393
258
|
}
|
|
394
259
|
}}
|
|
395
260
|
>
|
|
396
261
|
<div
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
position: "relative",
|
|
400
|
-
width: "100%",
|
|
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
|
-
}}
|
|
262
|
+
className="ai-popover ai-result-modal"
|
|
263
|
+
onClick={(e) => e.stopPropagation()}
|
|
407
264
|
>
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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>
|
|
265
|
+
<div className="ai-result-header">
|
|
266
|
+
<div className="ai-row">
|
|
267
|
+
<FileText size={18} />
|
|
268
|
+
<h2 className="ai-result-title">{resultModalTitle}</h2>
|
|
439
269
|
</div>
|
|
440
|
-
<div
|
|
270
|
+
<div className="ai-row">
|
|
441
271
|
<button
|
|
272
|
+
type="button"
|
|
273
|
+
className={`ai-btn ai-btn--ghost ai-btn--compact ${sizeClass} ${radiusClass}`}
|
|
442
274
|
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
275
|
>
|
|
469
276
|
<Download size={14} />
|
|
470
277
|
Sauvegarder
|
|
471
278
|
</button>
|
|
472
279
|
<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;
|
|
280
|
+
type="button"
|
|
281
|
+
className="ai-icon-btn"
|
|
282
|
+
onClick={() => {
|
|
283
|
+
setIsResultOpen(false);
|
|
284
|
+
setAnalysisResult(null);
|
|
493
285
|
}}
|
|
286
|
+
aria-label="Fermer"
|
|
494
287
|
>
|
|
495
|
-
<X size={
|
|
288
|
+
<X size={16} />
|
|
496
289
|
</button>
|
|
497
290
|
</div>
|
|
291
|
+
</div>
|
|
498
292
|
|
|
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>
|
|
293
|
+
<div className="ai-result-body">
|
|
294
|
+
<div className="ai-result-block">
|
|
295
|
+
<h3 className="ai-result-subtitle">Prompt utilisé</h3>
|
|
296
|
+
<pre className="ai-result-code">{analysisResult.prompt}</pre>
|
|
297
|
+
</div>
|
|
531
298
|
|
|
532
|
-
|
|
533
|
-
<
|
|
534
|
-
|
|
535
|
-
|
|
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>
|
|
299
|
+
<div className="ai-result-block">
|
|
300
|
+
<h3 className="ai-result-subtitle">Résultat</h3>
|
|
301
|
+
<div className="ai-result-content">
|
|
302
|
+
{analysisResult.content}
|
|
556
303
|
</div>
|
|
304
|
+
</div>
|
|
557
305
|
|
|
558
|
-
|
|
559
|
-
<
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
justifyContent: "space-between",
|
|
569
|
-
...getThemeStyles().content,
|
|
570
|
-
}}
|
|
571
|
-
>
|
|
572
|
-
<span>
|
|
573
|
-
Coût: $
|
|
574
|
-
{(apiKeyId?.includes("dev")
|
|
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>
|
|
306
|
+
<div className="ai-result-meta ai-between">
|
|
307
|
+
<span>
|
|
308
|
+
Coût: $
|
|
309
|
+
{(apiKeyId?.includes("dev")
|
|
310
|
+
? 0
|
|
311
|
+
: analysisResult.cost
|
|
312
|
+
).toFixed(6)}
|
|
313
|
+
</span>
|
|
314
|
+
<span>ID: {analysisResult.requestId?.slice(-8) || "N/A"}</span>
|
|
584
315
|
</div>
|
|
585
316
|
</div>
|
|
586
317
|
</div>
|
|
587
318
|
</div>
|
|
588
|
-
)}
|
|
319
|
+
) : null}
|
|
589
320
|
|
|
590
321
|
<LBSigninModal
|
|
591
322
|
isOpen={showAuthModal}
|
|
592
323
|
onClose={() => setShowAuthModal(false)}
|
|
593
324
|
/>
|
|
594
|
-
|
|
595
|
-
{/* Error Toast */}
|
|
596
325
|
<ErrorToast key={errorKey} error={errorData} onComplete={clearError} />
|
|
597
326
|
</>
|
|
598
327
|
);
|