@lastbrain/ai-ui-react 1.0.25 → 1.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/components/AiChipLabel.d.ts +12 -0
  2. package/dist/components/AiChipLabel.d.ts.map +1 -1
  3. package/dist/components/AiChipLabel.js +129 -1
  4. package/dist/components/AiContextButton.d.ts +18 -0
  5. package/dist/components/AiContextButton.d.ts.map +1 -0
  6. package/dist/components/AiContextButton.js +339 -0
  7. package/dist/components/AiImageButton.d.ts +12 -3
  8. package/dist/components/AiImageButton.d.ts.map +1 -1
  9. package/dist/components/AiImageButton.js +218 -8
  10. package/dist/components/AiStatusButton.d.ts.map +1 -1
  11. package/dist/components/AiStatusButton.js +1 -1
  12. package/dist/components/UsageToast.d.ts.map +1 -1
  13. package/dist/components/UsageToast.js +5 -3
  14. package/dist/examples/AiChipInputExample.d.ts +2 -0
  15. package/dist/examples/AiChipInputExample.d.ts.map +1 -0
  16. package/dist/examples/AiChipInputExample.js +14 -0
  17. package/dist/examples/AiContextButtonExample.d.ts +2 -0
  18. package/dist/examples/AiContextButtonExample.d.ts.map +1 -0
  19. package/dist/examples/AiContextButtonExample.js +88 -0
  20. package/dist/examples/AiImageButtonExample.d.ts +2 -0
  21. package/dist/examples/AiImageButtonExample.d.ts.map +1 -0
  22. package/dist/examples/AiImageButtonExample.js +26 -0
  23. package/dist/hooks/useAiCallImage.d.ts.map +1 -1
  24. package/dist/hooks/useAiCallImage.js +107 -1
  25. package/dist/hooks/useAiCallText.d.ts.map +1 -1
  26. package/dist/hooks/useAiCallText.js +25 -1
  27. package/dist/index.d.ts +4 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +4 -0
  30. package/dist/styles/inline.d.ts.map +1 -1
  31. package/dist/styles/inline.js +3 -1
  32. package/package.json +2 -2
  33. package/src/components/AiChipLabel.tsx +218 -1
  34. package/src/components/AiContextButton.tsx +553 -0
  35. package/src/components/AiImageButton.tsx +386 -38
  36. package/src/components/AiStatusButton.tsx +7 -3
  37. package/src/components/UsageToast.tsx +5 -3
  38. package/src/examples/AiChipInputExample.tsx +81 -0
  39. package/src/examples/AiContextButtonExample.tsx +338 -0
  40. package/src/examples/AiImageButtonExample.tsx +72 -0
  41. package/src/hooks/useAiCallImage.ts +149 -1
  42. package/src/hooks/useAiCallText.ts +30 -1
  43. package/src/index.ts +4 -0
  44. package/src/styles/inline.ts +3 -1
@@ -6,4 +6,16 @@ export interface AiChipLabelProps {
6
6
  style?: React.CSSProperties;
7
7
  }
8
8
  export declare function AiChipLabel({ children, variant, className, style: customStyle, }: AiChipLabelProps): import("react/jsx-runtime").JSX.Element;
9
+ export interface AiChipInputProps {
10
+ value?: string[];
11
+ onChange?: (chips: string[]) => void;
12
+ placeholder?: string;
13
+ context?: string;
14
+ maxChips?: number;
15
+ allowDuplicates?: boolean;
16
+ className?: string;
17
+ baseUrl?: string;
18
+ apiKeyId?: string;
19
+ }
20
+ export declare function AiChipInput({ value, onChange, placeholder, context, maxChips, allowDuplicates, className, baseUrl: propBaseUrl, apiKeyId: propApiKeyId, }: AiChipInputProps): import("react/jsx-runtime").JSX.Element;
9
21
  //# sourceMappingURL=AiChipLabel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AiChipLabel.d.ts","sourceRoot":"","sources":["../../src/components/AiChipLabel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,OAAmB,EACnB,SAAS,EACT,KAAK,EAAE,WAAW,GACnB,EAAE,gBAAgB,2CAgClB"}
1
+ {"version":3,"file":"AiChipLabel.d.ts","sourceRoot":"","sources":["../../src/components/AiChipLabel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAQ/D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,OAAmB,EACnB,SAAS,EACT,KAAK,EAAE,WAAW,GACnB,EAAE,gBAAgB,2CAgClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAU,EACV,QAAQ,EACR,WAAoE,EACpE,OAAO,EACP,QAAQ,EACR,eAAuB,EACvB,SAAS,EACT,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,GACvB,EAAE,gBAAgB,2CA2LlB"}
@@ -1,6 +1,12 @@
1
1
  "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useRef } from "react";
4
+ import { X, Sparkles } from "lucide-react";
3
5
  import { aiStyles } from "../styles/inline";
6
+ import { AiPromptPanel } from "./AiPromptPanel";
7
+ import { useAiCallText } from "../hooks/useAiCallText";
8
+ import { useAiModels } from "../hooks/useAiModels";
9
+ import { useAiContext } from "../context/AiProvider";
4
10
  export function AiChipLabel({ children, variant = "default", className, style: customStyle, }) {
5
11
  const variantStyles = {
6
12
  default: {},
@@ -26,3 +32,125 @@ export function AiChipLabel({ children, variant = "default", className, style: c
26
32
  ...customStyle,
27
33
  }, className: className, children: children }));
28
34
  }
35
+ export function AiChipInput({ value = [], onChange, placeholder = "Tapez et appuyez sur Entrée pour ajouter des tags...", context, maxChips, allowDuplicates = false, className, baseUrl: propBaseUrl, apiKeyId: propApiKeyId, }) {
36
+ const [inputValue, setInputValue] = useState("");
37
+ const [showPromptPanel, setShowPromptPanel] = useState(false);
38
+ const inputRef = useRef(null);
39
+ // Récupérer le contexte AiProvider avec fallback sur les props
40
+ const aiContext = useAiContext();
41
+ const baseUrl = propBaseUrl ?? aiContext.baseUrl;
42
+ const apiKeyId = propApiKeyId ?? aiContext.apiKeyId;
43
+ // Hooks pour l'IA avec les valeurs du contexte
44
+ const { models } = useAiModels({ baseUrl, apiKeyId });
45
+ const { generateText } = useAiCallText({ baseUrl, apiKeyId });
46
+ const addChip = (text) => {
47
+ if (!text.trim())
48
+ return;
49
+ const newChips = text
50
+ .split(/[,;]/)
51
+ .map((chip) => chip.trim())
52
+ .filter(Boolean);
53
+ const updatedChips = [...value];
54
+ newChips.forEach((chip) => {
55
+ if (maxChips && updatedChips.length >= maxChips)
56
+ return;
57
+ if (!allowDuplicates && updatedChips.includes(chip))
58
+ return;
59
+ updatedChips.push(chip);
60
+ });
61
+ onChange?.(updatedChips);
62
+ setInputValue("");
63
+ };
64
+ const removeChip = (index) => {
65
+ const updatedChips = value.filter((_, i) => i !== index);
66
+ onChange?.(updatedChips);
67
+ };
68
+ const handleKeyDown = (e) => {
69
+ if (e.key === "Enter") {
70
+ e.preventDefault();
71
+ addChip(inputValue);
72
+ }
73
+ else if (e.key === "Backspace" && !inputValue && value.length > 0) {
74
+ removeChip(value.length - 1);
75
+ }
76
+ };
77
+ const handleGenerateChips = () => {
78
+ setShowPromptPanel(true);
79
+ };
80
+ const handlePromptSubmit = async (model, prompt) => {
81
+ try {
82
+ // Construire le prompt avec l'instruction spécifique
83
+ const instruction = `${prompt}${context ? `\n\nContexte: ${context}` : ""}
84
+
85
+ IMPORTANT: Réponds UNIQUEMENT avec une liste de tags séparés par des virgules, sans explication ni formatage.
86
+ Exemple de réponse attendue: javascript, react, frontend, api, development`;
87
+ const response = await generateText({
88
+ model,
89
+ prompt: instruction,
90
+ });
91
+ const chips = parseChipsFromResponse(response.text);
92
+ // Fermer le modal immédiatement
93
+ setShowPromptPanel(false);
94
+ // Ajouter directement toutes les chips générées
95
+ addGeneratedChips(chips);
96
+ }
97
+ catch (error) {
98
+ console.error("Erreur lors de la génération des chips:", error);
99
+ setShowPromptPanel(false);
100
+ }
101
+ };
102
+ const parseChipsFromResponse = (response) => {
103
+ return response
104
+ .split(/[,;]/)
105
+ .map((chip) => chip.trim())
106
+ .filter(Boolean)
107
+ .slice(0, 10); // Limiter à 10 tags
108
+ };
109
+ const addGeneratedChips = (chips) => {
110
+ const updatedChips = [...value];
111
+ chips.forEach((chip) => {
112
+ if (maxChips && updatedChips.length >= maxChips)
113
+ return;
114
+ if (!allowDuplicates && updatedChips.includes(chip))
115
+ return;
116
+ updatedChips.push(chip);
117
+ });
118
+ onChange?.(updatedChips);
119
+ };
120
+ console.log("AiChipInput render - value:", value, "length:", value.length);
121
+ return (_jsxs("div", { className: className, children: [_jsxs("div", { style: { position: "relative", marginBottom: "8px" }, children: [_jsx("input", { ref: inputRef, type: "text", value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyDown: handleKeyDown, placeholder: placeholder, style: {
122
+ ...aiStyles.input,
123
+ paddingRight: "40px", // Space for button
124
+ } }), _jsx("button", { onClick: handleGenerateChips, style: {
125
+ position: "absolute",
126
+ right: "8px",
127
+ top: "50%",
128
+ transform: "translateY(-50%)",
129
+ background: "none",
130
+ border: "none",
131
+ cursor: "pointer",
132
+ padding: "4px",
133
+ borderRadius: "4px",
134
+ display: "flex",
135
+ alignItems: "center",
136
+ color: "#6366f1",
137
+ }, title: "G\u00E9n\u00E9rer des tags avec l'IA", children: _jsx(Sparkles, { size: 16 }) })] }), value.length > 0 && (_jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: "6px" }, children: value.map((chip, index) => {
138
+ console.log("Rendering chip:", chip, "at index:", index);
139
+ return (_jsxs("div", { style: {
140
+ ...aiStyles.chip,
141
+ display: "flex",
142
+ alignItems: "center",
143
+ gap: "6px",
144
+ paddingRight: "6px",
145
+ }, children: [_jsx("span", { children: chip }), _jsx("button", { onClick: () => removeChip(index), style: {
146
+ background: "none",
147
+ border: "none",
148
+ cursor: "pointer",
149
+ padding: "0",
150
+ display: "flex",
151
+ alignItems: "center",
152
+ color: "currentColor",
153
+ opacity: 0.7,
154
+ }, title: "Supprimer", children: _jsx(X, { size: 14 }) })] }, index));
155
+ }) })), _jsx(AiPromptPanel, { isOpen: showPromptPanel, onClose: () => setShowPromptPanel(false), onSubmit: handlePromptSubmit, models: models || undefined, baseUrl: baseUrl, sourceText: context ? `Contexte: ${context}` : undefined })] }));
156
+ }
@@ -0,0 +1,18 @@
1
+ import { type ButtonHTMLAttributes } from "react";
2
+ import type { BaseAiProps } from "../types";
3
+ export interface AiContextButtonProps extends Omit<BaseAiProps, "onValue" | "type">, Omit<ButtonHTMLAttributes<HTMLButtonElement>, "baseUrl" | "apiKeyId"> {
4
+ contextData: any;
5
+ contextDescription?: string;
6
+ onResult?: (result: string, metadata?: {
7
+ requestId: string;
8
+ tokens: number;
9
+ }) => void;
10
+ uiMode?: "modal" | "drawer";
11
+ resultModalTitle?: string;
12
+ storeOutputs?: boolean;
13
+ artifactTitle?: string;
14
+ baseUrl?: string;
15
+ apiKeyId?: string;
16
+ }
17
+ export declare function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode, contextData, contextDescription, onResult, onToast, disabled, className, children, resultModalTitle, storeOutputs, artifactTitle, context: _context, model: _model, prompt: _prompt, ...buttonProps }: AiContextButtonProps): import("react/jsx-runtime").JSX.Element;
18
+ //# sourceMappingURL=AiContextButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiContextButton.d.ts","sourceRoot":"","sources":["../../src/components/AiContextButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQ5C,MAAM,WAAW,oBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IAEvE,WAAW,EAAE,GAAG,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,CACT,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC7C,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,WAAW,EACX,kBAAyC,EACzC,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,gBAA0C,EAC1C,YAAY,EACZ,aAAa,EACb,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,GAAG,WAAW,EACf,EAAE,oBAAoB,2CAsftB"}
@@ -0,0 +1,339 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { Loader2, X, FileText, Sparkle, Download } from "lucide-react";
5
+ import { useAiCallText } from "../hooks/useAiCallText";
6
+ import { useAiModels } from "../hooks/useAiModels";
7
+ import { AiPromptPanel } from "./AiPromptPanel";
8
+ import { useUsageToast } from "./UsageToast";
9
+ import { aiStyles } from "../styles/inline";
10
+ import { useAiContext } from "../context/AiProvider";
11
+ export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode = "modal", contextData, contextDescription = "Données à analyser", onResult, onToast, disabled, className, children, resultModalTitle = "Résultat de l'analyse", storeOutputs, artifactTitle, context: _context, model: _model, prompt: _prompt, ...buttonProps }) {
12
+ const [isOpen, setIsOpen] = useState(false);
13
+ const [isResultOpen, setIsResultOpen] = useState(false);
14
+ const [analysisResult, setAnalysisResult] = useState(null);
15
+ const { showUsageToast, toastData, toastKey, clearToast } = useUsageToast();
16
+ // Récupérer le contexte AiProvider avec fallback sur les props
17
+ const aiContext = useAiContext();
18
+ const baseUrl = propBaseUrl ?? aiContext.baseUrl;
19
+ const apiKeyId = propApiKeyId ?? aiContext.apiKeyId;
20
+ const { models, loading: modelsLoading } = useAiModels({ baseUrl, apiKeyId });
21
+ const { generateText: callText, loading } = useAiCallText({
22
+ baseUrl,
23
+ apiKeyId,
24
+ });
25
+ const handleOpenPanel = () => {
26
+ console.log("Opening panel, models:", models, "loading:", modelsLoading);
27
+ setIsOpen(true);
28
+ };
29
+ const handleClosePanel = () => {
30
+ setIsOpen(false);
31
+ };
32
+ const handleCloseResult = () => {
33
+ setIsResultOpen(false);
34
+ setAnalysisResult(null);
35
+ };
36
+ const saveToFile = () => {
37
+ if (!analysisResult)
38
+ return;
39
+ const currentDate = new Date()
40
+ .toLocaleDateString("fr-FR")
41
+ .replace(/\//g, "-");
42
+ const defaultName = `analyse-${currentDate}.txt`;
43
+ const fileName = prompt("Nom du fichier :", defaultName) || defaultName;
44
+ const content = `ANALYSE DES DONNÉES - ${new Date().toLocaleString("fr-FR")}
45
+
46
+ PROMPT UTILISÉ :
47
+ ${analysisResult.prompt}
48
+
49
+ RÉSULTAT DE L'ANALYSE :
50
+ ${analysisResult.content}
51
+
52
+ --- MÉTADONNÉES ---
53
+ Tokens utilisés: ${analysisResult.tokens.toLocaleString()}
54
+ Coût: $${(apiKeyId?.includes("dev") ? 0 : analysisResult.cost).toFixed(6)}
55
+ ID de requête: ${analysisResult.requestId || "N/A"}
56
+ Date: ${new Date().toLocaleString("fr-FR")}`;
57
+ const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
58
+ const url = URL.createObjectURL(blob);
59
+ const a = document.createElement("a");
60
+ a.href = url;
61
+ a.download = fileName.endsWith(".txt") ? fileName : `${fileName}.txt`;
62
+ document.body.appendChild(a);
63
+ a.click();
64
+ document.body.removeChild(a);
65
+ URL.revokeObjectURL(url);
66
+ };
67
+ // Styles selon le thème
68
+ const getThemeStyles = () => {
69
+ const isDark = typeof document !== "undefined" &&
70
+ (document.documentElement.classList.contains("dark") ||
71
+ (!document.documentElement.classList.contains("light") &&
72
+ window.matchMedia("(prefers-color-scheme: dark)").matches));
73
+ return {
74
+ modal: {
75
+ backgroundColor: isDark ? "#1f2937" : "white",
76
+ border: `1px solid ${isDark ? "#374151" : "#e5e7eb"}`,
77
+ color: isDark ? "#f3f4f6" : "#374151",
78
+ },
79
+ header: {
80
+ color: isDark ? "#f9fafb" : "#1f2937",
81
+ borderBottom: `1px solid ${isDark ? "#374151" : "#e5e7eb"}`,
82
+ },
83
+ content: {
84
+ backgroundColor: isDark ? "#111827" : "#f9fafb",
85
+ border: `1px solid ${isDark ? "#374151" : "#e5e7eb"}`,
86
+ },
87
+ closeButton: {
88
+ color: isDark ? "#9ca3af" : "#6b7280",
89
+ hoverColor: isDark ? "#d1d5db" : "#374151",
90
+ },
91
+ };
92
+ };
93
+ const formatContextData = (data) => {
94
+ if (typeof data === "string")
95
+ return data;
96
+ if (typeof data === "object" && data !== null) {
97
+ return JSON.stringify(data, null, 2);
98
+ }
99
+ return String(data);
100
+ };
101
+ const handleSubmit = async (selectedModel, selectedPrompt) => {
102
+ try {
103
+ // Construire le prompt avec le contexte
104
+ const contextString = formatContextData(contextData);
105
+ const fullPrompt = `${selectedPrompt}
106
+
107
+ CONTEXTE (${contextDescription}):
108
+ ${contextString}
109
+
110
+ Analyse ces données et réponds de manière structurée et claire.`;
111
+ const result = await callText({
112
+ prompt: fullPrompt,
113
+ model: selectedModel || "gpt-4o-mini",
114
+ context: _context || undefined,
115
+ maxTokens: 4000,
116
+ temperature: 0.7,
117
+ });
118
+ if (result.text) {
119
+ // Calculer le total des tokens depuis la réponse réelle
120
+ const resultAny = result;
121
+ const totalTokens = (resultAny.inputTokens || 0) + (resultAny.outputTokens || 0) ||
122
+ result.debitTokens ||
123
+ 0;
124
+ const actualCost = resultAny.cost || 0;
125
+ const resultData = {
126
+ content: result.text,
127
+ prompt: selectedPrompt,
128
+ requestId: result.requestId,
129
+ tokens: totalTokens,
130
+ cost: actualCost,
131
+ };
132
+ setAnalysisResult(resultData);
133
+ setIsResultOpen(true);
134
+ onResult?.(result.text, {
135
+ requestId: result.requestId,
136
+ tokens: result.debitTokens || 0,
137
+ });
138
+ onToast?.({
139
+ type: "success",
140
+ message: `Analyse terminée - Coût: $${(apiKeyId?.includes("dev") ? 0 : actualCost).toFixed(6)}`,
141
+ });
142
+ // Afficher le toast de coût
143
+ showUsageToast({
144
+ requestId: result.requestId,
145
+ debitTokens: totalTokens,
146
+ usage: {
147
+ total_tokens: totalTokens,
148
+ prompt_tokens: resultAny.inputTokens || 0,
149
+ completion_tokens: resultAny.outputTokens || 0,
150
+ },
151
+ cost: apiKeyId?.includes("dev") ? 0 : actualCost,
152
+ });
153
+ }
154
+ }
155
+ catch (error) {
156
+ onToast?.({
157
+ type: "error",
158
+ message: "Erreur lors de l'analyse",
159
+ code: error instanceof Error ? error.message : undefined,
160
+ });
161
+ }
162
+ finally {
163
+ setIsOpen(false);
164
+ }
165
+ };
166
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { style: { position: "relative", display: "inline-block" }, children: [_jsx("button", { ...buttonProps, onClick: handleOpenPanel, disabled: disabled || loading, className: className, style: {
167
+ ...aiStyles.button,
168
+ display: "flex",
169
+ alignItems: "center",
170
+ gap: "8px",
171
+ cursor: disabled || loading ? "not-allowed" : "pointer",
172
+ opacity: disabled || loading ? 0.6 : 1,
173
+ backgroundColor: loading ? "#8b5cf6" : "#7c3aed",
174
+ color: "white",
175
+ border: "none",
176
+ borderRadius: "12px",
177
+ // padding: "12px 20px",
178
+ margin: "0px 8px",
179
+ fontSize: "14px",
180
+ fontWeight: "600",
181
+ minWidth: "20px",
182
+ height: "44px",
183
+ transition: "all 0.2s cubic-bezier(0.4, 0, 0.2, 1)",
184
+ boxShadow: loading
185
+ ? "0 4px 12px rgba(139, 92, 246, 0.3)"
186
+ : "0 2px 8px rgba(124, 58, 237, 0.2)",
187
+ transform: "scale(1)",
188
+ ...(loading && {
189
+ background: "linear-gradient(135deg, #7c3aed, #8b5cf6)",
190
+ animation: "pulse 2s ease-in-out infinite",
191
+ }),
192
+ ...buttonProps.style,
193
+ }, onMouseEnter: (e) => {
194
+ if (!disabled && !loading) {
195
+ e.currentTarget.style.transform = "scale(1.02)";
196
+ e.currentTarget.style.boxShadow =
197
+ "0 6px 16px rgba(124, 58, 237, 0.3)";
198
+ }
199
+ }, onMouseLeave: (e) => {
200
+ if (!disabled && !loading) {
201
+ e.currentTarget.style.transform = "scale(1)";
202
+ e.currentTarget.style.boxShadow = loading
203
+ ? "0 4px 12px rgba(139, 92, 246, 0.3)"
204
+ : "0 2px 8px rgba(124, 58, 237, 0.2)";
205
+ }
206
+ }, onMouseDown: (e) => {
207
+ if (!disabled && !loading) {
208
+ e.currentTarget.style.transform = "scale(0.98)";
209
+ }
210
+ }, onMouseUp: (e) => {
211
+ if (!disabled && !loading) {
212
+ e.currentTarget.style.transform = "scale(1.02)";
213
+ }
214
+ }, "data-ai-context-button": true, children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 18, className: "animate-spin", style: {
215
+ color: "white",
216
+ filter: "drop-shadow(0 0 2px rgba(255,255,255,0.3))",
217
+ } }), _jsx("span", { style: { letterSpacing: "0.025em" }, children: "Analyse..." })] })) : (_jsx(_Fragment, { children: _jsx(Sparkle, { size: 18, style: {
218
+ color: "white",
219
+ filter: "drop-shadow(0 0 2px rgba(255,255,255,0.2))",
220
+ } }) })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: models?.filter((m) => m.type === "language") || [], enableModelManagement: true }))] }), isResultOpen && analysisResult && (_jsx("div", { style: {
221
+ position: "fixed",
222
+ top: 0,
223
+ left: 0,
224
+ right: 0,
225
+ bottom: 0,
226
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
227
+ backdropFilter: "blur(8px)",
228
+ display: "flex",
229
+ alignItems: "center",
230
+ justifyContent: "center",
231
+ zIndex: 1000,
232
+ padding: "20px",
233
+ }, onClick: (e) => {
234
+ if (e.target === e.currentTarget) {
235
+ handleCloseResult();
236
+ }
237
+ }, children: _jsx("div", { style: {
238
+ maxWidth: "800px",
239
+ position: "relative",
240
+ width: "100%",
241
+ maxHeight: "90vh",
242
+ borderRadius: "16px",
243
+ boxShadow: "0 20px 25px -5px rgba(0, 0, 0, 0.1)",
244
+ overflow: "hidden",
245
+ ...getThemeStyles().modal,
246
+ }, children: _jsxs("div", { style: {
247
+ padding: "20px 24px 16px",
248
+ marginBottom: "12px",
249
+ display: "flex",
250
+ alignItems: "center",
251
+ gap: "12px",
252
+ flexDirection: "column",
253
+ justifyContent: "space-between",
254
+ ...getThemeStyles().header,
255
+ }, children: [_jsxs("div", { style: {
256
+ display: "flex",
257
+ alignItems: "center",
258
+ gap: "12px",
259
+ }, children: [_jsx(FileText, { size: 20 }), _jsx("h2", { style: {
260
+ fontSize: "18px",
261
+ fontWeight: "600",
262
+ margin: 0,
263
+ }, children: resultModalTitle })] }), _jsxs("div", { style: { display: "flex", gap: "8px" }, children: [_jsxs("button", { onClick: saveToFile, style: {
264
+ padding: "8px 12px",
265
+ borderRadius: "6px",
266
+ backgroundColor: "transparent",
267
+ border: `1px solid ${getThemeStyles().closeButton.color}30`,
268
+ cursor: "pointer",
269
+ color: getThemeStyles().closeButton.color,
270
+ transition: "all 0.2s",
271
+ display: "flex",
272
+ alignItems: "center",
273
+ gap: "6px",
274
+ fontSize: "12px",
275
+ }, onMouseEnter: (e) => {
276
+ e.currentTarget.style.backgroundColor =
277
+ getThemeStyles().closeButton.hoverColor + "10";
278
+ e.currentTarget.style.color =
279
+ getThemeStyles().closeButton.hoverColor;
280
+ }, onMouseLeave: (e) => {
281
+ e.currentTarget.style.backgroundColor = "transparent";
282
+ e.currentTarget.style.color =
283
+ getThemeStyles().closeButton.color;
284
+ }, title: "T\u00E9l\u00E9charger l'analyse", children: [_jsx(Download, { size: 14 }), "Sauvegarder"] }), _jsx("button", { onClick: handleCloseResult, style: {
285
+ position: "absolute",
286
+ top: "16px",
287
+ right: "16px",
288
+ padding: "4px",
289
+ borderRadius: "6px",
290
+ backgroundColor: "transparent",
291
+ border: "none",
292
+ cursor: "pointer",
293
+ color: getThemeStyles().closeButton.color,
294
+ transition: "color 0.2s",
295
+ }, onMouseEnter: (e) => {
296
+ e.currentTarget.style.color =
297
+ getThemeStyles().closeButton.hoverColor;
298
+ }, onMouseLeave: (e) => {
299
+ e.currentTarget.style.color =
300
+ getThemeStyles().closeButton.color;
301
+ }, children: _jsx(X, { size: 18 }) })] }), _jsxs("div", { style: {
302
+ padding: "0 24px 24px",
303
+ overflow: "auto",
304
+ maxHeight: "calc(90vh - 80px)",
305
+ }, children: [_jsxs("div", { style: { marginBottom: "20px" }, children: [_jsx("h3", { style: {
306
+ fontSize: "14px",
307
+ fontWeight: "600",
308
+ marginBottom: "8px",
309
+ color: getThemeStyles().modal.color,
310
+ }, children: "Prompt utilis\u00E9 :" }), _jsx("div", { style: {
311
+ padding: "12px",
312
+ borderRadius: "8px",
313
+ fontSize: "13px",
314
+ fontFamily: "monospace",
315
+ ...getThemeStyles().content,
316
+ }, children: analysisResult.prompt })] }), _jsxs("div", { children: [_jsx("h3", { style: {
317
+ fontSize: "14px",
318
+ fontWeight: "600",
319
+ marginBottom: "12px",
320
+ color: getThemeStyles().modal.color,
321
+ }, children: "R\u00E9sultat de l'analyse :" }), _jsx("div", { style: {
322
+ padding: "16px",
323
+ borderRadius: "8px",
324
+ lineHeight: "1.6",
325
+ fontSize: "14px",
326
+ whiteSpace: "pre-wrap",
327
+ ...getThemeStyles().content,
328
+ }, children: analysisResult.content })] }), _jsx("div", { style: { height: "120px" }, children: _jsxs("div", { style: {
329
+ marginTop: "20px",
330
+ padding: "12px",
331
+ fontSize: "12px",
332
+ borderRadius: "8px",
333
+ display: "flex",
334
+ justifyContent: "space-between",
335
+ ...getThemeStyles().content,
336
+ }, children: [_jsxs("span", { children: ["Co\u00FBt: $", (apiKeyId?.includes("dev")
337
+ ? 0
338
+ : analysisResult.cost).toFixed(6)] }), _jsxs("span", { children: ["ID: ", analysisResult.requestId?.slice(-8) || "N/A"] })] }) })] })] }) }) }))] }));
339
+ }
@@ -1,8 +1,17 @@
1
1
  import { type ButtonHTMLAttributes } from "react";
2
2
  import type { BaseAiProps } from "../types";
3
- export interface AiImageButtonProps extends Omit<BaseAiProps, "onValue" | "type">, ButtonHTMLAttributes<HTMLButtonElement> {
4
- onImage?: (imageUrl: string) => void;
3
+ export interface AiImageButtonProps extends Omit<BaseAiProps, "onValue" | "type">, Omit<ButtonHTMLAttributes<HTMLButtonElement>, "baseUrl" | "apiKeyId"> {
4
+ onImage?: (imageUrl: string, metadata?: {
5
+ requestId: string;
6
+ tokens: number;
7
+ }) => void;
5
8
  uiMode?: "modal" | "drawer";
9
+ showImageCard?: boolean;
10
+ onImageSave?: (url: string) => Promise<void>;
11
+ storeOutputs?: boolean;
12
+ artifactTitle?: string;
13
+ baseUrl?: string;
14
+ apiKeyId?: string;
6
15
  }
7
- export declare function AiImageButton({ baseUrl, apiKeyId, uiMode, context, model, prompt, onImage, onToast, disabled, className, children, ...buttonProps }: AiImageButtonProps): import("react/jsx-runtime").JSX.Element;
16
+ export declare function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode, context: _context, model: _model, prompt: _prompt, onImage, onToast, disabled, className, children, showImageCard, onImageSave, storeOutputs, artifactTitle, ...buttonProps }: AiImageButtonProps): import("react/jsx-runtime").JSX.Element;
8
17
  //# sourceMappingURL=AiImageButton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AiImageButton.d.ts","sourceRoot":"","sources":["../../src/components/AiImageButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAM5C,MAAM,WAAW,kBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,oBAAoB,CAAC,iBAAiB,CAAC;IACzC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC7B;AAED,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,OAAO,EACP,KAAK,EACL,MAAM,EACN,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,GAAG,WAAW,EACf,EAAE,kBAAkB,2CAmEpB"}
1
+ {"version":3,"file":"AiImageButton.d.ts","sourceRoot":"","sources":["../../src/components/AiImageButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAS5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQ5C,MAAM,WAAW,kBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACvE,OAAO,CAAC,EAAE,CACR,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC7C,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,aAAoB,EACpB,WAAW,EACX,YAAY,EACZ,aAAa,EACb,GAAG,WAAW,EACf,EAAE,kBAAkB,2CAuYpB"}