@lastbrain/module-ai 0.1.11 → 0.1.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.
@@ -12,7 +12,7 @@ export async function POST(request) {
12
12
  const { data: { user }, } = await supabase.auth.getUser();
13
13
  // L'utilisateur est déjà authentifié grâce au middleware
14
14
  const body = await request.json();
15
- const { prompt, model = "gpt-4o-mini", context, maxTokens = 100, temperature = 0.7, } = body;
15
+ const { prompt, model = "gpt-4o-mini", context, maxTokens = 3000, temperature = 0.7, } = body;
16
16
  if (!prompt) {
17
17
  return NextResponse.json({ error: "Le prompt est requis" }, { status: 400 });
18
18
  }
@@ -20,6 +20,8 @@ export interface ImageGenerativeProps {
20
20
  label?: string;
21
21
  description?: string;
22
22
  defaultStyle?: string;
23
+ hideStyleEditor?: boolean;
24
+ uploadPath?: string;
23
25
  }
24
- export declare function ImageGenerative({ defaultPrompt, model, size, quality, onChange, onError, className, disabled, apiEndpoint, showTokenBalance, label, description, defaultStyle, }: ImageGenerativeProps): import("react/jsx-runtime").JSX.Element;
26
+ export declare function ImageGenerative({ defaultPrompt, model, size, quality, onChange, onError, className, disabled, apiEndpoint, showTokenBalance, label, description, defaultStyle, hideStyleEditor, uploadPath, }: ImageGenerativeProps): import("react/jsx-runtime").JSX.Element;
25
27
  //# sourceMappingURL=ImageGenerative.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ImageGenerative.d.ts","sourceRoot":"","sources":["../../../src/web/components/ImageGenerative.tsx"],"names":[],"mappings":"AA2BA,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAiCD,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;IACvE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAC1E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,eAAe,CAAC,EAC9B,aAAkB,EAClB,KAAkB,EAClB,IAAkB,EAClB,OAAoB,EACpB,QAAQ,EACR,OAAO,EACP,SAAS,EACT,QAAgB,EAChB,WAAsC,EACtC,gBAAuB,EACvB,KAAK,EACL,WAAW,EACX,YAA0B,GAC3B,EAAE,oBAAoB,2CAoStB"}
1
+ {"version":3,"file":"ImageGenerative.d.ts","sourceRoot":"","sources":["../../../src/web/components/ImageGenerative.tsx"],"names":[],"mappings":"AA4BA,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAiCD,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;IACvE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAC1E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,eAAe,CAAC,EAC9B,aAAkB,EAClB,KAAkB,EAClB,IAAkB,EAClB,OAAoB,EACpB,QAAQ,EACR,OAAO,EACP,SAAS,EACT,QAAgB,EAChB,WAAsC,EACtC,gBAAuB,EACvB,KAAK,EACL,WAAW,EACX,YAA0B,EAC1B,eAAuB,EACvB,UAAU,GACX,EAAE,oBAAoB,2CA0TtB"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useState, useCallback } from "react";
4
- import { Button, Chip, Progress, Card, CardBody, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Textarea, Select, SelectItem, } from "@lastbrain/ui";
4
+ import { Button, Chip, Progress, Card, CardBody, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Textarea, Select, SelectItem, addToast, } from "@lastbrain/ui";
5
5
  import { Image as ImageIcon, Loader2, AlertCircle, Download, Wand2, } from "lucide-react";
6
6
  import Image from "next/image";
7
7
  const imageStyles = [
@@ -34,7 +34,7 @@ const imageStyles = [
34
34
  },
35
35
  { key: "pop-art", label: "Pop Art", description: "Style pop art vibrant" },
36
36
  ];
37
- export function ImageGenerative({ defaultPrompt = "", model = "dall-e-3", size = "1024x1024", quality = "standard", onChange, onError, className, disabled = false, apiEndpoint = "/api/ai/generate-image", showTokenBalance = true, label, description, defaultStyle = "realistic", }) {
37
+ export function ImageGenerative({ defaultPrompt = "", model = "dall-e-3", size = "1024x1024", quality = "standard", onChange, onError, className, disabled = false, apiEndpoint = "/api/ai/generate-image", showTokenBalance = true, label, description, defaultStyle = "realistic", hideStyleEditor = false, uploadPath, }) {
38
38
  const [isGenerating, setIsGenerating] = useState(false);
39
39
  const [generatedImage, setGeneratedImage] = useState(null);
40
40
  const [error, setError] = useState(null);
@@ -61,6 +61,7 @@ export function ImageGenerative({ defaultPrompt = "", model = "dall-e-3", size =
61
61
  model: selectedModel,
62
62
  size,
63
63
  quality,
64
+ uploadPath,
64
65
  }),
65
66
  });
66
67
  if (!response.ok) {
@@ -78,6 +79,12 @@ export function ImageGenerative({ defaultPrompt = "", model = "dall-e-3", size =
78
79
  };
79
80
  setGeneratedImage(imageResponse);
80
81
  setTokenBalance(data.tokensRemaining);
82
+ // Show success toast
83
+ addToast({
84
+ color: "success",
85
+ title: "Image générée avec succès",
86
+ description: `${imageResponse.tokensUsed} tokens utilisés, ${imageResponse.tokensRemaining} restants`,
87
+ });
81
88
  if (onChange) {
82
89
  onChange(data.imageUrl, imageResponse);
83
90
  }
@@ -85,6 +92,12 @@ export function ImageGenerative({ defaultPrompt = "", model = "dall-e-3", size =
85
92
  catch (err) {
86
93
  const errorMessage = err instanceof Error ? err.message : "Erreur inconnue";
87
94
  setError(errorMessage);
95
+ // Show error toast
96
+ addToast({
97
+ color: "danger",
98
+ title: "Erreur de génération d'image",
99
+ description: errorMessage,
100
+ });
88
101
  if (onError) {
89
102
  onError(err);
90
103
  }
@@ -99,6 +112,7 @@ export function ImageGenerative({ defaultPrompt = "", model = "dall-e-3", size =
99
112
  size,
100
113
  quality,
101
114
  apiEndpoint,
115
+ uploadPath,
102
116
  onChange,
103
117
  onError,
104
118
  isGenerating,
@@ -122,5 +136,5 @@ export function ImageGenerative({ defaultPrompt = "", model = "dall-e-3", size =
122
136
  console.error("Erreur lors du téléchargement:", error);
123
137
  }
124
138
  }, [generatedImage]);
125
- return (_jsxs("div", { className: className, children: [_jsxs("div", { className: "flex flex-col gap-4", children: [label && _jsx("label", { className: "text-sm font-medium", children: label }), description && _jsx("p", { className: "text-sm text-gray-500", children: description }), generatedImage && !isGenerating && (_jsx(Card, { children: _jsxs(CardBody, { children: [_jsx("div", { className: "relative w-full aspect-square", children: _jsx(Image, { src: generatedImage.imageUrl, alt: generatedImage.prompt, fill: true, className: "object-contain rounded-lg", priority: true }) }), _jsxs("div", { className: "mt-4", children: [_jsxs("p", { className: "text-sm text-default-600", children: [_jsx("strong", { children: "Prompt:" }), " ", generatedImage.prompt] }), _jsxs("p", { className: "text-xs text-default-400 mt-1", children: [size, " \u2022 ", quality] })] })] }) })), _jsx(Button, { color: "primary", onClick: () => setIsModalOpen(true), disabled: disabled || isGenerating, startContent: _jsx(Wand2, { size: 18 }), className: "w-full", children: isGenerating ? "Génération..." : "Générer une image" }), _jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [_jsx("div", { className: "flex items-center gap-2", children: showTokenBalance && tokenBalance !== null && (_jsxs(Chip, { size: "sm", variant: "flat", color: "success", children: [tokenBalance.toLocaleString(), " tokens restants"] })) }), generatedImage && (_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsxs(Chip, { size: "sm", variant: "flat", children: [generatedImage.tokensUsed, " tokens utilis\u00E9s"] }), _jsx(Chip, { size: "sm", variant: "flat", color: "primary", children: generatedImage.model }), generatedImage.cost && (_jsxs(Chip, { size: "sm", variant: "flat", color: "warning", children: ["$", generatedImage.cost.toFixed(4)] })), _jsx(Button, { size: "sm", variant: "flat", onClick: handleDownload, startContent: _jsx(Download, { size: 16 }), children: "T\u00E9l\u00E9charger" })] }))] }), isGenerating && (_jsxs("div", { className: "space-y-2", children: [_jsx(Progress, { size: "sm", isIndeterminate: true, "aria-label": "G\u00E9n\u00E9ration en cours...", className: "max-w-md" }), _jsx("p", { className: "text-sm text-default-500", children: "G\u00E9n\u00E9ration de l'image en cours... Cela peut prendre quelques instants." })] })), error && (_jsxs("div", { className: "flex items-center gap-2 text-danger text-sm p-3 bg-danger-50 rounded-md", children: [_jsx(AlertCircle, { size: 16 }), _jsx("span", { children: error })] }))] }), _jsx(Modal, { isOpen: isModalOpen, onClose: () => setIsModalOpen(false), size: "2xl", children: _jsxs(ModalContent, { children: [_jsx(ModalHeader, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(ImageIcon, { size: 20 }), _jsx("span", { children: "G\u00E9n\u00E9rer une image avec l'IA" })] }) }), _jsx(ModalBody, { children: _jsxs("div", { className: "space-y-4", children: [_jsx(Textarea, { label: "Description de l'image", placeholder: "D\u00E9crivez l'image que vous souhaitez g\u00E9n\u00E9rer...", value: userPrompt, onChange: (e) => setUserPrompt(e.target.value), minRows: 3, description: "Soyez pr\u00E9cis et d\u00E9taill\u00E9 pour de meilleurs r\u00E9sultats" }), _jsx(Select, { label: "Style artistique", selectedKeys: [selectedStyle], onSelectionChange: (keys) => setSelectedStyle(Array.from(keys)[0]), description: "Le style influence l'apparence finale de l'image", children: imageStyles.map((style) => (_jsx(SelectItem, { textValue: style.label, children: _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { className: "font-medium", children: style.label }), _jsx("span", { className: "text-xs text-gray-500", children: style.description })] }) }, style.key))) }), _jsxs(Select, { label: "Mod\u00E8le IA", selectedKeys: [selectedModel], onSelectionChange: (keys) => setSelectedModel(Array.from(keys)[0]), description: "DALL-E 3 offre la meilleure qualit\u00E9", children: [_jsx(SelectItem, { children: "DALL-E 3 (Haute qualit\u00E9)" }, "dall-e-3"), _jsx(SelectItem, { children: "DALL-E 2 (Standard)" }, "dall-e-2")] }), generatedImage && (_jsxs("div", { className: "p-3 bg-gray-50 dark:bg-gray-800 rounded-lg", children: [_jsx("p", { className: "text-sm font-medium mb-2", children: "Aper\u00E7u de l'image actuelle:" }), _jsx("div", { className: "relative w-full h-32", children: _jsx(Image, { src: generatedImage.imageUrl, alt: "Current", fill: true, className: "object-cover rounded" }) })] }))] }) }), _jsxs(ModalFooter, { children: [_jsx(Button, { variant: "light", onClick: () => setIsModalOpen(false), children: "Annuler" }), _jsx(Button, { color: "primary", onClick: handleGenerate, disabled: !userPrompt.trim() || isGenerating, startContent: isGenerating ? (_jsx(Loader2, { className: "animate-spin", size: 16 })) : (_jsx(ImageIcon, { size: 16 })), children: isGenerating ? "Génération..." : "Générer" })] })] }) })] }));
139
+ return (_jsxs("div", { className: className, children: [_jsxs("div", { className: "flex flex-col gap-4", children: [label && _jsx("label", { className: "text-sm font-medium", children: label }), description && _jsx("p", { className: "text-sm text-gray-500", children: description }), generatedImage && !isGenerating && (_jsx(Card, { children: _jsxs(CardBody, { children: [_jsx("div", { className: "relative w-full aspect-square", children: _jsx(Image, { src: generatedImage.imageUrl, alt: generatedImage.prompt, fill: true, className: "object-contain rounded-lg", priority: true }) }), _jsxs("div", { className: "mt-4", children: [_jsxs("p", { className: "text-sm text-default-600", children: [_jsx("strong", { children: "Prompt:" }), " ", generatedImage.prompt] }), _jsxs("p", { className: "text-xs text-default-400 mt-1", children: [size, " \u2022 ", quality] })] })] }) })), _jsx(Button, { color: "primary", onClick: () => setIsModalOpen(true), disabled: disabled || isGenerating, startContent: _jsx(Wand2, { size: 18 }), className: "w-full", children: isGenerating ? "Génération..." : "Générer une image" }), _jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [_jsx("div", { className: "flex items-center gap-2", children: showTokenBalance && tokenBalance !== null && (_jsxs(Chip, { size: "sm", variant: "flat", color: "success", children: [tokenBalance.toLocaleString(), " tokens restants"] })) }), generatedImage && (_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsxs(Chip, { size: "sm", variant: "flat", children: [generatedImage.tokensUsed, " tokens utilis\u00E9s"] }), _jsx(Chip, { size: "sm", variant: "flat", color: "primary", children: generatedImage.model }), generatedImage.cost && (_jsxs(Chip, { size: "sm", variant: "flat", color: "warning", children: ["$", generatedImage.cost.toFixed(4)] })), _jsx(Button, { size: "sm", variant: "flat", onClick: handleDownload, startContent: _jsx(Download, { size: 16 }), children: "T\u00E9l\u00E9charger" })] }))] }), isGenerating && (_jsxs("div", { className: "space-y-2", children: [_jsx(Progress, { size: "sm", isIndeterminate: true, "aria-label": "G\u00E9n\u00E9ration en cours...", className: "max-w-md" }), _jsx("p", { className: "text-sm text-default-500", children: "G\u00E9n\u00E9ration de l'image en cours... Cela peut prendre quelques instants." })] })), error && (_jsxs("div", { className: "flex items-center gap-2 text-danger text-sm p-3 bg-danger-50 rounded-md", children: [_jsx(AlertCircle, { size: 16 }), _jsx("span", { children: error })] }))] }), _jsx(Modal, { isOpen: isModalOpen, onClose: () => setIsModalOpen(false), size: "2xl", children: _jsxs(ModalContent, { children: [_jsx(ModalHeader, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(ImageIcon, { size: 20 }), _jsx("span", { children: "G\u00E9n\u00E9rer une image avec l'IA" })] }) }), _jsx(ModalBody, { children: _jsxs("div", { className: "space-y-4", children: [_jsx(Textarea, { label: "Description de l'image", placeholder: "D\u00E9crivez l'image que vous souhaitez g\u00E9n\u00E9rer...", value: userPrompt, onChange: (e) => setUserPrompt(e.target.value), minRows: 3, description: "Soyez pr\u00E9cis et d\u00E9taill\u00E9 pour de meilleurs r\u00E9sultats" }), !hideStyleEditor && (_jsxs(_Fragment, { children: [_jsx(Select, { label: "Style artistique", selectedKeys: [selectedStyle], onSelectionChange: (keys) => setSelectedStyle(Array.from(keys)[0]), description: "Le style influence l'apparence finale de l'image", children: imageStyles.map((style) => (_jsx(SelectItem, { textValue: style.label, children: _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { className: "font-medium", children: style.label }), _jsx("span", { className: "text-xs text-gray-500", children: style.description })] }) }, style.key))) }), _jsxs(Select, { label: "Mod\u00E8le IA", selectedKeys: [selectedModel], onSelectionChange: (keys) => setSelectedModel(Array.from(keys)[0]), description: "DALL-E 3 offre la meilleure qualit\u00E9", children: [_jsx(SelectItem, { children: "DALL-E 3 (Haute qualit\u00E9)" }, "dall-e-3"), _jsx(SelectItem, { children: "DALL-E 2 (Standard)" }, "dall-e-2")] })] })), generatedImage && (_jsxs("div", { className: "p-3 bg-gray-50 dark:bg-gray-800 rounded-lg", children: [_jsx("p", { className: "text-sm font-medium mb-2", children: "Aper\u00E7u de l'image actuelle:" }), _jsx("div", { className: "relative w-full h-32", children: _jsx(Image, { src: generatedImage.imageUrl, alt: "Current", fill: true, className: "object-cover rounded" }) })] }))] }) }), _jsxs(ModalFooter, { children: [_jsx(Button, { variant: "light", onClick: () => setIsModalOpen(false), children: "Annuler" }), _jsx(Button, { color: "primary", onClick: handleGenerate, disabled: !userPrompt.trim() || isGenerating, startContent: isGenerating ? (_jsx(Loader2, { className: "animate-spin", size: 16 })) : (_jsx(ImageIcon, { size: 16 })), children: isGenerating ? "Génération..." : "Générer" })] })] }) })] }));
126
140
  }
@@ -20,6 +20,8 @@ export interface TextareaGenerativeProps {
20
20
  showTokenBalance?: boolean;
21
21
  label?: string;
22
22
  description?: string;
23
+ hidePromptEditor?: boolean;
24
+ showStructuredResponse?: boolean;
23
25
  }
24
- export declare function TextareaGenerative({ defaultPrompt, model, placeholder, defaultValue, onChange, onError, className, disabled, minRows, maxRows, apiEndpoint, showTokenBalance, label, description, }: TextareaGenerativeProps): import("react/jsx-runtime").JSX.Element;
26
+ export declare function TextareaGenerative({ defaultPrompt, model, placeholder, defaultValue, onChange, onError, className, disabled, minRows, maxRows, apiEndpoint, showTokenBalance, label, description, hidePromptEditor, showStructuredResponse, }: TextareaGenerativeProps): import("react/jsx-runtime").JSX.Element;
25
27
  //# sourceMappingURL=TextareaGenerative.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TextareaGenerative.d.ts","sourceRoot":"","sources":["../../../src/web/components/TextareaGenerative.tsx"],"names":[],"mappings":"AAoBA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAClE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,aAAkB,EAClB,KAAqB,EACrB,WAA6D,EAC7D,YAAiB,EACjB,QAAQ,EACR,OAAO,EACP,SAAS,EACT,QAAgB,EAChB,OAAW,EACX,OAAY,EACZ,WAAqC,EACrC,gBAAuB,EACvB,KAAK,EACL,WAAW,GACZ,EAAE,uBAAuB,2CAiPzB"}
1
+ {"version":3,"file":"TextareaGenerative.d.ts","sourceRoot":"","sources":["../../../src/web/components/TextareaGenerative.tsx"],"names":[],"mappings":"AAqBA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAClE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,wBAAgB,kBAAkB,CAAC,EACjC,aAAkB,EAClB,KAAqB,EACrB,WAA6D,EAC7D,YAAiB,EACjB,QAAQ,EACR,OAAO,EACP,SAAS,EACT,QAAgB,EAChB,OAAW,EACX,OAAY,EACZ,WAAqC,EACrC,gBAAuB,EACvB,KAAK,EACL,WAAW,EACX,gBAAwB,EACxB,sBAA8B,GAC/B,EAAE,uBAAuB,2CAqRzB"}
@@ -1,32 +1,39 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useCallback } from "react";
4
- import { Textarea, Button, Chip, Progress, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Select, SelectItem, Skeleton, } from "@lastbrain/ui";
4
+ import { Textarea, Button, Chip, Progress, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Select, SelectItem, Skeleton, addToast, } from "@lastbrain/ui";
5
5
  import { Sparkles, Loader2, AlertCircle, Wand2 } from "lucide-react";
6
- export function TextareaGenerative({ defaultPrompt = "", model = "gpt-4o-mini", placeholder = "Saisissez votre texte ou générez avec l'IA...", defaultValue = "", onChange, onError, className, disabled = false, minRows = 4, maxRows = 20, apiEndpoint = "/api/ai/generate-text", showTokenBalance = true, label, description, }) {
7
- const [value, setValue] = useState(defaultValue);
6
+ export function TextareaGenerative({ defaultPrompt = "", model = "gpt-4o-mini", placeholder = "Saisissez votre texte ou générez avec l'IA...", defaultValue = "", onChange, onError, className, disabled = false, minRows = 4, maxRows = 20, apiEndpoint = "/api/ai/generate-text", showTokenBalance = true, label, description, hidePromptEditor = false, showStructuredResponse = false, }) {
7
+ const [userInput, setUserInput] = useState(defaultValue);
8
8
  const [isGenerating, setIsGenerating] = useState(false);
9
9
  const [lastResponse, setLastResponse] = useState(null);
10
10
  const [error, setError] = useState(null);
11
11
  const [tokenBalance, setTokenBalance] = useState(null);
12
- // Modal state
12
+ // Modal state (only for non-hidePromptEditor mode)
13
13
  const [isModalOpen, setIsModalOpen] = useState(false);
14
14
  const [userPrompt, setUserPrompt] = useState(defaultPrompt);
15
15
  const [selectedModel, setSelectedModel] = useState(model);
16
- const handleGenerate = useCallback(async () => {
17
- if (!userPrompt || isGenerating)
16
+ const handleGenerate = useCallback(async (prompt) => {
17
+ const finalPrompt = hidePromptEditor
18
+ ? defaultPrompt
19
+ : prompt || userPrompt;
20
+ const finalUserInput = hidePromptEditor ? userInput : "";
21
+ if (!finalPrompt || isGenerating)
18
22
  return;
19
23
  setIsGenerating(true);
20
24
  setError(null);
21
- setIsModalOpen(false);
25
+ if (!hidePromptEditor)
26
+ setIsModalOpen(false);
22
27
  try {
28
+ const fullPrompt = hidePromptEditor
29
+ ? `${finalPrompt}\n\nUtilisateur: ${finalUserInput}`
30
+ : finalPrompt;
23
31
  const response = await fetch(apiEndpoint, {
24
32
  method: "POST",
25
33
  headers: { "Content-Type": "application/json" },
26
34
  body: JSON.stringify({
27
- prompt: userPrompt,
28
- model: selectedModel,
29
- context: value,
35
+ prompt: fullPrompt,
36
+ model: hidePromptEditor ? model : selectedModel,
30
37
  }),
31
38
  });
32
39
  if (!response.ok) {
@@ -38,12 +45,17 @@ export function TextareaGenerative({ defaultPrompt = "", model = "gpt-4o-mini",
38
45
  text: data.text,
39
46
  tokensUsed: data.tokensUsed || 0,
40
47
  tokensRemaining: data.tokensRemaining || 0,
41
- model: data.model || selectedModel,
48
+ model: data.model || (hidePromptEditor ? model : selectedModel),
42
49
  cost: data.cost,
43
50
  };
44
- setValue(data.text);
45
51
  setLastResponse(generativeResponse);
46
52
  setTokenBalance(data.tokensRemaining);
53
+ // Show success toast
54
+ addToast({
55
+ color: "success",
56
+ title: "Génération réussie",
57
+ description: `${generativeResponse.tokensUsed} tokens utilisés`,
58
+ });
47
59
  if (onChange) {
48
60
  onChange(data.text, generativeResponse);
49
61
  }
@@ -51,6 +63,12 @@ export function TextareaGenerative({ defaultPrompt = "", model = "gpt-4o-mini",
51
63
  catch (err) {
52
64
  const errorMessage = err instanceof Error ? err.message : "Erreur inconnue";
53
65
  setError(errorMessage);
66
+ // Show error toast
67
+ addToast({
68
+ color: "danger",
69
+ title: "Erreur de génération",
70
+ description: errorMessage,
71
+ });
54
72
  if (onError) {
55
73
  onError(err);
56
74
  }
@@ -59,23 +77,26 @@ export function TextareaGenerative({ defaultPrompt = "", model = "gpt-4o-mini",
59
77
  setIsGenerating(false);
60
78
  }
61
79
  }, [
80
+ userInput,
62
81
  userPrompt,
63
82
  selectedModel,
64
- value,
83
+ hidePromptEditor,
84
+ defaultPrompt,
85
+ model,
65
86
  apiEndpoint,
66
87
  onChange,
67
88
  onError,
68
89
  isGenerating,
69
90
  ]);
70
- const handleValueChange = (newValue) => {
71
- setValue(newValue);
72
- if (onChange) {
73
- onChange(newValue);
74
- }
91
+ const handleInputChange = (newValue) => {
92
+ setUserInput(newValue);
75
93
  };
76
- return (_jsxs("div", { className: className, children: [_jsxs("div", { className: "flex flex-col gap-2", children: [label && _jsx("label", { className: "text-sm font-medium", children: label }), description && (_jsx("p", { className: "text-sm text-gray-500 mb-2", children: description })), !isGenerating && (_jsx(Textarea, { value: value, onChange: (e) => handleValueChange(e.target.value), placeholder: placeholder, disabled: disabled || isGenerating, minRows: minRows, maxRows: maxRows,
77
- // classNames={{
78
- // input: "resize-y",
79
- // }}
80
- endContent: _jsx(Button, { isIconOnly: true, size: "sm", color: "primary", variant: "light", onClick: () => setIsModalOpen(true), disabled: disabled || isGenerating, title: "G\u00E9n\u00E9rer avec l'IA", children: _jsx(Wand2, { size: 18 }) }) })), isGenerating && (_jsxs("div", { className: "w-full h-md border border-default-200 p-5 rounded-xl", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Skeleton, { className: "w-lg bg-content-2 h-8 rounded-lg" }), _jsx(Skeleton, { className: "w-md bg-content-2 h-4 rounded-lg" }), _jsx(Skeleton, { className: "w-sm bg-content-2 h-4 rounded-lg" })] }), _jsx("div", { className: "mt-2", children: _jsx(Progress, { size: "sm", isIndeterminate: true, "aria-label": "G\u00E9n\u00E9ration en cours...", label: "G\u00E9n\u00E9ration en cours..." }) })] })), _jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [_jsx("div", { className: "flex items-center gap-2", children: showTokenBalance && tokenBalance !== null && (_jsxs(Chip, { size: "sm", variant: "flat", color: "success", children: [tokenBalance.toLocaleString(), " tokens restants"] })) }), lastResponse && (_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsxs(Chip, { size: "sm", variant: "flat", children: [lastResponse.tokensUsed, " tokens utilis\u00E9s"] }), _jsx(Chip, { size: "sm", variant: "flat", color: "primary", children: lastResponse.model }), lastResponse.cost && (_jsxs(Chip, { size: "sm", variant: "flat", color: "warning", children: ["$", lastResponse.cost.toFixed(4)] }))] }))] }), error && (_jsxs("div", { className: "flex items-center gap-2 text-danger text-sm p-2 bg-danger-50 rounded-md", children: [_jsx(AlertCircle, { size: 16 }), _jsx("span", { children: error })] }))] }), _jsx(Modal, { isOpen: isModalOpen, onClose: () => setIsModalOpen(false), size: "2xl", backdrop: "blur", children: _jsxs(ModalContent, { children: [_jsx(ModalHeader, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Sparkles, { size: 20 }), _jsx("span", { children: "G\u00E9n\u00E9rer du texte avec l'IA" })] }) }), _jsx(ModalBody, { children: _jsxs("div", { className: "space-y-4", children: [_jsx(Textarea, { label: "Prompt de g\u00E9n\u00E9ration", placeholder: "D\u00E9crivez ce que vous souhaitez g\u00E9n\u00E9rer...", value: userPrompt, onChange: (e) => setUserPrompt(e.target.value), minRows: 3, description: "Soyez pr\u00E9cis pour obtenir de meilleurs r\u00E9sultats" }), _jsxs(Select, { label: "Mod\u00E8le IA", selectedKeys: [selectedModel], onSelectionChange: (keys) => setSelectedModel(Array.from(keys)[0]), description: "GPT-4o-mini est plus rapide et \u00E9conomique", children: [_jsx(SelectItem, { children: "GPT-4o Mini (Rapide)" }, "gpt-4o-mini"), _jsx(SelectItem, { children: "GPT-4o (Avanc\u00E9)" }, "gpt-4o"), _jsx(SelectItem, { children: "GPT-3.5 Turbo (\u00C9conomique)" }, "gpt-3.5-turbo")] }), value && (_jsxs("div", { className: "p-3 bg-gray-50 dark:bg-gray-800 rounded-lg", children: [_jsx("p", { className: "text-sm font-medium mb-1", children: "Contexte actuel:" }), _jsx("p", { className: "text-xs text-gray-600 dark:text-gray-400 line-clamp-3", children: value })] }))] }) }), _jsxs(ModalFooter, { children: [_jsx(Button, { variant: "light", onClick: () => setIsModalOpen(false), children: "Annuler" }), _jsx(Button, { color: "primary", onClick: handleGenerate, disabled: !userPrompt.trim() || isGenerating, startContent: isGenerating ? (_jsx(Loader2, { className: "animate-spin", size: 16 })) : (_jsx(Sparkles, { size: 16 })), children: isGenerating ? "Génération..." : "Générer" })] })] }) })] }));
94
+ return (_jsxs("div", { className: className, children: [_jsxs("div", { className: "flex flex-col gap-2", children: [label && _jsx("label", { className: "text-sm font-medium", children: label }), description && (_jsx("p", { className: "text-sm text-gray-500 mb-2", children: description })), isGenerating && (_jsxs("div", { className: "w-full border border-default-200 p-5 rounded-xl", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Skeleton, { className: "w-lg bg-content-2 h-8 rounded-lg" }), _jsx(Skeleton, { className: "w-md bg-content-2 h-4 rounded-lg" }), _jsx(Skeleton, { className: "w-sm bg-content-2 h-4 rounded-lg" })] }), _jsx("div", { className: "mt-2", children: _jsx(Progress, { size: "sm", isIndeterminate: true, "aria-label": "G\u00E9n\u00E9ration en cours...", label: "G\u00E9n\u00E9ration en cours..." }) })] })), !isGenerating && (_jsx(Textarea, { value: userInput, onChange: (e) => handleInputChange(e.target.value), placeholder: placeholder, disabled: disabled || isGenerating, minRows: minRows, maxRows: maxRows, endContent: _jsx(Button, { isIconOnly: true, size: "sm", color: "primary", variant: "light", onClick: () => {
95
+ if (hidePromptEditor) {
96
+ handleGenerate();
97
+ }
98
+ else {
99
+ setIsModalOpen(true);
100
+ }
101
+ }, disabled: disabled || isGenerating || !userInput.trim(), title: "G\u00E9n\u00E9rer avec l'IA", children: _jsx(Wand2, { size: 18 }) }) })), _jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [_jsx("div", { className: "flex items-center gap-2", children: showTokenBalance && tokenBalance !== null && (_jsxs(Chip, { size: "sm", variant: "flat", color: "success", children: [tokenBalance.toLocaleString(), " tokens restants"] })) }), lastResponse && (_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsxs(Chip, { size: "sm", variant: "flat", children: [lastResponse.tokensUsed, " tokens utilis\u00E9s"] }), _jsx(Chip, { size: "sm", variant: "flat", color: "primary", children: lastResponse.model }), lastResponse.cost && (_jsxs(Chip, { size: "sm", variant: "flat", color: "warning", children: ["$", lastResponse.cost.toFixed(4)] }))] }))] }), error && (_jsxs("div", { className: "flex items-center gap-2 text-danger text-sm p-2 bg-danger-50 rounded-md", children: [_jsx(AlertCircle, { size: 16 }), _jsx("span", { children: error })] }))] }), !hidePromptEditor && (_jsx(Modal, { isOpen: isModalOpen, onClose: () => setIsModalOpen(false), size: "2xl", backdrop: "blur", children: _jsxs(ModalContent, { children: [_jsx(ModalHeader, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Sparkles, { size: 20 }), _jsx("span", { children: "G\u00E9n\u00E9rer du texte avec l'IA" })] }) }), _jsx(ModalBody, { children: _jsxs("div", { className: "space-y-4", children: [_jsx(Textarea, { label: "Prompt de g\u00E9n\u00E9ration", placeholder: "D\u00E9crivez ce que vous souhaitez g\u00E9n\u00E9rer...", value: userPrompt, onChange: (e) => setUserPrompt(e.target.value), minRows: 3, description: "Soyez pr\u00E9cis pour obtenir de meilleurs r\u00E9sultats" }), _jsxs(Select, { label: "Mod\u00E8le IA", selectedKeys: [selectedModel], onSelectionChange: (keys) => setSelectedModel(Array.from(keys)[0]), description: "GPT-4o-mini est plus rapide et \u00E9conomique", children: [_jsx(SelectItem, { children: "GPT-4o Mini (Rapide)" }, "gpt-4o-mini"), _jsx(SelectItem, { children: "GPT-4o (Avanc\u00E9)" }, "gpt-4o"), _jsx(SelectItem, { children: "GPT-3.5 Turbo (\u00C9conomique)" }, "gpt-3.5-turbo")] }), userInput && (_jsxs("div", { className: "p-3 bg-gray-50 dark:bg-gray-800 rounded-lg", children: [_jsx("p", { className: "text-sm font-medium mb-1", children: "Contexte actuel:" }), _jsx("p", { className: "text-xs text-gray-600 dark:text-gray-400 line-clamp-3", children: userInput })] }))] }) }), _jsxs(ModalFooter, { children: [_jsx(Button, { variant: "light", onClick: () => setIsModalOpen(false), children: "Annuler" }), _jsx(Button, { color: "primary", onClick: () => handleGenerate(userPrompt), disabled: !userPrompt.trim() || isGenerating, startContent: isGenerating ? (_jsx(Loader2, { className: "animate-spin", size: 16 })) : (_jsx(Sparkles, { size: 16 })), children: isGenerating ? "Génération..." : "Générer" })] })] }) }))] }));
81
102
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lastbrain/module-ai",
3
- "version": "0.1.11",
3
+ "version": "0.1.14",
4
4
  "description": "Module de génération IA (texte et images) avec gestion de tokens pour LastBrain",
5
5
  "private": false,
6
6
  "type": "module",
@@ -34,12 +34,12 @@
34
34
  "@heroui/theme": "^2.4.23",
35
35
  "lucide-react": "^0.554.0",
36
36
  "next": "^16.0.4",
37
- "openai": "^4.76.0",
37
+ "openai": "^6.9.1",
38
38
  "react": "^19.0.0",
39
39
  "react-dom": "^19.0.0",
40
40
  "zod": "^3.23.8",
41
- "@lastbrain/core": "0.1.12",
42
- "@lastbrain/ui": "0.1.15"
41
+ "@lastbrain/core": "0.1.15",
42
+ "@lastbrain/ui": "0.1.18"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "next": ">=15.0.0"