@lastbrain/ai-ui-react 1.0.26 → 1.0.28

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.
@@ -1 +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"}
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,2CAshBtB"}
@@ -153,10 +153,40 @@ Analyse ces données et réponds de manière structurée et claire.`;
153
153
  }
154
154
  }
155
155
  catch (error) {
156
+ console.error("AiContextButton error:", error);
157
+ // Gestion spécifique des erreurs d'API
158
+ let errorMessage = "Erreur lors de l'analyse";
159
+ let errorCode;
160
+ if (error instanceof Error) {
161
+ try {
162
+ // Tenter de parser l'erreur JSON si c'est une erreur API
163
+ const errorData = JSON.parse(error.message);
164
+ if (errorData.error?.code === "INSUFFICIENT_TOKENS") {
165
+ errorMessage =
166
+ "Crédits insuffisants pour cette analyse. Veuillez recharger votre compte.";
167
+ errorCode = "INSUFFICIENT_TOKENS";
168
+ }
169
+ else if (errorData.error?.message) {
170
+ errorMessage = errorData.error.message;
171
+ errorCode = errorData.error.code;
172
+ }
173
+ }
174
+ catch {
175
+ // Si ce n'est pas du JSON, utiliser le message d'erreur direct
176
+ if (error.message.includes("INSUFFICIENT_TOKENS")) {
177
+ errorMessage =
178
+ "Crédits insuffisants pour cette analyse. Veuillez recharger votre compte.";
179
+ errorCode = "INSUFFICIENT_TOKENS";
180
+ }
181
+ else {
182
+ errorMessage = error.message;
183
+ }
184
+ }
185
+ }
156
186
  onToast?.({
157
187
  type: "error",
158
- message: "Erreur lors de l'analyse",
159
- code: error instanceof Error ? error.message : undefined,
188
+ message: errorMessage,
189
+ code: errorCode,
160
190
  });
161
191
  }
162
192
  finally {
@@ -217,7 +247,7 @@ Analyse ces données et réponds de manière structurée et claire.`;
217
247
  } }), _jsx("span", { style: { letterSpacing: "0.025em" }, children: "Analyse..." })] })) : (_jsx(_Fragment, { children: _jsx(Sparkle, { size: 18, style: {
218
248
  color: "white",
219
249
  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: {
250
+ } }) })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: models?.filter((m) => m.type === "language") || [], enableModelManagement: true, baseUrl: baseUrl, apiKey: apiKeyId }))] }), isResultOpen && analysisResult && (_jsx("div", { style: {
221
251
  position: "fixed",
222
252
  top: 0,
223
253
  left: 0,
@@ -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;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"}
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,2CA2ZpB"}
@@ -132,10 +132,29 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
132
132
  }
133
133
  }
134
134
  catch (error) {
135
+ let errorMessage = "Erreur lors de la génération de l'image";
136
+ let errorCode;
137
+ // Parse JSON error response for INSUFFICIENT_TOKENS
138
+ if (error instanceof Error) {
139
+ try {
140
+ const errorData = JSON.parse(error.message);
141
+ if (errorData.error?.code === "INSUFFICIENT_TOKENS") {
142
+ errorMessage = `Solde insuffisant. ${errorData.error.message || "Veuillez recharger votre compte."}`;
143
+ errorCode = "INSUFFICIENT_TOKENS";
144
+ }
145
+ }
146
+ catch {
147
+ // If parsing fails, check if error message contains the code
148
+ if (error.message.includes("INSUFFICIENT_TOKENS")) {
149
+ errorMessage = "Solde insuffisant. Veuillez recharger votre compte pour continuer.";
150
+ errorCode = "INSUFFICIENT_TOKENS";
151
+ }
152
+ }
153
+ }
135
154
  onToast?.({
136
155
  type: "error",
137
- message: "Erreur lors de la génération de l'image",
138
- code: error instanceof Error ? error.message : undefined,
156
+ message: errorMessage,
157
+ code: errorCode || (error instanceof Error ? error.message : undefined),
139
158
  });
140
159
  }
141
160
  finally {
@@ -33,5 +33,5 @@ export interface AiPromptPanelRenderProps {
33
33
  setShowAllModels?: (show: boolean) => void;
34
34
  onModelToggle?: (modelId: string, isActive: boolean) => Promise<void>;
35
35
  }
36
- export declare function AiPromptPanel({ isOpen, onClose, onSubmit, uiMode: _uiMode, models, sourceText, children, enableModelManagement, availableModels, userModels, onModelToggle, apiKey, baseUrl, }: AiPromptPanelProps): import("react/jsx-runtime").JSX.Element | null;
36
+ export declare function AiPromptPanel(props: AiPromptPanelProps): import("react/jsx-runtime").JSX.Element;
37
37
  //# sourceMappingURL=AiPromptPanel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AiPromptPanel.d.ts","sourceRoot":"","sources":["../../src/components/AiPromptPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAOvC,OAAO,EAAsB,KAAK,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAE/E,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,SAAS,CAAC;IAE1D,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,IAAI,CAAC;IAExB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE;AAED,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,OAAO,EACP,QAAQ,EACR,MAAM,EAAE,OAAiB,EACzB,MAAW,EACX,UAAU,EACV,QAAQ,EACR,qBAA6B,EAC7B,eAAoB,EACpB,UAAe,EACf,aAAa,EACb,MAAM,EACN,OAAO,GACR,EAAE,kBAAkB,kDAupCpB"}
1
+ {"version":3,"file":"AiPromptPanel.d.ts","sourceRoot":"","sources":["../../src/components/AiPromptPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAOvC,OAAO,EAAsB,KAAK,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAG/E,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,SAAS,CAAC;IAE1D,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,IAAI,CAAC;IAExB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,2CActD"}
@@ -5,7 +5,17 @@ import { BookOpen, Search, Sparkles, Star, Tag, Settings } from "lucide-react";
5
5
  import { aiStyles } from "../styles/inline";
6
6
  import { usePrompts, } from "../hooks/usePrompts";
7
7
  import { useModelManagement } from "../hooks/useModelManagement";
8
- export function AiPromptPanel({ isOpen, onClose, onSubmit, uiMode: _uiMode = "modal", models = [], sourceText, children, enableModelManagement = false, availableModels = [], userModels = [], onModelToggle, apiKey, baseUrl, }) {
8
+ import { AiProvider } from "../context/AiProvider";
9
+ export function AiPromptPanel(props) {
10
+ const { apiKey, baseUrl } = props;
11
+ // Si apiKey et baseUrl sont fournis, wrapper avec AiProvider
12
+ if (apiKey || baseUrl) {
13
+ return (_jsx(AiProvider, { baseUrl: baseUrl || "", apiKeyId: apiKey || "", children: _jsx(AiPromptPanelInternal, { ...props }) }));
14
+ }
15
+ // Sinon, utiliser le contexte existant
16
+ return _jsx(AiPromptPanelInternal, { ...props });
17
+ }
18
+ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "modal", models = [], sourceText, children, enableModelManagement = false, availableModels = [], userModels = [], onModelToggle, apiKey, baseUrl, }) {
9
19
  const [selectedModel, setSelectedModel] = useState("");
10
20
  const [prompt, setPrompt] = useState("");
11
21
  const [promptId, setPromptId] = useState(undefined);
@@ -31,9 +41,7 @@ export function AiPromptPanel({ isOpen, onClose, onSubmit, uiMode: _uiMode = "mo
31
41
  apiKey,
32
42
  baseUrl,
33
43
  category: "text", // Par défaut pour AiPromptPanel
34
- autoFetch: enableModelManagement &&
35
- availableModels.length === 0 &&
36
- (!!apiKey || !!baseUrl),
44
+ autoFetch: enableModelManagement && availableModels.length === 0,
37
45
  });
38
46
  // Utiliser soit les props externes soit la gestion automatique
39
47
  const effectiveAvailableModels = availableModels.length > 0
@@ -1 +1 @@
1
- {"version":3,"file":"useModelManagement.d.ts","sourceRoot":"","sources":["../../src/hooks/useModelManagement.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,0BAA0B,CAAC;AAQlC,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,yBAA0B,SAAQ,kBAAkB;IACnE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;CACjD;AAED,MAAM,WAAW,wBAAwB;IAEvC,eAAe,EAAE,OAAO,EAAE,CAAC;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAGrB,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAGvC,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5C,eAAe,EAAE,MAAM,OAAO,EAAE,CAAC;IACjC,iBAAiB,EAAE,MAAM,OAAO,EAAE,CAAC;CACpC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,yBAA8B,GACtC,wBAAwB,CA6M1B"}
1
+ {"version":3,"file":"useModelManagement.d.ts","sourceRoot":"","sources":["../../src/hooks/useModelManagement.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,0BAA0B,CAAC;AAQlC,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,yBAA0B,SAAQ,kBAAkB;IACnE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;CACjD;AAED,MAAM,WAAW,wBAAwB;IAEvC,eAAe,EAAE,OAAO,EAAE,CAAC;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAGrB,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAGvC,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5C,eAAe,EAAE,MAAM,OAAO,EAAE,CAAC;IACjC,iBAAiB,EAAE,MAAM,OAAO,EAAE,CAAC;CACpC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,yBAA8B,GACtC,wBAAwB,CAyN1B"}
@@ -1,4 +1,4 @@
1
- import { useState, useCallback, useEffect } from "react";
1
+ import { useState, useCallback, useEffect, useMemo } from "react";
2
2
  import { useAiContext } from "../context/AiProvider";
3
3
  import { toggleUserModel, getAvailableModels, getUserModels, } from "../utils/modelManagement";
4
4
  import { getCached, setCache, isRateLimited, getRateLimitResetTime, } from "../utils/cache";
@@ -12,12 +12,12 @@ export function useModelManagement(options = {}) {
12
12
  const [userModels, setUserModels] = useState([]);
13
13
  const [loading, setLoading] = useState(false);
14
14
  const [error, setError] = useState(null);
15
- // Utiliser baseUrl et apiKey du contexte
16
- const effectiveOptions = {
17
- ...apiOptions,
18
- baseUrl: apiOptions.baseUrl || baseUrl,
19
- apiKey: apiOptions.apiKey || apiKeyId,
20
- };
15
+ const [apiUnavailable, setApiUnavailable] = useState(false);
16
+ // Utiliser baseUrl et apiKey du contexte avec memoization
17
+ const effectiveOptions = useMemo(() => ({
18
+ baseUrl: options.baseUrl || baseUrl,
19
+ apiKey: options.apiKey || apiKeyId,
20
+ }), [options.baseUrl, options.apiKey, baseUrl, apiKeyId]);
21
21
  const refreshModels = useCallback(async () => {
22
22
  try {
23
23
  setLoading(true);
@@ -67,6 +67,11 @@ export function useModelManagement(options = {}) {
67
67
  }
68
68
  }, [category, effectiveOptions]);
69
69
  const refreshUserModels = useCallback(async () => {
70
+ // Si l'API est marquée comme indisponible, ne pas faire l'appel
71
+ if (apiUnavailable) {
72
+ console.log("[useModelManagement] Skipping user models fetch - API marked as unavailable");
73
+ return;
74
+ }
70
75
  try {
71
76
  setLoading(true);
72
77
  setError(null);
@@ -84,15 +89,17 @@ export function useModelManagement(options = {}) {
84
89
  const errorMessage = err instanceof Error ? err.message : "Erreur inconnue";
85
90
  console.error("[useModelManagement] Error fetching user models:", errorMessage);
86
91
  setError(errorMessage);
87
- // En cas d'erreur 404 (API non disponible), utiliser un array vide
92
+ // En cas d'erreur 404 (API non disponible), utiliser un array vide et ne pas retry
88
93
  if (errorMessage.includes("404") || errorMessage.includes("Not Found")) {
89
94
  setUserModels([]);
95
+ setApiUnavailable(true);
96
+ console.warn("[useModelManagement] User models API not available (404), disabling further requests");
90
97
  }
91
98
  }
92
99
  finally {
93
100
  setLoading(false);
94
101
  }
95
- }, [effectiveOptions]);
102
+ }, [effectiveOptions, apiUnavailable]);
96
103
  const toggleModel = useCallback(async (modelId, isActive) => {
97
104
  const currentlyActive = userModels.includes(modelId);
98
105
  const targetState = isActive !== undefined ? isActive : !currentlyActive;
@@ -129,7 +136,7 @@ export function useModelManagement(options = {}) {
129
136
  const getInactiveModels = useCallback(() => {
130
137
  return availableModels.filter((model) => !userModels.includes(model.id));
131
138
  }, [availableModels, userModels]);
132
- // Auto-fetch au mount - si autoFetch ET (apiKey OU baseUrl avec proxy externe)
139
+ // Auto-fetch au mount - si autoFetch ET (apiKey OU baseUrl avec proxy externe) ET API disponible
133
140
  useEffect(() => {
134
141
  const isExternalProxy = effectiveOptions.baseUrl &&
135
142
  effectiveOptions.baseUrl.includes("/api/lastbrain");
@@ -138,25 +145,30 @@ export function useModelManagement(options = {}) {
138
145
  hasApiKey: !!effectiveOptions.apiKey,
139
146
  hasBaseUrl: !!effectiveOptions.baseUrl,
140
147
  isExternalProxy,
148
+ apiUnavailable,
141
149
  apiKeyPreview: effectiveOptions.apiKey
142
150
  ? effectiveOptions.apiKey.substring(0, 10) + "..."
143
151
  : "none",
144
152
  baseUrl: effectiveOptions.baseUrl || "none",
145
153
  });
146
- // Auto-fetch si autoFetch est activé ET qu'on a soit une apiKey soit un proxy externe
147
- if (autoFetch && (effectiveOptions.apiKey || isExternalProxy)) {
154
+ // Auto-fetch si autoFetch est activé ET qu'on a soit une apiKey soit un proxy externe ET API disponible
155
+ if (autoFetch && !apiUnavailable && (effectiveOptions.apiKey || isExternalProxy)) {
148
156
  console.log("[useModelManagement] Starting auto-fetch...");
149
157
  Promise.all([refreshModels(), refreshUserModels()]);
150
158
  }
151
159
  else if (autoFetch && !effectiveOptions.apiKey && !isExternalProxy) {
152
160
  console.warn("[useModelManagement] autoFetch is enabled but no apiKey or baseUrl provided. Skipping automatic fetch.");
153
161
  }
162
+ else if (autoFetch && apiUnavailable) {
163
+ console.log("[useModelManagement] autoFetch skipped - API marked as unavailable");
164
+ }
154
165
  }, [
155
166
  autoFetch,
156
167
  refreshModels,
157
168
  refreshUserModels,
158
169
  effectiveOptions.apiKey,
159
170
  effectiveOptions.baseUrl,
171
+ apiUnavailable,
160
172
  ]);
161
173
  return {
162
174
  availableModels,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lastbrain/ai-ui-react",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "description": "Headless React components for LastBrain AI UI Kit",
5
5
  "private": false,
6
6
  "type": "module",
@@ -221,10 +221,40 @@ Analyse ces données et réponds de manière structurée et claire.`;
221
221
  });
222
222
  }
223
223
  } catch (error) {
224
+ console.error("AiContextButton error:", error);
225
+
226
+ // Gestion spécifique des erreurs d'API
227
+ let errorMessage = "Erreur lors de l'analyse";
228
+ let errorCode: string | undefined;
229
+
230
+ if (error instanceof Error) {
231
+ try {
232
+ // Tenter de parser l'erreur JSON si c'est une erreur API
233
+ const errorData = JSON.parse(error.message);
234
+ if (errorData.error?.code === "INSUFFICIENT_TOKENS") {
235
+ errorMessage =
236
+ "Crédits insuffisants pour cette analyse. Veuillez recharger votre compte.";
237
+ errorCode = "INSUFFICIENT_TOKENS";
238
+ } else if (errorData.error?.message) {
239
+ errorMessage = errorData.error.message;
240
+ errorCode = errorData.error.code;
241
+ }
242
+ } catch {
243
+ // Si ce n'est pas du JSON, utiliser le message d'erreur direct
244
+ if (error.message.includes("INSUFFICIENT_TOKENS")) {
245
+ errorMessage =
246
+ "Crédits insuffisants pour cette analyse. Veuillez recharger votre compte.";
247
+ errorCode = "INSUFFICIENT_TOKENS";
248
+ } else {
249
+ errorMessage = error.message;
250
+ }
251
+ }
252
+ }
253
+
224
254
  onToast?.({
225
255
  type: "error",
226
- message: "Erreur lors de l'analyse",
227
- code: error instanceof Error ? error.message : undefined,
256
+ message: errorMessage,
257
+ code: errorCode,
228
258
  });
229
259
  } finally {
230
260
  setIsOpen(false);
@@ -328,6 +358,8 @@ Analyse ces données et réponds de manière structurée et claire.`;
328
358
  uiMode={uiMode}
329
359
  models={models?.filter((m) => m.type === "language") || []}
330
360
  enableModelManagement={true}
361
+ baseUrl={baseUrl}
362
+ apiKey={apiKeyId}
331
363
  />
332
364
  )}
333
365
  </div>
@@ -193,10 +193,30 @@ export function AiImageButton({
193
193
  });
194
194
  }
195
195
  } catch (error) {
196
+ let errorMessage = "Erreur lors de la génération de l'image";
197
+ let errorCode: string | undefined;
198
+
199
+ // Parse JSON error response for INSUFFICIENT_TOKENS
200
+ if (error instanceof Error) {
201
+ try {
202
+ const errorData = JSON.parse(error.message);
203
+ if (errorData.error?.code === "INSUFFICIENT_TOKENS") {
204
+ errorMessage = `Solde insuffisant. ${errorData.error.message || "Veuillez recharger votre compte."}`;
205
+ errorCode = "INSUFFICIENT_TOKENS";
206
+ }
207
+ } catch {
208
+ // If parsing fails, check if error message contains the code
209
+ if (error.message.includes("INSUFFICIENT_TOKENS")) {
210
+ errorMessage = "Solde insuffisant. Veuillez recharger votre compte pour continuer.";
211
+ errorCode = "INSUFFICIENT_TOKENS";
212
+ }
213
+ }
214
+ }
215
+
196
216
  onToast?.({
197
217
  type: "error",
198
- message: "Erreur lors de la génération de l'image",
199
- code: error instanceof Error ? error.message : undefined,
218
+ message: errorMessage,
219
+ code: errorCode || (error instanceof Error ? error.message : undefined),
200
220
  });
201
221
  } finally {
202
222
  setIsOpen(false);
@@ -17,6 +17,7 @@ import {
17
17
  type PublicPrompt,
18
18
  } from "../hooks/usePrompts";
19
19
  import { useModelManagement, type AIModel } from "../hooks/useModelManagement";
20
+ import { AiProvider } from "../context/AiProvider";
20
21
 
21
22
  export interface AiPromptPanelProps {
22
23
  isOpen: boolean;
@@ -53,7 +54,23 @@ export interface AiPromptPanelRenderProps {
53
54
  onModelToggle?: (modelId: string, isActive: boolean) => Promise<void>;
54
55
  }
55
56
 
56
- export function AiPromptPanel({
57
+ export function AiPromptPanel(props: AiPromptPanelProps) {
58
+ const { apiKey, baseUrl } = props;
59
+
60
+ // Si apiKey et baseUrl sont fournis, wrapper avec AiProvider
61
+ if (apiKey || baseUrl) {
62
+ return (
63
+ <AiProvider baseUrl={baseUrl || ""} apiKeyId={apiKey || ""}>
64
+ <AiPromptPanelInternal {...props} />
65
+ </AiProvider>
66
+ );
67
+ }
68
+
69
+ // Sinon, utiliser le contexte existant
70
+ return <AiPromptPanelInternal {...props} />;
71
+ }
72
+
73
+ function AiPromptPanelInternal({
57
74
  isOpen,
58
75
  onClose,
59
76
  onSubmit,
@@ -101,10 +118,7 @@ export function AiPromptPanel({
101
118
  apiKey,
102
119
  baseUrl,
103
120
  category: "text", // Par défaut pour AiPromptPanel
104
- autoFetch:
105
- enableModelManagement &&
106
- availableModels.length === 0 &&
107
- (!!apiKey || !!baseUrl),
121
+ autoFetch: enableModelManagement && availableModels.length === 0,
108
122
  });
109
123
 
110
124
  // Utiliser soit les props externes soit la gestion automatique
@@ -1,4 +1,4 @@
1
- import { useState, useCallback, useEffect } from "react";
1
+ import { useState, useCallback, useEffect, useMemo } from "react";
2
2
  import { useAiContext } from "../context/AiProvider";
3
3
  import {
4
4
  toggleUserModel,
@@ -60,13 +60,13 @@ export function useModelManagement(
60
60
  const [userModels, setUserModels] = useState<string[]>([]);
61
61
  const [loading, setLoading] = useState(false);
62
62
  const [error, setError] = useState<string | null>(null);
63
+ const [apiUnavailable, setApiUnavailable] = useState(false);
63
64
 
64
- // Utiliser baseUrl et apiKey du contexte
65
- const effectiveOptions = {
66
- ...apiOptions,
67
- baseUrl: apiOptions.baseUrl || baseUrl,
68
- apiKey: apiOptions.apiKey || apiKeyId,
69
- };
65
+ // Utiliser baseUrl et apiKey du contexte avec memoization
66
+ const effectiveOptions = useMemo(() => ({
67
+ baseUrl: options.baseUrl || baseUrl,
68
+ apiKey: options.apiKey || apiKeyId,
69
+ }), [options.baseUrl, options.apiKey, baseUrl, apiKeyId]);
70
70
 
71
71
  const refreshModels = useCallback(async () => {
72
72
  try {
@@ -134,6 +134,12 @@ export function useModelManagement(
134
134
  }, [category, effectiveOptions]);
135
135
 
136
136
  const refreshUserModels = useCallback(async () => {
137
+ // Si l'API est marquée comme indisponible, ne pas faire l'appel
138
+ if (apiUnavailable) {
139
+ console.log("[useModelManagement] Skipping user models fetch - API marked as unavailable");
140
+ return;
141
+ }
142
+
137
143
  try {
138
144
  setLoading(true);
139
145
  setError(null);
@@ -156,14 +162,16 @@ export function useModelManagement(
156
162
  errorMessage
157
163
  );
158
164
  setError(errorMessage);
159
- // En cas d'erreur 404 (API non disponible), utiliser un array vide
165
+ // En cas d'erreur 404 (API non disponible), utiliser un array vide et ne pas retry
160
166
  if (errorMessage.includes("404") || errorMessage.includes("Not Found")) {
161
167
  setUserModels([]);
168
+ setApiUnavailable(true);
169
+ console.warn("[useModelManagement] User models API not available (404), disabling further requests");
162
170
  }
163
171
  } finally {
164
172
  setLoading(false);
165
173
  }
166
- }, [effectiveOptions]);
174
+ }, [effectiveOptions, apiUnavailable]);
167
175
 
168
176
  const toggleModel = useCallback(
169
177
  async (modelId: string, isActive?: boolean) => {
@@ -211,7 +219,7 @@ export function useModelManagement(
211
219
  return availableModels.filter((model) => !userModels.includes(model.id));
212
220
  }, [availableModels, userModels]);
213
221
 
214
- // Auto-fetch au mount - si autoFetch ET (apiKey OU baseUrl avec proxy externe)
222
+ // Auto-fetch au mount - si autoFetch ET (apiKey OU baseUrl avec proxy externe) ET API disponible
215
223
  useEffect(() => {
216
224
  const isExternalProxy =
217
225
  effectiveOptions.baseUrl &&
@@ -222,20 +230,23 @@ export function useModelManagement(
222
230
  hasApiKey: !!effectiveOptions.apiKey,
223
231
  hasBaseUrl: !!effectiveOptions.baseUrl,
224
232
  isExternalProxy,
233
+ apiUnavailable,
225
234
  apiKeyPreview: effectiveOptions.apiKey
226
235
  ? effectiveOptions.apiKey.substring(0, 10) + "..."
227
236
  : "none",
228
237
  baseUrl: effectiveOptions.baseUrl || "none",
229
238
  });
230
239
 
231
- // Auto-fetch si autoFetch est activé ET qu'on a soit une apiKey soit un proxy externe
232
- if (autoFetch && (effectiveOptions.apiKey || isExternalProxy)) {
240
+ // Auto-fetch si autoFetch est activé ET qu'on a soit une apiKey soit un proxy externe ET API disponible
241
+ if (autoFetch && !apiUnavailable && (effectiveOptions.apiKey || isExternalProxy)) {
233
242
  console.log("[useModelManagement] Starting auto-fetch...");
234
243
  Promise.all([refreshModels(), refreshUserModels()]);
235
244
  } else if (autoFetch && !effectiveOptions.apiKey && !isExternalProxy) {
236
245
  console.warn(
237
246
  "[useModelManagement] autoFetch is enabled but no apiKey or baseUrl provided. Skipping automatic fetch."
238
247
  );
248
+ } else if (autoFetch && apiUnavailable) {
249
+ console.log("[useModelManagement] autoFetch skipped - API marked as unavailable");
239
250
  }
240
251
  }, [
241
252
  autoFetch,
@@ -243,6 +254,7 @@ export function useModelManagement(
243
254
  refreshUserModels,
244
255
  effectiveOptions.apiKey,
245
256
  effectiveOptions.baseUrl,
257
+ apiUnavailable,
246
258
  ]);
247
259
 
248
260
  return {