Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added prompt autocomplete feature #460

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7,401 changes: 7,401 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@
"electron-is-dev": "^2.0.0",
"electron-squirrel-startup": "^1.0.0",
"electron-updater": "^5.3.0",
"fast-levenshtein": "^3.0.0",
"html2canvas": "^1.4.1",
"i18next": "^22.4.11",
"i18next-browser-languagedetector": "^7.0.1",
"i18next-http-backend": "^2.1.1",
"jspdf": "^2.5.1",
"katex": "^0.16.4",
"lodash": "^4.17.21",
"lodash.debounce": "^4.0.8",
"match-sorter": "^6.3.1",
"papaparse": "^5.4.1",
"react": "^18.2.0",
Expand All @@ -71,7 +73,10 @@
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.9",
"@types/dompurify": "^3.0.3",
"@types/fast-levenshtein": "^0.0.2",
"@types/lodash": "^4.14.192",
"@types/lodash.debounce": "^4.0.7",
"@types/papaparse": "^5.3.7",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
Expand All @@ -80,6 +85,7 @@
"@vitejs/plugin-react-swc": "^3.0.0",
"autoprefixer": "^10.4.13",
"concurrently": "^8.0.1",
"dompurify": "^3.0.6",
"electron": "^23.2.0",
"electron-builder": "^23.6.0",
"postcss": "^8.4.21",
Expand Down
3 changes: 2 additions & 1 deletion public/locales/da/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "Klon Chat",
"cloned": "Klonet",
"enterToSubmit": "Tryk Enter for at sende",
"submitPlaceholder": "Skriv en besked eller klik på [/] for opgave..."
"submitPlaceholder": "Skriv en besked eller klik på [/] for opgave...",
"promptSuggestions": "Forslag til prompt"
}
3 changes: 2 additions & 1 deletion public/locales/de/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "Chat klonen",
"cloned": "Klonen erfolgreich",
"enterToSubmit": "Drücke Enter zum absenden",
"submitPlaceholder": "Verfasse eine Nachricht oder klicke auf [/] für gespeicherte Prompts..."
"submitPlaceholder": "Verfasse eine Nachricht oder klicke auf [/] für gespeicherte Prompts...",
"promptSuggestions": "Aufforderungsvorschläge"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Prompt-Vorschläge" would be a better translation.

}
3 changes: 2 additions & 1 deletion public/locales/en-US/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "Clone Chat",
"cloned": "Cloned",
"enterToSubmit": "Enter to submit",
"submitPlaceholder": "Type a message or click [/] for prompts..."
"submitPlaceholder": "Type a message or click [/] for prompts...",
"promptSuggestions": "Prompt Suggestions"
}
3 changes: 2 additions & 1 deletion public/locales/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "Clone Chat",
"cloned": "Cloned",
"enterToSubmit": "Enter to submit",
"submitPlaceholder": "Type a message or click [/] for prompts..."
"submitPlaceholder": "Type a message or click [/] for prompts...",
"promptSuggestions": "Prompt Suggestions"
}
3 changes: 2 additions & 1 deletion public/locales/es/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "Clone Chat",
"cloned": "Cloned",
"enterToSubmit": "Enter to submit",
"submitPlaceholder": "Escribe un mensaje o haz clic en [/] para prompt..."
"submitPlaceholder": "Escribe un mensaje o haz clic en [/] para prompt...",
"promptSuggestions": "Sugerencias de indicación"
}
3 changes: 2 additions & 1 deletion public/locales/fr/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "Cloner la Conversation",
"cloned": "Clonée",
"enterToSubmit": "Entrée pour soumettre",
"submitPlaceholder": "Saisissez un message ou cliquez sur [/] pour des prompts..."
"submitPlaceholder": "Saisissez un message ou cliquez sur [/] pour des prompts...",
"promptSuggestions": "Suggestions de prompt"
}
3 changes: 2 additions & 1 deletion public/locales/it/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "Duplica Conversazione",
"cloned": "Duplicata",
"enterToSubmit": "Invio per inviare",
"submitPlaceholder": "Digita un messaggio o fai clic su [/] per prompt..."
"submitPlaceholder": "Digita un messaggio o fai clic su [/] per prompt...",
"promptSuggestions": "Suggerimenti per il prompt"
}
3 changes: 2 additions & 1 deletion public/locales/ja/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "チャットのコピーを作成",
"cloned": "完了しました",
"enterToSubmit": "Enterキーを押して送信",
"submitPlaceholder": "メッセージを入力するか、[/] をクリックしてプロンプトを表示します..."
"submitPlaceholder": "メッセージを入力するか、[/] をクリックしてプロンプトを表示します...",
"promptSuggestions": "プロンプトの提案"
}
3 changes: 2 additions & 1 deletion public/locales/ms/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "Buat salinan perbualan ini",
"cloned": "Dicipta",
"enterToSubmit": "Tekan Enter untuk hantar",
"submitPlaceholder": "Taip mesej atau klik [/] untuk arahan..."
"submitPlaceholder": "Taip mesej atau klik [/] untuk arahan...",
"promptSuggestions": "Cadangan Makluman"
}
3 changes: 2 additions & 1 deletion public/locales/nb/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "Klone chat",
"cloned": "Klonet",
"enterToSubmit": "Trykk enter for å sende",
"submitPlaceholder": "Skriv en melding eller klikk på [/] for oppgave..."
"submitPlaceholder": "Skriv en melding eller klikk på [/] for oppgave...",
"promptSuggestions": "Forslag til prompt"
}
3 changes: 2 additions & 1 deletion public/locales/ro/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "Clone Chat",
"cloned": "Clonat",
"enterToSubmit": "Intrați pentru a trimite",
"submitPlaceholder": "Tastați un mesaj sau faceți clic pe [/] pentru solicitări..."
"submitPlaceholder": "Tastați un mesaj sau faceți clic pe [/] pentru solicitări...",
"promptSuggestions": "Sugestii pentru prompt"
}
3 changes: 2 additions & 1 deletion public/locales/ru/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "Клонировать чат",
"cloned": "Клонировано",
"enterToSubmit": "Нажмите Enter для отправки",
"submitPlaceholder": "Напишите сообщение или нажмите [/] для подсказок..."
"submitPlaceholder": "Напишите сообщение или нажмите [/] для подсказок...",
"promptSuggestions": "Предложения для командной строки"
}
3 changes: 2 additions & 1 deletion public/locales/sv/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "Klona chatt",
"cloned": "Klonad",
"enterToSubmit": "Tryck på Enter för att skicka",
"submitPlaceholder": "Skriv ett meddelande eller klicka på [/] för uppmaning..."
"submitPlaceholder": "Skriv ett meddelande eller klicka på [/] för uppmaning...",
"promptSuggestions": "Förslag på uppmaning"
}
3 changes: 2 additions & 1 deletion public/locales/zh-CN/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "创建聊天副本",
"cloned": "已创建副本",
"enterToSubmit": "按回车键提交",
"submitPlaceholder": "输入消息或点击 [/] 以使用提示词…"
"submitPlaceholder": "输入消息或点击 [/] 以使用提示词…",
"promptSuggestions": "提示建议"
}
3 changes: 2 additions & 1 deletion public/locales/zh-HK/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "建立傾偈副本",
"cloned": "建立成功",
"enterToSubmit": "撳 Enter 鍵送出",
"submitPlaceholder": "輸入訊息或撳 [/] 以使用提示詞…"
"submitPlaceholder": "輸入訊息或撳 [/] 以使用提示詞…",
"promptSuggestions": "提示建議"
}
3 changes: 2 additions & 1 deletion public/locales/zh-TW/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"cloneChat": "建立聊天副本",
"cloned": "已建立副本",
"enterToSubmit": "按 Enter 鍵送出",
"submitPlaceholder": "輸入訊息或點選 [/] 以使用提示詞…"
"submitPlaceholder": "輸入訊息或點選 [/] 以使用提示詞…",
"promptSuggestions": "提示建议"
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React, { useEffect, useRef, useState } from 'react';
import useStore from '@store/store';

import { useTranslation } from 'react-i18next';
import { matchSorter } from 'match-sorter';
import { Prompt } from '@type/prompt';

import useHideOnOutsideClick from '@hooks/useHideOnOutsideClick';
import { findSimilarPrompts } from '@utils/findSimilarPrompts';

const CommandPrompt = ({
_setContent,
Expand All @@ -27,12 +26,20 @@ const CommandPrompt = ({
}
}, [dropDown]);

useEffect(() => {
const filteredPrompts = matchSorter(useStore.getState().prompts, input, {
keys: ['name'],
});
_setPrompts(filteredPrompts);
}, [input]);
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const inputValue = e.target.value;
setInput(inputValue);
if (inputValue.startsWith('/')) {
const filteredPrompts = matchSorter(useStore.getState().prompts, inputValue.slice(1), {
keys: ['name'],
});
_setPrompts(filteredPrompts);
} else {
// For non-commands add similar prompts functionality
const similarPrompts = findSimilarPrompts(inputValue, prompts);
_setPrompts(similarPrompts);
}
};

useEffect(() => {
_setPrompts(prompts);
Expand All @@ -59,10 +66,8 @@ const CommandPrompt = ({
type='text'
className='text-gray-800 dark:text-white p-3 text-sm border-none bg-gray-200 dark:bg-gray-600 m-0 w-full mr-0 h-8 focus:outline-none'
value={input}
placeholder={t('search') as string}
onChange={(e) => {
setInput(e.target.value);
}}
placeholder="Type a message or click [/] for prompts..."
onChange={handleInputChange}
/>
<ul className='text-sm text-gray-700 dark:text-gray-200 p-0 m-0 w-max max-w-sm max-md:max-w-[90vw] max-h-32 overflow-auto'>
{_prompts.map((cp) => (
Expand All @@ -84,3 +89,4 @@ const CommandPrompt = ({
};

export default CommandPrompt;

92 changes: 69 additions & 23 deletions src/components/Chat/ChatContent/Message/View/EditView.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import React, { memo, useEffect, useState } from 'react';
import escape from 'lodash/escape';
import { useTranslation } from 'react-i18next';
import useStore from '@store/store';

import { findSimilarPrompts } from '@utils/findSimilarPrompts';
import useSubmit from '@hooks/useSubmit';

import { ChatInterface } from '@type/chat';

import { Prompt } from 'src/types/prompt';
import PopupModal from '@components/PopupModal';
import TokenCount from '@components/TokenCount';
import CommandPrompt from '../CommandPrompt';
import DOMPurify from 'dompurify';

const EditView = ({
content,
Expand All @@ -24,13 +25,26 @@ const EditView = ({
const inputRole = useStore((state) => state.inputRole);
const setChats = useStore((state) => state.setChats);
const currentChatIndex = useStore((state) => state.currentChatIndex);
const prompts = useStore((state) => state.prompts);
const promptSuggestions = useStore((state) => state.promptSuggestions); // Add this line

const [_content, _setContent] = useState<string>(content);
const [input, setInput] = useState("");
const [suggestions, setSuggestions] = useState<Prompt[]>([]);
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const textareaRef = React.createRef<HTMLTextAreaElement>();

const { t } = useTranslation();

useEffect(() => {
if (input.length > 0 && promptSuggestions) {
const similarPrompts = findSimilarPrompts(input, prompts);
setSuggestions(similarPrompts);
} else {
setSuggestions([]);
}
}, [input, prompts, promptSuggestions]);

const resetTextAreaHeight = () => {
if (textareaRef.current) textareaRef.current.style.height = 'auto';
};
Expand Down Expand Up @@ -64,17 +78,17 @@ const EditView = ({
};

const handleSave = () => {
if (sticky && (_content === '' || useStore.getState().generating)) return;
if (sticky && (input === '' || useStore.getState().generating)) return;
const updatedChats: ChatInterface[] = JSON.parse(
JSON.stringify(useStore.getState().chats)
);
const updatedMessages = updatedChats[currentChatIndex].messages;
if (sticky) {
updatedMessages.push({ role: inputRole, content: _content });
_setContent('');
updatedMessages.push({ role: inputRole, content: input });
setInput('');
resetTextAreaHeight();
} else {
updatedMessages[messageIndex].content = _content;
updatedMessages[messageIndex].content = input;
setIsEdit(false);
}
setChats(updatedChats);
Expand All @@ -88,13 +102,13 @@ const EditView = ({
);
const updatedMessages = updatedChats[currentChatIndex].messages;
if (sticky) {
if (_content !== '') {
updatedMessages.push({ role: inputRole, content: _content });
if (input !== '') {
updatedMessages.push({ role: inputRole, content: input });
}
_setContent('');
setInput('');
resetTextAreaHeight();
} else {
updatedMessages[messageIndex].content = _content;
updatedMessages[messageIndex].content = input;
updatedChats[currentChatIndex].messages = updatedMessages.slice(
0,
messageIndex + 1
Expand All @@ -110,7 +124,7 @@ const EditView = ({
textareaRef.current.style.height = 'auto';
textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;
}
}, [_content]);
}, [input]);

useEffect(() => {
if (textareaRef.current) {
Expand All @@ -121,24 +135,56 @@ const EditView = ({

return (
<>
{useStore.getState().promptSuggestions && (
<div className="flex justify-center">
{(suggestions.length > 0 ? suggestions : prompts).slice(0, 2).map((suggestion, index, array) => (
<button
key={index}
className="btn btn-neutral flex flex-col items-center justify-between flex-1 h-32 overflow-auto"
aria-label={suggestion.name}
onClick={() => {
setInput(suggestion.prompt); // This sets the input for the default text box
_setContent(suggestion.prompt); // This sets the input for the non-default text box
const similarPrompts = findSimilarPrompts(suggestion.prompt, prompts);
setSuggestions(similarPrompts);
}}
style={{width: `${100 / array.length}%`}}
>
<div className="font-semibold text-center flex-shrink-0">{suggestion.name}</div>
<div
className="text-center flex-grow"
style={{maxWidth: '100%', wordWrap: 'break-word', overflowY: 'auto'}}
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(suggestion.prompt.replace(
new RegExp(escape(suggestion.formattedTerm || '').replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), 'gi'),
match => `<strong>${escape(match)}</strong>`
))
}}
/>
</button>
))}
</div>
)}
<div
className={`w-full ${
sticky
? 'py-2 md:py-3 px-2 md:px-4 border border-black/10 bg-white dark:border-gray-900/50 dark:text-white dark:bg-gray-700 rounded-md shadow-[0_0_10px_rgba(0,0,0,0.10)] dark:shadow-[0_0_15px_rgba(0,0,0,0.10)]'
: ''
}`}
>
<textarea
ref={textareaRef}
className='m-0 resize-none rounded-lg bg-transparent overflow-y-hidden focus:ring-0 focus-visible:ring-0 leading-7 w-full placeholder:text-gray-500/40'
onChange={(e) => {
_setContent(e.target.value);
}}
value={_content}
placeholder={t('submitPlaceholder') as string}
onKeyDown={handleKeyDown}
rows={1}
></textarea>
<textarea
ref={textareaRef}
className='m-0 resize-none rounded-lg bg-transparent overflow-y-hidden focus:ring-0 focus-visible:ring-0 leading-7 w-full placeholder:text-gray-500/40'
onChange={(e) => {
const newValue = e.target.value;
setInput(newValue);
_setContent(newValue); // Add this line
}}
value={_content}
placeholder={t('submitPlaceholder') as string}
onKeyDown={handleKeyDown}
rows={1}
></textarea>
</div>
<EditViewButtons
sticky={sticky}
Expand Down
Loading