@lastbrain/ai-ui-react 1.0.11 → 1.0.14
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/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +1 -1
- package/dist/components/AiPromptPanel.d.ts +14 -1
- package/dist/components/AiPromptPanel.d.ts.map +1 -1
- package/dist/components/AiPromptPanel.js +255 -7
- package/dist/components/AiSettingsButton.d.ts.map +1 -1
- package/dist/components/AiSettingsButton.js +1 -1
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +106 -11
- package/dist/examples/AiImageGenerator.d.ts +34 -0
- package/dist/examples/AiImageGenerator.d.ts.map +1 -0
- package/dist/examples/AiImageGenerator.js +85 -0
- package/dist/examples/AiPromptPanelAdvanced.d.ts +20 -0
- package/dist/examples/AiPromptPanelAdvanced.d.ts.map +1 -0
- package/dist/examples/AiPromptPanelAdvanced.js +222 -0
- package/dist/examples/ExternalIntegration.d.ts +2 -0
- package/dist/examples/ExternalIntegration.d.ts.map +1 -0
- package/dist/examples/ExternalIntegration.js +2 -0
- package/dist/hooks/useAiStatus.d.ts.map +1 -1
- package/dist/hooks/useAiStatus.js +3 -0
- package/dist/hooks/useModelManagement.d.ts +32 -0
- package/dist/hooks/useModelManagement.d.ts.map +1 -0
- package/dist/hooks/useModelManagement.js +135 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/styles/inline.d.ts.map +1 -1
- package/dist/styles/inline.js +10 -1
- package/dist/utils/modelManagement.d.ts +29 -0
- package/dist/utils/modelManagement.d.ts.map +1 -0
- package/dist/utils/modelManagement.js +82 -0
- package/package.json +2 -2
- package/src/components/AiInput.tsx +3 -0
- package/src/components/AiPromptPanel.tsx +502 -24
- package/src/components/AiSettingsButton.tsx +4 -2
- package/src/components/AiStatusButton.tsx +185 -39
- package/src/examples/AiImageGenerator.tsx +214 -0
- package/src/examples/AiPromptPanelAdvanced.tsx +381 -0
- package/src/examples/ExternalIntegration.ts +55 -0
- package/src/hooks/useAiStatus.ts +4 -0
- package/src/hooks/useModelManagement.ts +210 -0
- package/src/index.ts +8 -0
- package/src/styles/inline.ts +10 -1
- package/src/utils/modelManagement.ts +134 -0
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
useLayoutEffect,
|
|
8
8
|
type ReactNode,
|
|
9
9
|
} from "react";
|
|
10
|
-
import { BookOpen, Search, Sparkles, Star, Tag } from "lucide-react";
|
|
10
|
+
import { BookOpen, Search, Sparkles, Star, Tag, Settings } from "lucide-react";
|
|
11
11
|
import type { ModelRef } from "@lastbrain/ai-ui-core";
|
|
12
12
|
import type { UiMode } from "../types";
|
|
13
13
|
import { aiStyles } from "../styles/inline";
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type Prompt,
|
|
17
17
|
type PublicPrompt,
|
|
18
18
|
} from "../hooks/usePrompts";
|
|
19
|
+
import { useModelManagement, type AIModel } from "../hooks/useModelManagement";
|
|
19
20
|
|
|
20
21
|
export interface AiPromptPanelProps {
|
|
21
22
|
isOpen: boolean;
|
|
@@ -25,6 +26,13 @@ export interface AiPromptPanelProps {
|
|
|
25
26
|
models?: ModelRef[];
|
|
26
27
|
sourceText?: string; // Current text from input/textarea
|
|
27
28
|
children?: (props: AiPromptPanelRenderProps) => ReactNode;
|
|
29
|
+
// Nouvelles props pour la gestion avancée des modèles
|
|
30
|
+
enableModelManagement?: boolean;
|
|
31
|
+
availableModels?: AIModel[];
|
|
32
|
+
userModels?: string[];
|
|
33
|
+
onModelToggle?: (modelId: string, isActive: boolean) => Promise<void>;
|
|
34
|
+
apiKey?: string;
|
|
35
|
+
baseUrl?: string;
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
export interface AiPromptPanelRenderProps {
|
|
@@ -36,6 +44,13 @@ export interface AiPromptPanelRenderProps {
|
|
|
36
44
|
sourceText?: string;
|
|
37
45
|
handleSubmit: () => void;
|
|
38
46
|
handleClose: () => void;
|
|
47
|
+
// Nouvelles props pour la gestion des modèles
|
|
48
|
+
enableModelManagement?: boolean;
|
|
49
|
+
availableModels?: AIModel[];
|
|
50
|
+
userModels?: string[];
|
|
51
|
+
showAllModels?: boolean;
|
|
52
|
+
setShowAllModels?: (show: boolean) => void;
|
|
53
|
+
onModelToggle?: (modelId: string, isActive: boolean) => Promise<void>;
|
|
39
54
|
}
|
|
40
55
|
|
|
41
56
|
export function AiPromptPanel({
|
|
@@ -46,6 +61,12 @@ export function AiPromptPanel({
|
|
|
46
61
|
models = [],
|
|
47
62
|
sourceText,
|
|
48
63
|
children,
|
|
64
|
+
enableModelManagement = false,
|
|
65
|
+
availableModels = [],
|
|
66
|
+
userModels = [],
|
|
67
|
+
onModelToggle,
|
|
68
|
+
apiKey,
|
|
69
|
+
baseUrl,
|
|
49
70
|
}: AiPromptPanelProps) {
|
|
50
71
|
const [selectedModel, setSelectedModel] = useState("");
|
|
51
72
|
const [prompt, setPrompt] = useState("");
|
|
@@ -63,6 +84,11 @@ export function AiPromptPanel({
|
|
|
63
84
|
const promptRef = useRef<HTMLTextAreaElement>(null);
|
|
64
85
|
const closeTimeoutRef = useRef<number | null>(null);
|
|
65
86
|
|
|
87
|
+
// États pour la gestion des modèles
|
|
88
|
+
const [showAllModels, setShowAllModels] = useState(false);
|
|
89
|
+
const [isModelManagementOpen, setIsModelManagementOpen] = useState(false);
|
|
90
|
+
const [loadingModels, setLoadingModels] = useState<string[]>([]);
|
|
91
|
+
|
|
66
92
|
const {
|
|
67
93
|
prompts,
|
|
68
94
|
loading: promptsLoading,
|
|
@@ -70,6 +96,60 @@ export function AiPromptPanel({
|
|
|
70
96
|
incrementStat,
|
|
71
97
|
} = usePrompts();
|
|
72
98
|
|
|
99
|
+
// Hook de gestion des modèles (automatique si enableModelManagement et pas de props externes)
|
|
100
|
+
const autoModelManagement = useModelManagement({
|
|
101
|
+
apiKey,
|
|
102
|
+
baseUrl,
|
|
103
|
+
category: "text", // Par défaut pour AiPromptPanel
|
|
104
|
+
autoFetch:
|
|
105
|
+
enableModelManagement && availableModels.length === 0 && !!apiKey,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Utiliser soit les props externes soit la gestion automatique
|
|
109
|
+
const effectiveAvailableModels =
|
|
110
|
+
availableModels.length > 0
|
|
111
|
+
? availableModels
|
|
112
|
+
: autoModelManagement.availableModels;
|
|
113
|
+
const effectiveUserModels =
|
|
114
|
+
userModels.length > 0 ? userModels : autoModelManagement.userModels;
|
|
115
|
+
const effectiveToggleModel = onModelToggle || autoModelManagement.toggleModel;
|
|
116
|
+
|
|
117
|
+
// Gestion des modèles
|
|
118
|
+
const handleModelToggle = async (modelId: string, isActive: boolean) => {
|
|
119
|
+
if (!effectiveToggleModel) return;
|
|
120
|
+
|
|
121
|
+
setLoadingModels((prev) => [...prev, modelId]);
|
|
122
|
+
try {
|
|
123
|
+
await effectiveToggleModel(modelId, isActive);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error("Erreur lors du changement de modèle:", error);
|
|
126
|
+
} finally {
|
|
127
|
+
setLoadingModels((prev) => prev.filter((id) => id !== modelId));
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const getFilteredModels = () => {
|
|
132
|
+
if (!enableModelManagement || effectiveAvailableModels.length === 0) {
|
|
133
|
+
// Mode classique : transformer les ModelRef en AIModel
|
|
134
|
+
return models.map((m) => ({
|
|
135
|
+
id: m.id,
|
|
136
|
+
name: m.name,
|
|
137
|
+
category: m.type === "image" ? ("image" as const) : ("text" as const),
|
|
138
|
+
provider: "Unknown",
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const textModels = effectiveAvailableModels.filter(
|
|
143
|
+
(m) => m.category === "text"
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
if (showAllModels) {
|
|
147
|
+
return textModels;
|
|
148
|
+
} else {
|
|
149
|
+
return textModels.filter((m) => effectiveUserModels.includes(m.id));
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
73
153
|
// Fetch prompts when modal opens
|
|
74
154
|
useEffect(() => {
|
|
75
155
|
if (isOpen && models.length > 0) {
|
|
@@ -207,6 +287,13 @@ export function AiPromptPanel({
|
|
|
207
287
|
sourceText,
|
|
208
288
|
handleSubmit,
|
|
209
289
|
handleClose,
|
|
290
|
+
// Nouvelles props pour la gestion des modèles
|
|
291
|
+
enableModelManagement,
|
|
292
|
+
availableModels: effectiveAvailableModels,
|
|
293
|
+
userModels: effectiveUserModels,
|
|
294
|
+
showAllModels,
|
|
295
|
+
setShowAllModels,
|
|
296
|
+
onModelToggle: handleModelToggle,
|
|
210
297
|
};
|
|
211
298
|
|
|
212
299
|
if (children) {
|
|
@@ -329,29 +416,173 @@ export function AiPromptPanel({
|
|
|
329
416
|
)}
|
|
330
417
|
|
|
331
418
|
<div style={aiStyles.modalInputGroup}>
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
419
|
+
{enableModelManagement ? (
|
|
420
|
+
// Version avancée avec gestion des modèles
|
|
421
|
+
<div style={{ marginBottom: "16px" }}>
|
|
422
|
+
<div
|
|
423
|
+
style={{
|
|
424
|
+
display: "flex",
|
|
425
|
+
justifyContent: "space-between",
|
|
426
|
+
alignItems: "center",
|
|
427
|
+
marginBottom: "8px",
|
|
428
|
+
}}
|
|
429
|
+
>
|
|
430
|
+
<label htmlFor="model-select" style={aiStyles.modalLabel}>
|
|
431
|
+
AI Model
|
|
432
|
+
</label>
|
|
433
|
+
<div
|
|
434
|
+
style={{
|
|
435
|
+
display: "flex",
|
|
436
|
+
alignItems: "center",
|
|
437
|
+
gap: "8px",
|
|
438
|
+
}}
|
|
439
|
+
>
|
|
440
|
+
{effectiveAvailableModels.length > 0 && (
|
|
441
|
+
<button
|
|
442
|
+
onClick={() => setShowAllModels(!showAllModels)}
|
|
443
|
+
style={{
|
|
444
|
+
padding: "4px 8px",
|
|
445
|
+
fontSize: "12px",
|
|
446
|
+
color: "#8b5cf6",
|
|
447
|
+
background: "#8b5cf610",
|
|
448
|
+
border: "1px solid #8b5cf630",
|
|
449
|
+
borderRadius: "6px",
|
|
450
|
+
cursor: "pointer",
|
|
451
|
+
transition: "all 0.2s",
|
|
452
|
+
}}
|
|
453
|
+
onMouseEnter={(e) => {
|
|
454
|
+
e.currentTarget.style.background = "#8b5cf620";
|
|
455
|
+
}}
|
|
456
|
+
onMouseLeave={(e) => {
|
|
457
|
+
e.currentTarget.style.background = "#8b5cf610";
|
|
458
|
+
}}
|
|
459
|
+
>
|
|
460
|
+
{showAllModels ? "Mes modèles" : "Tous les modèles"}
|
|
461
|
+
{!showAllModels &&
|
|
462
|
+
effectiveAvailableModels.filter(
|
|
463
|
+
(m) =>
|
|
464
|
+
m.category === "text" &&
|
|
465
|
+
!effectiveUserModels.includes(m.id)
|
|
466
|
+
).length > 0 && (
|
|
467
|
+
<span
|
|
468
|
+
style={{
|
|
469
|
+
marginLeft: "4px",
|
|
470
|
+
padding: "2px 6px",
|
|
471
|
+
fontSize: "10px",
|
|
472
|
+
background: "#8b5cf6",
|
|
473
|
+
color: "white",
|
|
474
|
+
borderRadius: "10px",
|
|
475
|
+
}}
|
|
476
|
+
>
|
|
477
|
+
+
|
|
478
|
+
{
|
|
479
|
+
effectiveAvailableModels.filter(
|
|
480
|
+
(m) =>
|
|
481
|
+
m.category === "text" &&
|
|
482
|
+
!effectiveUserModels.includes(m.id)
|
|
483
|
+
).length
|
|
484
|
+
}
|
|
485
|
+
</span>
|
|
486
|
+
)}
|
|
487
|
+
</button>
|
|
488
|
+
)}
|
|
489
|
+
<button
|
|
490
|
+
onClick={() => setIsModelManagementOpen(true)}
|
|
491
|
+
style={{
|
|
492
|
+
padding: "4px 8px",
|
|
493
|
+
fontSize: "12px",
|
|
494
|
+
color: "#8b5cf6",
|
|
495
|
+
background: "#8b5cf610",
|
|
496
|
+
border: "1px solid #8b5cf630",
|
|
497
|
+
borderRadius: "6px",
|
|
498
|
+
cursor: "pointer",
|
|
499
|
+
transition: "all 0.2s",
|
|
500
|
+
display: "flex",
|
|
501
|
+
alignItems: "center",
|
|
502
|
+
gap: "4px",
|
|
503
|
+
}}
|
|
504
|
+
onMouseEnter={(e) => {
|
|
505
|
+
e.currentTarget.style.background = "#8b5cf620";
|
|
506
|
+
}}
|
|
507
|
+
onMouseLeave={(e) => {
|
|
508
|
+
e.currentTarget.style.background = "#8b5cf610";
|
|
509
|
+
}}
|
|
510
|
+
>
|
|
511
|
+
<Settings size={12} />
|
|
512
|
+
</button>
|
|
513
|
+
</div>
|
|
514
|
+
</div>
|
|
515
|
+
<select
|
|
516
|
+
id="model-select"
|
|
517
|
+
value={activeModelId}
|
|
518
|
+
onChange={(e) => setSelectedModel(e.target.value)}
|
|
519
|
+
onFocus={() => setModelFocused(true)}
|
|
520
|
+
onBlur={() => setModelFocused(false)}
|
|
521
|
+
style={{
|
|
522
|
+
...aiStyles.select,
|
|
523
|
+
...(modelFocused && aiStyles.selectFocus),
|
|
524
|
+
}}
|
|
525
|
+
>
|
|
526
|
+
{getFilteredModels().length === 0 && (
|
|
527
|
+
<option value="">Loading models...</option>
|
|
528
|
+
)}
|
|
529
|
+
{getFilteredModels().map((model) => {
|
|
530
|
+
const isActive = effectiveUserModels.includes(model.id);
|
|
531
|
+
return (
|
|
532
|
+
<option
|
|
533
|
+
key={model.id}
|
|
534
|
+
value={model.id}
|
|
535
|
+
style={{
|
|
536
|
+
opacity: showAllModels && !isActive ? 0.6 : 1,
|
|
537
|
+
}}
|
|
538
|
+
>
|
|
539
|
+
{model.name}
|
|
540
|
+
{showAllModels && isActive && " ✓"}
|
|
541
|
+
{showAllModels && !isActive && " (Désactivé)"}
|
|
542
|
+
</option>
|
|
543
|
+
);
|
|
544
|
+
})}
|
|
545
|
+
</select>
|
|
546
|
+
{showAllModels && onModelToggle && (
|
|
547
|
+
<div
|
|
548
|
+
style={{
|
|
549
|
+
fontSize: "11px",
|
|
550
|
+
color: "#6b7280",
|
|
551
|
+
marginTop: "4px",
|
|
552
|
+
}}
|
|
553
|
+
>
|
|
554
|
+
💡 Cliquez sur "⚙️" pour activer/désactiver les modèles
|
|
555
|
+
</div>
|
|
556
|
+
)}
|
|
557
|
+
</div>
|
|
558
|
+
) : (
|
|
559
|
+
// Version classique
|
|
560
|
+
<>
|
|
561
|
+
<label htmlFor="model-select" style={aiStyles.modalLabel}>
|
|
562
|
+
AI Model
|
|
563
|
+
</label>
|
|
564
|
+
<select
|
|
565
|
+
id="model-select"
|
|
566
|
+
value={activeModelId}
|
|
567
|
+
onChange={(e) => setSelectedModel(e.target.value)}
|
|
568
|
+
onFocus={() => setModelFocused(true)}
|
|
569
|
+
onBlur={() => setModelFocused(false)}
|
|
570
|
+
style={{
|
|
571
|
+
...aiStyles.select,
|
|
572
|
+
...(modelFocused && aiStyles.selectFocus),
|
|
573
|
+
}}
|
|
574
|
+
>
|
|
575
|
+
{models.length === 0 && (
|
|
576
|
+
<option value="">Loading models...</option>
|
|
577
|
+
)}
|
|
578
|
+
{models.map((model) => (
|
|
579
|
+
<option key={model.id} value={model.id}>
|
|
580
|
+
{model.name}
|
|
581
|
+
</option>
|
|
582
|
+
))}
|
|
583
|
+
</select>
|
|
584
|
+
</>
|
|
585
|
+
)}
|
|
355
586
|
</div>
|
|
356
587
|
|
|
357
588
|
<div style={aiStyles.modalInputGroup}>
|
|
@@ -758,6 +989,253 @@ export function AiPromptPanel({
|
|
|
758
989
|
</button>
|
|
759
990
|
</div>
|
|
760
991
|
</div>
|
|
992
|
+
|
|
993
|
+
{/* Modal de gestion des modèles */}
|
|
994
|
+
{enableModelManagement && isModelManagementOpen && (
|
|
995
|
+
<div
|
|
996
|
+
style={{
|
|
997
|
+
...aiStyles.modal,
|
|
998
|
+
zIndex: 1001, // Au-dessus du modal principal
|
|
999
|
+
}}
|
|
1000
|
+
>
|
|
1001
|
+
<div
|
|
1002
|
+
style={{
|
|
1003
|
+
...aiStyles.modalOverlay,
|
|
1004
|
+
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
|
1005
|
+
}}
|
|
1006
|
+
onClick={() => setIsModelManagementOpen(false)}
|
|
1007
|
+
/>
|
|
1008
|
+
<div
|
|
1009
|
+
style={{
|
|
1010
|
+
...aiStyles.modalContent,
|
|
1011
|
+
maxWidth: "600px",
|
|
1012
|
+
maxHeight: "80vh",
|
|
1013
|
+
overflow: "auto",
|
|
1014
|
+
}}
|
|
1015
|
+
>
|
|
1016
|
+
<div style={aiStyles.modalHeader}>
|
|
1017
|
+
<h2 style={aiStyles.modalTitle}>Gestion des modèles IA</h2>
|
|
1018
|
+
<button
|
|
1019
|
+
style={aiStyles.modalCloseButton}
|
|
1020
|
+
onClick={() => setIsModelManagementOpen(false)}
|
|
1021
|
+
aria-label="Close"
|
|
1022
|
+
>
|
|
1023
|
+
×
|
|
1024
|
+
</button>
|
|
1025
|
+
</div>
|
|
1026
|
+
|
|
1027
|
+
<div style={aiStyles.modalBody}>
|
|
1028
|
+
<div style={{ marginBottom: "16px" }}>
|
|
1029
|
+
<p
|
|
1030
|
+
style={{
|
|
1031
|
+
fontSize: "14px",
|
|
1032
|
+
color: "#6b7280",
|
|
1033
|
+
margin: "0 0 16px 0",
|
|
1034
|
+
}}
|
|
1035
|
+
>
|
|
1036
|
+
Activez ou désactivez les modèles selon vos besoins
|
|
1037
|
+
</p>
|
|
1038
|
+
</div>
|
|
1039
|
+
|
|
1040
|
+
<div
|
|
1041
|
+
style={{
|
|
1042
|
+
display: "flex",
|
|
1043
|
+
flexDirection: "column",
|
|
1044
|
+
gap: "12px",
|
|
1045
|
+
}}
|
|
1046
|
+
>
|
|
1047
|
+
{effectiveAvailableModels
|
|
1048
|
+
.filter((model) => model.category === "text")
|
|
1049
|
+
.map((modelData) => {
|
|
1050
|
+
const isActive = effectiveUserModels.includes(modelData.id);
|
|
1051
|
+
const isLoading = loadingModels.includes(modelData.id);
|
|
1052
|
+
|
|
1053
|
+
return (
|
|
1054
|
+
<div
|
|
1055
|
+
key={modelData.id}
|
|
1056
|
+
style={{
|
|
1057
|
+
display: "flex",
|
|
1058
|
+
alignItems: "center",
|
|
1059
|
+
justifyContent: "space-between",
|
|
1060
|
+
padding: "16px",
|
|
1061
|
+
border: "1px solid #e5e7eb",
|
|
1062
|
+
borderRadius: "8px",
|
|
1063
|
+
backgroundColor: isActive ? "#f0fdf4" : "#ffffff",
|
|
1064
|
+
transition: "all 0.2s",
|
|
1065
|
+
}}
|
|
1066
|
+
>
|
|
1067
|
+
<div
|
|
1068
|
+
style={{
|
|
1069
|
+
display: "flex",
|
|
1070
|
+
alignItems: "center",
|
|
1071
|
+
gap: "12px",
|
|
1072
|
+
}}
|
|
1073
|
+
>
|
|
1074
|
+
<div
|
|
1075
|
+
style={{
|
|
1076
|
+
width: "12px",
|
|
1077
|
+
height: "12px",
|
|
1078
|
+
borderRadius: "50%",
|
|
1079
|
+
backgroundColor: isActive ? "#10b981" : "#d1d5db",
|
|
1080
|
+
}}
|
|
1081
|
+
/>
|
|
1082
|
+
<div>
|
|
1083
|
+
<div
|
|
1084
|
+
style={{
|
|
1085
|
+
display: "flex",
|
|
1086
|
+
alignItems: "center",
|
|
1087
|
+
gap: "8px",
|
|
1088
|
+
marginBottom: "4px",
|
|
1089
|
+
}}
|
|
1090
|
+
>
|
|
1091
|
+
<span
|
|
1092
|
+
style={{
|
|
1093
|
+
fontWeight: "500",
|
|
1094
|
+
color: "#111827",
|
|
1095
|
+
}}
|
|
1096
|
+
>
|
|
1097
|
+
{modelData.name}
|
|
1098
|
+
</span>
|
|
1099
|
+
{modelData.isPro && (
|
|
1100
|
+
<span
|
|
1101
|
+
style={{
|
|
1102
|
+
padding: "2px 8px",
|
|
1103
|
+
fontSize: "11px",
|
|
1104
|
+
backgroundColor: "#8b5cf6",
|
|
1105
|
+
color: "white",
|
|
1106
|
+
borderRadius: "12px",
|
|
1107
|
+
fontWeight: "500",
|
|
1108
|
+
}}
|
|
1109
|
+
>
|
|
1110
|
+
PRO
|
|
1111
|
+
</span>
|
|
1112
|
+
)}
|
|
1113
|
+
</div>
|
|
1114
|
+
{modelData.description && (
|
|
1115
|
+
<p
|
|
1116
|
+
style={{
|
|
1117
|
+
fontSize: "13px",
|
|
1118
|
+
color: "#6b7280",
|
|
1119
|
+
margin: "0 0 4px 0",
|
|
1120
|
+
}}
|
|
1121
|
+
>
|
|
1122
|
+
{modelData.description}
|
|
1123
|
+
</p>
|
|
1124
|
+
)}
|
|
1125
|
+
<div
|
|
1126
|
+
style={{
|
|
1127
|
+
display: "flex",
|
|
1128
|
+
alignItems: "center",
|
|
1129
|
+
gap: "16px",
|
|
1130
|
+
fontSize: "12px",
|
|
1131
|
+
color: "#9ca3af",
|
|
1132
|
+
}}
|
|
1133
|
+
>
|
|
1134
|
+
<span>Fournisseur: {modelData.provider}</span>
|
|
1135
|
+
{modelData.costPer1M && (
|
|
1136
|
+
<span>
|
|
1137
|
+
Coût: ${modelData.costPer1M}/1M tokens
|
|
1138
|
+
</span>
|
|
1139
|
+
)}
|
|
1140
|
+
</div>
|
|
1141
|
+
</div>
|
|
1142
|
+
</div>
|
|
1143
|
+
<button
|
|
1144
|
+
onClick={() =>
|
|
1145
|
+
handleModelToggle(modelData.id, !isActive)
|
|
1146
|
+
}
|
|
1147
|
+
disabled={isLoading}
|
|
1148
|
+
style={{
|
|
1149
|
+
padding: "8px 16px",
|
|
1150
|
+
fontSize: "13px",
|
|
1151
|
+
fontWeight: "500",
|
|
1152
|
+
borderRadius: "6px",
|
|
1153
|
+
border: "1px solid",
|
|
1154
|
+
cursor: isLoading ? "not-allowed" : "pointer",
|
|
1155
|
+
transition: "all 0.2s",
|
|
1156
|
+
minWidth: "80px",
|
|
1157
|
+
...(isActive
|
|
1158
|
+
? {
|
|
1159
|
+
backgroundColor: "#fef2f2",
|
|
1160
|
+
borderColor: "#fecaca",
|
|
1161
|
+
color: "#dc2626",
|
|
1162
|
+
}
|
|
1163
|
+
: {
|
|
1164
|
+
backgroundColor: "#f0fdf4",
|
|
1165
|
+
borderColor: "#bbf7d0",
|
|
1166
|
+
color: "#16a34a",
|
|
1167
|
+
}),
|
|
1168
|
+
opacity: isLoading ? 0.6 : 1,
|
|
1169
|
+
}}
|
|
1170
|
+
onMouseEnter={(e) => {
|
|
1171
|
+
if (!isLoading) {
|
|
1172
|
+
if (isActive) {
|
|
1173
|
+
e.currentTarget.style.backgroundColor =
|
|
1174
|
+
"#fee2e2";
|
|
1175
|
+
} else {
|
|
1176
|
+
e.currentTarget.style.backgroundColor =
|
|
1177
|
+
"#dcfce7";
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}}
|
|
1181
|
+
onMouseLeave={(e) => {
|
|
1182
|
+
if (!isLoading) {
|
|
1183
|
+
if (isActive) {
|
|
1184
|
+
e.currentTarget.style.backgroundColor =
|
|
1185
|
+
"#fef2f2";
|
|
1186
|
+
} else {
|
|
1187
|
+
e.currentTarget.style.backgroundColor =
|
|
1188
|
+
"#f0fdf4";
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}}
|
|
1192
|
+
>
|
|
1193
|
+
{isLoading ? (
|
|
1194
|
+
<div
|
|
1195
|
+
style={{
|
|
1196
|
+
display: "flex",
|
|
1197
|
+
alignItems: "center",
|
|
1198
|
+
justifyContent: "center",
|
|
1199
|
+
gap: "4px",
|
|
1200
|
+
}}
|
|
1201
|
+
>
|
|
1202
|
+
<div
|
|
1203
|
+
style={{
|
|
1204
|
+
width: "12px",
|
|
1205
|
+
height: "12px",
|
|
1206
|
+
border: "2px solid currentColor",
|
|
1207
|
+
borderTop: "2px solid transparent",
|
|
1208
|
+
borderRadius: "50%",
|
|
1209
|
+
animation: "ai-spin 1s linear infinite",
|
|
1210
|
+
}}
|
|
1211
|
+
/>
|
|
1212
|
+
</div>
|
|
1213
|
+
) : isActive ? (
|
|
1214
|
+
"Désactiver"
|
|
1215
|
+
) : (
|
|
1216
|
+
"Activer"
|
|
1217
|
+
)}
|
|
1218
|
+
</button>
|
|
1219
|
+
</div>
|
|
1220
|
+
);
|
|
1221
|
+
})}
|
|
1222
|
+
</div>
|
|
1223
|
+
</div>
|
|
1224
|
+
|
|
1225
|
+
<div style={aiStyles.modalFooter}>
|
|
1226
|
+
<button
|
|
1227
|
+
onClick={() => setIsModelManagementOpen(false)}
|
|
1228
|
+
style={{
|
|
1229
|
+
...aiStyles.button,
|
|
1230
|
+
...aiStyles.buttonSecondary,
|
|
1231
|
+
}}
|
|
1232
|
+
>
|
|
1233
|
+
Fermer
|
|
1234
|
+
</button>
|
|
1235
|
+
</div>
|
|
1236
|
+
</div>
|
|
1237
|
+
</div>
|
|
1238
|
+
)}
|
|
761
1239
|
</div>
|
|
762
1240
|
);
|
|
763
1241
|
}
|
|
@@ -66,10 +66,12 @@ export function AiSettingsButton({
|
|
|
66
66
|
<span>Tokens:</span>
|
|
67
67
|
<span>{status.balance?.total || 0}</span>
|
|
68
68
|
</div>
|
|
69
|
-
{status.storage?.
|
|
69
|
+
{status.storage?.allocated_mb !== undefined && (
|
|
70
70
|
<div data-ai-status-item>
|
|
71
71
|
<span>Storage:</span>
|
|
72
|
-
<span>
|
|
72
|
+
<span>
|
|
73
|
+
{status.storage.allocated_mb.toFixed(2)} MB
|
|
74
|
+
</span>
|
|
73
75
|
</div>
|
|
74
76
|
)}
|
|
75
77
|
</div>
|