@lastbrain/ai-ui-react 1.0.9 → 1.0.10
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/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +2 -2
- package/dist/components/AiPromptPanel.d.ts +4 -2
- package/dist/components/AiPromptPanel.d.ts.map +1 -1
- package/dist/components/AiPromptPanel.js +127 -14
- package/dist/components/AiStatusButton.js +3 -3
- package/dist/components/AiTextarea.d.ts.map +1 -1
- package/dist/components/AiTextarea.js +2 -2
- package/package.json +1 -1
- package/src/components/AiInput.tsx +3 -1
- package/src/components/AiPromptPanel.tsx +246 -52
- package/src/components/AiStatusButton.tsx +3 -3
- package/src/components/AiTextarea.tsx +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiInput.d.ts","sourceRoot":"","sources":["../../src/components/AiInput.tsx"],"names":[],"mappings":"AAEA,OAAc,EAAoB,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAM5C,MAAM,WAAW,YACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC;IACxD,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC7B;AAED,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAgB,EAChB,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,OAAc,EAAoB,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAM5C,MAAM,WAAW,YACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC;IACxD,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC7B;AAED,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAgB,EAChB,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,UAAU,EACd,EAAE,YAAY,2CAmKd"}
|
|
@@ -20,7 +20,7 @@ export function AiInput({ baseUrl, apiKeyId, uiMode = "modal", context, model, p
|
|
|
20
20
|
const handleClosePanel = () => {
|
|
21
21
|
setIsOpen(false);
|
|
22
22
|
};
|
|
23
|
-
const handleSubmit = async (selectedModel, selectedPrompt) => {
|
|
23
|
+
const handleSubmit = async (selectedModel, selectedPrompt, promptId) => {
|
|
24
24
|
try {
|
|
25
25
|
const result = await generateText({
|
|
26
26
|
model: selectedModel,
|
|
@@ -87,5 +87,5 @@ export function AiInput({ baseUrl, apiKeyId, uiMode = "modal", context, model, p
|
|
|
87
87
|
...(disabled || loading
|
|
88
88
|
? { opacity: 0.5, cursor: "not-allowed" }
|
|
89
89
|
: {}),
|
|
90
|
-
}, onClick: hasConfiguration ? handleQuickGenerate : handleOpenPanel, onMouseEnter: () => setIsButtonHovered(true), onMouseLeave: () => setIsButtonHovered(false), disabled: disabled || loading, type: "button", title: hasConfiguration ? "Generate with AI" : "Setup AI", children: loading ? (_jsx("svg", { style: aiStyles.spinner, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) })) : hasConfiguration ? (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M12 5v14M5 12h14" }) })) : (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" }) })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: models || [] }))] }));
|
|
90
|
+
}, onClick: hasConfiguration ? handleQuickGenerate : handleOpenPanel, onMouseEnter: () => setIsButtonHovered(true), onMouseLeave: () => setIsButtonHovered(false), disabled: disabled || loading, type: "button", title: hasConfiguration ? "Generate with AI" : "Setup AI", children: loading ? (_jsx("svg", { style: aiStyles.spinner, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) })) : hasConfiguration ? (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M12 5v14M5 12h14" }) })) : (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" }) })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: models || [], sourceText: inputValue || undefined }))] }));
|
|
91
91
|
}
|
|
@@ -4,9 +4,10 @@ import type { UiMode } from "../types";
|
|
|
4
4
|
export interface AiPromptPanelProps {
|
|
5
5
|
isOpen: boolean;
|
|
6
6
|
onClose: () => void;
|
|
7
|
-
onSubmit: (model: string, prompt: string) => void;
|
|
7
|
+
onSubmit: (model: string, prompt: string, promptId?: string) => void;
|
|
8
8
|
uiMode?: UiMode;
|
|
9
9
|
models?: ModelRef[];
|
|
10
|
+
sourceText?: string;
|
|
10
11
|
children?: (props: AiPromptPanelRenderProps) => ReactNode;
|
|
11
12
|
}
|
|
12
13
|
export interface AiPromptPanelRenderProps {
|
|
@@ -15,8 +16,9 @@ export interface AiPromptPanelRenderProps {
|
|
|
15
16
|
setSelectedModel: (model: string) => void;
|
|
16
17
|
prompt: string;
|
|
17
18
|
setPrompt: (prompt: string) => void;
|
|
19
|
+
sourceText?: string;
|
|
18
20
|
handleSubmit: () => void;
|
|
19
21
|
handleClose: () => void;
|
|
20
22
|
}
|
|
21
|
-
export declare function AiPromptPanel({ isOpen, onClose, onSubmit, uiMode, models, children, }: AiPromptPanelProps): import("react/jsx-runtime").JSX.Element | null;
|
|
23
|
+
export declare function AiPromptPanel({ isOpen, onClose, onSubmit, uiMode, models, sourceText, children, }: AiPromptPanelProps): import("react/jsx-runtime").JSX.Element | null;
|
|
22
24
|
//# sourceMappingURL=AiPromptPanel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiPromptPanel.d.ts","sourceRoot":"","sources":["../../src/components/AiPromptPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"AiPromptPanel.d.ts","sourceRoot":"","sources":["../../src/components/AiPromptPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAIvC,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;CAC3D;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;CACzB;AAED,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,MAAW,EACX,UAAU,EACV,QAAQ,GACT,EAAE,kBAAkB,kDA0VpB"}
|
|
@@ -1,31 +1,48 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect } from "react";
|
|
4
4
|
import { aiStyles } from "../styles/inline";
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import { usePrompts } from "../hooks/usePrompts";
|
|
6
|
+
export function AiPromptPanel({ isOpen, onClose, onSubmit, uiMode = "modal", models = [], sourceText, children, }) {
|
|
7
|
+
const [selectedModel, setSelectedModel] = useState("");
|
|
7
8
|
const [prompt, setPrompt] = useState("");
|
|
9
|
+
const [promptId, setPromptId] = useState(undefined);
|
|
8
10
|
const [isCloseHovered, setIsCloseHovered] = useState(false);
|
|
9
11
|
const [isCancelHovered, setIsCancelHovered] = useState(false);
|
|
10
12
|
const [isSubmitHovered, setIsSubmitHovered] = useState(false);
|
|
11
13
|
const [promptFocused, setPromptFocused] = useState(false);
|
|
12
14
|
const [modelFocused, setModelFocused] = useState(false);
|
|
15
|
+
const [showPromptLibrary, setShowPromptLibrary] = useState(false);
|
|
16
|
+
const { prompts, loading: promptsLoading, fetchPrompts, incrementStat } = usePrompts();
|
|
17
|
+
// Set initial model when models change
|
|
13
18
|
useEffect(() => {
|
|
14
19
|
if (models.length > 0 && !selectedModel) {
|
|
15
20
|
setSelectedModel(models[0].id);
|
|
16
21
|
}
|
|
17
22
|
}, [models, selectedModel]);
|
|
23
|
+
// Fetch prompts when modal opens
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (isOpen && models.length > 0) {
|
|
26
|
+
const modelType = models.find((m) => m.id === selectedModel)?.type;
|
|
27
|
+
fetchPrompts({
|
|
28
|
+
type: modelType === "image" ? "image" : "text",
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}, [isOpen, selectedModel, models, fetchPrompts]);
|
|
18
32
|
if (!isOpen)
|
|
19
33
|
return null;
|
|
20
34
|
const handleSubmit = () => {
|
|
21
35
|
if (!selectedModel || !prompt.trim())
|
|
22
36
|
return;
|
|
23
|
-
onSubmit(selectedModel, prompt);
|
|
37
|
+
onSubmit(selectedModel, prompt, promptId);
|
|
24
38
|
setPrompt("");
|
|
39
|
+
setPromptId(undefined);
|
|
25
40
|
};
|
|
26
41
|
const handleClose = () => {
|
|
27
42
|
onClose();
|
|
28
43
|
setPrompt("");
|
|
44
|
+
setPromptId(undefined);
|
|
45
|
+
setShowPromptLibrary(false);
|
|
29
46
|
};
|
|
30
47
|
const handleKeyDown = (e) => {
|
|
31
48
|
if (e.key === "Escape") {
|
|
@@ -35,29 +52,125 @@ export function AiPromptPanel({ isOpen, onClose, onSubmit, uiMode = "modal", mod
|
|
|
35
52
|
handleSubmit();
|
|
36
53
|
}
|
|
37
54
|
};
|
|
55
|
+
const handleSelectPrompt = (promptData) => {
|
|
56
|
+
setPrompt(promptData.content);
|
|
57
|
+
setPromptId(promptData.id);
|
|
58
|
+
incrementStat(promptData.id, "picked");
|
|
59
|
+
setShowPromptLibrary(false);
|
|
60
|
+
};
|
|
61
|
+
const currentModelType = models.find((m) => m.id === selectedModel)?.type;
|
|
62
|
+
const filteredPrompts = prompts.filter((p) => {
|
|
63
|
+
const matchesType = currentModelType === "image" ? p.type === "image" : p.type !== "image";
|
|
64
|
+
return matchesType;
|
|
65
|
+
});
|
|
38
66
|
const renderProps = {
|
|
39
67
|
models,
|
|
40
68
|
selectedModel,
|
|
41
69
|
setSelectedModel,
|
|
42
70
|
prompt,
|
|
43
71
|
setPrompt,
|
|
72
|
+
sourceText,
|
|
44
73
|
handleSubmit,
|
|
45
74
|
handleClose,
|
|
46
75
|
};
|
|
47
76
|
if (children) {
|
|
48
77
|
return (_jsx("div", { style: aiStyles.modal, onKeyDown: handleKeyDown, children: children(renderProps) }));
|
|
49
78
|
}
|
|
50
|
-
return (_jsxs("div", { style: aiStyles.modal, onKeyDown: handleKeyDown, children: [_jsx("div", { style: aiStyles.modalOverlay, onClick: handleClose }), _jsxs("div", { style: aiStyles.modalContent, children: [_jsxs("div", { style: aiStyles.modalHeader, children: [_jsx("h2", { style: aiStyles.modalTitle, children: "AI Prompt Configuration" }), _jsx("button", { style: {
|
|
79
|
+
return (_jsxs("div", { style: aiStyles.modal, onKeyDown: handleKeyDown, children: [_jsx("div", { style: aiStyles.modalOverlay, onClick: handleClose }), _jsxs("div", { style: aiStyles.modalContent, children: [_jsxs("div", { style: aiStyles.modalHeader, children: [_jsx("h2", { style: aiStyles.modalTitle, children: showPromptLibrary ? "Select a Prompt" : "AI Prompt Configuration" }), _jsx("button", { style: {
|
|
51
80
|
...aiStyles.modalCloseButton,
|
|
52
81
|
...(isCloseHovered && aiStyles.modalCloseButtonHover),
|
|
53
|
-
}, onClick: handleClose, onMouseEnter: () => setIsCloseHovered(true), onMouseLeave: () => setIsCloseHovered(false), "aria-label": "Close", children: "\u00D7" })] }),
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
82
|
+
}, onClick: handleClose, onMouseEnter: () => setIsCloseHovered(true), onMouseLeave: () => setIsCloseHovered(false), "aria-label": "Close", children: "\u00D7" })] }), _jsx("div", { style: aiStyles.modalBody, children: !showPromptLibrary ? (_jsxs(_Fragment, { children: [sourceText && (_jsxs("div", { style: {
|
|
83
|
+
...aiStyles.modalInputGroup,
|
|
84
|
+
marginBottom: "16px",
|
|
85
|
+
}, children: [_jsx("label", { style: aiStyles.modalLabel, children: "Source Text" }), _jsx("div", { style: {
|
|
86
|
+
padding: "12px",
|
|
87
|
+
background: aiStyles.textarea.background,
|
|
88
|
+
border: `1px solid ${aiStyles.input.border}`,
|
|
89
|
+
borderRadius: "8px",
|
|
90
|
+
fontSize: "13px",
|
|
91
|
+
color: aiStyles.textarea.color,
|
|
92
|
+
maxHeight: "120px",
|
|
93
|
+
overflow: "auto",
|
|
94
|
+
whiteSpace: "pre-wrap",
|
|
95
|
+
wordBreak: "break-word",
|
|
96
|
+
}, children: sourceText })] })), _jsxs("div", { style: aiStyles.modalInputGroup, children: [_jsx("label", { htmlFor: "model-select", style: aiStyles.modalLabel, children: "AI Model" }), _jsxs("select", { id: "model-select", value: selectedModel, onChange: (e) => setSelectedModel(e.target.value), onFocus: () => setModelFocused(true), onBlur: () => setModelFocused(false), style: {
|
|
97
|
+
...aiStyles.select,
|
|
98
|
+
...(modelFocused && aiStyles.selectFocus),
|
|
99
|
+
}, 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: {
|
|
100
|
+
display: "flex",
|
|
101
|
+
justifyContent: "space-between",
|
|
102
|
+
alignItems: "center",
|
|
103
|
+
marginBottom: "8px",
|
|
104
|
+
}, children: [_jsxs("label", { htmlFor: "prompt-input", style: aiStyles.modalLabel, children: ["Prompt", _jsx("span", { style: {
|
|
105
|
+
color: "#6b7280",
|
|
106
|
+
marginLeft: "4px",
|
|
107
|
+
fontSize: "12px",
|
|
108
|
+
fontWeight: 400,
|
|
109
|
+
}, children: "(Cmd/Ctrl + Enter to submit)" })] }), filteredPrompts.length > 0 && (_jsxs("button", { onClick: () => setShowPromptLibrary(true), style: {
|
|
110
|
+
padding: "4px 12px",
|
|
111
|
+
fontSize: "12px",
|
|
112
|
+
color: "#ffffff",
|
|
113
|
+
background: "#8b5cf620",
|
|
114
|
+
border: "none",
|
|
115
|
+
borderRadius: "6px",
|
|
116
|
+
cursor: "pointer",
|
|
117
|
+
transition: "all 0.2s",
|
|
118
|
+
}, onMouseEnter: (e) => {
|
|
119
|
+
e.currentTarget.style.background = "#8b5cf630";
|
|
120
|
+
}, onMouseLeave: (e) => {
|
|
121
|
+
e.currentTarget.style.background = "#8b5cf620";
|
|
122
|
+
}, children: ["\uD83D\uDCDA Browse Prompts (", filteredPrompts.length, ")"] }))] }), _jsx("textarea", { id: "prompt-input", value: prompt, onChange: (e) => setPrompt(e.target.value), onFocus: () => setPromptFocused(true), onBlur: () => setPromptFocused(false), placeholder: sourceText
|
|
123
|
+
? "Enter your AI prompt... e.g., 'Correct spelling and grammar', 'Make it more professional', 'Translate to English'"
|
|
124
|
+
: "Enter your AI prompt... e.g., 'Write a blog post about AI', 'Generate product description'", rows: 6, style: {
|
|
125
|
+
...aiStyles.textarea,
|
|
126
|
+
padding: "12px 16px",
|
|
127
|
+
...(promptFocused && aiStyles.textareaFocus),
|
|
128
|
+
} })] })] })) : (_jsxs("div", { children: [_jsx("button", { onClick: () => setShowPromptLibrary(false), style: {
|
|
129
|
+
padding: "8px 0",
|
|
130
|
+
fontSize: "14px",
|
|
131
|
+
color: "#8b5cf6",
|
|
132
|
+
background: "transparent",
|
|
133
|
+
border: "none",
|
|
134
|
+
cursor: "pointer",
|
|
135
|
+
marginBottom: "16px",
|
|
136
|
+
display: "flex",
|
|
137
|
+
alignItems: "center",
|
|
138
|
+
gap: "4px",
|
|
139
|
+
}, children: "\u2190 Back to form" }), promptsLoading ? (_jsx("div", { style: { textAlign: "center", padding: "40px 0" }, children: "Loading prompts..." })) : filteredPrompts.length === 0 ? (_jsx("div", { style: { textAlign: "center", padding: "40px 0", color: "#6b7280" }, children: "No prompts available for this model type" })) : (_jsx("div", { style: {
|
|
140
|
+
display: "flex",
|
|
141
|
+
flexDirection: "column",
|
|
142
|
+
gap: "12px",
|
|
143
|
+
maxHeight: "400px",
|
|
144
|
+
overflow: "auto",
|
|
145
|
+
}, children: filteredPrompts.map((promptData) => (_jsxs("div", { onClick: () => handleSelectPrompt(promptData), style: {
|
|
146
|
+
padding: "16px",
|
|
147
|
+
border: `1px solid #e5e7eb`,
|
|
148
|
+
borderRadius: "8px",
|
|
149
|
+
cursor: "pointer",
|
|
150
|
+
transition: "all 0.2s",
|
|
151
|
+
}, onMouseEnter: (e) => {
|
|
152
|
+
e.currentTarget.style.background = "#8b5cf610";
|
|
153
|
+
e.currentTarget.style.borderColor = "#8b5cf6";
|
|
154
|
+
}, onMouseLeave: (e) => {
|
|
155
|
+
e.currentTarget.style.background = "transparent";
|
|
156
|
+
e.currentTarget.style.borderColor = "#e5e7eb";
|
|
157
|
+
}, children: [_jsx("div", { style: {
|
|
158
|
+
fontWeight: 600,
|
|
159
|
+
marginBottom: "4px",
|
|
160
|
+
color: "#111827",
|
|
161
|
+
}, children: promptData.title }), _jsx("div", { style: {
|
|
162
|
+
fontSize: "13px",
|
|
163
|
+
color: "#6b7280",
|
|
164
|
+
overflow: "hidden",
|
|
165
|
+
textOverflow: "ellipsis",
|
|
166
|
+
display: "-webkit-box",
|
|
167
|
+
WebkitLineClamp: 2,
|
|
168
|
+
WebkitBoxOrient: "vertical",
|
|
169
|
+
}, children: promptData.content }), ("category" in promptData && promptData.category) ? (_jsx("div", { style: {
|
|
170
|
+
marginTop: "8px",
|
|
171
|
+
fontSize: "11px",
|
|
172
|
+
color: "#8b5cf6",
|
|
173
|
+
}, children: String(promptData.category) })) : null] }, promptData.id))) }))] })) }), _jsxs("div", { style: aiStyles.modalFooter, children: [_jsx("button", { onClick: handleClose, onMouseEnter: () => setIsCancelHovered(true), onMouseLeave: () => setIsCancelHovered(false), style: {
|
|
61
174
|
...aiStyles.button,
|
|
62
175
|
...aiStyles.buttonSecondary,
|
|
63
176
|
...(isCancelHovered && aiStyles.buttonSecondaryHover),
|
|
@@ -65,5 +178,5 @@ export function AiPromptPanel({ isOpen, onClose, onSubmit, uiMode = "modal", mod
|
|
|
65
178
|
...aiStyles.button,
|
|
66
179
|
...(isSubmitHovered && !(!selectedModel || !prompt.trim()) && aiStyles.buttonHover),
|
|
67
180
|
...(!selectedModel || !prompt.trim() ? aiStyles.buttonDisabled : {}),
|
|
68
|
-
}, children: "Generate with AI" })] })] })] }));
|
|
181
|
+
}, children: sourceText ? "Transform with AI" : "Generate with AI" })] })] })] }));
|
|
69
182
|
}
|
|
@@ -63,15 +63,15 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
63
63
|
}, children: status.quota.is_active ? "Active" : "Inactive" })] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Storage" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Database:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: [status.storage.db_mb.toFixed(2), " MB"] })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Files:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: [status.storage.files_mb.toFixed(2), " MB"] })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), _jsxs("span", { style: {
|
|
64
64
|
...aiStyles.tooltipValue,
|
|
65
65
|
...aiStyles.tooltipValueBold,
|
|
66
|
-
}, children: [(status.storage.db_mb + status.storage.files_mb).toFixed(2), " MB"] })] })] }), _jsxs("div", { style: aiStyles.tooltipActions, children: [_jsx("a", { href: "https://
|
|
66
|
+
}, children: [(status.storage.db_mb + status.storage.files_mb).toFixed(2), " MB"] })] })] }), _jsxs("div", { style: aiStyles.tooltipActions, children: [_jsx("a", { href: "https://prompt.lastbrain.io/fr/auth/dashboard", target: "_blank", rel: "noopener noreferrer", style: aiStyles.tooltipLink, onMouseEnter: (e) => {
|
|
67
67
|
Object.assign(e.currentTarget.style, aiStyles.tooltipLinkHover);
|
|
68
68
|
}, onMouseLeave: (e) => {
|
|
69
69
|
Object.assign(e.currentTarget.style, aiStyles.tooltipLink);
|
|
70
|
-
}, children: "Dashboard" }), _jsx("a", { href: "https://
|
|
70
|
+
}, children: "Dashboard" }), _jsx("a", { href: "https://prompt.lastbrain.io/fr/auth/billing", target: "_blank", rel: "noopener noreferrer", style: aiStyles.tooltipLink, onMouseEnter: (e) => {
|
|
71
71
|
Object.assign(e.currentTarget.style, aiStyles.tooltipLinkHover);
|
|
72
72
|
}, onMouseLeave: (e) => {
|
|
73
73
|
Object.assign(e.currentTarget.style, aiStyles.tooltipLink);
|
|
74
|
-
}, children: "History" }), _jsx("a", { href: "https://
|
|
74
|
+
}, children: "History" }), _jsx("a", { href: "https://prompt.lastbrain.io/fr/auth/billing", target: "_blank", rel: "noopener noreferrer", style: aiStyles.tooltipLink, onMouseEnter: (e) => {
|
|
75
75
|
Object.assign(e.currentTarget.style, aiStyles.tooltipLinkHover);
|
|
76
76
|
}, onMouseLeave: (e) => {
|
|
77
77
|
Object.assign(e.currentTarget.style, aiStyles.tooltipLink);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiTextarea.d.ts","sourceRoot":"","sources":["../../src/components/AiTextarea.tsx"],"names":[],"mappings":"AAEA,OAAc,EAAoB,KAAK,sBAAsB,EAAE,MAAM,OAAO,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAM5C,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;CAC7B;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAgB,EAChB,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,OAAc,EAAoB,KAAK,sBAAsB,EAAE,MAAM,OAAO,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAM5C,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;CAC7B;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAgB,EAChB,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,aAAa,EACjB,EAAE,eAAe,2CAqKjB"}
|
|
@@ -22,7 +22,7 @@ export function AiTextarea({ baseUrl, apiKeyId, uiMode = "modal", context, model
|
|
|
22
22
|
const handleClosePanel = () => {
|
|
23
23
|
setIsOpen(false);
|
|
24
24
|
};
|
|
25
|
-
const handleSubmit = async (selectedModel, selectedPrompt) => {
|
|
25
|
+
const handleSubmit = async (selectedModel, selectedPrompt, promptId) => {
|
|
26
26
|
try {
|
|
27
27
|
const result = await generateText({
|
|
28
28
|
model: selectedModel,
|
|
@@ -89,5 +89,5 @@ export function AiTextarea({ baseUrl, apiKeyId, uiMode = "modal", context, model
|
|
|
89
89
|
...(disabled || loading
|
|
90
90
|
? { opacity: 0.5, cursor: "not-allowed" }
|
|
91
91
|
: {}),
|
|
92
|
-
}, onClick: hasConfiguration ? handleQuickGenerate : handleOpenPanel, onMouseEnter: () => setIsButtonHovered(true), onMouseLeave: () => setIsButtonHovered(false), disabled: disabled || loading, type: "button", title: hasConfiguration ? "Generate with AI" : "Setup AI", children: loading ? (_jsx("svg", { style: aiStyles.spinner, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) })) : hasConfiguration ? (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M12 5v14M5 12h14" }) })) : (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" }) })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: models || [] }))] }));
|
|
92
|
+
}, onClick: hasConfiguration ? handleQuickGenerate : handleOpenPanel, onMouseEnter: () => setIsButtonHovered(true), onMouseLeave: () => setIsButtonHovered(false), disabled: disabled || loading, type: "button", title: hasConfiguration ? "Generate with AI" : "Setup AI", children: loading ? (_jsx("svg", { style: aiStyles.spinner, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) })) : hasConfiguration ? (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M12 5v14M5 12h14" }) })) : (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" }) })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: models || [], sourceText: textareaValue || undefined }))] }));
|
|
93
93
|
}
|
package/package.json
CHANGED
|
@@ -51,7 +51,8 @@ export function AiInput({
|
|
|
51
51
|
|
|
52
52
|
const handleSubmit = async (
|
|
53
53
|
selectedModel: string,
|
|
54
|
-
selectedPrompt: string
|
|
54
|
+
selectedPrompt: string,
|
|
55
|
+
promptId?: string
|
|
55
56
|
) => {
|
|
56
57
|
try {
|
|
57
58
|
const result = await generateText({
|
|
@@ -184,6 +185,7 @@ export function AiInput({
|
|
|
184
185
|
onSubmit={handleSubmit}
|
|
185
186
|
uiMode={uiMode}
|
|
186
187
|
models={models || []}
|
|
188
|
+
sourceText={inputValue || undefined}
|
|
187
189
|
/>
|
|
188
190
|
)}
|
|
189
191
|
</div>
|
|
@@ -4,13 +4,15 @@ import { useState, useEffect, type ReactNode } from "react";
|
|
|
4
4
|
import type { ModelRef } from "@lastbrain/ai-ui-core";
|
|
5
5
|
import type { UiMode } from "../types";
|
|
6
6
|
import { aiStyles } from "../styles/inline";
|
|
7
|
+
import { usePrompts, type Prompt, type PublicPrompt } from "../hooks/usePrompts";
|
|
7
8
|
|
|
8
9
|
export interface AiPromptPanelProps {
|
|
9
10
|
isOpen: boolean;
|
|
10
11
|
onClose: () => void;
|
|
11
|
-
onSubmit: (model: string, prompt: string) => void;
|
|
12
|
+
onSubmit: (model: string, prompt: string, promptId?: string) => void;
|
|
12
13
|
uiMode?: UiMode;
|
|
13
14
|
models?: ModelRef[];
|
|
15
|
+
sourceText?: string; // Current text from input/textarea
|
|
14
16
|
children?: (props: AiPromptPanelRenderProps) => ReactNode;
|
|
15
17
|
}
|
|
16
18
|
|
|
@@ -20,6 +22,7 @@ export interface AiPromptPanelRenderProps {
|
|
|
20
22
|
setSelectedModel: (model: string) => void;
|
|
21
23
|
prompt: string;
|
|
22
24
|
setPrompt: (prompt: string) => void;
|
|
25
|
+
sourceText?: string;
|
|
23
26
|
handleSubmit: () => void;
|
|
24
27
|
handleClose: () => void;
|
|
25
28
|
}
|
|
@@ -30,33 +33,52 @@ export function AiPromptPanel({
|
|
|
30
33
|
onSubmit,
|
|
31
34
|
uiMode = "modal",
|
|
32
35
|
models = [],
|
|
36
|
+
sourceText,
|
|
33
37
|
children,
|
|
34
38
|
}: AiPromptPanelProps) {
|
|
35
|
-
const [selectedModel, setSelectedModel] = useState(
|
|
39
|
+
const [selectedModel, setSelectedModel] = useState("");
|
|
36
40
|
const [prompt, setPrompt] = useState("");
|
|
41
|
+
const [promptId, setPromptId] = useState<string | undefined>(undefined);
|
|
37
42
|
const [isCloseHovered, setIsCloseHovered] = useState(false);
|
|
38
43
|
const [isCancelHovered, setIsCancelHovered] = useState(false);
|
|
39
44
|
const [isSubmitHovered, setIsSubmitHovered] = useState(false);
|
|
40
45
|
const [promptFocused, setPromptFocused] = useState(false);
|
|
41
46
|
const [modelFocused, setModelFocused] = useState(false);
|
|
47
|
+
const [showPromptLibrary, setShowPromptLibrary] = useState(false);
|
|
42
48
|
|
|
49
|
+
const { prompts, loading: promptsLoading, fetchPrompts, incrementStat } = usePrompts();
|
|
50
|
+
|
|
51
|
+
// Set initial model when models change
|
|
43
52
|
useEffect(() => {
|
|
44
53
|
if (models.length > 0 && !selectedModel) {
|
|
45
54
|
setSelectedModel(models[0].id);
|
|
46
55
|
}
|
|
47
56
|
}, [models, selectedModel]);
|
|
48
57
|
|
|
58
|
+
// Fetch prompts when modal opens
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (isOpen && models.length > 0) {
|
|
61
|
+
const modelType = models.find((m) => m.id === selectedModel)?.type;
|
|
62
|
+
fetchPrompts({
|
|
63
|
+
type: modelType === "image" ? "image" : "text",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}, [isOpen, selectedModel, models, fetchPrompts]);
|
|
67
|
+
|
|
49
68
|
if (!isOpen) return null;
|
|
50
69
|
|
|
51
70
|
const handleSubmit = () => {
|
|
52
71
|
if (!selectedModel || !prompt.trim()) return;
|
|
53
|
-
onSubmit(selectedModel, prompt);
|
|
72
|
+
onSubmit(selectedModel, prompt, promptId);
|
|
54
73
|
setPrompt("");
|
|
74
|
+
setPromptId(undefined);
|
|
55
75
|
};
|
|
56
76
|
|
|
57
77
|
const handleClose = () => {
|
|
58
78
|
onClose();
|
|
59
79
|
setPrompt("");
|
|
80
|
+
setPromptId(undefined);
|
|
81
|
+
setShowPromptLibrary(false);
|
|
60
82
|
};
|
|
61
83
|
|
|
62
84
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
@@ -68,12 +90,27 @@ export function AiPromptPanel({
|
|
|
68
90
|
}
|
|
69
91
|
};
|
|
70
92
|
|
|
93
|
+
const handleSelectPrompt = (promptData: Prompt | PublicPrompt) => {
|
|
94
|
+
setPrompt(promptData.content);
|
|
95
|
+
setPromptId(promptData.id);
|
|
96
|
+
incrementStat(promptData.id, "picked");
|
|
97
|
+
setShowPromptLibrary(false);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const currentModelType = models.find((m) => m.id === selectedModel)?.type;
|
|
101
|
+
const filteredPrompts = prompts.filter((p: Prompt | PublicPrompt) => {
|
|
102
|
+
const matchesType =
|
|
103
|
+
currentModelType === "image" ? p.type === "image" : p.type !== "image";
|
|
104
|
+
return matchesType;
|
|
105
|
+
});
|
|
106
|
+
|
|
71
107
|
const renderProps: AiPromptPanelRenderProps = {
|
|
72
108
|
models,
|
|
73
109
|
selectedModel,
|
|
74
110
|
setSelectedModel,
|
|
75
111
|
prompt,
|
|
76
112
|
setPrompt,
|
|
113
|
+
sourceText,
|
|
77
114
|
handleSubmit,
|
|
78
115
|
handleClose,
|
|
79
116
|
};
|
|
@@ -91,7 +128,9 @@ export function AiPromptPanel({
|
|
|
91
128
|
<div style={aiStyles.modalOverlay} onClick={handleClose} />
|
|
92
129
|
<div style={aiStyles.modalContent}>
|
|
93
130
|
<div style={aiStyles.modalHeader}>
|
|
94
|
-
<h2 style={aiStyles.modalTitle}>
|
|
131
|
+
<h2 style={aiStyles.modalTitle}>
|
|
132
|
+
{showPromptLibrary ? "Select a Prompt" : "AI Prompt Configuration"}
|
|
133
|
+
</h2>
|
|
95
134
|
<button
|
|
96
135
|
style={{
|
|
97
136
|
...aiStyles.modalCloseButton,
|
|
@@ -107,54 +146,209 @@ export function AiPromptPanel({
|
|
|
107
146
|
</div>
|
|
108
147
|
|
|
109
148
|
<div style={aiStyles.modalBody}>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
149
|
+
{!showPromptLibrary ? (
|
|
150
|
+
<>
|
|
151
|
+
{sourceText && (
|
|
152
|
+
<div style={{
|
|
153
|
+
...aiStyles.modalInputGroup,
|
|
154
|
+
marginBottom: "16px",
|
|
155
|
+
}}>
|
|
156
|
+
<label style={aiStyles.modalLabel}>
|
|
157
|
+
Source Text
|
|
158
|
+
</label>
|
|
159
|
+
<div style={{
|
|
160
|
+
padding: "12px",
|
|
161
|
+
background: aiStyles.textarea.background,
|
|
162
|
+
border: `1px solid ${aiStyles.input.border}`,
|
|
163
|
+
borderRadius: "8px",
|
|
164
|
+
fontSize: "13px",
|
|
165
|
+
color: aiStyles.textarea.color,
|
|
166
|
+
maxHeight: "120px",
|
|
167
|
+
overflow: "auto",
|
|
168
|
+
whiteSpace: "pre-wrap",
|
|
169
|
+
wordBreak: "break-word",
|
|
170
|
+
}}>
|
|
171
|
+
{sourceText}
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
)}
|
|
175
|
+
|
|
176
|
+
<div style={aiStyles.modalInputGroup}>
|
|
177
|
+
<label htmlFor="model-select" style={aiStyles.modalLabel}>
|
|
178
|
+
AI Model
|
|
179
|
+
</label>
|
|
180
|
+
<select
|
|
181
|
+
id="model-select"
|
|
182
|
+
value={selectedModel}
|
|
183
|
+
onChange={(e) => setSelectedModel(e.target.value)}
|
|
184
|
+
onFocus={() => setModelFocused(true)}
|
|
185
|
+
onBlur={() => setModelFocused(false)}
|
|
186
|
+
style={{
|
|
187
|
+
...aiStyles.select,
|
|
188
|
+
...(modelFocused && aiStyles.selectFocus),
|
|
189
|
+
}}
|
|
190
|
+
>
|
|
191
|
+
{models.length === 0 && (
|
|
192
|
+
<option value="">Loading models...</option>
|
|
193
|
+
)}
|
|
194
|
+
{models.map((model) => (
|
|
195
|
+
<option key={model.id} value={model.id}>
|
|
196
|
+
{model.name}
|
|
197
|
+
</option>
|
|
198
|
+
))}
|
|
199
|
+
</select>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<div style={aiStyles.modalInputGroup}>
|
|
203
|
+
<div style={{
|
|
204
|
+
display: "flex",
|
|
205
|
+
justifyContent: "space-between",
|
|
206
|
+
alignItems: "center",
|
|
207
|
+
marginBottom: "8px",
|
|
208
|
+
}}>
|
|
209
|
+
<label htmlFor="prompt-input" style={aiStyles.modalLabel}>
|
|
210
|
+
Prompt
|
|
211
|
+
<span style={{
|
|
212
|
+
color: "#6b7280",
|
|
213
|
+
marginLeft: "4px",
|
|
214
|
+
fontSize: "12px",
|
|
215
|
+
fontWeight: 400,
|
|
216
|
+
}}>
|
|
217
|
+
(Cmd/Ctrl + Enter to submit)
|
|
218
|
+
</span>
|
|
219
|
+
</label>
|
|
220
|
+
{filteredPrompts.length > 0 && (
|
|
221
|
+
<button
|
|
222
|
+
onClick={() => setShowPromptLibrary(true)}
|
|
223
|
+
style={{
|
|
224
|
+
padding: "4px 12px",
|
|
225
|
+
fontSize: "12px",
|
|
226
|
+
color: "#ffffff",
|
|
227
|
+
background: "#8b5cf620",
|
|
228
|
+
border: "none",
|
|
229
|
+
borderRadius: "6px",
|
|
230
|
+
cursor: "pointer",
|
|
231
|
+
transition: "all 0.2s",
|
|
232
|
+
}}
|
|
233
|
+
onMouseEnter={(e) => {
|
|
234
|
+
e.currentTarget.style.background = "#8b5cf630";
|
|
235
|
+
}}
|
|
236
|
+
onMouseLeave={(e) => {
|
|
237
|
+
e.currentTarget.style.background = "#8b5cf620";
|
|
238
|
+
}}
|
|
239
|
+
>
|
|
240
|
+
📚 Browse Prompts ({filteredPrompts.length})
|
|
241
|
+
</button>
|
|
242
|
+
)}
|
|
243
|
+
</div>
|
|
244
|
+
<textarea
|
|
245
|
+
id="prompt-input"
|
|
246
|
+
value={prompt}
|
|
247
|
+
onChange={(e) => setPrompt(e.target.value)}
|
|
248
|
+
onFocus={() => setPromptFocused(true)}
|
|
249
|
+
onBlur={() => setPromptFocused(false)}
|
|
250
|
+
placeholder={sourceText
|
|
251
|
+
? "Enter your AI prompt... e.g., 'Correct spelling and grammar', 'Make it more professional', 'Translate to English'"
|
|
252
|
+
: "Enter your AI prompt... e.g., 'Write a blog post about AI', 'Generate product description'"
|
|
253
|
+
}
|
|
254
|
+
rows={6}
|
|
255
|
+
style={{
|
|
256
|
+
...aiStyles.textarea,
|
|
257
|
+
padding: "12px 16px",
|
|
258
|
+
...(promptFocused && aiStyles.textareaFocus),
|
|
259
|
+
}}
|
|
260
|
+
/>
|
|
261
|
+
</div>
|
|
262
|
+
</>
|
|
263
|
+
) : (
|
|
264
|
+
<div>
|
|
265
|
+
<button
|
|
266
|
+
onClick={() => setShowPromptLibrary(false)}
|
|
267
|
+
style={{
|
|
268
|
+
padding: "8px 0",
|
|
269
|
+
fontSize: "14px",
|
|
270
|
+
color: "#8b5cf6",
|
|
271
|
+
background: "transparent",
|
|
272
|
+
border: "none",
|
|
273
|
+
cursor: "pointer",
|
|
274
|
+
marginBottom: "16px",
|
|
275
|
+
display: "flex",
|
|
276
|
+
alignItems: "center",
|
|
277
|
+
gap: "4px",
|
|
278
|
+
}}
|
|
279
|
+
>
|
|
280
|
+
← Back to form
|
|
281
|
+
</button>
|
|
282
|
+
|
|
283
|
+
{promptsLoading ? (
|
|
284
|
+
<div style={{ textAlign: "center", padding: "40px 0" }}>
|
|
285
|
+
Loading prompts...
|
|
286
|
+
</div>
|
|
287
|
+
) : filteredPrompts.length === 0 ? (
|
|
288
|
+
<div style={{ textAlign: "center", padding: "40px 0", color: "#6b7280" }}>
|
|
289
|
+
No prompts available for this model type
|
|
290
|
+
</div>
|
|
291
|
+
) : (
|
|
292
|
+
<div style={{
|
|
293
|
+
display: "flex",
|
|
294
|
+
flexDirection: "column" as const,
|
|
295
|
+
gap: "12px",
|
|
296
|
+
maxHeight: "400px",
|
|
297
|
+
overflow: "auto",
|
|
298
|
+
}}>
|
|
299
|
+
{filteredPrompts.map((promptData: Prompt | PublicPrompt) => (
|
|
300
|
+
<div
|
|
301
|
+
key={promptData.id}
|
|
302
|
+
onClick={() => handleSelectPrompt(promptData)}
|
|
303
|
+
style={{
|
|
304
|
+
padding: "16px",
|
|
305
|
+
border: `1px solid #e5e7eb`,
|
|
306
|
+
borderRadius: "8px",
|
|
307
|
+
cursor: "pointer",
|
|
308
|
+
transition: "all 0.2s",
|
|
309
|
+
}}
|
|
310
|
+
onMouseEnter={(e) => {
|
|
311
|
+
e.currentTarget.style.background = "#8b5cf610";
|
|
312
|
+
e.currentTarget.style.borderColor = "#8b5cf6";
|
|
313
|
+
}}
|
|
314
|
+
onMouseLeave={(e) => {
|
|
315
|
+
e.currentTarget.style.background = "transparent";
|
|
316
|
+
e.currentTarget.style.borderColor = "#e5e7eb";
|
|
317
|
+
}}
|
|
318
|
+
>
|
|
319
|
+
<div style={{
|
|
320
|
+
fontWeight: 600,
|
|
321
|
+
marginBottom: "4px",
|
|
322
|
+
color: "#111827",
|
|
323
|
+
}}>
|
|
324
|
+
{promptData.title}
|
|
325
|
+
</div>
|
|
326
|
+
<div style={{
|
|
327
|
+
fontSize: "13px",
|
|
328
|
+
color: "#6b7280",
|
|
329
|
+
overflow: "hidden",
|
|
330
|
+
textOverflow: "ellipsis",
|
|
331
|
+
display: "-webkit-box",
|
|
332
|
+
WebkitLineClamp: 2,
|
|
333
|
+
WebkitBoxOrient: "vertical" as any,
|
|
334
|
+
}}>
|
|
335
|
+
{promptData.content}
|
|
336
|
+
</div>
|
|
337
|
+
{("category" in promptData && promptData.category) ? (
|
|
338
|
+
<div style={{
|
|
339
|
+
marginTop: "8px",
|
|
340
|
+
fontSize: "11px",
|
|
341
|
+
color: "#8b5cf6",
|
|
342
|
+
}}>
|
|
343
|
+
{String(promptData.category)}
|
|
344
|
+
</div>
|
|
345
|
+
) : null}
|
|
346
|
+
</div>
|
|
347
|
+
))}
|
|
348
|
+
</div>
|
|
127
349
|
)}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
{model.name}
|
|
131
|
-
</option>
|
|
132
|
-
))}
|
|
133
|
-
</select>
|
|
134
|
-
</div>
|
|
135
|
-
|
|
136
|
-
<div style={aiStyles.modalInputGroup}>
|
|
137
|
-
<label htmlFor="prompt-input" style={aiStyles.modalLabel}>
|
|
138
|
-
Prompt
|
|
139
|
-
<span style={{ color: aiStyles.textareaFocus.borderColor, marginLeft: "4px" }}>
|
|
140
|
-
(Cmd/Ctrl + Enter to submit)
|
|
141
|
-
</span>
|
|
142
|
-
</label>
|
|
143
|
-
<textarea
|
|
144
|
-
id="prompt-input"
|
|
145
|
-
value={prompt}
|
|
146
|
-
onChange={(e) => setPrompt(e.target.value)}
|
|
147
|
-
onFocus={() => setPromptFocused(true)}
|
|
148
|
-
onBlur={() => setPromptFocused(false)}
|
|
149
|
-
placeholder="Enter your AI prompt... e.g., 'Correct spelling and grammar'"
|
|
150
|
-
rows={6}
|
|
151
|
-
style={{
|
|
152
|
-
...aiStyles.textarea,
|
|
153
|
-
padding: "12px 16px",
|
|
154
|
-
...(promptFocused && aiStyles.textareaFocus),
|
|
155
|
-
}}
|
|
156
|
-
/>
|
|
157
|
-
</div>
|
|
350
|
+
</div>
|
|
351
|
+
)}
|
|
158
352
|
</div>
|
|
159
353
|
|
|
160
354
|
<div style={aiStyles.modalFooter}>
|
|
@@ -181,7 +375,7 @@ export function AiPromptPanel({
|
|
|
181
375
|
...(!selectedModel || !prompt.trim() ? aiStyles.buttonDisabled : {}),
|
|
182
376
|
}}
|
|
183
377
|
>
|
|
184
|
-
Generate with AI
|
|
378
|
+
{sourceText ? "Transform with AI" : "Generate with AI"}
|
|
185
379
|
</button>
|
|
186
380
|
</div>
|
|
187
381
|
</div>
|
|
@@ -256,7 +256,7 @@ export function AiStatusButton({
|
|
|
256
256
|
|
|
257
257
|
<div style={aiStyles.tooltipActions}>
|
|
258
258
|
<a
|
|
259
|
-
href="https://
|
|
259
|
+
href="https://prompt.lastbrain.io/fr/auth/dashboard"
|
|
260
260
|
target="_blank"
|
|
261
261
|
rel="noopener noreferrer"
|
|
262
262
|
style={aiStyles.tooltipLink}
|
|
@@ -270,7 +270,7 @@ export function AiStatusButton({
|
|
|
270
270
|
Dashboard
|
|
271
271
|
</a>
|
|
272
272
|
<a
|
|
273
|
-
href="https://
|
|
273
|
+
href="https://prompt.lastbrain.io/fr/auth/billing"
|
|
274
274
|
target="_blank"
|
|
275
275
|
rel="noopener noreferrer"
|
|
276
276
|
style={aiStyles.tooltipLink}
|
|
@@ -284,7 +284,7 @@ export function AiStatusButton({
|
|
|
284
284
|
History
|
|
285
285
|
</a>
|
|
286
286
|
<a
|
|
287
|
-
href="https://
|
|
287
|
+
href="https://prompt.lastbrain.io/fr/auth/billing"
|
|
288
288
|
target="_blank"
|
|
289
289
|
rel="noopener noreferrer"
|
|
290
290
|
style={aiStyles.tooltipLink}
|
|
@@ -53,7 +53,8 @@ export function AiTextarea({
|
|
|
53
53
|
|
|
54
54
|
const handleSubmit = async (
|
|
55
55
|
selectedModel: string,
|
|
56
|
-
selectedPrompt: string
|
|
56
|
+
selectedPrompt: string,
|
|
57
|
+
promptId?: string
|
|
57
58
|
) => {
|
|
58
59
|
try {
|
|
59
60
|
const result = await generateText({
|
|
@@ -186,6 +187,7 @@ export function AiTextarea({
|
|
|
186
187
|
onSubmit={handleSubmit}
|
|
187
188
|
uiMode={uiMode}
|
|
188
189
|
models={models || []}
|
|
190
|
+
sourceText={textareaValue || undefined}
|
|
189
191
|
/>
|
|
190
192
|
)}
|
|
191
193
|
</div>
|