mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-29 08:33:20 +00:00
feat: implementing the new custom modal design (#2090)
* feat: implementing the new custom modal design * feat: added tool variable dropdown * fix: ui fixes and link fixes * feat: implemented redisgn for edit prompt modal * (feat:prompts) matching figma * (fix:prompts) tool vars * (fix:promptsModal) responsive; disable save on text --------- Co-authored-by: Aqsa Aqeel <aqsa.aqeel17@example.com> Co-authored-by: ManishMadan2882 <manishmadan321@gmail.com>
This commit is contained in:
3
frontend/src/assets/book.svg
Normal file
3
frontend/src/assets/book.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M10.2857 14H2.57143C1.15179 14 0 12.8242 0 11.375V2.625C0 1.17578 1.15179 0 2.57143 0H10.7143C11.4241 0 12 0.587891 12 1.3125V9.1875C12 9.75898 11.6411 10.2457 11.1429 10.4262V12.25C11.617 12.25 12 12.641 12 13.125C12 13.609 11.617 14 11.1429 14H10.2857ZM2.57143 10.5C2.09732 10.5 1.71429 10.891 1.71429 11.375C1.71429 11.859 2.09732 12.25 2.57143 12.25H9.42857V10.5H2.57143ZM3.42857 4.15625C3.42857 4.51992 3.71518 4.8125 4.07143 4.8125H8.78571C9.14196 4.8125 9.42857 4.51992 9.42857 4.15625C9.42857 3.79258 9.14196 3.5 8.78571 3.5H4.07143C3.71518 3.5 3.42857 3.79258 3.42857 4.15625ZM4.07143 6.125C3.71518 6.125 3.42857 6.41758 3.42857 6.78125C3.42857 7.14492 3.71518 7.4375 4.07143 7.4375H8.78571C9.14196 7.4375 9.42857 7.14492 9.42857 6.78125C9.42857 6.41758 9.14196 6.125 8.78571 6.125H4.07143Z" fill="#6A4DF4"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 930 B |
@@ -60,7 +60,7 @@ function Dropdown<T extends DropdownOption>({
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{typeof selectedValue === 'string' ? (
|
{typeof selectedValue === 'string' ? (
|
||||||
<span className="dark:text-bright-gray truncate">
|
<span className={`dark:text-bright-gray truncate ${contentSize}`}>
|
||||||
{selectedValue}
|
{selectedValue}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const Input = ({
|
|||||||
onChange,
|
onChange,
|
||||||
onPaste,
|
onPaste,
|
||||||
onKeyDown,
|
onKeyDown,
|
||||||
|
edgeRoundness = 'rounded-full',
|
||||||
}: InputProps) => {
|
}: InputProps) => {
|
||||||
const colorStyles = {
|
const colorStyles = {
|
||||||
silver: 'border-silver dark:border-silver/40',
|
silver: 'border-silver dark:border-silver/40',
|
||||||
@@ -43,7 +44,7 @@ const Input = ({
|
|||||||
<div className={`relative ${className}`}>
|
<div className={`relative ${className}`}>
|
||||||
<input
|
<input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
className={`peer text-jet dark:text-bright-gray h-[42px] w-full rounded-full bg-transparent ${leftIcon ? 'pl-10' : 'px-3'} py-1 placeholder-transparent outline-hidden ${colorStyles[colorVariant]} ${borderStyles[borderVariant]} ${textSizeStyles[textSize]} [&:-webkit-autofill]:appearance-none [&:-webkit-autofill]:bg-transparent [&:-webkit-autofill_selected]:bg-transparent`}
|
className={`peer text-jet dark:text-bright-gray h-[42px] w-full ${edgeRoundness} bg-transparent ${leftIcon ? 'pl-10' : 'px-3'} py-1 placeholder-transparent outline-hidden ${colorStyles[colorVariant]} ${borderStyles[borderVariant]} ${textSizeStyles[textSize]} [&:-webkit-autofill]:appearance-none [&:-webkit-autofill]:bg-transparent [&:-webkit-autofill_selected]:bg-transparent`}
|
||||||
type={type}
|
type={type}
|
||||||
id={id}
|
id={id}
|
||||||
name={name}
|
name={name}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export type InputProps = {
|
|||||||
e: React.KeyboardEvent<HTMLTextAreaElement | HTMLInputElement>,
|
e: React.KeyboardEvent<HTMLTextAreaElement | HTMLInputElement>,
|
||||||
) => void;
|
) => void;
|
||||||
leftIcon?: React.ReactNode;
|
leftIcon?: React.ReactNode;
|
||||||
|
edgeRoundness?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MermaidRendererProps = {
|
export type MermaidRendererProps = {
|
||||||
|
|||||||
@@ -386,8 +386,20 @@
|
|||||||
"promptName": "Prompt Name",
|
"promptName": "Prompt Name",
|
||||||
"promptText": "Prompt Text",
|
"promptText": "Prompt Text",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
|
"cancel": "Cancel",
|
||||||
"nameExists": "Name already exists",
|
"nameExists": "Name already exists",
|
||||||
"deleteConfirmation": "Are you sure you want to delete the prompt '{{name}}'?"
|
"deleteConfirmation": "Are you sure you want to delete the prompt '{{name}}'?",
|
||||||
|
"placeholderText": "Type your prompt text here...",
|
||||||
|
"addExamplePlaceholder": "Please summarize this text:",
|
||||||
|
"variablesLabel": "Variables",
|
||||||
|
"variablesSubtext": "Click To Insert Into Prompt",
|
||||||
|
"variablesDescription": "Click to insert into prompt",
|
||||||
|
"systemVariables": "Click to insert into prompt",
|
||||||
|
"toolVariables": "Tool Variables",
|
||||||
|
"learnAboutPrompts": "Learn about Prompts →",
|
||||||
|
"publicPromptEditDisabled": "Public prompts cannot be edited",
|
||||||
|
"promptTypePublic": "public",
|
||||||
|
"promptTypePrivate": "private"
|
||||||
},
|
},
|
||||||
"chunk": {
|
"chunk": {
|
||||||
"add": "Add Chunk",
|
"add": "Add Chunk",
|
||||||
|
|||||||
@@ -386,8 +386,20 @@
|
|||||||
"promptName": "Nombre del Prompt",
|
"promptName": "Nombre del Prompt",
|
||||||
"promptText": "Texto del Prompt",
|
"promptText": "Texto del Prompt",
|
||||||
"save": "Guardar",
|
"save": "Guardar",
|
||||||
|
"cancel": "Cancelar",
|
||||||
"nameExists": "El nombre ya existe",
|
"nameExists": "El nombre ya existe",
|
||||||
"deleteConfirmation": "¿Estás seguro de que deseas eliminar el prompt '{{name}}'?"
|
"deleteConfirmation": "¿Estás seguro de que deseas eliminar el prompt '{{name}}'?",
|
||||||
|
"placeholderText": "Escribe tu texto de prompt aquí...",
|
||||||
|
"addExamplePlaceholder": "Por favor, resume este texto:",
|
||||||
|
"variablesLabel": "Variables",
|
||||||
|
"variablesSubtext": "Haz clic para insertar en el prompt",
|
||||||
|
"variablesDescription": "Haz clic para insertar en el prompt",
|
||||||
|
"systemVariables": "Variables del sistema",
|
||||||
|
"toolVariables": "Variables de herramientas",
|
||||||
|
"learnAboutPrompts": "Aprende sobre los Prompts →",
|
||||||
|
"publicPromptEditDisabled": "Los prompts públicos no se pueden editar",
|
||||||
|
"promptTypePublic": "público",
|
||||||
|
"promptTypePrivate": "privado"
|
||||||
},
|
},
|
||||||
"chunk": {
|
"chunk": {
|
||||||
"add": "Agregar Fragmento",
|
"add": "Agregar Fragmento",
|
||||||
|
|||||||
@@ -380,14 +380,26 @@
|
|||||||
},
|
},
|
||||||
"prompts": {
|
"prompts": {
|
||||||
"addPrompt": "プロンプトを追加",
|
"addPrompt": "プロンプトを追加",
|
||||||
"addDescription": "カスタムプロンプトを追加してDocsGPTに保存",
|
"addDescription": "カスタムプロンプトを追加して DocsGPT に保存します",
|
||||||
"editPrompt": "プロンプトを編集",
|
"editPrompt": "プロンプトを編集",
|
||||||
"editDescription": "カスタムプロンプトを編集してDocsGPTに保存",
|
"editDescription": "カスタムプロンプトを編集して DocsGPT に保存します",
|
||||||
"promptName": "プロンプト名",
|
"promptName": "プロンプト名",
|
||||||
"promptText": "プロンプトテキスト",
|
"promptText": "プロンプトのテキスト",
|
||||||
"save": "保存",
|
"save": "保存",
|
||||||
"nameExists": "名前が既に存在します",
|
"cancel": "キャンセル",
|
||||||
"deleteConfirmation": "プロンプト「{{name}}」を削除してもよろしいですか?"
|
"nameExists": "この名前はすでに存在します",
|
||||||
|
"deleteConfirmation": "プロンプト『{{name}}』を削除してもよろしいですか?",
|
||||||
|
"placeholderText": "ここにプロンプトのテキストを入力してください…",
|
||||||
|
"addExamplePlaceholder": "このテキストを要約してください:",
|
||||||
|
"variablesLabel": "変数",
|
||||||
|
"variablesSubtext": "クリックしてプロンプトに挿入",
|
||||||
|
"variablesDescription": "クリックしてプロンプトに挿入",
|
||||||
|
"systemVariables": "システム変数",
|
||||||
|
"toolVariables": "ツール変数",
|
||||||
|
"learnAboutPrompts": "プロンプトについて学ぶ →",
|
||||||
|
"publicPromptEditDisabled": "公開プロンプトは編集できません",
|
||||||
|
"promptTypePublic": "公開",
|
||||||
|
"promptTypePrivate": "非公開"
|
||||||
},
|
},
|
||||||
"chunk": {
|
"chunk": {
|
||||||
"add": "チャンクを追加",
|
"add": "チャンクを追加",
|
||||||
|
|||||||
@@ -379,15 +379,27 @@
|
|||||||
"customNamePlaceholder": "Enter custom name (optional)"
|
"customNamePlaceholder": "Enter custom name (optional)"
|
||||||
},
|
},
|
||||||
"prompts": {
|
"prompts": {
|
||||||
"addPrompt": "Добавить подсказку",
|
"addPrompt": "Добавить промпт",
|
||||||
"addDescription": "Добавить вашу пользовательскую подсказку и сохранить её в DocsGPT",
|
"addDescription": "Добавьте свой собственный промпт и сохраните его в DocsGPT",
|
||||||
"editPrompt": "Редактировать подсказку",
|
"editPrompt": "Редактировать промпт",
|
||||||
"editDescription": "Редактировать вашу пользовательскую подсказку и сохранить её в DocsGPT",
|
"editDescription": "Отредактируйте свой промпт и сохраните его в DocsGPT",
|
||||||
"promptName": "Название подсказки",
|
"promptName": "Название промпта",
|
||||||
"promptText": "Текст подсказки",
|
"promptText": "Текст промпта",
|
||||||
"save": "Сохранить",
|
"save": "Сохранить",
|
||||||
"nameExists": "Название уже существует",
|
"cancel": "Отмена",
|
||||||
"deleteConfirmation": "Вы уверены, что хотите удалить подсказку «{{name}}»?"
|
"nameExists": "Имя уже существует",
|
||||||
|
"deleteConfirmation": "Вы уверены, что хотите удалить промпт «{{name}}»?",
|
||||||
|
"placeholderText": "Введите текст вашего промпта здесь...",
|
||||||
|
"addExamplePlaceholder": "Пожалуйста, кратко изложите этот текст:",
|
||||||
|
"variablesLabel": "Переменные",
|
||||||
|
"variablesSubtext": "Нажмите, чтобы вставить в промпт",
|
||||||
|
"variablesDescription": "Нажмите, чтобы вставить в промпт",
|
||||||
|
"systemVariables": "Системные переменные",
|
||||||
|
"toolVariables": "Переменные инструментов",
|
||||||
|
"learnAboutPrompts": "Узнать о промптах →",
|
||||||
|
"publicPromptEditDisabled": "Публичные промпты нельзя редактировать",
|
||||||
|
"promptTypePublic": "публичный",
|
||||||
|
"promptTypePrivate": "приватный"
|
||||||
},
|
},
|
||||||
"chunk": {
|
"chunk": {
|
||||||
"add": "Добавить фрагмент",
|
"add": "Добавить фрагмент",
|
||||||
|
|||||||
@@ -386,8 +386,20 @@
|
|||||||
"promptName": "提示名稱",
|
"promptName": "提示名稱",
|
||||||
"promptText": "提示文字",
|
"promptText": "提示文字",
|
||||||
"save": "儲存",
|
"save": "儲存",
|
||||||
|
"cancel": "取消",
|
||||||
"nameExists": "名稱已存在",
|
"nameExists": "名稱已存在",
|
||||||
"deleteConfirmation": "您確定要刪除提示「{{name}}」嗎?"
|
"deleteConfirmation": "您確定要刪除提示「{{name}}」嗎?",
|
||||||
|
"placeholderText": "在此輸入提示內容...",
|
||||||
|
"addExamplePlaceholder": "請總結此文本:",
|
||||||
|
"variablesLabel": "變數",
|
||||||
|
"variablesSubtext": "點擊以插入提示中",
|
||||||
|
"variablesDescription": "點擊以插入到提示中",
|
||||||
|
"systemVariables": "點擊以插入提示中",
|
||||||
|
"toolVariables": "工具變數",
|
||||||
|
"learnAboutPrompts": "了解提示 →",
|
||||||
|
"publicPromptEditDisabled": "公共提示無法編輯",
|
||||||
|
"promptTypePublic": "公共",
|
||||||
|
"promptTypePrivate": "私人"
|
||||||
},
|
},
|
||||||
"chunk": {
|
"chunk": {
|
||||||
"add": "新增區塊",
|
"add": "新增區塊",
|
||||||
|
|||||||
@@ -387,7 +387,19 @@
|
|||||||
"promptText": "提示文本",
|
"promptText": "提示文本",
|
||||||
"save": "保存",
|
"save": "保存",
|
||||||
"nameExists": "名称已存在",
|
"nameExists": "名称已存在",
|
||||||
"deleteConfirmation": "您确定要删除提示'{{name}}'吗?"
|
"deleteConfirmation": "您确定要删除提示'{{name}}'吗?",
|
||||||
|
"cancel": "取消",
|
||||||
|
"placeholderText": "在此輸入提示內容...",
|
||||||
|
"addExamplePlaceholder": "請總結此文本:",
|
||||||
|
"variablesLabel": "變數",
|
||||||
|
"variablesSubtext": "點擊以插入提示中",
|
||||||
|
"variablesDescription": "點擊以插入到提示中",
|
||||||
|
"systemVariables": "點擊以插入提示中",
|
||||||
|
"toolVariables": "工具變數",
|
||||||
|
"learnAboutPrompts": "了解提示 →",
|
||||||
|
"publicPromptEditDisabled": "公共提示無法編輯",
|
||||||
|
"promptTypePublic": "公共",
|
||||||
|
"promptTypePrivate": "私人"
|
||||||
},
|
},
|
||||||
"chunk": {
|
"chunk": {
|
||||||
"add": "添加块",
|
"add": "添加块",
|
||||||
|
|||||||
@@ -1,8 +1,77 @@
|
|||||||
import { ActiveState } from '../models/misc';
|
import { ActiveState } from '../models/misc';
|
||||||
import Input from '../components/Input';
|
import Input from '../components/Input';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
import WrapperModal from '../modals/WrapperModal';
|
import WrapperModal from '../modals/WrapperModal';
|
||||||
|
import Dropdown from '../components/Dropdown';
|
||||||
|
import BookIcon from '../assets/book.svg';
|
||||||
|
import userService from '../api/services/userService';
|
||||||
|
import { selectToken } from '../preferences/preferenceSlice';
|
||||||
|
import { UserToolType } from '../settings/types';
|
||||||
|
|
||||||
|
// Custom hook for fetching tool variables
|
||||||
|
const useToolVariables = () => {
|
||||||
|
const token = useSelector(selectToken);
|
||||||
|
const [toolVariables, setToolVariables] = React.useState<
|
||||||
|
{ label: string; value: string }[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const fetchToolVariables = async () => {
|
||||||
|
try {
|
||||||
|
const response = await userService.getUserTools(token);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.success && data.tools) {
|
||||||
|
const filteredActions: { label: string; value: string }[] = [];
|
||||||
|
|
||||||
|
data.tools.forEach((tool: UserToolType) => {
|
||||||
|
if (tool.actions && tool.status) {
|
||||||
|
// Only include active tools
|
||||||
|
tool.actions.forEach((action: any) => {
|
||||||
|
if (action.active) {
|
||||||
|
const canUseAction =
|
||||||
|
!action.parameters?.properties ||
|
||||||
|
Object.entries(action.parameters.properties).every(
|
||||||
|
([paramName, param]: [string, any]) => {
|
||||||
|
// Parameter is usable if:
|
||||||
|
// 1. It's filled by LLM (true) OR
|
||||||
|
// 2. It has a value in the tool config
|
||||||
|
return (
|
||||||
|
param.filled_by_llm === true ||
|
||||||
|
(tool.config &&
|
||||||
|
tool.config[paramName] &&
|
||||||
|
tool.config[paramName] !== '')
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (canUseAction) {
|
||||||
|
filteredActions.push({
|
||||||
|
label: `${action.name} (${tool.displayName || tool.name})`,
|
||||||
|
value: `tools.${tool.name}.${action.name}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setToolVariables(filteredActions);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching tool variables:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchToolVariables();
|
||||||
|
}, [token]);
|
||||||
|
|
||||||
|
return toolVariables;
|
||||||
|
};
|
||||||
|
|
||||||
function AddPrompt({
|
function AddPrompt({
|
||||||
setModalState,
|
setModalState,
|
||||||
@@ -22,52 +91,188 @@ function AddPrompt({
|
|||||||
disableSave: boolean;
|
disableSave: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const toolVariables = useToolVariables();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p className="text-jet dark:text-bright-gray mb-1 text-xl">
|
<p className="mb-1 text-xl font-semibold text-[#2B2B2B] dark:text-white">
|
||||||
{t('modals.prompts.addPrompt')}
|
{t('modals.prompts.addPrompt')}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sonic-silver mb-7 text-xs dark:text-[#7F7F82]">
|
<p className="mb-6 text-sm text-[#6B6B6B] dark:text-[#9A9AA0]">
|
||||||
{t('modals.prompts.addDescription')}
|
{t('modals.prompts.addDescription')}
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Input
|
||||||
placeholder={t('modals.prompts.promptName')}
|
placeholder={t('modals.prompts.promptName')}
|
||||||
type="text"
|
type="text"
|
||||||
className="mb-4"
|
className="mb-5"
|
||||||
|
edgeRoundness="rounded"
|
||||||
|
textSize="medium"
|
||||||
value={newPromptName}
|
value={newPromptName}
|
||||||
onChange={(e) => setNewPromptName(e.target.value)}
|
onChange={(e) => setNewPromptName(e.target.value)}
|
||||||
labelBgClassName="bg-white dark:bg-[#26272E]"
|
labelBgClassName="bg-white dark:bg-[#26272E]"
|
||||||
borderVariant="thin"
|
borderVariant="thick"
|
||||||
/>
|
/>
|
||||||
<div className="relative top-[7px] left-3">
|
|
||||||
<span className="text-silver dark:text-silver bg-white px-1 text-xs dark:bg-[#26272E]">
|
<div className="relative w-full">
|
||||||
|
<textarea
|
||||||
|
id="new-prompt-content"
|
||||||
|
className="peer border-silver dark:border-silver/40 h-48 w-full resize-none rounded border-2 bg-white px-3 py-2 text-base text-gray-800 outline-none dark:bg-[#26272E] dark:text-white"
|
||||||
|
value={newPromptContent}
|
||||||
|
onChange={(e) => setNewPromptContent(e.target.value)}
|
||||||
|
placeholder=" "
|
||||||
|
aria-label={t('prompts.textAriaLabel')}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="new-prompt-content"
|
||||||
|
className={`absolute select-none ${
|
||||||
|
newPromptContent ? '-top-2.5 left-3 text-xs' : ''
|
||||||
|
} text-gray-4000 pointer-events-none max-w-[calc(100%-24px)] cursor-none overflow-hidden bg-white px-2 text-ellipsis whitespace-nowrap transition-all peer-placeholder-shown:top-2.5 peer-placeholder-shown:left-3 peer-placeholder-shown:text-base peer-focus:-top-2.5 peer-focus:left-3 peer-focus:text-xs dark:bg-[#26272E] dark:text-gray-400`}
|
||||||
|
>
|
||||||
{t('modals.prompts.promptText')}
|
{t('modals.prompts.promptText')}
|
||||||
</span>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<label htmlFor="new-prompt-content" className="sr-only">
|
|
||||||
{t('modals.prompts.promptText')}
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
id="new-prompt-content"
|
|
||||||
className="border-silver dark:border-silver/40 h-56 w-full resize-none rounded-lg border-2 px-3 py-2 outline-hidden dark:bg-transparent dark:text-white"
|
|
||||||
value={newPromptContent}
|
|
||||||
onChange={(e) => setNewPromptContent(e.target.value)}
|
|
||||||
aria-label={t('prompts.textAriaLabel')}
|
|
||||||
></textarea>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 flex flex-row-reverse">
|
|
||||||
<button
|
<div className="mt-6 flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center sm:gap-4">
|
||||||
onClick={handleAddPrompt}
|
<p className="flex flex-col text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
className="bg-purple-30 hover:bg-violets-are-blue disabled:hover:bg-purple-30 rounded-3xl px-5 py-2 text-sm text-white transition-all"
|
<span className="font-bold">
|
||||||
disabled={disableSave}
|
{t('modals.prompts.variablesLabel')}
|
||||||
title={
|
</span>
|
||||||
disableSave && newPromptName ? t('modals.prompts.nameExists') : ''
|
<span className="text-xs text-[10px] font-medium text-gray-500">
|
||||||
}
|
{t('modals.prompts.variablesDescription')}
|
||||||
>
|
</span>
|
||||||
{t('modals.prompts.save')}
|
</p>
|
||||||
</button>
|
|
||||||
|
<div className="flex flex-wrap items-center gap-2 sm:gap-3">
|
||||||
|
<Dropdown
|
||||||
|
options={[{ label: 'Summaries', value: 'summaries' }]}
|
||||||
|
selectedValue={'System Variables'}
|
||||||
|
onSelect={(option) => {
|
||||||
|
const textarea = document.getElementById(
|
||||||
|
'new-prompt-content',
|
||||||
|
) as HTMLTextAreaElement;
|
||||||
|
if (textarea) {
|
||||||
|
const cursorPosition = textarea.selectionStart;
|
||||||
|
const textBefore = newPromptContent.slice(0, cursorPosition);
|
||||||
|
const textAfter = newPromptContent.slice(cursorPosition);
|
||||||
|
|
||||||
|
// Add leading space if needed
|
||||||
|
const needsSpace =
|
||||||
|
cursorPosition > 0 &&
|
||||||
|
newPromptContent.charAt(cursorPosition - 1) !== ' ';
|
||||||
|
|
||||||
|
const newText =
|
||||||
|
textBefore +
|
||||||
|
(needsSpace ? ' ' : '') +
|
||||||
|
`{${option.value}}` +
|
||||||
|
textAfter;
|
||||||
|
setNewPromptContent(newText);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
textarea.focus();
|
||||||
|
textarea.setSelectionRange(
|
||||||
|
cursorPosition +
|
||||||
|
option.value.length +
|
||||||
|
2 +
|
||||||
|
(needsSpace ? 1 : 0),
|
||||||
|
cursorPosition +
|
||||||
|
option.value.length +
|
||||||
|
2 +
|
||||||
|
(needsSpace ? 1 : 0),
|
||||||
|
);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
placeholder="System Variables"
|
||||||
|
size="w-[140px] sm:w-[185px]"
|
||||||
|
rounded="3xl"
|
||||||
|
border="border"
|
||||||
|
contentSize="text-[12px] sm:text-[14px]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Dropdown
|
||||||
|
options={toolVariables}
|
||||||
|
selectedValue={'Tool Variables'}
|
||||||
|
onSelect={(option) => {
|
||||||
|
const textarea = document.getElementById(
|
||||||
|
'new-prompt-content',
|
||||||
|
) as HTMLTextAreaElement;
|
||||||
|
if (textarea) {
|
||||||
|
const cursorPosition = textarea.selectionStart;
|
||||||
|
const textBefore = newPromptContent.slice(0, cursorPosition);
|
||||||
|
const textAfter = newPromptContent.slice(cursorPosition);
|
||||||
|
|
||||||
|
// Add leading space if needed
|
||||||
|
const needsSpace =
|
||||||
|
cursorPosition > 0 &&
|
||||||
|
newPromptContent.charAt(cursorPosition - 1) !== ' ';
|
||||||
|
|
||||||
|
const newText =
|
||||||
|
textBefore +
|
||||||
|
(needsSpace ? ' ' : '') +
|
||||||
|
`{{ ${option.value} }}` +
|
||||||
|
textAfter;
|
||||||
|
setNewPromptContent(newText);
|
||||||
|
setTimeout(() => {
|
||||||
|
textarea.focus();
|
||||||
|
textarea.setSelectionRange(
|
||||||
|
cursorPosition +
|
||||||
|
option.value.length +
|
||||||
|
6 +
|
||||||
|
(needsSpace ? 1 : 0),
|
||||||
|
cursorPosition +
|
||||||
|
option.value.length +
|
||||||
|
6 +
|
||||||
|
(needsSpace ? 1 : 0),
|
||||||
|
);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
placeholder="Tool Variables"
|
||||||
|
size="w-[140px] sm:w-[171px]"
|
||||||
|
rounded="3xl"
|
||||||
|
border="border"
|
||||||
|
contentSize="text-[12px] sm:text-[14px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 flex flex-col justify-between gap-4 text-[14px] sm:flex-row sm:gap-0">
|
||||||
|
<div className="flex justify-start">
|
||||||
|
<Link
|
||||||
|
to="https://docs.docsgpt.cloud/Guides/Customising-prompts"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="flex items-center gap-2 text-sm font-medium text-[#6A4DF4] hover:underline"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={BookIcon}
|
||||||
|
alt=""
|
||||||
|
className="flex h-4 w-3 flex-shrink-0 items-center justify-center"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
<span className="text-[14px] font-bold">
|
||||||
|
{t('modals.prompts.learnAboutPrompts')}
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-2 sm:gap-4">
|
||||||
|
<button
|
||||||
|
onClick={() => setModalState('INACTIVE')}
|
||||||
|
className="rounded-3xl border border-[#D9534F] px-5 py-2 text-sm font-medium text-[#D9534F] transition-all hover:bg-[#D9534F] hover:text-white"
|
||||||
|
>
|
||||||
|
{t('modals.prompts.cancel')}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleAddPrompt}
|
||||||
|
className="rounded-3xl bg-[#6A4DF4] px-6 py-2 text-sm font-medium text-white transition-all hover:bg-[#563DD1] disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-[#6A4DF4]"
|
||||||
|
disabled={disableSave}
|
||||||
|
>
|
||||||
|
{t('modals.prompts.save')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -93,54 +298,191 @@ function EditPrompt({
|
|||||||
disableSave: boolean;
|
disableSave: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const toolVariables = useToolVariables();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="">
|
<p className="mb-1 text-xl font-semibold text-[#2B2B2B] dark:text-white">
|
||||||
<p className="text-jet dark:text-bright-gray mb-1 text-xl">
|
{t('modals.prompts.editPrompt')}
|
||||||
{t('modals.prompts.editPrompt')}
|
</p>
|
||||||
</p>
|
<p className="mb-6 text-sm text-[#6B6B6B] dark:text-[#9A9AA0]">
|
||||||
<p className="text-sonic-silver mb-7 text-xs dark:text-[#7F7F82]">
|
{t('modals.prompts.editDescription')}
|
||||||
{t('modals.prompts.editDescription')}
|
</p>
|
||||||
</p>
|
<div>
|
||||||
<div>
|
<Input
|
||||||
<Input
|
placeholder={t('modals.prompts.promptName')}
|
||||||
placeholder={t('modals.prompts.promptName')}
|
type="text"
|
||||||
type="text"
|
className="mb-5"
|
||||||
className="mb-4"
|
edgeRoundness="rounded"
|
||||||
value={editPromptName}
|
textSize="medium"
|
||||||
onChange={(e) => setEditPromptName(e.target.value)}
|
value={editPromptName}
|
||||||
labelBgClassName="bg-white dark:bg-charleston-green-2"
|
onChange={(e) => setEditPromptName(e.target.value)}
|
||||||
borderVariant="thin"
|
labelBgClassName="bg-white dark:bg-[#26272E]"
|
||||||
/>
|
borderVariant="thick"
|
||||||
<div className="relative top-[7px] left-3">
|
/>
|
||||||
<span className="text-silver dark:bg-charleston-green-2 dark:text-silver bg-white px-1 text-xs">
|
|
||||||
{t('modals.prompts.promptText')}
|
<div className="relative w-full">
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<label htmlFor="edit-prompt-content" className="sr-only">
|
|
||||||
{t('modals.prompts.promptText')}
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
id="edit-prompt-content"
|
id="edit-prompt-content"
|
||||||
className="border-silver dark:border-silver/40 h-56 w-full resize-none rounded-lg border-2 px-3 py-2 outline-hidden dark:bg-transparent dark:text-white"
|
className="peer border-silver dark:border-silver/40 h-48 w-full resize-none rounded border-2 bg-white px-3 py-2 text-base text-gray-800 outline-none dark:bg-[#26272E] dark:text-white"
|
||||||
value={editPromptContent}
|
value={editPromptContent}
|
||||||
onChange={(e) => setEditPromptContent(e.target.value)}
|
onChange={(e) => setEditPromptContent(e.target.value)}
|
||||||
|
placeholder=" "
|
||||||
aria-label={t('prompts.textAriaLabel')}
|
aria-label={t('prompts.textAriaLabel')}
|
||||||
></textarea>
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="edit-prompt-content"
|
||||||
|
className={`absolute select-none ${
|
||||||
|
editPromptContent ? '-top-2.5 left-3 text-xs' : ''
|
||||||
|
} text-gray-4000 pointer-events-none max-w-[calc(100%-24px)] cursor-none overflow-hidden bg-white px-2 text-ellipsis whitespace-nowrap transition-all peer-placeholder-shown:top-2.5 peer-placeholder-shown:left-3 peer-placeholder-shown:text-base peer-focus:-top-2.5 peer-focus:left-3 peer-focus:text-xs dark:bg-[#26272E] dark:text-gray-400`}
|
||||||
|
>
|
||||||
|
{t('modals.prompts.promptText')}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 flex flex-row-reverse gap-4">
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-6 flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center sm:gap-4">
|
||||||
|
<p className="flex flex-col text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
|
<span className="font-bold">
|
||||||
|
{t('modals.prompts.variablesLabel')}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-[10px] font-medium text-gray-500">
|
||||||
|
{t('modals.prompts.variablesDescription')}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap items-center gap-2 sm:gap-3">
|
||||||
|
<Dropdown
|
||||||
|
options={[{ label: 'Summaries', value: 'summaries' }]}
|
||||||
|
selectedValue={'System Variables'}
|
||||||
|
onSelect={(option) => {
|
||||||
|
const textarea = document.getElementById(
|
||||||
|
'edit-prompt-content',
|
||||||
|
) as HTMLTextAreaElement;
|
||||||
|
if (textarea) {
|
||||||
|
const cursorPosition = textarea.selectionStart;
|
||||||
|
const textBefore = editPromptContent.slice(0, cursorPosition);
|
||||||
|
const textAfter = editPromptContent.slice(cursorPosition);
|
||||||
|
|
||||||
|
// Add leading space if needed
|
||||||
|
const needsSpace =
|
||||||
|
cursorPosition > 0 &&
|
||||||
|
editPromptContent.charAt(cursorPosition - 1) !== ' ';
|
||||||
|
|
||||||
|
const newText =
|
||||||
|
textBefore +
|
||||||
|
(needsSpace ? ' ' : '') +
|
||||||
|
`{${option.value}}` +
|
||||||
|
textAfter;
|
||||||
|
setEditPromptContent(newText);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
textarea.focus();
|
||||||
|
textarea.setSelectionRange(
|
||||||
|
cursorPosition +
|
||||||
|
option.value.length +
|
||||||
|
2 +
|
||||||
|
(needsSpace ? 1 : 0),
|
||||||
|
cursorPosition +
|
||||||
|
option.value.length +
|
||||||
|
2 +
|
||||||
|
(needsSpace ? 1 : 0),
|
||||||
|
);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
placeholder="System Variables"
|
||||||
|
size="w-[140px] sm:w-[185px]"
|
||||||
|
rounded="3xl"
|
||||||
|
border="border"
|
||||||
|
contentSize="text-[12px] sm:text-[14px]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Dropdown
|
||||||
|
options={toolVariables}
|
||||||
|
selectedValue={'Tool Variables'}
|
||||||
|
onSelect={(option) => {
|
||||||
|
const textarea = document.getElementById(
|
||||||
|
'edit-prompt-content',
|
||||||
|
) as HTMLTextAreaElement;
|
||||||
|
if (textarea) {
|
||||||
|
const cursorPosition = textarea.selectionStart;
|
||||||
|
const textBefore = editPromptContent.slice(0, cursorPosition);
|
||||||
|
const textAfter = editPromptContent.slice(cursorPosition);
|
||||||
|
|
||||||
|
// Add leading space if needed
|
||||||
|
const needsSpace =
|
||||||
|
cursorPosition > 0 &&
|
||||||
|
editPromptContent.charAt(cursorPosition - 1) !== ' ';
|
||||||
|
|
||||||
|
const newText =
|
||||||
|
textBefore +
|
||||||
|
(needsSpace ? ' ' : '') +
|
||||||
|
`{{ ${option.value} }}` +
|
||||||
|
textAfter;
|
||||||
|
setEditPromptContent(newText);
|
||||||
|
setTimeout(() => {
|
||||||
|
textarea.focus();
|
||||||
|
textarea.setSelectionRange(
|
||||||
|
cursorPosition +
|
||||||
|
option.value.length +
|
||||||
|
6 +
|
||||||
|
(needsSpace ? 1 : 0),
|
||||||
|
cursorPosition +
|
||||||
|
option.value.length +
|
||||||
|
6 +
|
||||||
|
(needsSpace ? 1 : 0),
|
||||||
|
);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
placeholder="Tool Variables"
|
||||||
|
size="w-[140px] sm:w-[171px]"
|
||||||
|
rounded="3xl"
|
||||||
|
border="border"
|
||||||
|
contentSize="text-[12px] sm:text-[14px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 flex flex-col justify-between gap-4 text-[14px] sm:flex-row sm:gap-0">
|
||||||
|
<div className="flex justify-start">
|
||||||
|
<Link
|
||||||
|
to="https://docs.docsgpt.cloud/Guides/Customising-prompts"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="flex items-center gap-2 text-sm font-medium text-[#6A4DF4] hover:underline"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={BookIcon}
|
||||||
|
alt=""
|
||||||
|
className="flex h-4 w-3 flex-shrink-0 items-center justify-center"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
<span className="text-[14px] font-bold">
|
||||||
|
{t('modals.prompts.learnAboutPrompts')}
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-2 sm:gap-4">
|
||||||
|
<button
|
||||||
|
onClick={() => setModalState('INACTIVE')}
|
||||||
|
className="rounded-3xl border border-[#D9534F] px-5 py-2 text-sm font-medium text-[#D9534F] transition-all hover:bg-[#D9534F] hover:text-white"
|
||||||
|
>
|
||||||
|
{t('modals.prompts.cancel')}
|
||||||
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className={`bg-purple-30 hover:bg-violets-are-blue disabled:hover:bg-purple-30 rounded-3xl px-5 py-2 text-sm text-white transition-all ${
|
|
||||||
currentPromptEdit.type === 'public'
|
|
||||||
? 'cursor-not-allowed opacity-50'
|
|
||||||
: ''
|
|
||||||
}`}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleEditPrompt &&
|
handleEditPrompt &&
|
||||||
handleEditPrompt(currentPromptEdit.id, currentPromptEdit.type);
|
handleEditPrompt(currentPromptEdit.id, currentPromptEdit.type);
|
||||||
}}
|
}}
|
||||||
disabled={currentPromptEdit.type === 'public' || disableSave}
|
className="rounded-3xl bg-[#6A4DF4] px-6 py-2 text-sm font-medium text-white transition-all hover:bg-[#563DD1] disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-[#6A4DF4]"
|
||||||
|
disabled={
|
||||||
|
currentPromptEdit.type === 'public' ||
|
||||||
|
disableSave ||
|
||||||
|
!editPromptName
|
||||||
|
}
|
||||||
title={
|
title={
|
||||||
disableSave && editPromptName
|
disableSave && editPromptName
|
||||||
? t('modals.prompts.nameExists')
|
? t('modals.prompts.nameExists')
|
||||||
@@ -200,23 +542,28 @@ export default function PromptsModal({
|
|||||||
(prompt) =>
|
(prompt) =>
|
||||||
newName === prompt.name && prompt.id !== currentPromptEdit.id,
|
newName === prompt.name && prompt.id !== currentPromptEdit.id,
|
||||||
);
|
);
|
||||||
const nameValid = newName && !nameExists;
|
setDisableSave(
|
||||||
const contentChanged = editPromptContent !== currentPromptEdit.content;
|
!(
|
||||||
|
newName &&
|
||||||
setDisableSave(!(nameValid || contentChanged));
|
!nameExists &&
|
||||||
|
editPromptName &&
|
||||||
|
editPromptContent.trim() !== ''
|
||||||
|
),
|
||||||
|
);
|
||||||
setEditPromptName(newName);
|
setEditPromptName(newName);
|
||||||
} else {
|
} else {
|
||||||
const nameExists = existingPrompts.find(
|
const nameExists = existingPrompts.find(
|
||||||
(prompt) => newName === prompt.name,
|
(prompt) => newName === prompt.name,
|
||||||
);
|
);
|
||||||
setDisableSave(!(newName && !nameExists));
|
setDisableSave(
|
||||||
|
!(newName && !nameExists && newPromptContent.trim() !== ''),
|
||||||
|
);
|
||||||
setNewPromptName(newName);
|
setNewPromptName(newName);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleContentChange = (edit: boolean, newContent: string) => {
|
const handleContentChange = (edit: boolean, newContent: string) => {
|
||||||
if (edit) {
|
if (edit) {
|
||||||
const contentChanged = newContent !== currentPromptEdit.content;
|
|
||||||
const nameValid =
|
const nameValid =
|
||||||
editPromptName &&
|
editPromptName &&
|
||||||
!existingPrompts.find(
|
!existingPrompts.find(
|
||||||
@@ -224,10 +571,13 @@ export default function PromptsModal({
|
|||||||
editPromptName === prompt.name &&
|
editPromptName === prompt.name &&
|
||||||
prompt.id !== currentPromptEdit.id,
|
prompt.id !== currentPromptEdit.id,
|
||||||
);
|
);
|
||||||
|
setDisableSave(!(nameValid && newContent.trim() !== ''));
|
||||||
setDisableSave(!(nameValid || contentChanged));
|
|
||||||
setEditPromptContent(newContent);
|
setEditPromptContent(newContent);
|
||||||
} else {
|
} else {
|
||||||
|
const nameValid =
|
||||||
|
newPromptName &&
|
||||||
|
!existingPrompts.find((prompt) => newPromptName === prompt.name);
|
||||||
|
setDisableSave(!(nameValid && newContent.trim() !== ''));
|
||||||
setNewPromptContent(newContent);
|
setNewPromptContent(newContent);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -272,7 +622,8 @@ export default function PromptsModal({
|
|||||||
setNewPromptContent('');
|
setNewPromptContent('');
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="mt-24 sm:w-[512px]"
|
className="mx-4 mt-16 w-[95vw] max-w-[650px] rounded-2xl bg-white px-4 py-4 sm:px-6 sm:py-6 md:px-8 md:py-6 dark:bg-[#1E1E2A]"
|
||||||
|
contentClassName="!overflow-visible"
|
||||||
>
|
>
|
||||||
{view}
|
{view}
|
||||||
</WrapperModal>
|
</WrapperModal>
|
||||||
|
|||||||
Reference in New Issue
Block a user