@lastbrain/ai-ui-react 1.0.73 → 1.0.74
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/AiChipLabel.d.ts.map +1 -1
- package/dist/components/AiChipLabel.js +10 -7
- package/dist/components/AiContextButton.d.ts +1 -1
- package/dist/components/AiContextButton.d.ts.map +1 -1
- package/dist/components/AiContextButton.js +25 -12
- package/dist/components/AiImageButton.d.ts.map +1 -1
- package/dist/components/AiImageButton.js +32 -16
- package/dist/components/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +15 -5
- package/dist/components/AiModelSelect.d.ts.map +1 -1
- package/dist/components/AiModelSelect.js +3 -1
- package/dist/components/AiPromptPanel.d.ts.map +1 -1
- package/dist/components/AiPromptPanel.js +72 -47
- package/dist/components/AiSelect.d.ts.map +1 -1
- package/dist/components/AiSelect.js +8 -3
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +23 -20
- package/dist/components/AiTextarea.d.ts.map +1 -1
- package/dist/components/AiTextarea.js +19 -6
- package/dist/components/ErrorToast.d.ts.map +1 -1
- package/dist/components/ErrorToast.js +4 -2
- package/dist/components/LBApiKeySelector.d.ts.map +1 -1
- package/dist/components/LBApiKeySelector.js +13 -5
- package/dist/components/LBConnectButton.d.ts.map +1 -1
- package/dist/components/LBConnectButton.js +8 -3
- package/dist/components/LBKeyPicker.d.ts.map +1 -1
- package/dist/components/LBKeyPicker.js +8 -4
- package/dist/components/LBSigninModal.d.ts.map +1 -1
- package/dist/components/LBSigninModal.js +13 -7
- package/dist/components/UsageToast.d.ts.map +1 -1
- package/dist/components/UsageToast.js +4 -2
- package/dist/context/I18nContext.d.ts +15 -0
- package/dist/context/I18nContext.d.ts.map +1 -0
- package/dist/context/I18nContext.js +44 -0
- package/dist/context/LBAuthProvider.d.ts +4 -1
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +3 -2
- package/dist/hooks/useAiCallImage.d.ts.map +1 -1
- package/dist/hooks/useAiCallImage.js +1 -107
- package/dist/hooks/useAiCallText.d.ts.map +1 -1
- package/dist/hooks/useAiCallText.js +1 -25
- package/dist/hooks/useLoadingTimer.d.ts +5 -0
- package/dist/hooks/useLoadingTimer.d.ts.map +1 -0
- package/dist/hooks/useLoadingTimer.js +27 -0
- package/dist/i18n/de.json +62 -0
- package/dist/i18n/en.json +128 -0
- package/dist/i18n/es.json +70 -0
- package/dist/i18n/fr.json +128 -0
- package/dist/i18n/it.json +62 -0
- package/dist/i18n/pt.json +62 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/styles.css +141 -1
- package/package.json +3 -3
- package/src/components/AiChipLabel.tsx +17 -8
- package/src/components/AiContextButton.tsx +44 -20
- package/src/components/AiImageButton.tsx +52 -25
- package/src/components/AiInput.tsx +20 -5
- package/src/components/AiModelSelect.tsx +3 -1
- package/src/components/AiPromptPanel.tsx +177 -59
- package/src/components/AiSelect.tsx +8 -3
- package/src/components/AiStatusButton.tsx +51 -40
- package/src/components/AiTextarea.tsx +24 -6
- package/src/components/ErrorToast.tsx +4 -2
- package/src/components/LBApiKeySelector.tsx +33 -13
- package/src/components/LBConnectButton.tsx +9 -3
- package/src/components/LBKeyPicker.tsx +10 -4
- package/src/components/LBSigninModal.tsx +31 -15
- package/src/components/UsageToast.tsx +4 -2
- package/src/context/I18nContext.tsx +71 -0
- package/src/context/LBAuthProvider.tsx +9 -1
- package/src/hooks/useAiCallImage.ts +1 -149
- package/src/hooks/useAiCallText.ts +1 -30
- package/src/hooks/useLoadingTimer.ts +32 -0
- package/src/i18n/de.json +62 -0
- package/src/i18n/en.json +128 -0
- package/src/i18n/es.json +70 -0
- package/src/i18n/fr.json +128 -0
- package/src/i18n/it.json +62 -0
- package/src/i18n/pt.json +62 -0
- package/src/index.ts +2 -0
- package/src/styles.css +141 -1
|
@@ -3,12 +3,14 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import "../styles/register";
|
|
4
4
|
import { useState, useEffect, useRef, useLayoutEffect, } from "react";
|
|
5
5
|
import { createPortal } from "react-dom";
|
|
6
|
-
import { BookOpen, Search, Sparkles, Star, Tag, Settings } from "lucide-react";
|
|
6
|
+
import { BookOpen, 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";
|
|
10
10
|
import { useModelManagement } from "../hooks/useModelManagement";
|
|
11
11
|
import { AiProvider, useAiContext } from "../context/AiProvider";
|
|
12
|
+
import { useI18n } from "../context/I18nContext";
|
|
13
|
+
import { useLoadingTimer } from "../hooks/useLoadingTimer";
|
|
12
14
|
export function AiPromptPanel(props) {
|
|
13
15
|
const { apiKey, baseUrl } = props;
|
|
14
16
|
let hasContext = false;
|
|
@@ -31,6 +33,7 @@ export function AiPromptPanel(props) {
|
|
|
31
33
|
return _jsx(AiPromptPanelInternal, { ...props });
|
|
32
34
|
}
|
|
33
35
|
function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "modal", models = [], sourceText, children, enableModelManagement = true, modelCategory = "text", availableModels = [], userModels = [], onModelToggle, apiKey, baseUrl, showOnlyUserModels = false, }) {
|
|
36
|
+
const { t } = useI18n();
|
|
34
37
|
const [selectedModel, setSelectedModel] = useState("");
|
|
35
38
|
const [prompt, setPrompt] = useState("");
|
|
36
39
|
const [promptId, setPromptId] = useState(undefined);
|
|
@@ -50,6 +53,7 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
50
53
|
const [isModelManagementOpen, setIsModelManagementOpen] = useState(false);
|
|
51
54
|
const [loadingModels, setLoadingModels] = useState([]);
|
|
52
55
|
const [modelSearchQuery, setModelSearchQuery] = useState("");
|
|
56
|
+
const { formatted: loadingElapsed } = useLoadingTimer(isGenerating);
|
|
53
57
|
const { prompts, loading: promptsLoading, fetchPrompts, incrementStat, } = usePrompts();
|
|
54
58
|
// Hook de gestion des modèles (automatique si enableModelManagement et pas de models/availableModels externes)
|
|
55
59
|
const autoModelManagement = useModelManagement({
|
|
@@ -76,6 +80,7 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
76
80
|
: autoModelManagement.availableModels;
|
|
77
81
|
const effectiveUserModels = userModels.length > 0 ? userModels : autoModelManagement.userModels;
|
|
78
82
|
const effectiveToggleModel = onModelToggle || autoModelManagement.toggleModel;
|
|
83
|
+
const isModelsLoading = autoModelManagement.loading && effectiveAvailableModels.length === 0;
|
|
79
84
|
// Gestion des modèles
|
|
80
85
|
const handleModelToggle = async (modelId, isActive) => {
|
|
81
86
|
if (!effectiveToggleModel)
|
|
@@ -99,7 +104,7 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
99
104
|
id: m.id,
|
|
100
105
|
name: m.name,
|
|
101
106
|
category: m.type === "image" ? "image" : "text",
|
|
102
|
-
provider: "Unknown",
|
|
107
|
+
provider: t("common.unknown", "Unknown"),
|
|
103
108
|
}));
|
|
104
109
|
}
|
|
105
110
|
const categoryModels = effectiveAvailableModels.filter((m) => m.category === modelCategory);
|
|
@@ -309,23 +314,27 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
309
314
|
fontWeight: 600,
|
|
310
315
|
color: "var(--ai-text-secondary)",
|
|
311
316
|
letterSpacing: "0.02em",
|
|
312
|
-
}, children: "
|
|
317
|
+
}, children: t("prompt.modal.generating", "Generating...") }), _jsx("div", { className: "ai-loading-meta", children: t("ai.loading.elapsed", "{seconds}", {
|
|
318
|
+
seconds: loadingElapsed,
|
|
319
|
+
}) })] })), _jsxs("div", { style: {
|
|
313
320
|
...aiStyles.modalHeader,
|
|
314
321
|
position: "sticky",
|
|
315
322
|
top: 0,
|
|
316
323
|
zIndex: 5,
|
|
317
324
|
backdropFilter: "blur(8px)",
|
|
318
|
-
}, children: [_jsx("h2", { style: aiStyles.modalTitle, children: showPromptLibrary
|
|
325
|
+
}, children: [_jsx("h2", { style: aiStyles.modalTitle, children: showPromptLibrary
|
|
326
|
+
? t("prompt.modal.selectPrompt", "Select a Prompt")
|
|
327
|
+
: t("prompt.modal.title", "AI Prompt Configuration") }), _jsx("button", { style: {
|
|
319
328
|
...aiStyles.modalCloseButton,
|
|
320
329
|
...(isCloseHovered && aiStyles.modalCloseButtonHover),
|
|
321
|
-
}, onClick: handleClose, onMouseEnter: () => setIsCloseHovered(true), onMouseLeave: () => setIsCloseHovered(false), "aria-label": "Close", children: "\u00D7" })] }), _jsx("div", { style: {
|
|
330
|
+
}, onClick: handleClose, onMouseEnter: () => setIsCloseHovered(true), onMouseLeave: () => setIsCloseHovered(false), "aria-label": t("common.closeLabel", "Close"), children: "\u00D7" })] }), _jsx("div", { style: {
|
|
322
331
|
...aiStyles.modalBody,
|
|
323
332
|
flex: 1,
|
|
324
333
|
overflow: "auto",
|
|
325
334
|
}, children: !showPromptLibrary ? (_jsxs(_Fragment, { children: [sourceText && (_jsxs("div", { style: {
|
|
326
335
|
...aiStyles.modalInputGroup,
|
|
327
336
|
marginBottom: "16px",
|
|
328
|
-
}, children: [_jsx("label", { style: aiStyles.modalLabel, children: "Source Text" }), _jsx("div", { style: {
|
|
337
|
+
}, children: [_jsx("label", { style: aiStyles.modalLabel, children: t("prompt.modal.sourceText", "Source Text") }), _jsx("div", { style: {
|
|
329
338
|
padding: "12px",
|
|
330
339
|
background: aiStyles.textarea.background,
|
|
331
340
|
border: `1px solid ${aiStyles.input.border}`,
|
|
@@ -343,7 +352,7 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
343
352
|
justifyContent: "space-between",
|
|
344
353
|
alignItems: "center",
|
|
345
354
|
marginBottom: "8px",
|
|
346
|
-
}, children: [_jsx("label", { htmlFor: "model-select", style: aiStyles.modalLabel, children: "AI Model" }), effectiveAvailableModels.length > 0
|
|
355
|
+
}, children: [_jsx("label", { htmlFor: "model-select", style: aiStyles.modalLabel, children: "AI Model" }), isModelsLoading ? (_jsx("span", { className: "ai-inline-skeleton", "aria-hidden": "true" })) : effectiveAvailableModels.length > 0 ? (_jsxs("button", { onClick: () => setIsModelManagementOpen(true), className: "ai-inline-btn", title: t("prompt.modal.manageModels", "Manage models"), children: [_jsx(Settings, { size: 14 }), t("prompt.modal.manageModels", "Manage models"), effectiveAvailableModels.filter((m) => m.category === modelCategory &&
|
|
347
356
|
!effectiveUserModels.includes(m.id)).length > 0 && (_jsxs("span", { style: {
|
|
348
357
|
marginLeft: "2px",
|
|
349
358
|
padding: "2px 6px",
|
|
@@ -353,42 +362,44 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
353
362
|
borderRadius: "10px",
|
|
354
363
|
fontWeight: "600",
|
|
355
364
|
}, children: ["+", effectiveAvailableModels.filter((m) => m.category === modelCategory &&
|
|
356
|
-
!effectiveUserModels.includes(m.id)).length] }))] }))] }), _jsxs("select", { id: "model-select", value: activeModelId, onChange: (e) => setSelectedModel(e.target.value), onFocus: () => setModelFocused(true), onBlur: () => setModelFocused(false), style: {
|
|
365
|
+
!effectiveUserModels.includes(m.id)).length] }))] })) : null] }), _jsxs("select", { id: "model-select", value: activeModelId, onChange: (e) => setSelectedModel(e.target.value), onFocus: () => setModelFocused(true), onBlur: () => setModelFocused(false), style: {
|
|
357
366
|
...aiStyles.select,
|
|
358
367
|
...(modelFocused && aiStyles.selectFocus),
|
|
359
368
|
}, children: [modelOptions.length === 0 && (_jsx("option", { value: "", children: showOnlyUserModels
|
|
360
|
-
? "No active models. Open '
|
|
361
|
-
: "Loading models..." })), modelOptions.map((model) => {
|
|
369
|
+
? t("prompt.modal.noActiveModels", "No active models. Open 'Manage models'.")
|
|
370
|
+
: t("prompt.modal.loadingModels", "Loading models...") })), modelOptions.map((model) => {
|
|
362
371
|
const isActive = effectiveUserModels.includes(model.id);
|
|
363
372
|
return (_jsxs("option", { value: model.id, style: {
|
|
364
373
|
opacity: showAllModels && !isActive ? 0.6 : 1,
|
|
365
|
-
}, children: [model.name, showAllModels && isActive && " ✓", showAllModels && !isActive
|
|
366
|
-
|
|
374
|
+
}, children: [model.name, showAllModels && isActive && " ✓", showAllModels && !isActive
|
|
375
|
+
? ` (${t("common.inactive", "Inactive")})`
|
|
376
|
+
: ""] }, model.id));
|
|
377
|
+
})] }), showAllModels && onModelToggle && (_jsxs("div", { style: {
|
|
367
378
|
fontSize: "11px",
|
|
368
379
|
color: "var(--ai-text-secondary)",
|
|
369
380
|
marginTop: "4px",
|
|
370
|
-
}, children: "\uD83D\uDCA1
|
|
381
|
+
}, children: ["\uD83D\uDCA1", " ", t("prompt.modal.clickSettingsHint", "Click ⚙️ to enable/disable models")] }))] })) : (
|
|
371
382
|
// Version classique
|
|
372
383
|
_jsxs(_Fragment, { children: [_jsx("label", { htmlFor: "model-select", style: aiStyles.modalLabel, children: "AI Model" }), _jsxs("select", { id: "model-select", value: activeModelId, onChange: (e) => setSelectedModel(e.target.value), onFocus: () => setModelFocused(true), onBlur: () => setModelFocused(false), style: {
|
|
373
384
|
...aiStyles.select,
|
|
374
385
|
...(modelFocused && aiStyles.selectFocus),
|
|
375
|
-
}, children: [models.length === 0 && (_jsx("option", { value: "", children: "Loading models..." })), models.map((model) => (_jsx("option", { value: model.id, children: model.name }, model.id)))] })] })) }), _jsxs("div", { style: aiStyles.modalInputGroup, children: [_jsxs("div", { style: {
|
|
386
|
+
}, children: [models.length === 0 && (_jsx("option", { value: "", children: t("prompt.modal.loadingModels", "Loading models...") })), models.map((model) => (_jsx("option", { value: model.id, children: model.name }, model.id)))] })] })) }), _jsxs("div", { style: aiStyles.modalInputGroup, children: [_jsxs("div", { style: {
|
|
376
387
|
display: "flex",
|
|
377
388
|
justifyContent: "space-between",
|
|
378
389
|
alignItems: "center",
|
|
379
390
|
marginBottom: "8px",
|
|
380
|
-
}, children: [_jsxs("label", { htmlFor: "prompt-input", style: aiStyles.modalLabel, children: ["Prompt", _jsx("span", { style: {
|
|
391
|
+
}, children: [_jsxs("label", { htmlFor: "prompt-input", style: aiStyles.modalLabel, children: [t("prompt.modal.prompt", "Prompt"), _jsx("span", { style: {
|
|
381
392
|
color: "var(--ai-text-secondary)",
|
|
382
393
|
marginLeft: "4px",
|
|
383
394
|
fontSize: "12px",
|
|
384
395
|
fontWeight: 400,
|
|
385
|
-
}, children: "(Cmd/Ctrl + Enter to submit)" })] }), filteredPrompts.length > 0
|
|
386
|
-
? "Enter your AI prompt... e.g., '
|
|
387
|
-
: "Enter your AI prompt... e.g., 'Write a blog post about AI', 'Generate product description'", rows: 6, style: {
|
|
396
|
+
}, children: t("prompt.modal.promptHint", "(Cmd/Ctrl + Enter to submit)") })] }), promptsLoading ? (_jsx("span", { className: "ai-inline-skeleton", "aria-hidden": "true" })) : filteredPrompts.length > 0 ? (_jsxs("button", { onClick: () => setShowPromptLibrary(true), className: "ai-inline-btn", 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
|
|
397
|
+
? t("prompt.modal.promptPlaceholderWithSource", "Enter your AI prompt... e.g., 'Fix grammar', 'Make it more professional', 'Translate to English'")
|
|
398
|
+
: t("prompt.modal.promptPlaceholderNoSource", "Enter your AI prompt... e.g., 'Write a blog post about AI', 'Generate product description'"), rows: 6, style: {
|
|
388
399
|
...aiStyles.textarea,
|
|
389
400
|
padding: "12px 16px",
|
|
390
401
|
...(promptFocused && aiStyles.textareaFocus),
|
|
391
|
-
} })] })] })) : (_jsxs("div", { children: [
|
|
402
|
+
} })] })] })) : (_jsxs("div", { children: [_jsxs("button", { onClick: () => setShowPromptLibrary(false), style: {
|
|
392
403
|
padding: "8px 0",
|
|
393
404
|
fontSize: "14px",
|
|
394
405
|
color: "var(--ai-primary)",
|
|
@@ -399,7 +410,7 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
399
410
|
display: "flex",
|
|
400
411
|
alignItems: "center",
|
|
401
412
|
gap: "4px",
|
|
402
|
-
}, children: "\u2190 Back to form" }), _jsxs("div", { style: { marginBottom: "12px" }, children: [_jsxs("div", { style: {
|
|
413
|
+
}, children: ["\u2190 ", t("prompt.modal.backToFormNoArrow", "Back to form")] }), _jsxs("div", { style: { marginBottom: "12px" }, children: [_jsxs("div", { style: {
|
|
403
414
|
...aiStyles.inputWrapper,
|
|
404
415
|
marginBottom: "10px",
|
|
405
416
|
}, children: [_jsx(Search, { size: 16, style: {
|
|
@@ -408,7 +419,7 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
408
419
|
top: "50%",
|
|
409
420
|
transform: "translateY(-50%)",
|
|
410
421
|
color: aiStyles.tooltipLabel.color,
|
|
411
|
-
} }), _jsx("input", { value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: "Search prompts...", style: {
|
|
422
|
+
} }), _jsx("input", { value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: t("prompt.modal.searchPrompts", "Search prompts..."), style: {
|
|
412
423
|
...aiStyles.input,
|
|
413
424
|
padding: "10px 12px 10px 36px",
|
|
414
425
|
} })] }), availableTags.length > 0 && (_jsxs("div", { style: {
|
|
@@ -422,7 +433,7 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
422
433
|
gap: "6px",
|
|
423
434
|
fontSize: "12px",
|
|
424
435
|
color: aiStyles.tooltipLabel.color,
|
|
425
|
-
}, children: [_jsx(Tag, { size: 14 }), "Tags"] }), _jsx("button", { onClick: () => setSelectedTag("all"), style: {
|
|
436
|
+
}, children: [_jsx(Tag, { size: 14 }), t("prompt.modal.tags", "Tags")] }), _jsx("button", { onClick: () => setSelectedTag("all"), style: {
|
|
426
437
|
...aiStyles.chip,
|
|
427
438
|
borderColor: selectedTag === "all"
|
|
428
439
|
? "var(--ai-primary)"
|
|
@@ -430,7 +441,7 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
430
441
|
background: selectedTag === "all"
|
|
431
442
|
? "color-mix(in srgb, var(--ai-primary) 20%, transparent)"
|
|
432
443
|
: aiStyles.chip.background,
|
|
433
|
-
}, children: "All" }), availableTags.map((tag) => (_jsx("button", { onClick: () => setSelectedTag(tag), style: {
|
|
444
|
+
}, children: t("common.all", "All") }), availableTags.map((tag) => (_jsx("button", { onClick: () => setSelectedTag(tag), style: {
|
|
434
445
|
...aiStyles.chip,
|
|
435
446
|
borderColor: selectedTag === tag
|
|
436
447
|
? "var(--ai-primary)"
|
|
@@ -438,11 +449,17 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
438
449
|
background: selectedTag === tag
|
|
439
450
|
? "color-mix(in srgb, var(--ai-primary) 20%, transparent)"
|
|
440
451
|
: aiStyles.chip.background,
|
|
441
|
-
}, children: tag }, tag)))] }))] }), promptsLoading ? (_jsx("div", { style: {
|
|
452
|
+
}, children: tag }, tag)))] }))] }), promptsLoading ? (_jsx("div", { style: {
|
|
453
|
+
display: "flex",
|
|
454
|
+
flexDirection: "column",
|
|
455
|
+
gap: "12px",
|
|
456
|
+
maxHeight: "400px",
|
|
457
|
+
overflow: "auto",
|
|
458
|
+
}, children: Array.from({ length: 4 }).map((_, idx) => (_jsxs("div", { className: "ai-list-skeleton", children: [_jsx("div", { className: "ai-list-skeleton__line ai-list-skeleton__line--lg" }), _jsx("div", { className: "ai-list-skeleton__line ai-list-skeleton__line--md" })] }, `prompt-skeleton-${idx}`))) })) : visiblePrompts.length === 0 ? (_jsx("div", { style: {
|
|
442
459
|
textAlign: "center",
|
|
443
460
|
padding: "40px 0",
|
|
444
461
|
color: "var(--ai-text-secondary)",
|
|
445
|
-
}, children: "No prompts available for this model type" })) : (_jsxs("div", { style: {
|
|
462
|
+
}, children: t("prompt.modal.noPrompts", "No prompts available for this model type") })) : (_jsxs("div", { style: {
|
|
446
463
|
display: "flex",
|
|
447
464
|
flexDirection: "column",
|
|
448
465
|
gap: "12px",
|
|
@@ -454,7 +471,7 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
454
471
|
fontWeight: 600,
|
|
455
472
|
textTransform: "uppercase",
|
|
456
473
|
marginBottom: "8px",
|
|
457
|
-
}, children: "Favorites" }), _jsx("div", { style: {
|
|
474
|
+
}, children: t("prompt.modal.favorites", "Favorites") }), _jsx("div", { style: {
|
|
458
475
|
display: "flex",
|
|
459
476
|
flexDirection: "column",
|
|
460
477
|
gap: "12px",
|
|
@@ -499,7 +516,7 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
499
516
|
textTransform: "uppercase",
|
|
500
517
|
marginTop: favoritePrompts.length > 0 ? "12px" : undefined,
|
|
501
518
|
marginBottom: "8px",
|
|
502
|
-
}, children: "All prompts" }), _jsx("div", { style: {
|
|
519
|
+
}, children: t("prompt.modal.allPrompts", "All prompts") }), _jsx("div", { style: {
|
|
503
520
|
display: "flex",
|
|
504
521
|
flexDirection: "column",
|
|
505
522
|
gap: "12px",
|
|
@@ -540,7 +557,11 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
540
557
|
bottom: 0,
|
|
541
558
|
zIndex: 5,
|
|
542
559
|
backdropFilter: "blur(8px)",
|
|
543
|
-
}, children: [_jsx("button", { onClick: handleClose, className: "ai-btn ai-btn--ghost", children: "Cancel" }), _jsx("button", { onClick: handleSubmit, disabled: !selectedModel || !prompt.trim(), className: "ai-btn ai-btn--primary", children:
|
|
560
|
+
}, children: [_jsx("button", { onClick: handleClose, className: "ai-btn ai-btn--ghost", children: t("prompt.modal.cancel", "Cancel") }), _jsx("button", { onClick: handleSubmit, disabled: isGenerating || !selectedModel || !prompt.trim(), className: "ai-btn ai-btn--primary", children: isGenerating ? (_jsxs("span", { className: "ai-loading-stack", children: [_jsxs("span", { className: "ai-loading-row", children: [_jsx(Loader2, { size: 16, className: "ai-spinner" }), _jsx("span", { children: sourceText
|
|
561
|
+
? t("prompt.modal.transforming", "Transforming...")
|
|
562
|
+
: t("prompt.modal.generating", "Generating...") })] }), _jsx("span", { className: "ai-loading-meta", children: t("ai.loading.elapsed", "{seconds}", {
|
|
563
|
+
seconds: loadingElapsed,
|
|
564
|
+
}) })] })) : sourceText ? (t("prompt.modal.transform", "Transform with AI")) : (t("prompt.modal.generate", "Generate with AI")) })] })] }), enableModelManagement && isModelManagementOpen && (_jsxs("div", { style: {
|
|
544
565
|
...aiStyles.modal,
|
|
545
566
|
zIndex: 2147483646, // Au-dessus du modal principal
|
|
546
567
|
}, children: [_jsx("div", { style: {
|
|
@@ -557,10 +578,10 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
557
578
|
display: "flex",
|
|
558
579
|
flexDirection: "column",
|
|
559
580
|
boxShadow: "var(--ai-shadow-lg)",
|
|
560
|
-
}, children: [_jsxs("div", { style: aiStyles.modalHeader, children: [_jsx("h2", { style: aiStyles.modalTitle, children: "
|
|
581
|
+
}, children: [_jsxs("div", { style: aiStyles.modalHeader, children: [_jsx("h2", { style: aiStyles.modalTitle, children: t("prompt.modal.modelMgmtTitle", "AI Model Management") }), _jsx("button", { style: aiStyles.modalCloseButton, onClick: () => {
|
|
561
582
|
setIsModelManagementOpen(false);
|
|
562
583
|
setModelSearchQuery("");
|
|
563
|
-
}, "aria-label": "Close", children: "\u00D7" })] }), _jsxs("div", { style: {
|
|
584
|
+
}, "aria-label": t("common.closeLabel", "Close"), children: "\u00D7" })] }), _jsxs("div", { style: {
|
|
564
585
|
...aiStyles.modalBody,
|
|
565
586
|
flex: 1,
|
|
566
587
|
overflow: "auto",
|
|
@@ -568,11 +589,14 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
568
589
|
fontSize: "14px",
|
|
569
590
|
color: "var(--ai-muted)",
|
|
570
591
|
margin: "0 0 8px 0",
|
|
571
|
-
}, children: "
|
|
592
|
+
}, children: t("prompt.modal.modelMgmtSubtitle", "Enable or disable models based on your needs") }), _jsx("p", { style: {
|
|
572
593
|
fontSize: "12px",
|
|
573
594
|
color: "var(--ai-text-tertiary)",
|
|
574
595
|
margin: "0 0 16px 0",
|
|
575
|
-
}, children:
|
|
596
|
+
}, children: t("prompt.modal.modelAvailableCount", "{available} models available • {active} enabled", {
|
|
597
|
+
available: effectiveAvailableModels.filter((m) => m.category === modelCategory).length,
|
|
598
|
+
active: effectiveUserModels.filter((id) => effectiveAvailableModels.some((m) => m.id === id && m.category === modelCategory)).length,
|
|
599
|
+
}) }), _jsxs("div", { style: {
|
|
576
600
|
...aiStyles.inputWrapper,
|
|
577
601
|
marginBottom: "16px",
|
|
578
602
|
}, children: [_jsx(Search, { size: 16, style: {
|
|
@@ -581,11 +605,11 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
581
605
|
top: "50%",
|
|
582
606
|
transform: "translateY(-50%)",
|
|
583
607
|
color: "var(--ai-text-tertiary)",
|
|
584
|
-
} }), _jsx("input", { value: modelSearchQuery, onChange: (e) => setModelSearchQuery(e.target.value), placeholder: "
|
|
608
|
+
} }), _jsx("input", { value: modelSearchQuery, onChange: (e) => setModelSearchQuery(e.target.value), placeholder: t("prompt.modal.searchModel", "Search a model..."), style: {
|
|
585
609
|
...aiStyles.input,
|
|
586
610
|
padding: "10px 12px 10px 36px",
|
|
587
611
|
background: "var(--ai-bg)",
|
|
588
|
-
} })] })] }), _jsxs("div", { className: "ai-model-mgmt-list", children: [effectiveAvailableModels
|
|
612
|
+
} })] })] }), _jsxs("div", { className: "ai-model-mgmt-list", children: [isModelsLoading ? (_jsx(_Fragment, { children: Array.from({ length: 4 }).map((_, idx) => (_jsxs("div", { className: "ai-list-skeleton", children: [_jsx("div", { className: "ai-list-skeleton__line ai-list-skeleton__line--lg" }), _jsx("div", { className: "ai-list-skeleton__line ai-list-skeleton__line--md" })] }, `model-skeleton-${idx}`))) })) : null, effectiveAvailableModels
|
|
589
613
|
.filter((model) => {
|
|
590
614
|
if (model.category !== modelCategory)
|
|
591
615
|
return false;
|
|
@@ -624,24 +648,25 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
624
648
|
height: 0,
|
|
625
649
|
position: "absolute",
|
|
626
650
|
} }), _jsx("span", { className: `ai-toggle ${isActive ? "ai-toggle--active" : ""}` })] })] }, modelData.id));
|
|
627
|
-
}),
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
model.
|
|
635
|
-
|
|
636
|
-
|
|
651
|
+
}), !isModelsLoading &&
|
|
652
|
+
effectiveAvailableModels.filter((model) => {
|
|
653
|
+
if (model.category !== modelCategory)
|
|
654
|
+
return false;
|
|
655
|
+
if (!modelSearchQuery.trim())
|
|
656
|
+
return true;
|
|
657
|
+
const query = modelSearchQuery.toLowerCase();
|
|
658
|
+
return (model.name.toLowerCase().includes(query) ||
|
|
659
|
+
model.provider.toLowerCase().includes(query) ||
|
|
660
|
+
model.description?.toLowerCase().includes(query));
|
|
661
|
+
}).length === 0 && (_jsx("div", { style: {
|
|
637
662
|
textAlign: "center",
|
|
638
663
|
padding: "32px 16px",
|
|
639
664
|
color: "var(--ai-text-tertiary)",
|
|
640
665
|
fontSize: "14px",
|
|
641
666
|
}, children: modelSearchQuery.trim()
|
|
642
|
-
? "
|
|
643
|
-
: "
|
|
667
|
+
? t("prompt.modal.noModelMatch", "No model matches your search")
|
|
668
|
+
: t("prompt.modal.noModelAvailable", "No models available") }))] })] }), _jsx("div", { style: aiStyles.modalFooter, children: _jsx("button", { onClick: () => {
|
|
644
669
|
setIsModelManagementOpen(false);
|
|
645
670
|
setModelSearchQuery("");
|
|
646
|
-
}, className: "ai-btn ai-btn--ghost", children: "
|
|
671
|
+
}, className: "ai-btn ai-btn--ghost", children: t("prompt.modal.closeModelMgmt", "Close") }) })] })] }))] }), portalRoot);
|
|
647
672
|
}
|
|
@@ -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;
|
|
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,EACL,MAAM,EACN,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,GAAG,WAAW,EACf,EAAE,aAAa,2CAgJf"}
|
|
@@ -11,7 +11,9 @@ import { LBSigninModal } from "./LBSigninModal";
|
|
|
11
11
|
import { handleAIError } from "../utils/errorHandler";
|
|
12
12
|
import { useLB } from "../context/LBAuthProvider";
|
|
13
13
|
import { useAiContext } from "../context/AiProvider";
|
|
14
|
+
import { useI18n } from "../context/I18nContext";
|
|
14
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 }) {
|
|
16
|
+
const { t } = useI18n();
|
|
15
17
|
const [isOpen, setIsOpen] = useState(false);
|
|
16
18
|
const [showAuthModal, setShowAuthModal] = useState(false);
|
|
17
19
|
const { showUsageToast, toastData, toastKey, clearToast } = useUsageToast();
|
|
@@ -68,7 +70,10 @@ export function AiSelect({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode
|
|
|
68
70
|
});
|
|
69
71
|
if (result.text) {
|
|
70
72
|
onValue?.(result.text);
|
|
71
|
-
onToast?.({
|
|
73
|
+
onToast?.({
|
|
74
|
+
type: "success",
|
|
75
|
+
message: t("ai.select.suggestionReady", "AI suggestion ready"),
|
|
76
|
+
});
|
|
72
77
|
showUsageToast(result);
|
|
73
78
|
}
|
|
74
79
|
}
|
|
@@ -82,6 +87,6 @@ export function AiSelect({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode
|
|
|
82
87
|
const sizeClass = `ai-size-${size}`;
|
|
83
88
|
const radiusClass = `ai-radius-${radius}`;
|
|
84
89
|
return (_jsxs("div", { className: `ai-control-group ai-glow ${className || ""}`, children: [_jsxs("div", { className: `ai-shell ${sizeClass} ${radiusClass}`, children: [_jsx("select", { ...selectProps, className: `ai-control ai-control-input ai-control-select ${sizeClass} ${radiusClass}`, disabled: disabled || loading, children: children }), _jsx("span", { className: "ai-control-select-chevron", "aria-hidden": "true", children: _jsx(ChevronDown, { size: 14 }) }), _jsx("button", { className: `ai-control-action ai-spark ${sizeClass} ${radiusClass}`, onClick: handleOpenPanel, title: shouldShowSparkles
|
|
85
|
-
? "
|
|
86
|
-
: "
|
|
90
|
+
? t("ai.generate", "Generate with AI")
|
|
91
|
+
: t("auth.connectToUseAi", "Sign in to use AI"), disabled: disabled || loading, type: "button", children: loading ? (_jsx(Loader2, { size: 16, className: "ai-spinner" })) : shouldShowSparkles ? (_jsx(Sparkles, { size: 16 })) : (_jsx(Lock, { size: 16 })) })] }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: models || [] })), Boolean(toastData) && (_jsx(UsageToast, { result: toastData, position: "bottom-right", onComplete: clearToast }, toastKey)), _jsx(LBSigninModal, { isOpen: showAuthModal, onClose: () => setShowAuthModal(false) })] }));
|
|
87
92
|
}
|
|
@@ -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;
|
|
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,2CAyhBrB"}
|
|
@@ -8,30 +8,31 @@ import { LBContext, } from "../context/LBAuthProvider";
|
|
|
8
8
|
import { AiContext } from "../context/AiProvider";
|
|
9
9
|
import { LBApiKeySelector } from "./LBApiKeySelector";
|
|
10
10
|
import { LBSigninModal } from "./LBSigninModal";
|
|
11
|
+
import { useI18n } from "../context/I18nContext";
|
|
11
12
|
const QUICK_LINKS = [
|
|
12
13
|
{
|
|
13
14
|
href: "https://prompt.lastbrain.io/auth/ai/tokens",
|
|
14
|
-
|
|
15
|
+
titleKey: "status.dashboard",
|
|
15
16
|
icon: BarChart3,
|
|
16
17
|
},
|
|
17
18
|
{
|
|
18
19
|
href: "https://prompt.lastbrain.io/auth/ai/history",
|
|
19
|
-
|
|
20
|
+
titleKey: "status.history",
|
|
20
21
|
icon: History,
|
|
21
22
|
},
|
|
22
23
|
{
|
|
23
24
|
href: "https://prompt.lastbrain.io/auth/ai/settings",
|
|
24
|
-
|
|
25
|
+
titleKey: "status.settings",
|
|
25
26
|
icon: Settings,
|
|
26
27
|
},
|
|
27
28
|
{
|
|
28
29
|
href: "https://prompt.lastbrain.io/auth/ai/prompts",
|
|
29
|
-
|
|
30
|
+
titleKey: "status.prompts",
|
|
30
31
|
icon: FileText,
|
|
31
32
|
},
|
|
32
33
|
{
|
|
33
34
|
href: "https://prompt.lastbrain.io/auth/folder",
|
|
34
|
-
|
|
35
|
+
titleKey: "status.folders",
|
|
35
36
|
icon: Folder,
|
|
36
37
|
},
|
|
37
38
|
];
|
|
@@ -64,6 +65,7 @@ function UsageCircle({ percentage }) {
|
|
|
64
65
|
: "ai-usage-circle-text--low", fontWeight: "700", children: [safe.toFixed(0), "%"] })] }));
|
|
65
66
|
}
|
|
66
67
|
export function AiStatusButton({ status, loading = false, className = "", size = "md", radius = "full", }) {
|
|
68
|
+
const { t } = useI18n();
|
|
67
69
|
let lbStatus;
|
|
68
70
|
let user = null;
|
|
69
71
|
let logout;
|
|
@@ -272,28 +274,29 @@ export function AiStatusButton({ status, loading = false, className = "", size =
|
|
|
272
274
|
.filter(Boolean)
|
|
273
275
|
.join(" ");
|
|
274
276
|
const tooltipNode = showTooltip && canPortal
|
|
275
|
-
? createPortal(_jsx("div", { ref: tooltipRef, className: "ai-popover ai-tooltip ai-status-tooltip", style: tooltipStyle, onMouseEnter: () => setShowTooltip(true), onMouseLeave: closeTooltip, children: lbStatus === "ready" && user ? (_jsx(_Fragment, { children: _jsxs("div", { className: "ai-popover-body", children: [_jsx("div", { className: "ai-popover-header", children: "API Status" }), _jsx("div", { className: "ai-popover-section ai-popover-section--first", children: _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: "User" }), _jsx("span", { className: "ai-popover-value ai-truncate
|
|
277
|
+
? createPortal(_jsx("div", { ref: tooltipRef, className: "ai-popover ai-tooltip ai-status-tooltip", style: tooltipStyle, onMouseEnter: () => setShowTooltip(true), onMouseLeave: closeTooltip, children: lbStatus === "ready" && user ? (_jsx(_Fragment, { children: _jsxs("div", { className: "ai-popover-body", children: [_jsx("div", { className: "ai-popover-header", children: t("status.title", "API Status") }), _jsx("div", { className: "ai-popover-section ai-popover-section--first", children: _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: t("status.user", "User") }), _jsx("span", { className: "ai-popover-value ai-truncate", style: { maxWidth: 200 }, children: user.email })] }) }), _jsxs("div", { className: "ai-popover-section", children: [_jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: t("status.apiKey", "API Key") }), _jsxs("div", { className: "ai-row", children: [lbIsLoadingStatus || isImplicitStatusLoading ? (_jsx("div", { className: "ai-kv-skeleton ai-kv-skeleton--110" })) : (_jsx("span", { className: "ai-popover-value", children: effectiveStatus?.apiKey?.name ||
|
|
276
278
|
effectiveStatus?.api_key?.name ||
|
|
277
|
-
"Unknown" })), switchApiKey &&
|
|
279
|
+
t("common.unknown", "Unknown") })), switchApiKey &&
|
|
278
280
|
!isApiKeyAuthMode &&
|
|
279
281
|
!isImplicitStatusLoading ? (_jsx("button", { type: "button", className: "ai-icon-btn", onClick: (e) => {
|
|
280
282
|
e.stopPropagation();
|
|
281
283
|
setShowTooltip(false);
|
|
282
284
|
setShowApiKeySelector(true);
|
|
283
|
-
}, title: "
|
|
284
|
-
!effectiveStatus?.apiKey?.env ? (_jsx("div", { className: "ai-kv-skeleton
|
|
285
|
+
}, title: t("status.changeApiKey", "Switch API key"), children: _jsx(ArrowRightLeft, { size: 12 }) })) : null] })] }), _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: t("status.env", "Env") }), (lbIsLoadingStatus || isImplicitStatusLoading) &&
|
|
286
|
+
!effectiveStatus?.apiKey?.env ? (_jsx("div", { className: "ai-kv-skeleton ai-kv-skeleton--48" })) : (_jsx("span", { className: "ai-popover-value", children: effectiveStatus?.apiKey?.env ||
|
|
285
287
|
effectiveStatus?.api_key?.env ||
|
|
286
|
-
"N/A" }))] }), _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: "Rate Limit" }), (lbIsLoadingStatus || isImplicitStatusLoading) &&
|
|
287
|
-
!effectiveStatus?.apiKey?.rate_limit_rpm ? (_jsx("div", { className: "ai-kv-skeleton
|
|
288
|
+
"N/A" }))] }), _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: t("status.rateLimit", "Rate Limit") }), (lbIsLoadingStatus || isImplicitStatusLoading) &&
|
|
289
|
+
!effectiveStatus?.apiKey?.rate_limit_rpm ? (_jsx("div", { className: "ai-kv-skeleton ai-kv-skeleton--92" })) : (_jsxs("span", { className: "ai-popover-value", children: [effectiveStatus?.apiKey?.rate_limit_rpm ||
|
|
288
290
|
effectiveStatus?.api_key?.rate_limit_rpm ||
|
|
289
|
-
0, " ", "req/min"] }))] }), _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: "Auth" }), _jsx("span", { className: "ai-popover-value", children: lbIsLoadingStatus || isImplicitStatusLoading
|
|
291
|
+
0, " ", "req/min"] }))] }), _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: t("status.auth", "Auth") }), _jsx("span", { className: "ai-popover-value", children: lbIsLoadingStatus || isImplicitStatusLoading
|
|
290
292
|
? "..."
|
|
291
293
|
: authTypeValue ||
|
|
292
294
|
lbStatus ||
|
|
293
|
-
"unknown" })] })] }), _jsxs("div", { className: "ai-popover-section", children: [_jsx("div", { className: "ai-popover-header
|
|
294
|
-
!effectiveStatus?.balance ? (_jsxs("div", { className: "ai-row", children: [_jsx("div", { className: "ai-kv-skeleton
|
|
295
|
+
t("status.unknown", "unknown") })] })] }), _jsxs("div", { className: "ai-popover-section", children: [_jsx("div", { className: "ai-popover-header ai-popover-header--sub", children: t("status.wallet", "Wallet") }), _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: t("status.total", "Total") }), (lbIsLoadingStatus || isImplicitStatusLoading) &&
|
|
296
|
+
!effectiveStatus?.balance ? (_jsxs("div", { className: "ai-row", children: [_jsx("div", { className: "ai-kv-skeleton ai-kv-skeleton--120" }), _jsx("div", { className: "ai-kv-skeleton ai-kv-skeleton--circle" })] })) : (_jsxs("div", { className: "ai-row", children: [_jsxs("span", { className: "ai-popover-value", children: ["$", fixed(balanceUsed, 2), " / $", fixed(balanceTotal, 2)] }), _jsx(UsageCircle, { percentage: balancePct })] }))] })] }), _jsxs("div", { className: "ai-popover-section", children: [_jsx("div", { className: "ai-popover-header ai-popover-header--sub", children: t("status.storage", "Storage") }), _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: t("status.total", "Total") }), lbIsLoadingStorage || isImplicitStorageLoading ? (_jsxs("div", { className: "ai-row", children: [_jsx("div", { className: "ai-kv-skeleton ai-kv-skeleton--120" }), _jsx("div", { className: "ai-kv-skeleton ai-kv-skeleton--circle" })] })) : (_jsxs("div", { className: "ai-row", children: [_jsxs("span", { className: "ai-popover-value", children: [formatStorage(storageUsed), " /", " ", formatStorage(storageTotal)] }), _jsx(UsageCircle, { percentage: storagePct })] }))] })] }), _jsxs("div", { className: "ai-status-actions", children: [QUICK_LINKS.map((item) => {
|
|
295
297
|
const Icon = item.icon;
|
|
296
|
-
|
|
298
|
+
const label = t(item.titleKey, item.titleKey);
|
|
299
|
+
return (_jsx("button", { type: "button", className: "ai-status-action-btn", onClick: () => window.open(item.href, "_blank"), title: label, "data-ai-tip": label, children: _jsx(Icon, { size: 17 }) }, item.href));
|
|
297
300
|
}), logout && !isApiKeyAuthMode ? (_jsx("button", { type: "button", className: "ai-status-action-btn ai-status-action-btn--danger", onClick: async () => {
|
|
298
301
|
try {
|
|
299
302
|
await logout();
|
|
@@ -305,12 +308,12 @@ export function AiStatusButton({ status, loading = false, className = "", size =
|
|
|
305
308
|
catch (error) {
|
|
306
309
|
console.error("Logout failed:", error);
|
|
307
310
|
}
|
|
308
|
-
}, title: "Logout", children: _jsx(LogOut, { size: 17 }) })) : null] })] }) })) : (_jsxs("div", { className: "ai-popover-body", children: [_jsx("div", { className: "ai-popover-header", children: "LastBrain Authentication" }), _jsx("p", { className: "ai-signin-subtitle
|
|
311
|
+
}, title: t("status.logout", "Logout"), "data-ai-tip": t("status.logout", "Logout"), children: _jsx(LogOut, { size: 17 }) })) : null] })] }) })) : (_jsxs("div", { className: "ai-popover-body", children: [_jsx("div", { className: "ai-popover-header", children: t("status.lastbrainAuth", "LastBrain Authentication") }), _jsx("p", { className: "ai-signin-subtitle", style: { marginTop: 0 }, children: t("status.connectToAccess", "Sign in to access AI features.") }), _jsx("button", { type: "button", className: "ai-btn ai-btn--auth", style: { width: "100%", marginTop: 8 }, onClick: () => {
|
|
309
312
|
setShowSigninModal(true);
|
|
310
313
|
setShowTooltip(false);
|
|
311
|
-
}, children: "
|
|
314
|
+
}, children: t("auth.signIn", "Sign in") })] })) }), document.body)
|
|
312
315
|
: null;
|
|
313
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", {
|
|
316
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { style: { position: "relative", display: "inline-block" }, children: [_jsx("button", { ref: buttonRef, className: triggerClass, onMouseEnter: openTooltip, onMouseLeave: closeTooltip, onClick: () => {
|
|
314
317
|
if (requiresApiKeySelection) {
|
|
315
318
|
setShowApiKeySelector(true);
|
|
316
319
|
return;
|
|
@@ -319,8 +322,8 @@ export function AiStatusButton({ status, loading = false, className = "", size =
|
|
|
319
322
|
setShowSigninModal(true);
|
|
320
323
|
}
|
|
321
324
|
}, disabled: loading || isSelectingApiKey, title: requiresApiKeySelection
|
|
322
|
-
? "
|
|
323
|
-
: "
|
|
325
|
+
? t("status.selectApiKey", "Select an API key")
|
|
326
|
+
: t("status.view", "View status"), "aria-label": t("status.aiStatusAria", "AI status"), children: renderTriggerIcon() }), showCornerLoading ? (_jsx("span", { className: "ai-status-loading-dot", "aria-hidden": "true", children: _jsx(Loader2, { size: 7, className: "ai-spinner" }) })) : null] }), tooltipNode, showApiKeySelector && apiKeys.length > 0 ? (_jsx(LBApiKeySelector, { isOpen: showApiKeySelector, apiKeys: apiKeys, onSelect: async (keyId) => {
|
|
324
327
|
setIsSelectingApiKey(true);
|
|
325
328
|
try {
|
|
326
329
|
if (!switchApiKey) {
|
|
@@ -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;
|
|
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;CACnB;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,IAAW,EACX,MAAa,EACb,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAgB,EAChB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,aAAa,EACjB,EAAE,eAAe,2CA+OjB"}
|
|
@@ -11,7 +11,10 @@ import { handleAIError } from "../utils/errorHandler";
|
|
|
11
11
|
import { useLB } from "../context/LBAuthProvider";
|
|
12
12
|
import { LBSigninModal } from "./LBSigninModal";
|
|
13
13
|
import { useAiContext } from "../context/AiProvider";
|
|
14
|
+
import { useI18n } from "../context/I18nContext";
|
|
15
|
+
import { useLoadingTimer } from "../hooks/useLoadingTimer";
|
|
14
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 }) {
|
|
17
|
+
const { t } = useI18n();
|
|
15
18
|
const [isOpen, setIsOpen] = useState(false);
|
|
16
19
|
const [showAuthModal, setShowAuthModal] = useState(false);
|
|
17
20
|
const [textareaValue, setTextareaValue] = useState(textareaProps.value?.toString() ||
|
|
@@ -48,6 +51,7 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
48
51
|
modelType: "text-or-language",
|
|
49
52
|
});
|
|
50
53
|
const { generateText, loading } = useAiCallText({ baseUrl, apiKeyId });
|
|
54
|
+
const { formatted: loadingElapsed } = useLoadingTimer(loading);
|
|
51
55
|
const hasConfiguration = Boolean(model && prompt);
|
|
52
56
|
const isAuthReady = lbStatus === "ready" || Boolean(process.env.LB_API_KEY);
|
|
53
57
|
const shouldShowSparkles = isAuthReady && !disabled;
|
|
@@ -82,12 +86,18 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
82
86
|
textareaRef.current.value = result.text;
|
|
83
87
|
}
|
|
84
88
|
onValue?.(result.text);
|
|
85
|
-
onToast?.({
|
|
89
|
+
onToast?.({
|
|
90
|
+
type: "success",
|
|
91
|
+
message: t("ai.generationSuccess", "AI generation successful"),
|
|
92
|
+
});
|
|
86
93
|
showUsageToast(result);
|
|
87
94
|
}
|
|
88
95
|
}
|
|
89
96
|
catch (error) {
|
|
90
|
-
onToast?.({
|
|
97
|
+
onToast?.({
|
|
98
|
+
type: "error",
|
|
99
|
+
message: t("ai.generationError", "Failed to generate text"),
|
|
100
|
+
});
|
|
91
101
|
}
|
|
92
102
|
finally {
|
|
93
103
|
setIsOpen(false);
|
|
@@ -114,7 +124,10 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
114
124
|
textareaRef.current.value = result.text;
|
|
115
125
|
}
|
|
116
126
|
onValue?.(result.text);
|
|
117
|
-
onToast?.({
|
|
127
|
+
onToast?.({
|
|
128
|
+
type: "success",
|
|
129
|
+
message: t("ai.generationSuccess", "AI generation successful"),
|
|
130
|
+
});
|
|
118
131
|
showUsageToast(result);
|
|
119
132
|
}
|
|
120
133
|
}
|
|
@@ -146,8 +159,8 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
146
159
|
}, onBlur: (e) => {
|
|
147
160
|
textareaProps.onBlur?.(e);
|
|
148
161
|
}, "aria-invalid": Boolean(textareaProps["aria-invalid"]), disabled: disabled || loading }), _jsx("button", { className: `ai-control-action ai-spark ${sizeClass} ${radiusClass}`, onClick: hasConfiguration ? handleQuickGenerate : handleOpenPanel, disabled: disabled || loading || !isAuthReady, type: "button", title: !isAuthReady
|
|
149
|
-
? "Authentication required"
|
|
162
|
+
? t("auth.required", "Authentication required")
|
|
150
163
|
: hasConfiguration
|
|
151
|
-
? "Generate with AI"
|
|
152
|
-
: "Setup AI", children: loading ? (_jsx(Loader2, { size: 16, className: "ai-spinner" })) : shouldShowSparkles ? (_jsx(Sparkles, { size: 16 })) : (_jsx(Lock, { size: 16 })) })] }), 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) })] }));
|
|
164
|
+
? 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) })] }));
|
|
153
166
|
}
|