@lastbrain/ai-ui-react 1.0.79 → 1.0.80
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.map +1 -1
- package/dist/components/AiContextButton.js +12 -3
- package/dist/components/AiImageButton.d.ts.map +1 -1
- package/dist/components/AiImageButton.js +11 -2
- package/dist/components/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +4 -1
- package/dist/components/AiPromptPanel.d.ts +2 -0
- package/dist/components/AiPromptPanel.d.ts.map +1 -1
- package/dist/components/AiPromptPanel.js +138 -5
- package/dist/components/AiSelect.d.ts.map +1 -1
- package/dist/components/AiSelect.js +4 -1
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +4 -1
- package/dist/components/AiTextarea.d.ts.map +1 -1
- package/dist/components/AiTextarea.js +15 -2
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +31 -16
- package/package.json +1 -1
- package/src/components/AiContextButton.tsx +14 -3
- package/src/components/AiImageButton.tsx +12 -3
- package/src/components/AiInput.tsx +4 -1
- package/src/components/AiPromptPanel.tsx +279 -12
- package/src/components/AiSelect.tsx +4 -1
- package/src/components/AiStatusButton.tsx +4 -1
- package/src/components/AiTextarea.tsx +16 -3
- package/src/context/LBAuthProvider.tsx +33 -15
|
@@ -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,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,
|
|
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,2CAuUtB"}
|
|
@@ -24,12 +24,14 @@ export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId,
|
|
|
24
24
|
let lbStatus;
|
|
25
25
|
let lbHasSession = false;
|
|
26
26
|
let lbHasSelectedApiKeyCookie = false;
|
|
27
|
+
let lbHasSelectedKey = false;
|
|
27
28
|
let hasLBProvider = false;
|
|
28
29
|
try {
|
|
29
30
|
const lbContext = useLB();
|
|
30
31
|
lbStatus = lbContext.status;
|
|
31
32
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
32
33
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
34
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
33
35
|
hasLBProvider = true;
|
|
34
36
|
}
|
|
35
37
|
catch {
|
|
@@ -47,7 +49,8 @@ export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId,
|
|
|
47
49
|
const needsApiKeySelection = hasLBProvider &&
|
|
48
50
|
lbStatus === "ready" &&
|
|
49
51
|
lbHasSession &&
|
|
50
|
-
!lbHasSelectedApiKeyCookie
|
|
52
|
+
!lbHasSelectedApiKeyCookie &&
|
|
53
|
+
!lbHasSelectedKey;
|
|
51
54
|
const isAuthReady = !needsApiKeySelection &&
|
|
52
55
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
53
56
|
const handleOpenPanel = () => {
|
|
@@ -153,9 +156,15 @@ export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId,
|
|
|
153
156
|
const sizeClass = `ai-size-${size}`;
|
|
154
157
|
const radiusClass = `ai-radius-${radius}`;
|
|
155
158
|
const variantClass = variant === "light" ? "ai-btn--light" : "";
|
|
156
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative inline-block ai-glow", children: [_jsx("button", { ...buttonProps, onClick:
|
|
159
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative inline-block ai-glow", children: [_jsx("button", { ...buttonProps, onClick: () => {
|
|
160
|
+
if (!isAuthReady) {
|
|
161
|
+
setShowAuthModal(true);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
handleOpenPanel();
|
|
165
|
+
}, disabled: disabled || loading, className: `ai-btn ai-context-btn ${variantClass} ${sizeClass} ${radiusClass} ${className || ""}`, title: !isAuthReady
|
|
157
166
|
? t("auth.required", "Authentication required")
|
|
158
|
-
: t("ai.analyze", "Analyze with AI"), children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 16, className: "ai-spinner" }), _jsx("span", { children: t("ai.analyzing", "Analyzing...") })] })) : !isAuthReady ? (_jsxs(_Fragment, { children: [_jsx(Lock, { size: 16 }), children || (_jsx("span", { children: t("auth.connectRequired", "Connection required") }))] })) : (_jsxs(_Fragment, { children: [_jsx(Sparkles, { size: 16 }), children || (_jsx("span", { children: t("ai.context.buttonAnalyze", "Analyze") }))] })) }), isOpen ? (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: () => setIsOpen(false), onSubmit: handleSubmit, uiMode: uiMode, models: [], enableModelManagement: true, modelCategory: "text", baseUrl: baseUrl, apiKey: apiKeyId })) : null] }), isResultOpen && analysisResult ? (_jsx("div", { className: "ai-signin-overlay ai-overlay-panel", onClick: (e) => {
|
|
167
|
+
: t("ai.analyze", "Analyze with AI"), children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 16, className: "ai-spinner" }), _jsx("span", { children: t("ai.analyzing", "Analyzing...") })] })) : !isAuthReady ? (_jsxs(_Fragment, { children: [_jsx(Lock, { size: 16 }), children || (_jsx("span", { children: t("auth.connectRequired", "Connection required") }))] })) : (_jsxs(_Fragment, { children: [_jsx(Sparkles, { size: 16 }), children || (_jsx("span", { children: t("ai.context.buttonAnalyze", "Analyze") }))] })) }), isOpen ? (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: () => setIsOpen(false), onSubmit: handleSubmit, uiMode: uiMode, models: [], enableModelManagement: true, modelCategory: "text", baseUrl: baseUrl, apiKey: apiKeyId, contextPreview: formatContextData(contextData), contextPreviewTitle: resolvedContextDescription })) : null] }), isResultOpen && analysisResult ? (_jsx("div", { className: "ai-signin-overlay ai-overlay-panel", onClick: (e) => {
|
|
159
168
|
if (e.target === e.currentTarget) {
|
|
160
169
|
setIsResultOpen(false);
|
|
161
170
|
setAnalysisResult(null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiImageButton.d.ts","sourceRoot":"","sources":["../../src/components/AiImageButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAS5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAY5D,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;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;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,IAAW,EACX,MAAe,EACf,OAAmB,EACnB,GAAG,WAAW,EACf,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"AiImageButton.d.ts","sourceRoot":"","sources":["../../src/components/AiImageButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAS5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAY5D,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;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;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,IAAW,EACX,MAAe,EACf,OAAmB,EACnB,GAAG,WAAW,EACf,EAAE,kBAAkB,2CA4SpB"}
|
|
@@ -24,12 +24,14 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
|
|
|
24
24
|
let lbStatus;
|
|
25
25
|
let lbHasSession = false;
|
|
26
26
|
let lbHasSelectedApiKeyCookie = false;
|
|
27
|
+
let lbHasSelectedKey = false;
|
|
27
28
|
let hasLBProvider = false;
|
|
28
29
|
try {
|
|
29
30
|
const lbContext = useLB();
|
|
30
31
|
lbStatus = lbContext.status;
|
|
31
32
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
32
33
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
34
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
33
35
|
hasLBProvider = true;
|
|
34
36
|
}
|
|
35
37
|
catch {
|
|
@@ -47,7 +49,8 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
|
|
|
47
49
|
const needsApiKeySelection = hasLBProvider &&
|
|
48
50
|
lbStatus === "ready" &&
|
|
49
51
|
lbHasSession &&
|
|
50
|
-
!lbHasSelectedApiKeyCookie
|
|
52
|
+
!lbHasSelectedApiKeyCookie &&
|
|
53
|
+
!lbHasSelectedKey;
|
|
51
54
|
const isAuthReady = !needsApiKeySelection &&
|
|
52
55
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
53
56
|
const handleOpenPanel = () => {
|
|
@@ -147,7 +150,13 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
|
|
|
147
150
|
const sizeClass = `ai-size-${size}`;
|
|
148
151
|
const radiusClass = `ai-radius-${radius}`;
|
|
149
152
|
const variantClass = variant === "light" ? "ai-btn--light" : "";
|
|
150
|
-
return (_jsxs("div", { className: "flex items-start gap-4", children: [_jsxs("div", { className: "relative inline-block ai-glow", children: [_jsx("button", { ...buttonProps, onClick:
|
|
153
|
+
return (_jsxs("div", { className: "flex items-start gap-4", children: [_jsxs("div", { className: "relative inline-block ai-glow", children: [_jsx("button", { ...buttonProps, onClick: () => {
|
|
154
|
+
if (!isAuthReady) {
|
|
155
|
+
setShowAuthModal(true);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
handleOpenPanel();
|
|
159
|
+
}, disabled: disabled || loading, className: `ai-btn ai-image-btn ${variantClass} ${sizeClass} ${radiusClass} ${className || ""}`, style: buttonProps.style, "data-ai-image-button": true, title: !isAuthReady
|
|
151
160
|
? t("auth.required", "Authentication required")
|
|
152
161
|
: t("ai.image.generate", "Generate image"), children: loading ? (_jsxs("span", { className: "ai-loading-stack", children: [_jsxs("span", { className: "ai-loading-row", children: [_jsx(Loader2, { size: 18, className: "ai-spinner" }), _jsx("span", { className: "ai-text-microtracking", children: t("ai.image.generating", "Generating...") })] }), _jsx("span", { className: "ai-loading-meta", children: t("ai.loading.elapsed", "{seconds}", {
|
|
153
162
|
seconds: loadingElapsed,
|
|
@@ -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,QAAQ,EAAE,SAAiB,EAC3B,qBAA4B,EAC5B,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,UAAU,EACd,EAAE,YAAY,
|
|
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,2CAgPd"}
|
|
@@ -24,12 +24,14 @@ export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode =
|
|
|
24
24
|
let lbStatus;
|
|
25
25
|
let lbHasSession = false;
|
|
26
26
|
let lbHasSelectedApiKeyCookie = false;
|
|
27
|
+
let lbHasSelectedKey = false;
|
|
27
28
|
let hasLBProvider = false;
|
|
28
29
|
try {
|
|
29
30
|
const lbContext = useLB();
|
|
30
31
|
lbStatus = lbContext.status;
|
|
31
32
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
32
33
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
34
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
33
35
|
hasLBProvider = true;
|
|
34
36
|
}
|
|
35
37
|
catch {
|
|
@@ -62,7 +64,8 @@ export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode =
|
|
|
62
64
|
const needsApiKeySelection = hasLBProvider &&
|
|
63
65
|
lbStatus === "ready" &&
|
|
64
66
|
lbHasSession &&
|
|
65
|
-
!lbHasSelectedApiKeyCookie
|
|
67
|
+
!lbHasSelectedApiKeyCookie &&
|
|
68
|
+
!lbHasSelectedKey;
|
|
66
69
|
const isAuthReady = !needsApiKeySelection &&
|
|
67
70
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
68
71
|
const shouldShowSparkles = isAuthReady && !disabled;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiPromptPanel.d.ts","sourceRoot":"","sources":["../../src/components/AiPromptPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,
|
|
1
|
+
{"version":3,"file":"AiPromptPanel.d.ts","sourceRoot":"","sources":["../../src/components/AiPromptPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAcf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AASvC,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAkBrD,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,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,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;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;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,2CA0BtD"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import "../styles/register";
|
|
4
|
-
import { useState, useEffect, useRef, useLayoutEffect, } from "react";
|
|
4
|
+
import { useState, useEffect, useRef, useLayoutEffect, useMemo, } from "react";
|
|
5
5
|
import { createPortal } from "react-dom";
|
|
6
|
-
import { BookOpen, Search, Sparkles, Star, Tag, Settings, Loader2, } from "lucide-react";
|
|
6
|
+
import { BookOpen, Check, Copy, Eye, Search, Sparkles, Star, Tag, Settings, Loader2, } from "lucide-react";
|
|
7
7
|
import { aiStyles } from "../styles/inline";
|
|
8
8
|
import { handleAIError } from "../utils/errorHandler";
|
|
9
9
|
import { usePrompts, } from "../hooks/usePrompts";
|
|
@@ -11,6 +11,19 @@ import { useModelManagement } from "../hooks/useModelManagement";
|
|
|
11
11
|
import { AiProvider, useAiContext } from "../context/AiProvider";
|
|
12
12
|
import { useI18n } from "../context/I18nContext";
|
|
13
13
|
import { useLoadingTimer } from "../hooks/useLoadingTimer";
|
|
14
|
+
function tryFormatJson(input) {
|
|
15
|
+
const raw = (input || "").trim();
|
|
16
|
+
if (!raw) {
|
|
17
|
+
return { formatted: "", isJson: false };
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const parsed = JSON.parse(raw);
|
|
21
|
+
return { formatted: JSON.stringify(parsed, null, 2), isJson: true };
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return { formatted: input || "", isJson: false };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
14
27
|
export function AiPromptPanel(props) {
|
|
15
28
|
const { apiKey, baseUrl } = props;
|
|
16
29
|
let hasContext = false;
|
|
@@ -32,7 +45,7 @@ export function AiPromptPanel(props) {
|
|
|
32
45
|
// Sinon, utiliser le contexte existant
|
|
33
46
|
return _jsx(AiPromptPanelInternal, { ...props });
|
|
34
47
|
}
|
|
35
|
-
function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode = "modal", models = [], sourceText, children, enableModelManagement = true, modelCategory = "text", availableModels = [], userModels = [], onModelToggle, apiKey, baseUrl, showOnlyUserModels = false, }) {
|
|
48
|
+
function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode = "modal", models = [], sourceText, children, enableModelManagement = true, modelCategory = "text", availableModels = [], userModels = [], onModelToggle, apiKey, baseUrl, showOnlyUserModels = false, contextPreview, contextPreviewTitle, }) {
|
|
36
49
|
const { t } = useI18n();
|
|
37
50
|
const [selectedModel, setSelectedModel] = useState("");
|
|
38
51
|
const [prompt, setPrompt] = useState("");
|
|
@@ -45,6 +58,8 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode = "modal", mo
|
|
|
45
58
|
const [selectedTag, setSelectedTag] = useState("all");
|
|
46
59
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
47
60
|
const [isClosing, setIsClosing] = useState(false);
|
|
61
|
+
const [showContextPreview, setShowContextPreview] = useState(false);
|
|
62
|
+
const [contextCopied, setContextCopied] = useState(false);
|
|
48
63
|
const [portalRoot, setPortalRoot] = useState(null);
|
|
49
64
|
const promptRef = useRef(null);
|
|
50
65
|
const closeTimeoutRef = useRef(null);
|
|
@@ -157,6 +172,8 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode = "modal", mo
|
|
|
157
172
|
setPrompt("");
|
|
158
173
|
setPromptId(undefined);
|
|
159
174
|
setShowPromptLibrary(false);
|
|
175
|
+
setShowContextPreview(false);
|
|
176
|
+
setContextCopied(false);
|
|
160
177
|
setSearchQuery("");
|
|
161
178
|
setSelectedTag("all");
|
|
162
179
|
setIsClosing(false);
|
|
@@ -177,6 +194,12 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode = "modal", mo
|
|
|
177
194
|
}
|
|
178
195
|
};
|
|
179
196
|
}, []);
|
|
197
|
+
useEffect(() => {
|
|
198
|
+
if (!contextCopied)
|
|
199
|
+
return;
|
|
200
|
+
const timeout = window.setTimeout(() => setContextCopied(false), 1200);
|
|
201
|
+
return () => window.clearTimeout(timeout);
|
|
202
|
+
}, [contextCopied]);
|
|
180
203
|
useEffect(() => {
|
|
181
204
|
setPortalRoot(document.body);
|
|
182
205
|
}, []);
|
|
@@ -221,6 +244,8 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode = "modal", mo
|
|
|
221
244
|
useLayoutEffect(() => {
|
|
222
245
|
adjustPromptHeight();
|
|
223
246
|
}, [prompt]);
|
|
247
|
+
const contextPreviewData = useMemo(() => tryFormatJson(contextPreview), [contextPreview]);
|
|
248
|
+
const contextPreviewLines = useMemo(() => contextPreviewData.formatted.split("\n"), [contextPreviewData.formatted]);
|
|
224
249
|
if (!isOpen)
|
|
225
250
|
return null;
|
|
226
251
|
const activeModelId = selectedModel || modelOptions[0]?.id || "";
|
|
@@ -270,6 +295,8 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode = "modal", mo
|
|
|
270
295
|
return null;
|
|
271
296
|
}
|
|
272
297
|
const isDrawer = uiMode === "drawer";
|
|
298
|
+
const hasContextPreview = Boolean(contextPreview?.trim());
|
|
299
|
+
const contextCharCount = contextPreview?.length || 0;
|
|
273
300
|
const panelContainerStyle = isDrawer
|
|
274
301
|
? {
|
|
275
302
|
...aiStyles.modal,
|
|
@@ -288,6 +315,40 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode = "modal", mo
|
|
|
288
315
|
borderRadius: "16px 0 0 16px",
|
|
289
316
|
}
|
|
290
317
|
: aiStyles.modalContent;
|
|
318
|
+
const renderContextLine = (line, lineIndex) => {
|
|
319
|
+
if (!contextPreviewData.isJson) {
|
|
320
|
+
return (_jsx("span", { style: { color: "var(--ai-text-secondary)" }, children: line || " " }, `line-${lineIndex}`));
|
|
321
|
+
}
|
|
322
|
+
const tokenRegex = /("(?:\\.|[^"\\])*"(?=\s*:)|"(?:\\.|[^"\\])*"|true|false|null|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|[{}[\],:])/g;
|
|
323
|
+
const parts = [];
|
|
324
|
+
let lastIndex = 0;
|
|
325
|
+
let match;
|
|
326
|
+
while ((match = tokenRegex.exec(line)) !== null) {
|
|
327
|
+
const token = match[0];
|
|
328
|
+
const index = match.index;
|
|
329
|
+
if (index > lastIndex) {
|
|
330
|
+
parts.push(_jsx("span", { style: { color: "var(--ai-text-secondary)" }, children: line.slice(lastIndex, index) }, `txt-${lineIndex}-${lastIndex}`));
|
|
331
|
+
}
|
|
332
|
+
const isKey = /^".*"$/.test(token) && line.slice(match.index).includes(":");
|
|
333
|
+
const color = isKey
|
|
334
|
+
? "#7cc5ff"
|
|
335
|
+
: /^".*"$/.test(token)
|
|
336
|
+
? "#9ad07f"
|
|
337
|
+
: /^(true|false)$/.test(token)
|
|
338
|
+
? "#f4a259"
|
|
339
|
+
: token === "null"
|
|
340
|
+
? "#d3869b"
|
|
341
|
+
: /^-?\d/.test(token)
|
|
342
|
+
? "#f7dc6f"
|
|
343
|
+
: "var(--ai-text-secondary)";
|
|
344
|
+
parts.push(_jsx("span", { style: { color }, children: token }, `tok-${lineIndex}-${index}`));
|
|
345
|
+
lastIndex = index + token.length;
|
|
346
|
+
}
|
|
347
|
+
if (lastIndex < line.length) {
|
|
348
|
+
parts.push(_jsx("span", { style: { color: "var(--ai-text-secondary)" }, children: line.slice(lastIndex) }, `txt-end-${lineIndex}`));
|
|
349
|
+
}
|
|
350
|
+
return (_jsx("span", { style: { whiteSpace: "pre" }, children: parts.length > 0 ? parts : " " }, `line-${lineIndex}`));
|
|
351
|
+
};
|
|
291
352
|
if (children) {
|
|
292
353
|
return createPortal(_jsx("div", { style: panelContainerStyle, onKeyDown: handleKeyDown, children: children(renderProps) }), portalRoot);
|
|
293
354
|
}
|
|
@@ -417,7 +478,13 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode = "modal", mo
|
|
|
417
478
|
marginLeft: "4px",
|
|
418
479
|
fontSize: "12px",
|
|
419
480
|
fontWeight: 400,
|
|
420
|
-
}, children: t("prompt.modal.promptHint", "(Cmd/Ctrl + Enter to submit)") })] }),
|
|
481
|
+
}, children: t("prompt.modal.promptHint", "(Cmd/Ctrl + Enter to submit)") })] }), _jsxs("div", { style: {
|
|
482
|
+
display: "flex",
|
|
483
|
+
alignItems: "center",
|
|
484
|
+
flexWrap: "wrap",
|
|
485
|
+
justifyContent: "flex-end",
|
|
486
|
+
gap: "8px",
|
|
487
|
+
}, children: [hasContextPreview ? (_jsxs("button", { type: "button", onClick: () => setShowContextPreview(true), className: "ai-inline-btn", style: { whiteSpace: "nowrap" }, children: [_jsx(Eye, { size: 14 }), t("prompt.modal.contextPreview", "Context preview")] })) : null, promptsLoading ? (_jsx("span", { className: "ai-inline-skeleton", "aria-hidden": "true" })) : filteredPrompts.length > 0 ? (_jsxs("button", { onClick: () => setShowPromptLibrary(true), className: "ai-inline-btn", style: { whiteSpace: "nowrap" }, children: [_jsx(BookOpen, { size: 14 }), t("prompt.modal.browsePrompts", "Browse Prompts"), " (", filteredPrompts.length, ")"] })) : null] })] }), _jsx("textarea", { id: "prompt-input", ref: promptRef, value: prompt, onChange: (e) => setPrompt(e.target.value), onFocus: () => setPromptFocused(true), onBlur: () => setPromptFocused(false), placeholder: sourceText
|
|
421
488
|
? t("prompt.modal.promptPlaceholderWithSource", "Enter your AI prompt... e.g., 'Fix grammar', 'Make it more professional', 'Translate to English'")
|
|
422
489
|
: t("prompt.modal.promptPlaceholderNoSource", "Enter your AI prompt... e.g., 'Write a blog post about AI', 'Generate product description'"), rows: 6, style: {
|
|
423
490
|
...aiStyles.textarea,
|
|
@@ -575,7 +642,73 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode = "modal", mo
|
|
|
575
642
|
marginTop: "8px",
|
|
576
643
|
fontSize: "11px",
|
|
577
644
|
color: "var(--ai-primary)",
|
|
578
|
-
}, children: String(promptData.category) })) : null] }, promptData.id))) })] }))] }))] })) }),
|
|
645
|
+
}, children: String(promptData.category) })) : null] }, promptData.id))) })] }))] }))] })) }), showContextPreview ? (_jsx("div", { style: {
|
|
646
|
+
position: "absolute",
|
|
647
|
+
inset: 0,
|
|
648
|
+
zIndex: 12,
|
|
649
|
+
background: "var(--ai-overlay)",
|
|
650
|
+
backdropFilter: "blur(3px)",
|
|
651
|
+
WebkitBackdropFilter: "blur(3px)",
|
|
652
|
+
display: "flex",
|
|
653
|
+
alignItems: "stretch",
|
|
654
|
+
justifyContent: "center",
|
|
655
|
+
padding: "10px",
|
|
656
|
+
}, onClick: () => setShowContextPreview(false), children: _jsxs("div", { style: {
|
|
657
|
+
width: "min(980px, 100%)",
|
|
658
|
+
maxHeight: "100%",
|
|
659
|
+
background: "var(--ai-bg-secondary)",
|
|
660
|
+
border: "1px solid var(--ai-border)",
|
|
661
|
+
borderRadius: "12px",
|
|
662
|
+
boxShadow: "var(--ai-shadow-lg)",
|
|
663
|
+
overflow: "hidden",
|
|
664
|
+
display: "flex",
|
|
665
|
+
flexDirection: "column",
|
|
666
|
+
}, onClick: (e) => e.stopPropagation(), children: [_jsxs("div", { style: {
|
|
667
|
+
padding: "12px 14px",
|
|
668
|
+
borderBottom: "1px solid var(--ai-border)",
|
|
669
|
+
display: "flex",
|
|
670
|
+
alignItems: "center",
|
|
671
|
+
justifyContent: "space-between",
|
|
672
|
+
gap: "10px",
|
|
673
|
+
}, children: [_jsxs("div", { style: { minWidth: 0 }, children: [_jsx("div", { style: { fontSize: "14px", fontWeight: 600 }, children: contextPreviewTitle ||
|
|
674
|
+
t("prompt.modal.contextPreview", "Context preview") }), _jsx("div", { style: {
|
|
675
|
+
marginTop: "2px",
|
|
676
|
+
fontSize: "12px",
|
|
677
|
+
color: "var(--ai-text-secondary)",
|
|
678
|
+
}, children: t("prompt.modal.contextChars", "{count} chars", {
|
|
679
|
+
count: contextCharCount.toLocaleString(),
|
|
680
|
+
}) })] }), _jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [_jsxs("button", { type: "button", className: "ai-inline-btn", onClick: async () => {
|
|
681
|
+
try {
|
|
682
|
+
await navigator.clipboard.writeText(contextPreviewData.formatted || "");
|
|
683
|
+
setContextCopied(true);
|
|
684
|
+
}
|
|
685
|
+
catch {
|
|
686
|
+
setContextCopied(false);
|
|
687
|
+
}
|
|
688
|
+
}, style: { whiteSpace: "nowrap" }, children: [contextCopied ? _jsx(Check, { size: 14 }) : _jsx(Copy, { size: 14 }), contextCopied
|
|
689
|
+
? t("common.copied", "Copied")
|
|
690
|
+
: t("common.copy", "Copy")] }), _jsx("button", { type: "button", className: "ai-icon-btn", onClick: () => setShowContextPreview(false), "aria-label": t("common.closeLabel", "Close"), children: "\u00D7" })] })] }), _jsx("pre", { style: {
|
|
691
|
+
margin: 0,
|
|
692
|
+
padding: "14px",
|
|
693
|
+
flex: 1,
|
|
694
|
+
overflow: "auto",
|
|
695
|
+
background: "color-mix(in srgb, var(--ai-bg) 70%, transparent)",
|
|
696
|
+
borderTop: "1px solid var(--ai-border)",
|
|
697
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
|
|
698
|
+
fontSize: "12px",
|
|
699
|
+
lineHeight: 1.5,
|
|
700
|
+
}, children: contextPreviewLines.map((line, index) => (_jsxs("div", { style: {
|
|
701
|
+
display: "grid",
|
|
702
|
+
gridTemplateColumns: "44px 1fr",
|
|
703
|
+
gap: "12px",
|
|
704
|
+
minHeight: "18px",
|
|
705
|
+
}, children: [_jsx("span", { style: {
|
|
706
|
+
color: "var(--ai-text-tertiary)",
|
|
707
|
+
textAlign: "right",
|
|
708
|
+
userSelect: "none",
|
|
709
|
+
paddingRight: "8px",
|
|
710
|
+
borderRight: "1px solid var(--ai-border)",
|
|
711
|
+
}, children: index + 1 }), renderContextLine(line, index)] }, `context-line-${index}`))) })] }) })) : null, _jsxs("div", { style: {
|
|
579
712
|
...aiStyles.modalFooter,
|
|
580
713
|
position: "sticky",
|
|
581
714
|
bottom: 0,
|
|
@@ -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,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,
|
|
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,2CAkKf"}
|
|
@@ -21,12 +21,14 @@ export function AiSelect({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode
|
|
|
21
21
|
let lbStatus;
|
|
22
22
|
let lbHasSession = false;
|
|
23
23
|
let lbHasSelectedApiKeyCookie = false;
|
|
24
|
+
let lbHasSelectedKey = false;
|
|
24
25
|
let hasLBProvider = false;
|
|
25
26
|
try {
|
|
26
27
|
const lbContext = useLB();
|
|
27
28
|
lbStatus = lbContext.status;
|
|
28
29
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
29
30
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
31
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
30
32
|
hasLBProvider = true;
|
|
31
33
|
}
|
|
32
34
|
catch {
|
|
@@ -57,7 +59,8 @@ export function AiSelect({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode
|
|
|
57
59
|
const needsApiKeySelection = hasLBProvider &&
|
|
58
60
|
lbStatus === "ready" &&
|
|
59
61
|
lbHasSession &&
|
|
60
|
-
!lbHasSelectedApiKeyCookie
|
|
62
|
+
!lbHasSelectedApiKeyCookie &&
|
|
63
|
+
!lbHasSelectedKey;
|
|
61
64
|
const isAuthReady = !needsApiKeySelection &&
|
|
62
65
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
63
66
|
const shouldShowSparkles = isAuthReady && !disabled;
|
|
@@ -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;AA8B9D,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;AA8B9D,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,2CA6jBrB"}
|
|
@@ -133,7 +133,10 @@ export function AiStatusButton({ status, loading = false, className = "", size =
|
|
|
133
133
|
? effectiveStatus.authType
|
|
134
134
|
: undefined;
|
|
135
135
|
const hasLbSession = Boolean(lbSessionToken);
|
|
136
|
-
const requiresApiKeySelection = lbStatus === "ready" &&
|
|
136
|
+
const requiresApiKeySelection = lbStatus === "ready" &&
|
|
137
|
+
hasLbSession &&
|
|
138
|
+
!lbHasSelectedApiKeyCookie &&
|
|
139
|
+
!hasApiKeySelected;
|
|
137
140
|
const isApiKeyAuthMode = authTypeValue === "api_key";
|
|
138
141
|
const [tooltipStyle, setTooltipStyle] = useState({});
|
|
139
142
|
useEffect(() => {
|
|
@@ -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;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,
|
|
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,2CAgRjB"}
|
|
@@ -26,12 +26,14 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
26
26
|
let lbStatus;
|
|
27
27
|
let lbHasSession = false;
|
|
28
28
|
let lbHasSelectedApiKeyCookie = false;
|
|
29
|
+
let lbHasSelectedKey = false;
|
|
29
30
|
let hasLBProvider = false;
|
|
30
31
|
try {
|
|
31
32
|
const lbContext = useLB();
|
|
32
33
|
lbStatus = lbContext.status;
|
|
33
34
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
34
35
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
36
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
35
37
|
hasLBProvider = true;
|
|
36
38
|
}
|
|
37
39
|
catch {
|
|
@@ -55,7 +57,8 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
55
57
|
const needsApiKeySelection = hasLBProvider &&
|
|
56
58
|
lbStatus === "ready" &&
|
|
57
59
|
lbHasSession &&
|
|
58
|
-
!lbHasSelectedApiKeyCookie
|
|
60
|
+
!lbHasSelectedApiKeyCookie &&
|
|
61
|
+
!lbHasSelectedKey;
|
|
59
62
|
const { models: _models } = useAiModels({
|
|
60
63
|
baseUrl,
|
|
61
64
|
apiKeyId,
|
|
@@ -175,7 +178,17 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
175
178
|
adjustHeight();
|
|
176
179
|
}, onBlur: (e) => {
|
|
177
180
|
textareaProps.onBlur?.(e);
|
|
178
|
-
}, "aria-invalid": Boolean(textareaProps["aria-invalid"]), disabled: disabled || loading }), _jsx("button", { className: `ai-control-action ai-spark ${sizeClass} ${radiusClass}`, onClick:
|
|
181
|
+
}, "aria-invalid": Boolean(textareaProps["aria-invalid"]), disabled: disabled || loading }), _jsx("button", { className: `ai-control-action ai-spark ${sizeClass} ${radiusClass}`, onClick: () => {
|
|
182
|
+
if (!isAuthReady) {
|
|
183
|
+
setShowAuthModal(true);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (hasConfiguration) {
|
|
187
|
+
void handleQuickGenerate();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
handleOpenPanel();
|
|
191
|
+
}, disabled: disabled || loading, type: "button", title: !isAuthReady
|
|
179
192
|
? t("auth.required", "Authentication required")
|
|
180
193
|
: hasConfiguration
|
|
181
194
|
? t("ai.generate", "Generate with AI")
|
|
@@ -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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"}
|
|
@@ -113,20 +113,32 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
113
113
|
}, [checkSession]);
|
|
114
114
|
const fetchWalletSummary = useCallback(async () => {
|
|
115
115
|
const headers = lbClient.getAuthHeaders(state.session?.sessionToken);
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
116
|
+
const walletUrls = Array.from(new Set([
|
|
117
|
+
`${proxyUrl}/auth/wallet`,
|
|
118
|
+
`${proxyUrl.replace("/api/lastbrain", "/api/ai")}/auth/wallet`,
|
|
119
|
+
]));
|
|
120
|
+
for (const url of walletUrls) {
|
|
121
|
+
try {
|
|
122
|
+
const response = await fetch(url, {
|
|
123
|
+
method: "GET",
|
|
124
|
+
credentials: "include",
|
|
125
|
+
headers,
|
|
126
|
+
});
|
|
127
|
+
if (!response.ok)
|
|
128
|
+
continue;
|
|
129
|
+
const data = (await response.json());
|
|
130
|
+
return {
|
|
131
|
+
used: Number(data.totalUsed || 0),
|
|
132
|
+
total: Number(data.totalAdded || 0),
|
|
133
|
+
percentage: Number(data.percentUsed ?? data.percentage ?? 0),
|
|
134
|
+
remaining: Number(data.walletSellValueUsd || 0),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// try next endpoint
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
130
142
|
}, [lbClient, proxyUrl, state.session?.sessionToken]);
|
|
131
143
|
/**
|
|
132
144
|
* Récupère les clés API de l'utilisateur
|
|
@@ -396,8 +408,11 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
396
408
|
}));
|
|
397
409
|
}
|
|
398
410
|
setHasSelectedApiKeyCookie(true);
|
|
399
|
-
|
|
400
|
-
|
|
411
|
+
// Ne pas bloquer la validation UI de sélection sur des requêtes status/wallet/storage.
|
|
412
|
+
void refreshBasicStatus();
|
|
413
|
+
setTimeout(() => {
|
|
414
|
+
void refreshStorageStatus();
|
|
415
|
+
}, 100);
|
|
401
416
|
}
|
|
402
417
|
else {
|
|
403
418
|
throw new Error("No valid authentication method available");
|
package/package.json
CHANGED
|
@@ -85,12 +85,14 @@ export function AiContextButton({
|
|
|
85
85
|
let lbStatus: string | undefined;
|
|
86
86
|
let lbHasSession = false;
|
|
87
87
|
let lbHasSelectedApiKeyCookie = false;
|
|
88
|
+
let lbHasSelectedKey = false;
|
|
88
89
|
let hasLBProvider = false;
|
|
89
90
|
try {
|
|
90
91
|
const lbContext = useLB();
|
|
91
92
|
lbStatus = lbContext.status;
|
|
92
93
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
93
94
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
95
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
94
96
|
hasLBProvider = true;
|
|
95
97
|
} catch {
|
|
96
98
|
lbStatus = undefined;
|
|
@@ -111,7 +113,8 @@ export function AiContextButton({
|
|
|
111
113
|
hasLBProvider &&
|
|
112
114
|
lbStatus === "ready" &&
|
|
113
115
|
lbHasSession &&
|
|
114
|
-
!lbHasSelectedApiKeyCookie
|
|
116
|
+
!lbHasSelectedApiKeyCookie &&
|
|
117
|
+
!lbHasSelectedKey;
|
|
115
118
|
const isAuthReady =
|
|
116
119
|
!needsApiKeySelection &&
|
|
117
120
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
@@ -247,8 +250,14 @@ export function AiContextButton({
|
|
|
247
250
|
<div className="relative inline-block ai-glow">
|
|
248
251
|
<button
|
|
249
252
|
{...buttonProps}
|
|
250
|
-
onClick={
|
|
251
|
-
|
|
253
|
+
onClick={() => {
|
|
254
|
+
if (!isAuthReady) {
|
|
255
|
+
setShowAuthModal(true);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
handleOpenPanel();
|
|
259
|
+
}}
|
|
260
|
+
disabled={disabled || loading}
|
|
252
261
|
className={`ai-btn ai-context-btn ${variantClass} ${sizeClass} ${radiusClass} ${className || ""}`}
|
|
253
262
|
title={
|
|
254
263
|
!isAuthReady
|
|
@@ -289,6 +298,8 @@ export function AiContextButton({
|
|
|
289
298
|
modelCategory="text"
|
|
290
299
|
baseUrl={baseUrl}
|
|
291
300
|
apiKey={apiKeyId}
|
|
301
|
+
contextPreview={formatContextData(contextData)}
|
|
302
|
+
contextPreviewTitle={resolvedContextDescription}
|
|
292
303
|
/>
|
|
293
304
|
) : null}
|
|
294
305
|
</div>
|
|
@@ -81,12 +81,14 @@ export function AiImageButton({
|
|
|
81
81
|
let lbStatus: string | undefined;
|
|
82
82
|
let lbHasSession = false;
|
|
83
83
|
let lbHasSelectedApiKeyCookie = false;
|
|
84
|
+
let lbHasSelectedKey = false;
|
|
84
85
|
let hasLBProvider = false;
|
|
85
86
|
try {
|
|
86
87
|
const lbContext = useLB();
|
|
87
88
|
lbStatus = lbContext.status;
|
|
88
89
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
89
90
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
91
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
90
92
|
hasLBProvider = true;
|
|
91
93
|
} catch {
|
|
92
94
|
// LBProvider n'est pas disponible, ignorer
|
|
@@ -107,7 +109,8 @@ export function AiImageButton({
|
|
|
107
109
|
hasLBProvider &&
|
|
108
110
|
lbStatus === "ready" &&
|
|
109
111
|
lbHasSession &&
|
|
110
|
-
!lbHasSelectedApiKeyCookie
|
|
112
|
+
!lbHasSelectedApiKeyCookie &&
|
|
113
|
+
!lbHasSelectedKey;
|
|
111
114
|
const isAuthReady =
|
|
112
115
|
!needsApiKeySelection &&
|
|
113
116
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
@@ -226,8 +229,14 @@ export function AiImageButton({
|
|
|
226
229
|
<div className="relative inline-block ai-glow">
|
|
227
230
|
<button
|
|
228
231
|
{...buttonProps}
|
|
229
|
-
onClick={
|
|
230
|
-
|
|
232
|
+
onClick={() => {
|
|
233
|
+
if (!isAuthReady) {
|
|
234
|
+
setShowAuthModal(true);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
handleOpenPanel();
|
|
238
|
+
}}
|
|
239
|
+
disabled={disabled || loading}
|
|
231
240
|
className={`ai-btn ai-image-btn ${variantClass} ${sizeClass} ${radiusClass} ${className || ""}`}
|
|
232
241
|
style={buttonProps.style}
|
|
233
242
|
data-ai-image-button
|
|
@@ -62,12 +62,14 @@ export function AiInput({
|
|
|
62
62
|
let lbStatus: string | undefined;
|
|
63
63
|
let lbHasSession = false;
|
|
64
64
|
let lbHasSelectedApiKeyCookie = false;
|
|
65
|
+
let lbHasSelectedKey = false;
|
|
65
66
|
let hasLBProvider = false;
|
|
66
67
|
try {
|
|
67
68
|
const lbContext = useLB();
|
|
68
69
|
lbStatus = lbContext.status;
|
|
69
70
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
70
71
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
72
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
71
73
|
hasLBProvider = true;
|
|
72
74
|
} catch {
|
|
73
75
|
// LBProvider n'est pas disponible, ignorer
|
|
@@ -103,7 +105,8 @@ export function AiInput({
|
|
|
103
105
|
hasLBProvider &&
|
|
104
106
|
lbStatus === "ready" &&
|
|
105
107
|
lbHasSession &&
|
|
106
|
-
!lbHasSelectedApiKeyCookie
|
|
108
|
+
!lbHasSelectedApiKeyCookie &&
|
|
109
|
+
!lbHasSelectedKey;
|
|
107
110
|
const isAuthReady =
|
|
108
111
|
!needsApiKeySelection &&
|
|
109
112
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
@@ -6,11 +6,15 @@ import {
|
|
|
6
6
|
useEffect,
|
|
7
7
|
useRef,
|
|
8
8
|
useLayoutEffect,
|
|
9
|
+
useMemo,
|
|
9
10
|
type ReactNode,
|
|
10
11
|
} from "react";
|
|
11
12
|
import { createPortal } from "react-dom";
|
|
12
13
|
import {
|
|
13
14
|
BookOpen,
|
|
15
|
+
Check,
|
|
16
|
+
Copy,
|
|
17
|
+
Eye,
|
|
14
18
|
Search,
|
|
15
19
|
Sparkles,
|
|
16
20
|
Star,
|
|
@@ -33,6 +37,19 @@ import { AiProvider, useAiContext } from "../context/AiProvider";
|
|
|
33
37
|
import { useI18n } from "../context/I18nContext";
|
|
34
38
|
import { useLoadingTimer } from "../hooks/useLoadingTimer";
|
|
35
39
|
|
|
40
|
+
function tryFormatJson(input?: string): { formatted: string; isJson: boolean } {
|
|
41
|
+
const raw = (input || "").trim();
|
|
42
|
+
if (!raw) {
|
|
43
|
+
return { formatted: "", isJson: false };
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const parsed = JSON.parse(raw);
|
|
47
|
+
return { formatted: JSON.stringify(parsed, null, 2), isJson: true };
|
|
48
|
+
} catch {
|
|
49
|
+
return { formatted: input || "", isJson: false };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
36
53
|
export interface AiPromptPanelProps {
|
|
37
54
|
isOpen: boolean;
|
|
38
55
|
onClose: () => void;
|
|
@@ -50,6 +67,8 @@ export interface AiPromptPanelProps {
|
|
|
50
67
|
apiKey?: string;
|
|
51
68
|
baseUrl?: string;
|
|
52
69
|
showOnlyUserModels?: boolean;
|
|
70
|
+
contextPreview?: string;
|
|
71
|
+
contextPreviewTitle?: string;
|
|
53
72
|
}
|
|
54
73
|
|
|
55
74
|
export interface AiPromptPanelRenderProps {
|
|
@@ -114,6 +133,8 @@ function AiPromptPanelInternal({
|
|
|
114
133
|
apiKey,
|
|
115
134
|
baseUrl,
|
|
116
135
|
showOnlyUserModels = false,
|
|
136
|
+
contextPreview,
|
|
137
|
+
contextPreviewTitle,
|
|
117
138
|
}: AiPromptPanelProps) {
|
|
118
139
|
const { t } = useI18n();
|
|
119
140
|
const [selectedModel, setSelectedModel] = useState("");
|
|
@@ -127,6 +148,8 @@ function AiPromptPanelInternal({
|
|
|
127
148
|
const [selectedTag, setSelectedTag] = useState<string | "all">("all");
|
|
128
149
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
129
150
|
const [isClosing, setIsClosing] = useState(false);
|
|
151
|
+
const [showContextPreview, setShowContextPreview] = useState(false);
|
|
152
|
+
const [contextCopied, setContextCopied] = useState(false);
|
|
130
153
|
const [portalRoot, setPortalRoot] = useState<HTMLElement | null>(null);
|
|
131
154
|
const promptRef = useRef<HTMLTextAreaElement>(null);
|
|
132
155
|
const closeTimeoutRef = useRef<number | null>(null);
|
|
@@ -265,6 +288,8 @@ function AiPromptPanelInternal({
|
|
|
265
288
|
setPrompt("");
|
|
266
289
|
setPromptId(undefined);
|
|
267
290
|
setShowPromptLibrary(false);
|
|
291
|
+
setShowContextPreview(false);
|
|
292
|
+
setContextCopied(false);
|
|
268
293
|
setSearchQuery("");
|
|
269
294
|
setSelectedTag("all");
|
|
270
295
|
setIsClosing(false);
|
|
@@ -287,6 +312,12 @@ function AiPromptPanelInternal({
|
|
|
287
312
|
};
|
|
288
313
|
}, []);
|
|
289
314
|
|
|
315
|
+
useEffect(() => {
|
|
316
|
+
if (!contextCopied) return;
|
|
317
|
+
const timeout = window.setTimeout(() => setContextCopied(false), 1200);
|
|
318
|
+
return () => window.clearTimeout(timeout);
|
|
319
|
+
}, [contextCopied]);
|
|
320
|
+
|
|
290
321
|
useEffect(() => {
|
|
291
322
|
setPortalRoot(document.body);
|
|
292
323
|
}, []);
|
|
@@ -340,6 +371,15 @@ function AiPromptPanelInternal({
|
|
|
340
371
|
adjustPromptHeight();
|
|
341
372
|
}, [prompt]);
|
|
342
373
|
|
|
374
|
+
const contextPreviewData = useMemo(
|
|
375
|
+
() => tryFormatJson(contextPreview),
|
|
376
|
+
[contextPreview]
|
|
377
|
+
);
|
|
378
|
+
const contextPreviewLines = useMemo(
|
|
379
|
+
() => contextPreviewData.formatted.split("\n"),
|
|
380
|
+
[contextPreviewData.formatted]
|
|
381
|
+
);
|
|
382
|
+
|
|
343
383
|
if (!isOpen) return null;
|
|
344
384
|
|
|
345
385
|
const activeModelId = selectedModel || modelOptions[0]?.id || "";
|
|
@@ -406,6 +446,8 @@ function AiPromptPanelInternal({
|
|
|
406
446
|
}
|
|
407
447
|
|
|
408
448
|
const isDrawer = uiMode === "drawer";
|
|
449
|
+
const hasContextPreview = Boolean(contextPreview?.trim());
|
|
450
|
+
const contextCharCount = contextPreview?.length || 0;
|
|
409
451
|
const panelContainerStyle = isDrawer
|
|
410
452
|
? {
|
|
411
453
|
...aiStyles.modal,
|
|
@@ -425,6 +467,78 @@ function AiPromptPanelInternal({
|
|
|
425
467
|
}
|
|
426
468
|
: aiStyles.modalContent;
|
|
427
469
|
|
|
470
|
+
const renderContextLine = (line: string, lineIndex: number) => {
|
|
471
|
+
if (!contextPreviewData.isJson) {
|
|
472
|
+
return (
|
|
473
|
+
<span
|
|
474
|
+
key={`line-${lineIndex}`}
|
|
475
|
+
style={{ color: "var(--ai-text-secondary)" }}
|
|
476
|
+
>
|
|
477
|
+
{line || " "}
|
|
478
|
+
</span>
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const tokenRegex =
|
|
483
|
+
/("(?:\\.|[^"\\])*"(?=\s*:)|"(?:\\.|[^"\\])*"|true|false|null|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|[{}[\],:])/g;
|
|
484
|
+
const parts: ReactNode[] = [];
|
|
485
|
+
let lastIndex = 0;
|
|
486
|
+
let match: RegExpExecArray | null;
|
|
487
|
+
|
|
488
|
+
while ((match = tokenRegex.exec(line)) !== null) {
|
|
489
|
+
const token = match[0];
|
|
490
|
+
const index = match.index;
|
|
491
|
+
if (index > lastIndex) {
|
|
492
|
+
parts.push(
|
|
493
|
+
<span
|
|
494
|
+
key={`txt-${lineIndex}-${lastIndex}`}
|
|
495
|
+
style={{ color: "var(--ai-text-secondary)" }}
|
|
496
|
+
>
|
|
497
|
+
{line.slice(lastIndex, index)}
|
|
498
|
+
</span>
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const isKey = /^".*"$/.test(token) && line.slice(match.index).includes(":");
|
|
503
|
+
const color = isKey
|
|
504
|
+
? "#7cc5ff"
|
|
505
|
+
: /^".*"$/.test(token)
|
|
506
|
+
? "#9ad07f"
|
|
507
|
+
: /^(true|false)$/.test(token)
|
|
508
|
+
? "#f4a259"
|
|
509
|
+
: token === "null"
|
|
510
|
+
? "#d3869b"
|
|
511
|
+
: /^-?\d/.test(token)
|
|
512
|
+
? "#f7dc6f"
|
|
513
|
+
: "var(--ai-text-secondary)";
|
|
514
|
+
|
|
515
|
+
parts.push(
|
|
516
|
+
<span key={`tok-${lineIndex}-${index}`} style={{ color }}>
|
|
517
|
+
{token}
|
|
518
|
+
</span>
|
|
519
|
+
);
|
|
520
|
+
|
|
521
|
+
lastIndex = index + token.length;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (lastIndex < line.length) {
|
|
525
|
+
parts.push(
|
|
526
|
+
<span
|
|
527
|
+
key={`txt-end-${lineIndex}`}
|
|
528
|
+
style={{ color: "var(--ai-text-secondary)" }}
|
|
529
|
+
>
|
|
530
|
+
{line.slice(lastIndex)}
|
|
531
|
+
</span>
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return (
|
|
536
|
+
<span key={`line-${lineIndex}`} style={{ whiteSpace: "pre" }}>
|
|
537
|
+
{parts.length > 0 ? parts : " "}
|
|
538
|
+
</span>
|
|
539
|
+
);
|
|
540
|
+
};
|
|
541
|
+
|
|
428
542
|
if (children) {
|
|
429
543
|
return createPortal(
|
|
430
544
|
<div style={panelContainerStyle} onKeyDown={handleKeyDown}>
|
|
@@ -755,18 +869,40 @@ function AiPromptPanelInternal({
|
|
|
755
869
|
)}
|
|
756
870
|
</span>
|
|
757
871
|
</label>
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
872
|
+
<div
|
|
873
|
+
style={{
|
|
874
|
+
display: "flex",
|
|
875
|
+
alignItems: "center",
|
|
876
|
+
flexWrap: "wrap",
|
|
877
|
+
justifyContent: "flex-end",
|
|
878
|
+
gap: "8px",
|
|
879
|
+
}}
|
|
880
|
+
>
|
|
881
|
+
{hasContextPreview ? (
|
|
882
|
+
<button
|
|
883
|
+
type="button"
|
|
884
|
+
onClick={() => setShowContextPreview(true)}
|
|
885
|
+
className="ai-inline-btn"
|
|
886
|
+
style={{ whiteSpace: "nowrap" }}
|
|
887
|
+
>
|
|
888
|
+
<Eye size={14} />
|
|
889
|
+
{t("prompt.modal.contextPreview", "Context preview")}
|
|
890
|
+
</button>
|
|
891
|
+
) : null}
|
|
892
|
+
{promptsLoading ? (
|
|
893
|
+
<span className="ai-inline-skeleton" aria-hidden="true" />
|
|
894
|
+
) : filteredPrompts.length > 0 ? (
|
|
895
|
+
<button
|
|
896
|
+
onClick={() => setShowPromptLibrary(true)}
|
|
897
|
+
className="ai-inline-btn"
|
|
898
|
+
style={{ whiteSpace: "nowrap" }}
|
|
899
|
+
>
|
|
900
|
+
<BookOpen size={14} />
|
|
901
|
+
{t("prompt.modal.browsePrompts", "Browse Prompts")} (
|
|
902
|
+
{filteredPrompts.length})
|
|
903
|
+
</button>
|
|
904
|
+
) : null}
|
|
905
|
+
</div>
|
|
770
906
|
</div>
|
|
771
907
|
<textarea
|
|
772
908
|
id="prompt-input"
|
|
@@ -1124,6 +1260,137 @@ function AiPromptPanelInternal({
|
|
|
1124
1260
|
)}
|
|
1125
1261
|
</div>
|
|
1126
1262
|
|
|
1263
|
+
{showContextPreview ? (
|
|
1264
|
+
<div
|
|
1265
|
+
style={{
|
|
1266
|
+
position: "absolute",
|
|
1267
|
+
inset: 0,
|
|
1268
|
+
zIndex: 12,
|
|
1269
|
+
background: "var(--ai-overlay)",
|
|
1270
|
+
backdropFilter: "blur(3px)",
|
|
1271
|
+
WebkitBackdropFilter: "blur(3px)",
|
|
1272
|
+
display: "flex",
|
|
1273
|
+
alignItems: "stretch",
|
|
1274
|
+
justifyContent: "center",
|
|
1275
|
+
padding: "10px",
|
|
1276
|
+
}}
|
|
1277
|
+
onClick={() => setShowContextPreview(false)}
|
|
1278
|
+
>
|
|
1279
|
+
<div
|
|
1280
|
+
style={{
|
|
1281
|
+
width: "min(980px, 100%)",
|
|
1282
|
+
maxHeight: "100%",
|
|
1283
|
+
background: "var(--ai-bg-secondary)",
|
|
1284
|
+
border: "1px solid var(--ai-border)",
|
|
1285
|
+
borderRadius: "12px",
|
|
1286
|
+
boxShadow: "var(--ai-shadow-lg)",
|
|
1287
|
+
overflow: "hidden",
|
|
1288
|
+
display: "flex",
|
|
1289
|
+
flexDirection: "column",
|
|
1290
|
+
}}
|
|
1291
|
+
onClick={(e) => e.stopPropagation()}
|
|
1292
|
+
>
|
|
1293
|
+
<div
|
|
1294
|
+
style={{
|
|
1295
|
+
padding: "12px 14px",
|
|
1296
|
+
borderBottom: "1px solid var(--ai-border)",
|
|
1297
|
+
display: "flex",
|
|
1298
|
+
alignItems: "center",
|
|
1299
|
+
justifyContent: "space-between",
|
|
1300
|
+
gap: "10px",
|
|
1301
|
+
}}
|
|
1302
|
+
>
|
|
1303
|
+
<div style={{ minWidth: 0 }}>
|
|
1304
|
+
<div style={{ fontSize: "14px", fontWeight: 600 }}>
|
|
1305
|
+
{contextPreviewTitle ||
|
|
1306
|
+
t("prompt.modal.contextPreview", "Context preview")}
|
|
1307
|
+
</div>
|
|
1308
|
+
<div
|
|
1309
|
+
style={{
|
|
1310
|
+
marginTop: "2px",
|
|
1311
|
+
fontSize: "12px",
|
|
1312
|
+
color: "var(--ai-text-secondary)",
|
|
1313
|
+
}}
|
|
1314
|
+
>
|
|
1315
|
+
{t("prompt.modal.contextChars", "{count} chars", {
|
|
1316
|
+
count: contextCharCount.toLocaleString(),
|
|
1317
|
+
})}
|
|
1318
|
+
</div>
|
|
1319
|
+
</div>
|
|
1320
|
+
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
|
1321
|
+
<button
|
|
1322
|
+
type="button"
|
|
1323
|
+
className="ai-inline-btn"
|
|
1324
|
+
onClick={async () => {
|
|
1325
|
+
try {
|
|
1326
|
+
await navigator.clipboard.writeText(
|
|
1327
|
+
contextPreviewData.formatted || ""
|
|
1328
|
+
);
|
|
1329
|
+
setContextCopied(true);
|
|
1330
|
+
} catch {
|
|
1331
|
+
setContextCopied(false);
|
|
1332
|
+
}
|
|
1333
|
+
}}
|
|
1334
|
+
style={{ whiteSpace: "nowrap" }}
|
|
1335
|
+
>
|
|
1336
|
+
{contextCopied ? <Check size={14} /> : <Copy size={14} />}
|
|
1337
|
+
{contextCopied
|
|
1338
|
+
? t("common.copied", "Copied")
|
|
1339
|
+
: t("common.copy", "Copy")}
|
|
1340
|
+
</button>
|
|
1341
|
+
<button
|
|
1342
|
+
type="button"
|
|
1343
|
+
className="ai-icon-btn"
|
|
1344
|
+
onClick={() => setShowContextPreview(false)}
|
|
1345
|
+
aria-label={t("common.closeLabel", "Close")}
|
|
1346
|
+
>
|
|
1347
|
+
×
|
|
1348
|
+
</button>
|
|
1349
|
+
</div>
|
|
1350
|
+
</div>
|
|
1351
|
+
<pre
|
|
1352
|
+
style={{
|
|
1353
|
+
margin: 0,
|
|
1354
|
+
padding: "14px",
|
|
1355
|
+
flex: 1,
|
|
1356
|
+
overflow: "auto",
|
|
1357
|
+
background: "color-mix(in srgb, var(--ai-bg) 70%, transparent)",
|
|
1358
|
+
borderTop: "1px solid var(--ai-border)",
|
|
1359
|
+
fontFamily:
|
|
1360
|
+
"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
|
|
1361
|
+
fontSize: "12px",
|
|
1362
|
+
lineHeight: 1.5,
|
|
1363
|
+
}}
|
|
1364
|
+
>
|
|
1365
|
+
{contextPreviewLines.map((line, index) => (
|
|
1366
|
+
<div
|
|
1367
|
+
key={`context-line-${index}`}
|
|
1368
|
+
style={{
|
|
1369
|
+
display: "grid",
|
|
1370
|
+
gridTemplateColumns: "44px 1fr",
|
|
1371
|
+
gap: "12px",
|
|
1372
|
+
minHeight: "18px",
|
|
1373
|
+
}}
|
|
1374
|
+
>
|
|
1375
|
+
<span
|
|
1376
|
+
style={{
|
|
1377
|
+
color: "var(--ai-text-tertiary)",
|
|
1378
|
+
textAlign: "right",
|
|
1379
|
+
userSelect: "none",
|
|
1380
|
+
paddingRight: "8px",
|
|
1381
|
+
borderRight: "1px solid var(--ai-border)",
|
|
1382
|
+
}}
|
|
1383
|
+
>
|
|
1384
|
+
{index + 1}
|
|
1385
|
+
</span>
|
|
1386
|
+
{renderContextLine(line, index)}
|
|
1387
|
+
</div>
|
|
1388
|
+
))}
|
|
1389
|
+
</pre>
|
|
1390
|
+
</div>
|
|
1391
|
+
</div>
|
|
1392
|
+
) : null}
|
|
1393
|
+
|
|
1127
1394
|
<div
|
|
1128
1395
|
style={{
|
|
1129
1396
|
...aiStyles.modalFooter,
|
|
@@ -51,12 +51,14 @@ export function AiSelect({
|
|
|
51
51
|
let lbStatus: string | undefined;
|
|
52
52
|
let lbHasSession = false;
|
|
53
53
|
let lbHasSelectedApiKeyCookie = false;
|
|
54
|
+
let lbHasSelectedKey = false;
|
|
54
55
|
let hasLBProvider = false;
|
|
55
56
|
try {
|
|
56
57
|
const lbContext = useLB();
|
|
57
58
|
lbStatus = lbContext.status;
|
|
58
59
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
59
60
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
61
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
60
62
|
hasLBProvider = true;
|
|
61
63
|
} catch {
|
|
62
64
|
// LBProvider n'est pas disponible, ignorer
|
|
@@ -90,7 +92,8 @@ export function AiSelect({
|
|
|
90
92
|
hasLBProvider &&
|
|
91
93
|
lbStatus === "ready" &&
|
|
92
94
|
lbHasSession &&
|
|
93
|
-
!lbHasSelectedApiKeyCookie
|
|
95
|
+
!lbHasSelectedApiKeyCookie &&
|
|
96
|
+
!lbHasSelectedKey;
|
|
94
97
|
const isAuthReady =
|
|
95
98
|
!needsApiKeySelection &&
|
|
96
99
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
@@ -243,7 +243,10 @@ export function AiStatusButton({
|
|
|
243
243
|
: undefined;
|
|
244
244
|
const hasLbSession = Boolean(lbSessionToken);
|
|
245
245
|
const requiresApiKeySelection =
|
|
246
|
-
lbStatus === "ready" &&
|
|
246
|
+
lbStatus === "ready" &&
|
|
247
|
+
hasLbSession &&
|
|
248
|
+
!lbHasSelectedApiKeyCookie &&
|
|
249
|
+
!hasApiKeySelected;
|
|
247
250
|
const isApiKeyAuthMode = authTypeValue === "api_key";
|
|
248
251
|
|
|
249
252
|
const [tooltipStyle, setTooltipStyle] = useState<Record<string, string>>({});
|
|
@@ -65,12 +65,14 @@ export function AiTextarea({
|
|
|
65
65
|
let lbStatus: string | undefined;
|
|
66
66
|
let lbHasSession = false;
|
|
67
67
|
let lbHasSelectedApiKeyCookie = false;
|
|
68
|
+
let lbHasSelectedKey = false;
|
|
68
69
|
let hasLBProvider = false;
|
|
69
70
|
try {
|
|
70
71
|
const lbContext = useLB();
|
|
71
72
|
lbStatus = lbContext.status;
|
|
72
73
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
73
74
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
75
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
74
76
|
hasLBProvider = true;
|
|
75
77
|
} catch {
|
|
76
78
|
// LBProvider n'est pas disponible, ignorer
|
|
@@ -95,7 +97,8 @@ export function AiTextarea({
|
|
|
95
97
|
hasLBProvider &&
|
|
96
98
|
lbStatus === "ready" &&
|
|
97
99
|
lbHasSession &&
|
|
98
|
-
!lbHasSelectedApiKeyCookie
|
|
100
|
+
!lbHasSelectedApiKeyCookie &&
|
|
101
|
+
!lbHasSelectedKey;
|
|
99
102
|
|
|
100
103
|
const { models: _models } = useAiModels({
|
|
101
104
|
baseUrl,
|
|
@@ -252,8 +255,18 @@ export function AiTextarea({
|
|
|
252
255
|
/>
|
|
253
256
|
<button
|
|
254
257
|
className={`ai-control-action ai-spark ${sizeClass} ${radiusClass}`}
|
|
255
|
-
onClick={
|
|
256
|
-
|
|
258
|
+
onClick={() => {
|
|
259
|
+
if (!isAuthReady) {
|
|
260
|
+
setShowAuthModal(true);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (hasConfiguration) {
|
|
264
|
+
void handleQuickGenerate();
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
handleOpenPanel();
|
|
268
|
+
}}
|
|
269
|
+
disabled={disabled || loading}
|
|
257
270
|
type="button"
|
|
258
271
|
title={
|
|
259
272
|
!isAuthReady
|
|
@@ -258,19 +258,34 @@ export function LBProvider({
|
|
|
258
258
|
remaining?: number;
|
|
259
259
|
} | null> => {
|
|
260
260
|
const headers = lbClient.getAuthHeaders(state.session?.sessionToken);
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
261
|
+
const walletUrls = Array.from(
|
|
262
|
+
new Set([
|
|
263
|
+
`${proxyUrl}/auth/wallet`,
|
|
264
|
+
`${proxyUrl.replace("/api/lastbrain", "/api/ai")}/auth/wallet`,
|
|
265
|
+
])
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
for (const url of walletUrls) {
|
|
269
|
+
try {
|
|
270
|
+
const response = await fetch(url, {
|
|
271
|
+
method: "GET",
|
|
272
|
+
credentials: "include",
|
|
273
|
+
headers,
|
|
274
|
+
});
|
|
275
|
+
if (!response.ok) continue;
|
|
276
|
+
const data = (await response.json()) as WalletStatusResponse;
|
|
277
|
+
return {
|
|
278
|
+
used: Number(data.totalUsed || 0),
|
|
279
|
+
total: Number(data.totalAdded || 0),
|
|
280
|
+
percentage: Number(data.percentUsed ?? data.percentage ?? 0),
|
|
281
|
+
remaining: Number(data.walletSellValueUsd || 0),
|
|
282
|
+
};
|
|
283
|
+
} catch {
|
|
284
|
+
// try next endpoint
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return null;
|
|
274
289
|
}, [lbClient, proxyUrl, state.session?.sessionToken]);
|
|
275
290
|
|
|
276
291
|
/**
|
|
@@ -604,8 +619,11 @@ export function LBProvider({
|
|
|
604
619
|
}));
|
|
605
620
|
}
|
|
606
621
|
setHasSelectedApiKeyCookie(true);
|
|
607
|
-
|
|
608
|
-
|
|
622
|
+
// Ne pas bloquer la validation UI de sélection sur des requêtes status/wallet/storage.
|
|
623
|
+
void refreshBasicStatus();
|
|
624
|
+
setTimeout(() => {
|
|
625
|
+
void refreshStorageStatus();
|
|
626
|
+
}, 100);
|
|
609
627
|
} else {
|
|
610
628
|
throw new Error("No valid authentication method available");
|
|
611
629
|
}
|