@lastbrain/ai-ui-react 1.0.30 → 1.0.32

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.
@@ -16,15 +16,19 @@ export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId,
16
16
  const [analysisResult, setAnalysisResult] = useState(null);
17
17
  const { showUsageToast, toastData, toastKey, clearToast } = useUsageToast();
18
18
  const { showErrorToast, errorData, errorKey, clearError } = useErrorToast();
19
- // Récupérer le contexte AiProvider avec fallback sur les props
19
+ // Utiliser directement les props pour les hooks (pas de context pour éviter les appels inutiles)
20
+ const { models, loading: modelsLoading } = useAiModels({
21
+ baseUrl: propBaseUrl,
22
+ apiKeyId: propApiKeyId,
23
+ });
24
+ const { generateText: callText, loading } = useAiCallText({
25
+ baseUrl: propBaseUrl,
26
+ apiKeyId: propApiKeyId,
27
+ });
28
+ // Récupérer le contexte AiProvider avec fallback sur les props pour l'affichage
20
29
  const aiContext = useAiContext();
21
30
  const baseUrl = propBaseUrl ?? aiContext.baseUrl;
22
31
  const apiKeyId = propApiKeyId ?? aiContext.apiKeyId;
23
- const { models, loading: modelsLoading } = useAiModels({ baseUrl, apiKeyId });
24
- const { generateText: callText, loading } = useAiCallText({
25
- baseUrl,
26
- apiKeyId,
27
- });
28
32
  const handleOpenPanel = () => {
29
33
  console.log("Opening panel, models:", models, "loading:", modelsLoading);
30
34
  setIsOpen(true);
@@ -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;AAS5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAU5C,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,2CA2YpB"}
1
+ {"version":3,"file":"AiImageButton.d.ts","sourceRoot":"","sources":["../../src/components/AiImageButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAU5C,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,2CAkXpB"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
- import { ImageIcon, Loader2, Download, Copy, ExternalLink, X, } from "lucide-react";
4
+ import { ImageIcon, Loader2, Download, ExternalLink, X } from "lucide-react";
5
5
  import { useAiCallImage } from "../hooks/useAiCallImage";
6
6
  import { useAiModels } from "../hooks/useAiModels";
7
7
  import { AiPromptPanel } from "./AiPromptPanel";
@@ -36,20 +36,6 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
36
36
  link.download = `ai-image-${generatedImage.requestId}.png`;
37
37
  link.click();
38
38
  };
39
- const handleCopyUrl = async () => {
40
- if (!generatedImage)
41
- return;
42
- try {
43
- await navigator.clipboard.writeText(generatedImage.url);
44
- onToast?.({
45
- type: "success",
46
- message: "URL copiée dans le presse-papier",
47
- });
48
- }
49
- catch (_error) {
50
- onToast?.({ type: "error", message: "Erreur lors de la copie" });
51
- }
52
- };
53
39
  const handleSave = async () => {
54
40
  if (!generatedImage || !onImageSave)
55
41
  return;
@@ -106,6 +92,14 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
106
92
  model: selectedModel,
107
93
  prompt: selectedPrompt,
108
94
  size: "1024x1024", // Taille par défaut
95
+ storeOutputs,
96
+ artifactTitle,
97
+ });
98
+ console.log("[AiImageButton] Image generation result:", {
99
+ hasUrl: !!result.url,
100
+ urlLength: result.url?.length,
101
+ urlPreview: result.url?.substring(0, 100),
102
+ requestId: result.requestId,
109
103
  });
110
104
  if (result.url) {
111
105
  // Stocker l'image générée
@@ -116,6 +110,7 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
116
110
  tokens: result.debitTokens,
117
111
  };
118
112
  setGeneratedImage(imageData);
113
+ console.log("[AiImageButton] Image data stored:", imageData);
119
114
  onImage?.(result.url, {
120
115
  requestId: result.requestId,
121
116
  tokens: result.debitTokens,
@@ -222,27 +217,21 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
222
217
  maxHeight: "200px",
223
218
  objectFit: "contain",
224
219
  display: "block",
225
- } }) }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsxs("button", { onClick: handleDownload, className: "flex items-center gap-1 px-3 py-2 text-xs font-medium rounded-lg transition-colors", style: getThemeStyles().actionButton, onMouseEnter: (e) => {
226
- e.currentTarget.style.backgroundColor =
227
- getThemeStyles().actionButton.hoverBackground;
228
- e.currentTarget.style.color =
229
- getThemeStyles().actionButton.hoverColor;
230
- }, onMouseLeave: (e) => {
231
- e.currentTarget.style.backgroundColor =
232
- getThemeStyles().actionButton.backgroundColor;
233
- e.currentTarget.style.color =
234
- getThemeStyles().actionButton.color;
235
- }, title: "T\u00E9l\u00E9charger l'image", children: [_jsx(Download, { size: 14 }), "T\u00E9l\u00E9charger"] }), _jsxs("button", { onClick: handleCopyUrl, className: "flex items-center gap-1 px-3 py-2 text-xs font-medium rounded-lg transition-colors", style: getThemeStyles().actionButton, onMouseEnter: (e) => {
236
- e.currentTarget.style.backgroundColor =
237
- getThemeStyles().actionButton.hoverBackground;
238
- e.currentTarget.style.color =
239
- getThemeStyles().actionButton.hoverColor;
220
+ } }) }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsxs("button", { onClick: handleDownload, className: "flex items-center gap-2 px-4 py-2.5 text-sm font-semibold rounded-lg transition-all shadow-sm", style: {
221
+ backgroundColor: "#10b981",
222
+ color: "white",
223
+ border: "none",
224
+ cursor: "pointer",
225
+ }, onMouseEnter: (e) => {
226
+ e.currentTarget.style.backgroundColor = "#059669";
227
+ e.currentTarget.style.transform = "translateY(-1px)";
228
+ e.currentTarget.style.boxShadow =
229
+ "0 4px 12px rgba(16, 185, 129, 0.3)";
240
230
  }, onMouseLeave: (e) => {
241
- e.currentTarget.style.backgroundColor =
242
- getThemeStyles().actionButton.backgroundColor;
243
- e.currentTarget.style.color =
244
- getThemeStyles().actionButton.color;
245
- }, title: "Copier l'URL", children: [_jsx(Copy, { size: 14 }), "Copier URL"] }), onImageSave && (_jsxs("button", { onClick: handleSave, className: "flex items-center gap-1 px-3 py-2 text-xs font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors", title: "Sauvegarder en base", children: [_jsx(ExternalLink, { size: 14 }), "Sauvegarder"] }))] }), _jsx("div", { className: "mt-3 pt-3 text-xs", style: {
231
+ e.currentTarget.style.backgroundColor = "#10b981";
232
+ e.currentTarget.style.transform = "translateY(0)";
233
+ e.currentTarget.style.boxShadow = "";
234
+ }, title: "T\u00E9l\u00E9charger l'image", children: [_jsx(Download, { size: 16 }), "T\u00E9l\u00E9charger l'image"] }), onImageSave && (_jsxs("button", { onClick: handleSave, className: "flex items-center gap-1 px-3 py-2 text-xs font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors", title: "Sauvegarder en base", children: [_jsx(ExternalLink, { size: 14 }), "Sauvegarder"] }))] }), _jsx("div", { className: "mt-3 pt-3 text-xs", style: {
246
235
  ...getThemeStyles().metadata,
247
236
  }, children: _jsx("div", { className: "flex justify-center", children: _jsxs("span", { children: ["ID: ", generatedImage.requestId.slice(-8)] }) }) })] })), _jsx(ErrorToast, { error: errorData, onComplete: clearError }, errorKey)] }));
248
237
  }
@@ -4,5 +4,5 @@ export interface AiInputProps extends Omit<BaseAiProps, "type">, Omit<InputHTMLA
4
4
  uiMode?: "modal" | "drawer";
5
5
  enableModelManagement?: boolean;
6
6
  }
7
- export declare function AiInput({ baseUrl, apiKeyId, uiMode, context, model, prompt, editMode, onValue, onToast, disabled, className, enableModelManagement, ...inputProps }: AiInputProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare function AiInput({ baseUrl, apiKeyId, uiMode, context, model, prompt, editMode, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...inputProps }: AiInputProps): import("react/jsx-runtime").JSX.Element;
8
8
  //# sourceMappingURL=AiInput.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AiInput.d.ts","sourceRoot":"","sources":["../../src/components/AiInput.tsx"],"names":[],"mappings":"AAEA,OAAc,EAAoB,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAE1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQ5C,MAAM,WAAW,YACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC;IACxD,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAgB,EAChB,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,qBAA6B,EAC7B,GAAG,UAAU,EACd,EAAE,YAAY,2CA2Kd"}
1
+ {"version":3,"file":"AiInput.d.ts","sourceRoot":"","sources":["../../src/components/AiInput.tsx"],"names":[],"mappings":"AAEA,OAAc,EAAoB,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAE1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQ5C,MAAM,WAAW,YACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC;IACxD,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAgB,EAChB,qBAA6B,EAC7B,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,UAAU,EACd,EAAE,YAAY,2CA6Kd"}
@@ -8,7 +8,7 @@ import { AiPromptPanel } from "./AiPromptPanel";
8
8
  import { UsageToast, useUsageToast } from "./UsageToast";
9
9
  import { aiStyles } from "../styles/inline";
10
10
  import { handleAIError } from "../utils/errorHandler";
11
- export function AiInput({ baseUrl, apiKeyId, uiMode = "modal", context, model, prompt, editMode = false, onValue, onToast, disabled, className, enableModelManagement = false, ...inputProps }) {
11
+ export function AiInput({ baseUrl, apiKeyId, uiMode = "modal", context, model, prompt, editMode = false, enableModelManagement = false, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...inputProps }) {
12
12
  const [isOpen, setIsOpen] = useState(false);
13
13
  const [inputValue, setInputValue] = useState(inputProps.value?.toString() || inputProps.defaultValue?.toString() || "");
14
14
  const [isFocused, setIsFocused] = useState(false);
@@ -36,6 +36,8 @@ export function AiInput({ baseUrl, apiKeyId, uiMode = "modal", context, model, p
36
36
  prompt: promptWithContext,
37
37
  context: resolvedContext,
38
38
  actionType: hasContext ? "generate-text" : "autocomplete",
39
+ storeOutputs,
40
+ artifactTitle,
39
41
  });
40
42
  if (result.text) {
41
43
  setInputValue(result.text);
@@ -4,5 +4,5 @@ export interface AiSelectProps extends Omit<BaseAiProps, "type">, Omit<SelectHTM
4
4
  children: React.ReactNode;
5
5
  uiMode?: "modal" | "drawer";
6
6
  }
7
- export declare function AiSelect({ baseUrl, apiKeyId, uiMode, context, model, prompt, onValue, onToast, disabled, className, children, ...selectProps }: AiSelectProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare function AiSelect({ baseUrl, apiKeyId, uiMode, context, model, prompt, storeOutputs, artifactTitle, onValue, onToast, disabled, className, children, ...selectProps }: AiSelectProps): import("react/jsx-runtime").JSX.Element;
8
8
  //# sourceMappingURL=AiSelect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AiSelect.d.ts","sourceRoot":"","sources":["../../src/components/AiSelect.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQ5C,MAAM,WAAW,aACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,CAAC;IAC1D,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC7B;AAED,wBAAgB,QAAQ,CAAC,EACvB,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,aAAa,2CA+Ef"}
1
+ {"version":3,"file":"AiSelect.d.ts","sourceRoot":"","sources":["../../src/components/AiSelect.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQ5C,MAAM,WAAW,aACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,CAAC;IAC1D,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC7B;AAED,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,OAAO,EACP,KAAK,EACL,MAAM,EACN,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,GAAG,WAAW,EACf,EAAE,aAAa,2CAiFf"}
@@ -7,7 +7,7 @@ import { AiPromptPanel } from "./AiPromptPanel";
7
7
  import { UsageToast, useUsageToast } from "./UsageToast";
8
8
  import { aiStyles } from "../styles/inline";
9
9
  import { handleAIError } from "../utils/errorHandler";
10
- export function AiSelect({ baseUrl, apiKeyId, uiMode = "modal", context, model, prompt, onValue, onToast, disabled, className, children, ...selectProps }) {
10
+ export function AiSelect({ baseUrl, apiKeyId, uiMode = "modal", context, model, prompt, storeOutputs, artifactTitle, onValue, onToast, disabled, className, children, ...selectProps }) {
11
11
  const [isOpen, setIsOpen] = useState(false);
12
12
  const [isFocused, setIsFocused] = useState(false);
13
13
  const { showUsageToast, toastData, toastKey, clearToast } = useUsageToast();
@@ -26,6 +26,8 @@ export function AiSelect({ baseUrl, apiKeyId, uiMode = "modal", context, model,
26
26
  prompt: selectedPrompt,
27
27
  context: context || undefined,
28
28
  actionType: "autocomplete",
29
+ storeOutputs,
30
+ artifactTitle,
29
31
  });
30
32
  if (result.text) {
31
33
  onValue?.(result.text);
@@ -3,5 +3,5 @@ import type { BaseAiProps } from "../types";
3
3
  export interface AiTextareaProps extends Omit<BaseAiProps, "type">, Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "onValue"> {
4
4
  uiMode?: "modal" | "drawer";
5
5
  }
6
- export declare function AiTextarea({ baseUrl, apiKeyId, uiMode, context, model, prompt, editMode, onValue, onToast, disabled, className, ...textareaProps }: AiTextareaProps): import("react/jsx-runtime").JSX.Element;
6
+ export declare function AiTextarea({ baseUrl, apiKeyId, uiMode, context, model, prompt, editMode, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...textareaProps }: AiTextareaProps): import("react/jsx-runtime").JSX.Element;
7
7
  //# sourceMappingURL=AiTextarea.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AiTextarea.d.ts","sourceRoot":"","sources":["../../src/components/AiTextarea.tsx"],"names":[],"mappings":"AAEA,OAAc,EAIZ,KAAK,sBAAsB,EAC5B,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQ5C,MAAM,WAAW,eACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,sBAAsB,CAAC,mBAAmB,CAAC,EAAE,SAAS,CAAC;IAC9D,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC7B;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAgB,EAChB,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,aAAa,EACjB,EAAE,eAAe,2CAyLjB"}
1
+ {"version":3,"file":"AiTextarea.d.ts","sourceRoot":"","sources":["../../src/components/AiTextarea.tsx"],"names":[],"mappings":"AAEA,OAAc,EAIZ,KAAK,sBAAsB,EAC5B,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQ5C,MAAM,WAAW,eACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,sBAAsB,CAAC,mBAAmB,CAAC,EAAE,SAAS,CAAC;IAC9D,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC7B;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAgB,EAChB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,aAAa,EACjB,EAAE,eAAe,2CA8LjB"}
@@ -8,7 +8,7 @@ import { AiPromptPanel } from "./AiPromptPanel";
8
8
  import { UsageToast, useUsageToast } from "./UsageToast";
9
9
  import { aiStyles } from "../styles/inline";
10
10
  import { handleAIError } from "../utils/errorHandler";
11
- export function AiTextarea({ baseUrl, apiKeyId, uiMode = "modal", context, model, prompt, editMode = false, onValue, onToast, disabled, className, ...textareaProps }) {
11
+ export function AiTextarea({ baseUrl, apiKeyId, uiMode = "modal", context, model, prompt, editMode = false, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...textareaProps }) {
12
12
  const [isOpen, setIsOpen] = useState(false);
13
13
  const [textareaValue, setTextareaValue] = useState(textareaProps.value?.toString() ||
14
14
  textareaProps.defaultValue?.toString() ||
@@ -38,6 +38,8 @@ export function AiTextarea({ baseUrl, apiKeyId, uiMode = "modal", context, model
38
38
  prompt: promptWithContext,
39
39
  context: resolvedContext,
40
40
  actionType: hasContext ? "generate-text" : "autocomplete",
41
+ storeOutputs,
42
+ artifactTitle,
41
43
  });
42
44
  if (result.text) {
43
45
  setTextareaValue(result.text);
@@ -117,5 +119,5 @@ export function AiTextarea({ baseUrl, apiKeyId, uiMode = "modal", context, model
117
119
  ...(disabled || loading
118
120
  ? { opacity: 0.5, cursor: "not-allowed" }
119
121
  : {}),
120
- }, onClick: hasConfiguration ? handleQuickGenerate : handleOpenPanel, onMouseEnter: () => setIsButtonHovered(true), onMouseLeave: () => setIsButtonHovered(false), disabled: disabled || loading, type: "button", title: hasConfiguration ? "Generate with AI" : "Setup AI", children: loading ? (_jsx("svg", { style: aiStyles.spinner, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) })) : (_jsx(Sparkles, { size: 16 })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: models || [], sourceText: textareaValue || undefined })), Boolean(toastData) && (_jsx(UsageToast, { result: toastData, position: "bottom-right", onComplete: clearToast }, toastKey))] }));
122
+ }, onClick: hasConfiguration ? handleQuickGenerate : handleOpenPanel, onMouseEnter: () => setIsButtonHovered(true), onMouseLeave: () => setIsButtonHovered(false), disabled: disabled || loading, type: "button", title: hasConfiguration ? "Generate with AI" : "Setup AI", children: loading ? (_jsx("svg", { style: aiStyles.spinner, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) })) : (_jsx(Sparkles, { size: 16 })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: models || [], sourceText: textareaValue || undefined, baseUrl: baseUrl, apiKey: apiKeyId, enableModelManagement: enableModelManagement })), Boolean(toastData) && (_jsx(UsageToast, { result: toastData, position: "bottom-right", onComplete: clearToast }, toastKey))] }));
121
123
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ErrorToast.d.ts","sourceRoot":"","sources":["../../src/components/ErrorToast.tsx"],"names":[],"mappings":"AAKA,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,eAAe;IACvB,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;IACrE,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAyB,EACzB,UAAU,GACX,EAAE,eAAe,kDA0IjB;AAED,wBAAgB,aAAa;4BAII,cAAc;;;;EAgB9C"}
1
+ {"version":3,"file":"ErrorToast.d.ts","sourceRoot":"","sources":["../../src/components/ErrorToast.tsx"],"names":[],"mappings":"AAKA,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,eAAe;IACvB,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;IACrE,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAyB,EACzB,UAAU,GACX,EAAE,eAAe,kDA4IjB;AAED,wBAAgB,aAAa;4BAII,cAAc;;;;EAgB9C"}
package/dist/types.d.ts CHANGED
@@ -12,6 +12,9 @@ export interface BaseAiProps {
12
12
  model?: string | null;
13
13
  prompt?: string | null;
14
14
  editMode?: boolean;
15
+ enableModelManagement?: boolean;
16
+ storeOutputs?: boolean;
17
+ artifactTitle?: string;
15
18
  onValue?: (value: string) => void;
16
19
  onToast?: (toast: ToastType) => void;
17
20
  disabled?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAExC,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAExC,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lastbrain/ai-ui-react",
3
- "version": "1.0.30",
3
+ "version": "1.0.32",
4
4
  "description": "Headless React components for LastBrain AI UI Kit",
5
5
  "private": false,
6
6
  "type": "module",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "lucide-react": "^0.257.0",
51
- "@lastbrain/ai-ui-core": "1.0.19"
51
+ "@lastbrain/ai-ui-core": "1.0.21"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/react": "^19.2.0",
@@ -63,17 +63,21 @@ export function AiContextButton({
63
63
  const { showUsageToast, toastData, toastKey, clearToast } = useUsageToast();
64
64
  const { showErrorToast, errorData, errorKey, clearError } = useErrorToast();
65
65
 
66
- // Récupérer le contexte AiProvider avec fallback sur les props
66
+ // Utiliser directement les props pour les hooks (pas de context pour éviter les appels inutiles)
67
+ const { models, loading: modelsLoading } = useAiModels({
68
+ baseUrl: propBaseUrl,
69
+ apiKeyId: propApiKeyId,
70
+ });
71
+ const { generateText: callText, loading } = useAiCallText({
72
+ baseUrl: propBaseUrl,
73
+ apiKeyId: propApiKeyId,
74
+ });
75
+
76
+ // Récupérer le contexte AiProvider avec fallback sur les props pour l'affichage
67
77
  const aiContext = useAiContext();
68
78
  const baseUrl = propBaseUrl ?? aiContext.baseUrl;
69
79
  const apiKeyId = propApiKeyId ?? aiContext.apiKeyId;
70
80
 
71
- const { models, loading: modelsLoading } = useAiModels({ baseUrl, apiKeyId });
72
- const { generateText: callText, loading } = useAiCallText({
73
- baseUrl,
74
- apiKeyId,
75
- });
76
-
77
81
  const handleOpenPanel = () => {
78
82
  console.log("Opening panel, models:", models, "loading:", modelsLoading);
79
83
  setIsOpen(true);
@@ -550,13 +554,9 @@ Analyse ces données et réponds de manière structurée et claire.`;
550
554
  </div>
551
555
  </div>
552
556
  )}
553
-
557
+
554
558
  {/* Error Toast */}
555
- <ErrorToast
556
- key={errorKey}
557
- error={errorData}
558
- onComplete={clearError}
559
- />
559
+ <ErrorToast key={errorKey} error={errorData} onComplete={clearError} />
560
560
  </>
561
561
  );
562
562
  }
@@ -1,14 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useState, type ButtonHTMLAttributes } from "react";
4
- import {
5
- ImageIcon,
6
- Loader2,
7
- Download,
8
- Copy,
9
- ExternalLink,
10
- X,
11
- } from "lucide-react";
4
+ import { ImageIcon, Loader2, Download, ExternalLink, X } from "lucide-react";
12
5
  import type { BaseAiProps } from "../types";
13
6
  import { useAiCallImage } from "../hooks/useAiCallImage";
14
7
  import { useAiModels } from "../hooks/useAiModels";
@@ -90,19 +83,6 @@ export function AiImageButton({
90
83
  link.click();
91
84
  };
92
85
 
93
- const handleCopyUrl = async () => {
94
- if (!generatedImage) return;
95
- try {
96
- await navigator.clipboard.writeText(generatedImage.url);
97
- onToast?.({
98
- type: "success",
99
- message: "URL copiée dans le presse-papier",
100
- });
101
- } catch (_error) {
102
- onToast?.({ type: "error", message: "Erreur lors de la copie" });
103
- }
104
- };
105
-
106
86
  const handleSave = async () => {
107
87
  if (!generatedImage || !onImageSave) return;
108
88
  try {
@@ -165,6 +145,15 @@ export function AiImageButton({
165
145
  model: selectedModel,
166
146
  prompt: selectedPrompt,
167
147
  size: "1024x1024", // Taille par défaut
148
+ storeOutputs,
149
+ artifactTitle,
150
+ });
151
+
152
+ console.log("[AiImageButton] Image generation result:", {
153
+ hasUrl: !!result.url,
154
+ urlLength: result.url?.length,
155
+ urlPreview: result.url?.substring(0, 100),
156
+ requestId: result.requestId,
168
157
  });
169
158
 
170
159
  if (result.url) {
@@ -176,6 +165,7 @@ export function AiImageButton({
176
165
  tokens: result.debitTokens,
177
166
  };
178
167
  setGeneratedImage(imageData);
168
+ console.log("[AiImageButton] Image data stored:", imageData);
179
169
 
180
170
  onImage?.(result.url, {
181
171
  requestId: result.requestId,
@@ -373,46 +363,28 @@ export function AiImageButton({
373
363
  <div className="flex items-center gap-2 flex-wrap">
374
364
  <button
375
365
  onClick={handleDownload}
376
- className="flex items-center gap-1 px-3 py-2 text-xs font-medium rounded-lg transition-colors"
377
- style={getThemeStyles().actionButton}
378
- onMouseEnter={(e) => {
379
- e.currentTarget.style.backgroundColor =
380
- getThemeStyles().actionButton.hoverBackground;
381
- e.currentTarget.style.color =
382
- getThemeStyles().actionButton.hoverColor;
383
- }}
384
- onMouseLeave={(e) => {
385
- e.currentTarget.style.backgroundColor =
386
- getThemeStyles().actionButton.backgroundColor;
387
- e.currentTarget.style.color =
388
- getThemeStyles().actionButton.color;
366
+ className="flex items-center gap-2 px-4 py-2.5 text-sm font-semibold rounded-lg transition-all shadow-sm"
367
+ style={{
368
+ backgroundColor: "#10b981",
369
+ color: "white",
370
+ border: "none",
371
+ cursor: "pointer",
389
372
  }}
390
- title="Télécharger l'image"
391
- >
392
- <Download size={14} />
393
- Télécharger
394
- </button>
395
-
396
- <button
397
- onClick={handleCopyUrl}
398
- className="flex items-center gap-1 px-3 py-2 text-xs font-medium rounded-lg transition-colors"
399
- style={getThemeStyles().actionButton}
400
373
  onMouseEnter={(e) => {
401
- e.currentTarget.style.backgroundColor =
402
- getThemeStyles().actionButton.hoverBackground;
403
- e.currentTarget.style.color =
404
- getThemeStyles().actionButton.hoverColor;
374
+ e.currentTarget.style.backgroundColor = "#059669";
375
+ e.currentTarget.style.transform = "translateY(-1px)";
376
+ e.currentTarget.style.boxShadow =
377
+ "0 4px 12px rgba(16, 185, 129, 0.3)";
405
378
  }}
406
379
  onMouseLeave={(e) => {
407
- e.currentTarget.style.backgroundColor =
408
- getThemeStyles().actionButton.backgroundColor;
409
- e.currentTarget.style.color =
410
- getThemeStyles().actionButton.color;
380
+ e.currentTarget.style.backgroundColor = "#10b981";
381
+ e.currentTarget.style.transform = "translateY(0)";
382
+ e.currentTarget.style.boxShadow = "";
411
383
  }}
412
- title="Copier l'URL"
384
+ title="Télécharger l'image"
413
385
  >
414
- <Copy size={14} />
415
- Copier URL
386
+ <Download size={16} />
387
+ Télécharger l'image
416
388
  </button>
417
389
 
418
390
  {onImageSave && (
@@ -440,13 +412,9 @@ export function AiImageButton({
440
412
  </div>
441
413
  </div>
442
414
  )}
443
-
415
+
444
416
  {/* Error Toast */}
445
- <ErrorToast
446
- key={errorKey}
447
- error={errorData}
448
- onComplete={clearError}
449
- />
417
+ <ErrorToast key={errorKey} error={errorData} onComplete={clearError} />
450
418
  </div>
451
419
  );
452
420
  }
@@ -26,11 +26,13 @@ export function AiInput({
26
26
  model,
27
27
  prompt,
28
28
  editMode = false,
29
+ enableModelManagement = false,
30
+ storeOutputs,
31
+ artifactTitle,
29
32
  onValue,
30
33
  onToast,
31
34
  disabled,
32
35
  className,
33
- enableModelManagement = false,
34
36
  ...inputProps
35
37
  }: AiInputProps) {
36
38
  const [isOpen, setIsOpen] = useState(false);
@@ -73,6 +75,8 @@ export function AiInput({
73
75
  prompt: promptWithContext,
74
76
  context: resolvedContext,
75
77
  actionType: hasContext ? "generate-text" : "autocomplete",
78
+ storeOutputs,
79
+ artifactTitle,
76
80
  });
77
81
 
78
82
  if (result.text) {
@@ -24,6 +24,8 @@ export function AiSelect({
24
24
  context,
25
25
  model,
26
26
  prompt,
27
+ storeOutputs,
28
+ artifactTitle,
27
29
  onValue,
28
30
  onToast,
29
31
  disabled,
@@ -56,6 +58,8 @@ export function AiSelect({
56
58
  prompt: selectedPrompt,
57
59
  context: context || undefined,
58
60
  actionType: "autocomplete",
61
+ storeOutputs,
62
+ artifactTitle,
59
63
  });
60
64
 
61
65
  if (result.text) {
@@ -30,6 +30,9 @@ export function AiTextarea({
30
30
  model,
31
31
  prompt,
32
32
  editMode = false,
33
+ enableModelManagement,
34
+ storeOutputs,
35
+ artifactTitle,
33
36
  onValue,
34
37
  onToast,
35
38
  disabled,
@@ -78,6 +81,8 @@ export function AiTextarea({
78
81
  prompt: promptWithContext,
79
82
  context: resolvedContext,
80
83
  actionType: hasContext ? "generate-text" : "autocomplete",
84
+ storeOutputs,
85
+ artifactTitle,
81
86
  });
82
87
 
83
88
  if (result.text) {
@@ -208,6 +213,9 @@ export function AiTextarea({
208
213
  uiMode={uiMode}
209
214
  models={models || []}
210
215
  sourceText={textareaValue || undefined}
216
+ baseUrl={baseUrl}
217
+ apiKey={apiKeyId}
218
+ enableModelManagement={enableModelManagement}
211
219
  />
212
220
  )}
213
221
  {Boolean(toastData) && (
@@ -50,12 +50,12 @@ export function ErrorToast({
50
50
 
51
51
  const handleClose = () => {
52
52
  if (isClosing) return;
53
-
53
+
54
54
  // Clear auto-close timeout if user closes manually
55
55
  if (autoCloseTimeoutRef.current) {
56
56
  window.clearTimeout(autoCloseTimeoutRef.current);
57
57
  }
58
-
58
+
59
59
  setIsClosing(true);
60
60
  fadeTimeoutRef.current = window.setTimeout(() => {
61
61
  setIsVisible(false);
@@ -125,7 +125,9 @@ export function ErrorToast({
125
125
  </span>
126
126
  )}
127
127
  </div>
128
- <div style={{ fontSize: "12px", opacity: 0.9, wordBreak: "break-word" }}>
128
+ <div
129
+ style={{ fontSize: "12px", opacity: 0.9, wordBreak: "break-word" }}
130
+ >
129
131
  {error.message}
130
132
  </div>
131
133
  </div>
package/src/types.ts CHANGED
@@ -13,6 +13,9 @@ export interface BaseAiProps {
13
13
  model?: string | null;
14
14
  prompt?: string | null;
15
15
  editMode?: boolean;
16
+ enableModelManagement?: boolean;
17
+ storeOutputs?: boolean; // Whether to store outputs (images in bucket, text in database)
18
+ artifactTitle?: string; // Title for stored artifacts
16
19
  onValue?: (value: string) => void;
17
20
  onToast?: (toast: ToastType) => void;
18
21
  disabled?: boolean;