@lastbrain/ai-ui-react 1.0.75 → 1.0.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/AiContextButton.d.ts +1 -1
- package/dist/components/AiContextButton.d.ts.map +1 -1
- package/dist/components/AiImageButton.js +2 -2
- package/dist/components/AiInput.d.ts +1 -1
- package/dist/components/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +4 -4
- package/dist/components/AiPromptPanel.js +30 -6
- package/dist/components/AiSelect.d.ts +1 -1
- package/dist/components/AiSelect.d.ts.map +1 -1
- package/dist/components/AiSelect.js +1 -1
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +2 -2
- package/dist/components/AiTextarea.d.ts +2 -1
- package/dist/components/AiTextarea.d.ts.map +1 -1
- package/dist/components/AiTextarea.js +16 -6
- package/dist/components/ErrorToast.js +3 -3
- package/dist/components/LBKeyPicker.js +9 -9
- package/dist/components/LBSigninModal.d.ts.map +1 -1
- package/dist/components/UsageToast.d.ts +3 -3
- package/dist/components/UsageToast.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +12 -5
- package/dist/examples/AiImageGenerator.js +1 -1
- package/dist/hooks/useAiStatus.d.ts.map +1 -1
- package/dist/hooks/useAiStatus.js +43 -5
- package/dist/styles.css +3 -3
- package/dist/utils/errorHandler.d.ts +2 -2
- package/dist/utils/errorHandler.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/AiContextButton.tsx +1 -1
- package/src/components/AiImageButton.tsx +2 -2
- package/src/components/AiInput.tsx +9 -4
- package/src/components/AiPromptPanel.tsx +31 -6
- package/src/components/AiSelect.tsx +2 -2
- package/src/components/AiStatusButton.tsx +11 -8
- package/src/components/AiTextarea.tsx +25 -9
- package/src/components/ErrorToast.tsx +3 -3
- package/src/components/LBKeyPicker.tsx +10 -10
- package/src/components/LBSigninModal.tsx +2 -1
- package/src/components/UsageToast.tsx +4 -4
- package/src/context/LBAuthProvider.tsx +12 -5
- package/src/examples/AiImageGenerator.tsx +1 -1
- package/src/hooks/useAiStatus.ts +47 -5
- package/src/styles.css +3 -3
- package/src/utils/errorHandler.ts +3 -3
- package/src/utils/modelManagement.ts +3 -3
|
@@ -3,7 +3,7 @@ import { type ButtonHTMLAttributes } from "react";
|
|
|
3
3
|
import type { BaseAiProps } from "../types";
|
|
4
4
|
import type { AiRadius, AiSize, AiVariant } from "../types";
|
|
5
5
|
type ContextData = string | number | boolean | object | unknown[] | {
|
|
6
|
-
[key: string]:
|
|
6
|
+
[key: string]: any;
|
|
7
7
|
};
|
|
8
8
|
export interface AiContextButtonProps extends Omit<BaseAiProps, "onValue" | "type">, Omit<ButtonHTMLAttributes<HTMLButtonElement>, "baseUrl" | "apiKeyId"> {
|
|
9
9
|
contextData: ContextData;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiContextButton.d.ts","sourceRoot":"","sources":["../../src/components/AiContextButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAY5D,KAAK,WAAW,GACZ,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,OAAO,EAAE,GACT;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,
|
|
1
|
+
{"version":3,"file":"AiContextButton.d.ts","sourceRoot":"","sources":["../../src/components/AiContextButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAY5D,KAAK,WAAW,GACZ,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,OAAO,EAAE,GACT;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,CAAC;AAE3B,MAAM,WAAW,oBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACvE,WAAW,EAAE,WAAW,CAAC;IACzB,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;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,IAAW,EACX,MAAe,EACf,OAAmB,EACnB,OAAO,EAAE,QAAQ,EACjB,GAAG,WAAW,EACf,EAAE,oBAAoB,2CA6StB"}
|
|
@@ -18,7 +18,7 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
|
|
|
18
18
|
const [isOpen, setIsOpen] = useState(false);
|
|
19
19
|
const [showAuthModal, setShowAuthModal] = useState(false);
|
|
20
20
|
const [generatedImage, setGeneratedImage] = useState(null);
|
|
21
|
-
const { showUsageToast
|
|
21
|
+
const { showUsageToast } = useUsageToast();
|
|
22
22
|
const { showErrorToast, errorData, errorKey, clearError } = useErrorToast();
|
|
23
23
|
// Rendre l'authentification optionnelle
|
|
24
24
|
let lbStatus;
|
|
@@ -66,7 +66,7 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
|
|
|
66
66
|
message: t("ai.image.savedSuccess", "Image saved"),
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
|
-
catch
|
|
69
|
+
catch {
|
|
70
70
|
onToast?.({
|
|
71
71
|
type: "error",
|
|
72
72
|
message: t("ai.image.saveError", "Error while saving"),
|
|
@@ -7,5 +7,5 @@ export interface AiInputProps extends Omit<BaseAiProps, "type">, Omit<InputHTMLA
|
|
|
7
7
|
size?: AiSize;
|
|
8
8
|
radius?: AiRadius;
|
|
9
9
|
}
|
|
10
|
-
export declare function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode, size, radius, context, model, prompt, editMode, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...inputProps }: AiInputProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode, size, radius, context, model, prompt, editMode: _editMode, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...inputProps }: AiInputProps): import("react/jsx-runtime").JSX.Element;
|
|
11
11
|
//# sourceMappingURL=AiInput.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiInput.d.ts","sourceRoot":"","sources":["../../src/components/AiInput.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAc,EAAoB,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAE1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAY9D,MAAM,WAAW,YACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IACjE,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,wBAAgB,OAAO,CAAC,EACtB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,IAAW,EACX,MAAe,EACf,OAAO,EACP,KAAK,EACL,MAAM,EACN,
|
|
1
|
+
{"version":3,"file":"AiInput.d.ts","sourceRoot":"","sources":["../../src/components/AiInput.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAc,EAAoB,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAE1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAY9D,MAAM,WAAW,YACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IACjE,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,wBAAgB,OAAO,CAAC,EACtB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,IAAW,EACX,MAAe,EACf,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAQ,EAAE,SAAiB,EAC3B,qBAA4B,EAC5B,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,UAAU,EACd,EAAE,YAAY,2CA8Nd"}
|
|
@@ -13,13 +13,13 @@ import { LBSigninModal } from "./LBSigninModal";
|
|
|
13
13
|
import { useAiContext } from "../context/AiProvider";
|
|
14
14
|
import { useI18n } from "../context/I18nContext";
|
|
15
15
|
import { useLoadingTimer } from "../hooks/useLoadingTimer";
|
|
16
|
-
export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode = "modal", size = "md", radius = "full", context, model, prompt, editMode = false, enableModelManagement = true, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...inputProps }) {
|
|
16
|
+
export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode = "modal", size = "md", radius = "full", context, model, prompt, editMode: _editMode = false, enableModelManagement = true, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...inputProps }) {
|
|
17
17
|
const { t } = useI18n();
|
|
18
18
|
const [isOpen, setIsOpen] = useState(false);
|
|
19
19
|
const [showAuthModal, setShowAuthModal] = useState(false);
|
|
20
20
|
const [inputValue, setInputValue] = useState(inputProps.value?.toString() || inputProps.defaultValue?.toString() || "");
|
|
21
21
|
const inputRef = useRef(null);
|
|
22
|
-
const { showUsageToast, toastData, toastKey, clearToast } = useUsageToast();
|
|
22
|
+
const { showUsageToast: _showUsageToast, toastData, toastKey, clearToast, } = useUsageToast();
|
|
23
23
|
// Rendre l'authentification optionnelle
|
|
24
24
|
let lbStatus;
|
|
25
25
|
try {
|
|
@@ -43,7 +43,7 @@ export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode =
|
|
|
43
43
|
}
|
|
44
44
|
const baseUrl = propBaseUrl ?? ctxBaseUrl;
|
|
45
45
|
const apiKeyId = propApiKeyId ?? ctxApiKeyId;
|
|
46
|
-
const { models } = useAiModels({
|
|
46
|
+
const { models: _models } = useAiModels({
|
|
47
47
|
baseUrl,
|
|
48
48
|
apiKeyId,
|
|
49
49
|
modelType: "text-or-language",
|
|
@@ -63,7 +63,7 @@ export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode =
|
|
|
63
63
|
const handleClosePanel = () => {
|
|
64
64
|
setIsOpen(false);
|
|
65
65
|
};
|
|
66
|
-
const handleSubmit = async (selectedModel, selectedPrompt,
|
|
66
|
+
const handleSubmit = async (selectedModel, selectedPrompt, _promptId) => {
|
|
67
67
|
try {
|
|
68
68
|
const resolvedContext = inputValue || context || undefined;
|
|
69
69
|
const hasContext = Boolean(resolvedContext && String(resolvedContext).trim());
|
|
@@ -32,7 +32,7 @@ export function AiPromptPanel(props) {
|
|
|
32
32
|
// Sinon, utiliser le contexte existant
|
|
33
33
|
return _jsx(AiPromptPanelInternal, { ...props });
|
|
34
34
|
}
|
|
35
|
-
function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode
|
|
35
|
+
function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode = "modal", models = [], sourceText, children, enableModelManagement = true, modelCategory = "text", availableModels = [], userModels = [], onModelToggle, apiKey, baseUrl, showOnlyUserModels = false, }) {
|
|
36
36
|
const { t } = useI18n();
|
|
37
37
|
const [selectedModel, setSelectedModel] = useState("");
|
|
38
38
|
const [prompt, setPrompt] = useState("");
|
|
@@ -269,21 +269,45 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
269
269
|
if (!portalRoot) {
|
|
270
270
|
return null;
|
|
271
271
|
}
|
|
272
|
+
const isDrawer = uiMode === "drawer";
|
|
273
|
+
const panelContainerStyle = isDrawer
|
|
274
|
+
? {
|
|
275
|
+
...aiStyles.modal,
|
|
276
|
+
alignItems: "stretch",
|
|
277
|
+
justifyContent: "flex-end",
|
|
278
|
+
padding: "0",
|
|
279
|
+
}
|
|
280
|
+
: aiStyles.modal;
|
|
281
|
+
const panelContentStyle = isDrawer
|
|
282
|
+
? {
|
|
283
|
+
...aiStyles.modalContent,
|
|
284
|
+
width: "min(92vw, 640px)",
|
|
285
|
+
maxWidth: "640px",
|
|
286
|
+
maxHeight: "100vh",
|
|
287
|
+
height: "100vh",
|
|
288
|
+
borderRadius: "16px 0 0 16px",
|
|
289
|
+
}
|
|
290
|
+
: aiStyles.modalContent;
|
|
272
291
|
if (children) {
|
|
273
|
-
return createPortal(_jsx("div", { style:
|
|
292
|
+
return createPortal(_jsx("div", { style: panelContainerStyle, onKeyDown: handleKeyDown, children: children(renderProps) }), portalRoot);
|
|
274
293
|
}
|
|
275
|
-
return createPortal(_jsxs("div", { style:
|
|
294
|
+
return createPortal(_jsxs("div", { style: panelContainerStyle, onKeyDown: handleKeyDown, children: [_jsx("div", { style: {
|
|
276
295
|
...aiStyles.modalOverlay,
|
|
277
296
|
opacity: isClosing ? 0 : 1,
|
|
278
297
|
transition: "opacity 200ms ease",
|
|
279
298
|
}, onClick: handleClose }), _jsxs("div", { style: {
|
|
280
|
-
...
|
|
299
|
+
...panelContentStyle,
|
|
281
300
|
opacity: isClosing ? 0 : 1,
|
|
282
|
-
transform: isClosing
|
|
301
|
+
transform: isClosing
|
|
302
|
+
? isDrawer
|
|
303
|
+
? "translateX(16px)"
|
|
304
|
+
: "translateY(12px)"
|
|
305
|
+
: isDrawer
|
|
306
|
+
? "translateX(0)"
|
|
307
|
+
: "translateY(0)",
|
|
283
308
|
transition: "opacity 200ms ease, transform 200ms ease",
|
|
284
309
|
display: "flex",
|
|
285
310
|
flexDirection: "column",
|
|
286
|
-
maxHeight: "85vh",
|
|
287
311
|
overflow: "hidden",
|
|
288
312
|
}, children: [isGenerating && (_jsxs("div", { style: {
|
|
289
313
|
position: "absolute",
|
|
@@ -7,5 +7,5 @@ export interface AiSelectProps extends Omit<BaseAiProps, "type">, Omit<SelectHTM
|
|
|
7
7
|
size?: AiSize;
|
|
8
8
|
radius?: AiRadius;
|
|
9
9
|
}
|
|
10
|
-
export declare function AiSelect({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode, size, radius, context, model, prompt, storeOutputs, artifactTitle, onValue, onToast, disabled, className, children, ...selectProps }: AiSelectProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare function AiSelect({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode, size, radius, context, model: _model, prompt: _prompt, storeOutputs, artifactTitle, onValue, onToast, disabled, className, children, ...selectProps }: AiSelectProps): import("react/jsx-runtime").JSX.Element;
|
|
11
11
|
//# sourceMappingURL=AiSelect.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiSelect.d.ts","sourceRoot":"","sources":["../../src/components/AiSelect.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAEnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAW9D,MAAM,WAAW,aACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IACnE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,IAAW,EACX,MAAe,EACf,OAAO,EACP,KAAK,
|
|
1
|
+
{"version":3,"file":"AiSelect.d.ts","sourceRoot":"","sources":["../../src/components/AiSelect.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAEnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAW9D,MAAM,WAAW,aACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IACnE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,IAAW,EACX,MAAe,EACf,OAAO,EACP,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,GAAG,WAAW,EACf,EAAE,aAAa,2CAgJf"}
|
|
@@ -12,7 +12,7 @@ import { handleAIError } from "../utils/errorHandler";
|
|
|
12
12
|
import { useLB } from "../context/LBAuthProvider";
|
|
13
13
|
import { useAiContext } from "../context/AiProvider";
|
|
14
14
|
import { useI18n } from "../context/I18nContext";
|
|
15
|
-
export function AiSelect({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode = "modal", size = "md", radius = "full", context, model, prompt, storeOutputs, artifactTitle, onValue, onToast, disabled, className, children, ...selectProps }) {
|
|
15
|
+
export function AiSelect({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode = "modal", size = "md", radius = "full", context, model: _model, prompt: _prompt, storeOutputs, artifactTitle, onValue, onToast, disabled, className, children, ...selectProps }) {
|
|
16
16
|
const { t } = useI18n();
|
|
17
17
|
const [isOpen, setIsOpen] = useState(false);
|
|
18
18
|
const [showAuthModal, setShowAuthModal] = useState(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiStatusButton.d.ts","sourceRoot":"","sources":["../../src/components/AiStatusButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,QAAQ,EAAU,MAAM,uBAAuB,CAAC;AAuB9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGjD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAmHD,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,OAAe,EACf,SAAc,EACd,IAAW,EACX,MAAe,GAChB,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"AiStatusButton.d.ts","sourceRoot":"","sources":["../../src/components/AiStatusButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,QAAQ,EAAU,MAAM,uBAAuB,CAAC;AAuB9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGjD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAmHD,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,OAAe,EACf,SAAc,EACd,IAAW,EACX,MAAe,GAChB,EAAE,mBAAmB,2CA8iBrB"}
|
|
@@ -113,13 +113,13 @@ export function AiStatusButton({ status, loading = false, className = "", size =
|
|
|
113
113
|
const buttonRef = useRef(null);
|
|
114
114
|
const tooltipRef = useRef(null);
|
|
115
115
|
const canPortal = typeof document !== "undefined";
|
|
116
|
-
const effectiveStatus = lbStatus === "ready"
|
|
116
|
+
const effectiveStatus = useMemo(() => lbStatus === "ready"
|
|
117
117
|
? {
|
|
118
118
|
...(lbApiStatus || {}),
|
|
119
119
|
...(lbBasicStatus || {}),
|
|
120
120
|
storage: lbStorageStatus?.storage || lbApiStatus?.storage,
|
|
121
121
|
}
|
|
122
|
-
: status || null;
|
|
122
|
+
: status || null, [lbApiStatus, lbBasicStatus, lbStatus, lbStorageStatus, status]);
|
|
123
123
|
const hasApiKeySelected = Boolean(effectiveStatus?.apiKey?.id ||
|
|
124
124
|
effectiveStatus?.api_key?.id ||
|
|
125
125
|
lbSelectedKey?.id);
|
|
@@ -5,6 +5,7 @@ export interface AiTextareaProps extends Omit<BaseAiProps, "type">, Omit<Textare
|
|
|
5
5
|
uiMode?: "modal" | "drawer";
|
|
6
6
|
size?: AiSize;
|
|
7
7
|
radius?: AiRadius;
|
|
8
|
+
onPanelOpenChange?: (isOpen: boolean) => void;
|
|
8
9
|
}
|
|
9
|
-
export declare function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode, size, radius, context, model, prompt, editMode, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...textareaProps }: AiTextareaProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode, size, radius, onPanelOpenChange, context, model, prompt, editMode: _editMode, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...textareaProps }: AiTextareaProps): import("react/jsx-runtime").JSX.Element;
|
|
10
11
|
//# sourceMappingURL=AiTextarea.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiTextarea.d.ts","sourceRoot":"","sources":["../../src/components/AiTextarea.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAc,EAIZ,KAAK,sBAAsB,EAC5B,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAY9D,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;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"AiTextarea.d.ts","sourceRoot":"","sources":["../../src/components/AiTextarea.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAc,EAIZ,KAAK,sBAAsB,EAC5B,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAY9D,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;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/C;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,IAAW,EACX,MAAa,EACb,iBAAiB,EACjB,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAQ,EAAE,SAAiB,EAC3B,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,aAAa,EACjB,EAAE,eAAe,2CA6PjB"}
|
|
@@ -13,7 +13,7 @@ import { LBSigninModal } from "./LBSigninModal";
|
|
|
13
13
|
import { useAiContext } from "../context/AiProvider";
|
|
14
14
|
import { useI18n } from "../context/I18nContext";
|
|
15
15
|
import { useLoadingTimer } from "../hooks/useLoadingTimer";
|
|
16
|
-
export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode = "modal", size = "md", radius = "lg", context, model, prompt, editMode = false, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...textareaProps }) {
|
|
16
|
+
export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode = "modal", size = "md", radius = "lg", onPanelOpenChange, context, model, prompt, editMode: _editMode = false, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...textareaProps }) {
|
|
17
17
|
const { t } = useI18n();
|
|
18
18
|
const [isOpen, setIsOpen] = useState(false);
|
|
19
19
|
const [showAuthModal, setShowAuthModal] = useState(false);
|
|
@@ -24,13 +24,16 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
24
24
|
const { showUsageToast, toastData, toastKey, clearToast } = useUsageToast();
|
|
25
25
|
// Rendre l'authentification optionnelle
|
|
26
26
|
let lbStatus;
|
|
27
|
+
let hasLBProvider = false;
|
|
27
28
|
try {
|
|
28
29
|
const lbContext = useLB();
|
|
29
30
|
lbStatus = lbContext.status;
|
|
31
|
+
hasLBProvider = true;
|
|
30
32
|
}
|
|
31
33
|
catch {
|
|
32
34
|
// LBProvider n'est pas disponible, ignorer
|
|
33
35
|
lbStatus = undefined;
|
|
36
|
+
hasLBProvider = false;
|
|
34
37
|
}
|
|
35
38
|
let ctxBaseUrl;
|
|
36
39
|
let ctxApiKeyId;
|
|
@@ -45,7 +48,9 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
45
48
|
}
|
|
46
49
|
const baseUrl = propBaseUrl ?? ctxBaseUrl;
|
|
47
50
|
const apiKeyId = propApiKeyId ?? ctxApiKeyId;
|
|
48
|
-
const
|
|
51
|
+
const supportsSessionAuth = typeof baseUrl === "string" &&
|
|
52
|
+
(baseUrl.includes("/api/ai") || baseUrl.includes("/api/lastbrain"));
|
|
53
|
+
const { models: _models } = useAiModels({
|
|
49
54
|
baseUrl,
|
|
50
55
|
apiKeyId,
|
|
51
56
|
modelType: "text-or-language",
|
|
@@ -53,7 +58,9 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
53
58
|
const { generateText, loading } = useAiCallText({ baseUrl, apiKeyId });
|
|
54
59
|
const { formatted: loadingElapsed } = useLoadingTimer(loading);
|
|
55
60
|
const hasConfiguration = Boolean(model && prompt);
|
|
56
|
-
const isAuthReady =
|
|
61
|
+
const isAuthReady = supportsSessionAuth ||
|
|
62
|
+
lbStatus === "ready" ||
|
|
63
|
+
Boolean(process.env.LB_API_KEY);
|
|
57
64
|
const shouldShowSparkles = isAuthReady && !disabled;
|
|
58
65
|
const handleOpenPanel = () => {
|
|
59
66
|
if (!isAuthReady) {
|
|
@@ -61,11 +68,13 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
61
68
|
return;
|
|
62
69
|
}
|
|
63
70
|
setIsOpen(true);
|
|
71
|
+
onPanelOpenChange?.(true);
|
|
64
72
|
};
|
|
65
73
|
const handleClosePanel = () => {
|
|
66
74
|
setIsOpen(false);
|
|
75
|
+
onPanelOpenChange?.(false);
|
|
67
76
|
};
|
|
68
|
-
const handleSubmit = async (selectedModel, selectedPrompt,
|
|
77
|
+
const handleSubmit = async (selectedModel, selectedPrompt, _promptId) => {
|
|
69
78
|
try {
|
|
70
79
|
const resolvedContext = textareaValue || context || undefined;
|
|
71
80
|
const hasContext = Boolean(resolvedContext && String(resolvedContext).trim());
|
|
@@ -93,7 +102,7 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
93
102
|
showUsageToast(result);
|
|
94
103
|
}
|
|
95
104
|
}
|
|
96
|
-
catch
|
|
105
|
+
catch {
|
|
97
106
|
onToast?.({
|
|
98
107
|
type: "error",
|
|
99
108
|
message: t("ai.generationError", "Failed to generate text"),
|
|
@@ -101,6 +110,7 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
101
110
|
}
|
|
102
111
|
finally {
|
|
103
112
|
setIsOpen(false);
|
|
113
|
+
onPanelOpenChange?.(false);
|
|
104
114
|
}
|
|
105
115
|
};
|
|
106
116
|
const handleQuickGenerate = async () => {
|
|
@@ -162,5 +172,5 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
162
172
|
? t("auth.required", "Authentication required")
|
|
163
173
|
: hasConfiguration
|
|
164
174
|
? t("ai.generate", "Generate with AI")
|
|
165
|
-
: t("ai.setup", "Setup AI"), children: loading ? (_jsx(Loader2, { size: 16, className: "ai-spinner" })) : shouldShowSparkles ? (_jsx(Sparkles, { size: 16 })) : (_jsx(Lock, { size: 16 })) })] }), loading ? (_jsx("span", { className: "ai-control-timer", children: t("ai.loading.elapsed", "{seconds}", { seconds: loadingElapsed }) })) : null, isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: [], modelCategory: "text", sourceText: textareaValue || undefined, baseUrl: baseUrl, apiKey: apiKeyId, enableModelManagement: enableModelManagement, showOnlyUserModels: true })), Boolean(toastData) && (_jsx(UsageToast, { result: toastData, position: "bottom-right", onComplete: clearToast }, toastKey)), _jsx(LBSigninModal, { isOpen: showAuthModal, onClose: () => setShowAuthModal(false) })] }));
|
|
175
|
+
: t("ai.setup", "Setup AI"), children: loading ? (_jsx(Loader2, { size: 16, className: "ai-spinner" })) : shouldShowSparkles ? (_jsx(Sparkles, { size: 16 })) : (_jsx(Lock, { size: 16 })) })] }), loading ? (_jsx("span", { className: "ai-control-timer", children: t("ai.loading.elapsed", "{seconds}", { seconds: loadingElapsed }) })) : null, isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: [], modelCategory: "text", sourceText: textareaValue || undefined, baseUrl: baseUrl, apiKey: apiKeyId, enableModelManagement: enableModelManagement, showOnlyUserModels: true })), Boolean(toastData) && (_jsx(UsageToast, { result: toastData, position: "bottom-right", onComplete: clearToast }, toastKey)), hasLBProvider ? (_jsx(LBSigninModal, { isOpen: showAuthModal, onClose: () => setShowAuthModal(false) })) : null] }));
|
|
166
176
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import "../styles/register";
|
|
4
|
-
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
5
5
|
import { X, AlertCircle } from "lucide-react";
|
|
6
6
|
import { useI18n } from "../context/I18nContext";
|
|
7
7
|
export function ErrorToast({ error, position = "bottom-right", onComplete, }) {
|
|
@@ -10,7 +10,7 @@ export function ErrorToast({ error, position = "bottom-right", onComplete, }) {
|
|
|
10
10
|
const [isClosing, setIsClosing] = useState(false);
|
|
11
11
|
const fadeTimeoutRef = useRef(null);
|
|
12
12
|
const autoCloseTimeoutRef = useRef(null);
|
|
13
|
-
const handleClose = () => {
|
|
13
|
+
const handleClose = useCallback(() => {
|
|
14
14
|
if (isClosing)
|
|
15
15
|
return;
|
|
16
16
|
// Clear auto-close timeout if user closes manually
|
|
@@ -22,7 +22,7 @@ export function ErrorToast({ error, position = "bottom-right", onComplete, }) {
|
|
|
22
22
|
setIsVisible(false);
|
|
23
23
|
onComplete?.();
|
|
24
24
|
}, 200);
|
|
25
|
-
};
|
|
25
|
+
}, [isClosing, onComplete]);
|
|
26
26
|
useEffect(() => {
|
|
27
27
|
if (error) {
|
|
28
28
|
// Show toast immediately
|
|
@@ -5,7 +5,7 @@ import "../styles/register";
|
|
|
5
5
|
* Composant LBKeyPicker
|
|
6
6
|
* Permet de changer de clé API sans se reconnecter
|
|
7
7
|
*/
|
|
8
|
-
import { useEffect, useState } from "react";
|
|
8
|
+
import { useCallback, useEffect, useState } from "react";
|
|
9
9
|
import { useLB } from "../hooks/useLB";
|
|
10
10
|
import { useI18n } from "../context/I18nContext";
|
|
11
11
|
export function LBKeyPicker({ className = "", onKeyChanged, }) {
|
|
@@ -15,12 +15,7 @@ export function LBKeyPicker({ className = "", onKeyChanged, }) {
|
|
|
15
15
|
const [loading, setLoading] = useState(false);
|
|
16
16
|
const [error, setError] = useState("");
|
|
17
17
|
const [showDropdown, setShowDropdown] = useState(false);
|
|
18
|
-
|
|
19
|
-
if (status === "ready" && accessToken) {
|
|
20
|
-
loadKeys();
|
|
21
|
-
}
|
|
22
|
-
}, [status, accessToken]);
|
|
23
|
-
const loadKeys = async () => {
|
|
18
|
+
const loadKeys = useCallback(async () => {
|
|
24
19
|
if (!accessToken)
|
|
25
20
|
return;
|
|
26
21
|
try {
|
|
@@ -28,13 +23,18 @@ export function LBKeyPicker({ className = "", onKeyChanged, }) {
|
|
|
28
23
|
const keys = await fetchApiKeys(accessToken);
|
|
29
24
|
setApiKeys(keys);
|
|
30
25
|
}
|
|
31
|
-
catch
|
|
26
|
+
catch {
|
|
32
27
|
setError(t("lb.keypicker.loadError", "Unable to load API keys"));
|
|
33
28
|
}
|
|
34
29
|
finally {
|
|
35
30
|
setLoading(false);
|
|
36
31
|
}
|
|
37
|
-
};
|
|
32
|
+
}, [accessToken, fetchApiKeys, t]);
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (status === "ready" && accessToken) {
|
|
35
|
+
loadKeys();
|
|
36
|
+
}
|
|
37
|
+
}, [accessToken, loadKeys, status]);
|
|
38
38
|
const handleSelectKey = async (keyId) => {
|
|
39
39
|
if (!accessToken)
|
|
40
40
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LBSigninModal.d.ts","sourceRoot":"","sources":["../../src/components/LBSigninModal.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"LBSigninModal.d.ts","sourceRoot":"","sources":["../../src/components/LBSigninModal.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAS5B,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,sCAsPpE"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import "../styles/register";
|
|
2
2
|
interface UsageToastProps {
|
|
3
|
-
result:
|
|
3
|
+
result: any;
|
|
4
4
|
position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
5
5
|
onComplete?: () => void;
|
|
6
6
|
}
|
|
7
7
|
export declare function UsageToast({ result, position, onComplete, }: UsageToastProps): import("react/jsx-runtime").JSX.Element | null;
|
|
8
8
|
export declare function useUsageToast(): {
|
|
9
|
-
showUsageToast: (result:
|
|
10
|
-
toastData:
|
|
9
|
+
showUsageToast: (result: any) => void;
|
|
10
|
+
toastData: any;
|
|
11
11
|
toastKey: number;
|
|
12
12
|
clearToast: () => void;
|
|
13
13
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UsageToast.d.ts","sourceRoot":"","sources":["../../src/components/UsageToast.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAK5B,UAAU,eAAe;IACvB,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"UsageToast.d.ts","sourceRoot":"","sources":["../../src/components/UsageToast.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAK5B,UAAU,eAAe;IACvB,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;IACrE,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,QAAyB,EACzB,UAAU,GACX,EAAE,eAAe,kDAmJjB;AAED,wBAAgB,aAAa;6BAIK,GAAG;;;;EAgBpC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"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"}
|
|
@@ -239,7 +239,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
239
239
|
try {
|
|
240
240
|
let data;
|
|
241
241
|
try {
|
|
242
|
-
data = await lbClient.getStatus();
|
|
242
|
+
data = (await lbClient.getStatus());
|
|
243
243
|
}
|
|
244
244
|
catch {
|
|
245
245
|
// Backward compatibility: older backends may not expose /auth/status
|
|
@@ -302,14 +302,16 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
302
302
|
}
|
|
303
303
|
setIsLoadingStorage(true);
|
|
304
304
|
try {
|
|
305
|
-
const data = await lbClient.getStorageStatus();
|
|
306
|
-
const storageData = data?.storage
|
|
305
|
+
const data = (await lbClient.getStorageStatus());
|
|
306
|
+
const storageData = data?.storage
|
|
307
|
+
? { storage: data.storage }
|
|
308
|
+
: data;
|
|
307
309
|
setStorageStatus(storageData);
|
|
308
310
|
setStorageLastFetch(now);
|
|
309
311
|
// Combiner avec le basic status
|
|
310
312
|
const combinedStatus = {
|
|
311
313
|
...basicStatus,
|
|
312
|
-
storage: storageData
|
|
314
|
+
storage: storageData.storage,
|
|
313
315
|
};
|
|
314
316
|
setApiStatus(combinedStatus);
|
|
315
317
|
}
|
|
@@ -433,7 +435,12 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
433
435
|
setStorageStatus(null);
|
|
434
436
|
setApiKeys([]);
|
|
435
437
|
}
|
|
436
|
-
}, [
|
|
438
|
+
}, [
|
|
439
|
+
fetchApiKeysWithSession,
|
|
440
|
+
refreshBasicStatus,
|
|
441
|
+
refreshStorageStatus,
|
|
442
|
+
state.status,
|
|
443
|
+
]);
|
|
437
444
|
const value = {
|
|
438
445
|
...state,
|
|
439
446
|
login,
|
|
@@ -53,7 +53,7 @@ export function ModelManagementStats({ apiKey, baseUrl, category = "image", } =
|
|
|
53
53
|
* Composant exemple pour afficher et gérer une liste de modèles
|
|
54
54
|
*/
|
|
55
55
|
export function ModelManagementList({ apiKey, baseUrl, category = "image", onModelToggle, }) {
|
|
56
|
-
const { availableModels, userModels, loading, error, toggleModel, isModelActive, } = useModelManagement({
|
|
56
|
+
const { availableModels, userModels: _userModels, loading, error, toggleModel, isModelActive, } = useModelManagement({
|
|
57
57
|
apiKey,
|
|
58
58
|
baseUrl,
|
|
59
59
|
category,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAiStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useAiStatus.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"useAiStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useAiStatus.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,CA4E3E"}
|
|
@@ -1,12 +1,40 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useState, useEffect, useCallback } from "react";
|
|
2
|
+
import { useState, useEffect, useCallback, useContext, useMemo } from "react";
|
|
3
3
|
import { useAiClient } from "./useAiClient";
|
|
4
|
+
import { LBContext } from "../context/LBAuthProvider";
|
|
4
5
|
export function useAiStatus(options) {
|
|
5
6
|
const client = useAiClient(options);
|
|
7
|
+
const lbContext = useContext(LBContext);
|
|
6
8
|
const [status, setStatus] = useState(null);
|
|
7
9
|
const [loading, setLoading] = useState(false);
|
|
8
10
|
const [error, setError] = useState(null);
|
|
11
|
+
const statusFromLB = useMemo(() => {
|
|
12
|
+
if (!lbContext || lbContext.status !== "ready") {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
if (lbContext.apiStatus) {
|
|
16
|
+
return lbContext.apiStatus;
|
|
17
|
+
}
|
|
18
|
+
if (lbContext.basicStatus) {
|
|
19
|
+
return {
|
|
20
|
+
...lbContext.basicStatus,
|
|
21
|
+
storage: lbContext.storageStatus?.storage ||
|
|
22
|
+
lbContext.basicStatus?.storage,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}, [lbContext]);
|
|
9
27
|
const fetchStatus = useCallback(async () => {
|
|
28
|
+
// If LBProvider is available, it already handles fast status + storage split.
|
|
29
|
+
if (lbContext && lbContext.status === "ready") {
|
|
30
|
+
try {
|
|
31
|
+
await lbContext.refreshBasicStatus();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Ignore: provider already tracks errors/status
|
|
35
|
+
}
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
10
38
|
console.log("[useAiStatus] Starting status fetch");
|
|
11
39
|
setLoading(true);
|
|
12
40
|
setError(null);
|
|
@@ -22,13 +50,23 @@ export function useAiStatus(options) {
|
|
|
22
50
|
finally {
|
|
23
51
|
setLoading(false);
|
|
24
52
|
}
|
|
25
|
-
}, [client]);
|
|
53
|
+
}, [client, lbContext]);
|
|
26
54
|
useEffect(() => {
|
|
55
|
+
if (statusFromLB) {
|
|
56
|
+
setStatus(statusFromLB);
|
|
57
|
+
setLoading(false);
|
|
58
|
+
setError(null);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
27
61
|
fetchStatus();
|
|
28
|
-
}, [fetchStatus]);
|
|
62
|
+
}, [fetchStatus, statusFromLB]);
|
|
63
|
+
const isLoadingFromLB = !!lbContext &&
|
|
64
|
+
lbContext.status === "ready" &&
|
|
65
|
+
!statusFromLB &&
|
|
66
|
+
(lbContext.isLoadingStatus || lbContext.isLoadingStorage);
|
|
29
67
|
return {
|
|
30
|
-
status,
|
|
31
|
-
loading,
|
|
68
|
+
status: statusFromLB || status,
|
|
69
|
+
loading: isLoadingFromLB || loading,
|
|
32
70
|
error,
|
|
33
71
|
refetch: fetchStatus,
|
|
34
72
|
};
|
package/dist/styles.css
CHANGED
|
@@ -195,7 +195,7 @@
|
|
|
195
195
|
align-items: center;
|
|
196
196
|
gap: 10px;
|
|
197
197
|
min-height: var(--ai-control-h, var(--ai-size-md-h));
|
|
198
|
-
padding: 0
|
|
198
|
+
padding: 0 8px;
|
|
199
199
|
border-radius: var(--ai-radius-current, var(--ai-radius-full));
|
|
200
200
|
border: 1px solid var(--ai-border);
|
|
201
201
|
background:
|
|
@@ -362,8 +362,8 @@
|
|
|
362
362
|
.ai-shell--textarea .ai-control-action,
|
|
363
363
|
.ai-shell--textarea .ai-spark {
|
|
364
364
|
position: absolute;
|
|
365
|
-
right:
|
|
366
|
-
bottom:
|
|
365
|
+
right: 8px;
|
|
366
|
+
bottom: 8px;
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
/* Generic buttons */
|
|
@@ -6,7 +6,7 @@ export interface ParsedError {
|
|
|
6
6
|
/**
|
|
7
7
|
* Parse et uniformise la gestion des erreurs des composants AI
|
|
8
8
|
*/
|
|
9
|
-
export declare function parseAIError(error:
|
|
9
|
+
export declare function parseAIError(error: any): ParsedError;
|
|
10
10
|
/**
|
|
11
11
|
* Interface pour la callback de gestion d'erreur uniforme
|
|
12
12
|
*/
|
|
@@ -23,7 +23,7 @@ export interface ErrorToastCallback {
|
|
|
23
23
|
* @param onToast Callback externe optionnelle fournie par le parent
|
|
24
24
|
* @param showInternalToast Callback interne optionnelle pour afficher un toast dans le composant
|
|
25
25
|
*/
|
|
26
|
-
export declare function handleAIError(error:
|
|
26
|
+
export declare function handleAIError(error: any, onToast?: ErrorToastCallback, showInternalToast?: (error: {
|
|
27
27
|
message: string;
|
|
28
28
|
code?: string;
|
|
29
29
|
}) => void): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/utils/errorHandler.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,OAAO,CAAC;CACzB;AAeD;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/utils/errorHandler.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,OAAO,CAAC;CACzB;AAeD;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,GAAG,GAAG,WAAW,CA8CpD;AAyFD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAClE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,GAAG,EACV,OAAO,CAAC,EAAE,kBAAkB,EAC5B,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,GACtE,IAAI,CAuBN"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lastbrain/ai-ui-react",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.76",
|
|
4
4
|
"description": "Headless React components for LastBrain AI UI Kit",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"lucide-react": "^0.257.0",
|
|
54
|
-
"@lastbrain/ai-ui-core": "1.0.
|
|
54
|
+
"@lastbrain/ai-ui-core": "1.0.55"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/react": "^19.2.0",
|