From 937c60c9cf4e902abb2a3a94aa91611355f3aee0 Mon Sep 17 00:00:00 2001 From: utin-francis-peter Date: Sat, 29 Jun 2024 18:55:10 +0100 Subject: [PATCH 01/13] style: updated custom css class to match textInput component's --- frontend/src/index.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/index.css b/frontend/src/index.css index 519b5f74..80a1aa8e 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -413,7 +413,7 @@ template { bottom: env(safe-area-inset-bottom, 0); } -.inputbox-style[contenteditable] { +.text-input { padding-left: 36px; padding-right: 36px; } From 522e966194d8d8de7f11b8789295ba04c508b790 Mon Sep 17 00:00:00 2001 From: utin-francis-peter Date: Sat, 29 Jun 2024 18:58:13 +0100 Subject: [PATCH 02/13] refactor: custom input component is used. inputRef is also replaced with state value --- frontend/src/conversation/Conversation.tsx | 36 +++++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/frontend/src/conversation/Conversation.tsx b/frontend/src/conversation/Conversation.tsx index 4fb34186..ee85cbbd 100644 --- a/frontend/src/conversation/Conversation.tsx +++ b/frontend/src/conversation/Conversation.tsx @@ -20,12 +20,15 @@ import { sendFeedback } from './conversationApi'; import { useTranslation } from 'react-i18next'; import ArrowDown from './../assets/arrow-down.svg'; import RetryIcon from '../components/RetryIcon'; +import TextInput from '../components/inputs/TextInput'; export default function Conversation() { const queries = useSelector(selectQueries); const status = useSelector(selectStatus); const dispatch = useDispatch(); const endMessageRef = useRef(null); - const inputRef = useRef(null); + // const inputRef = useRef(null); + // migrating to useState for managing input values and onChange + const [prompt, setPrompt] = useState(''); const [isDarkTheme] = useDarkTheme(); const [hasScrolledToLast, setHasScrolledToLast] = useState(true); const fetchStream = useRef(null); @@ -112,14 +115,14 @@ export default function Conversation() { }; const handleQuestionSubmission = () => { - if (inputRef.current?.textContent && status !== 'loading') { + if (prompt.length && status !== 'loading') { if (lastQueryReturnedErr) { // update last failed query with new prompt dispatch( updateQuery({ index: queries.length - 1, query: { - prompt: inputRef.current.textContent, + prompt, }, }), ); @@ -128,9 +131,9 @@ export default function Conversation() { isRetry: true, }); } else { - handleQuestion({ question: inputRef.current.textContent }); + handleQuestion({ question: prompt }); } - inputRef.current.textContent = ''; + setPrompt(''); } }; @@ -190,6 +193,8 @@ export default function Conversation() { document.execCommand('insertText', false, text); }; + // console.log('inputRef: ', inputRef); + return (
-
{ if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleQuestionSubmission(); } }} - >
+ >
*/} + setPrompt(e.target.value)} + placeholder={t('inputPlaceholder')} + onPaste={handlePaste} + onKeyDown={(e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleQuestionSubmission(); + } + }} + > {status === 'loading' ? ( Date: Sat, 29 Jun 2024 20:45:33 +0100 Subject: [PATCH 03/13] style: removed custom padding and used twClasses --- frontend/src/index.css | 5 ----- 1 file changed, 5 deletions(-) diff --git a/frontend/src/index.css b/frontend/src/index.css index 80a1aa8e..ac90fc66 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -412,8 +412,3 @@ template { .bottom-safe { bottom: env(safe-area-inset-bottom, 0); } - -.text-input { - padding-left: 36px; - padding-right: 36px; -} From 7408454a7566970815c7f37d612c7526ab3c56a4 Mon Sep 17 00:00:00 2001 From: utin-francis-peter Date: Mon, 1 Jul 2024 19:54:31 +0100 Subject: [PATCH 04/13] chore: prompts input now uses useState hook for state change and inbuilt autoFocus --- frontend/src/conversation/Conversation.tsx | 26 ---------------------- 1 file changed, 26 deletions(-) diff --git a/frontend/src/conversation/Conversation.tsx b/frontend/src/conversation/Conversation.tsx index ee85cbbd..96f1eecb 100644 --- a/frontend/src/conversation/Conversation.tsx +++ b/frontend/src/conversation/Conversation.tsx @@ -26,8 +26,6 @@ export default function Conversation() { const status = useSelector(selectStatus); const dispatch = useDispatch(); const endMessageRef = useRef(null); - // const inputRef = useRef(null); - // migrating to useState for managing input values and onChange const [prompt, setPrompt] = useState(''); const [isDarkTheme] = useDarkTheme(); const [hasScrolledToLast, setHasScrolledToLast] = useState(true); @@ -43,13 +41,6 @@ export default function Conversation() { !eventInterrupt && scrollIntoView(); }, [queries.length, queries[queries.length - 1]]); - useEffect(() => { - const element = document.getElementById('inputbox') as HTMLInputElement; - if (element) { - element.focus(); - } - }, []); - useEffect(() => { return () => { if (status !== 'idle') { @@ -241,25 +232,8 @@ export default function Conversation() {
- {/*
{ - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - handleQuestionSubmission(); - } - }} - >
*/} setPrompt(e.target.value)} placeholder={t('inputPlaceholder')} From 15b0e321bdbafb66f204b4c596bca9201e3b4a93 Mon Sep 17 00:00:00 2001 From: utin-francis-peter Date: Wed, 3 Jul 2024 11:24:29 +0100 Subject: [PATCH 05/13] chore: TextArea component to replace Div contentEditable for entering prompts --- frontend/src/components/TextArea.tsx | 71 ++++++++++++++++++++++ frontend/src/conversation/Conversation.tsx | 8 +-- 2 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 frontend/src/components/TextArea.tsx diff --git a/frontend/src/components/TextArea.tsx b/frontend/src/components/TextArea.tsx new file mode 100644 index 00000000..b650423f --- /dev/null +++ b/frontend/src/components/TextArea.tsx @@ -0,0 +1,71 @@ +import React, { useEffect, useRef } from 'react'; + +type Props = { + value: string | string[] | number; + isAutoFocused: boolean; + id?: string; + maxLength?: number; + name?: string; + placeholder?: string; + className?: string; + children?: React.ReactElement; + onChange: (e: React.ChangeEvent) => void; + onPaste?: (e: React.ClipboardEvent) => void; + onKeyDown?: (e: React.KeyboardEvent) => void; +}; + +const TextArea = ({ + value, + isAutoFocused, + id, + maxLength, + name, + placeholder, + className, + children, + onChange, + onPaste, + onKeyDown, +}: Props) => { + const textAreaRef = useRef(null); + + useEffect(() => { + const autoResizeTextArea = () => { + if (textAreaRef.current) { + textAreaRef.current.style.height = 'auto'; + + const maxHeight = 96; + const currentContentHeight = textAreaRef.current.scrollHeight; + + const newHeight = Math.min(maxHeight, currentContentHeight); + + textAreaRef.current.style.height = `${newHeight}px`; + } + }; + + autoResizeTextArea(); + }, [value]); + + return ( + + ); +}; + +export default TextArea; diff --git a/frontend/src/conversation/Conversation.tsx b/frontend/src/conversation/Conversation.tsx index 96f1eecb..58af6d09 100644 --- a/frontend/src/conversation/Conversation.tsx +++ b/frontend/src/conversation/Conversation.tsx @@ -20,7 +20,7 @@ import { sendFeedback } from './conversationApi'; import { useTranslation } from 'react-i18next'; import ArrowDown from './../assets/arrow-down.svg'; import RetryIcon from '../components/RetryIcon'; -import TextInput from '../components/inputs/TextInput'; +import TextArea from '../components/TextArea'; export default function Conversation() { const queries = useSelector(selectQueries); const status = useSelector(selectStatus); @@ -232,9 +232,9 @@ export default function Conversation() {
- setPrompt(e.target.value)} placeholder={t('inputPlaceholder')} onPaste={handlePaste} @@ -244,7 +244,7 @@ export default function Conversation() { handleQuestionSubmission(); } }} - > + >{' '} {status === 'loading' ? ( Date: Wed, 3 Jul 2024 11:49:49 +0100 Subject: [PATCH 06/13] chore: migrated prop type definition into a types declaration file for components. other components prop types will live here --- frontend/src/components/TextArea.tsx | 17 ++--------------- frontend/src/components/types/index.ts | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 frontend/src/components/types/index.ts diff --git a/frontend/src/components/TextArea.tsx b/frontend/src/components/TextArea.tsx index b650423f..311efe81 100644 --- a/frontend/src/components/TextArea.tsx +++ b/frontend/src/components/TextArea.tsx @@ -1,18 +1,5 @@ import React, { useEffect, useRef } from 'react'; - -type Props = { - value: string | string[] | number; - isAutoFocused: boolean; - id?: string; - maxLength?: number; - name?: string; - placeholder?: string; - className?: string; - children?: React.ReactElement; - onChange: (e: React.ChangeEvent) => void; - onPaste?: (e: React.ClipboardEvent) => void; - onKeyDown?: (e: React.KeyboardEvent) => void; -}; +import { TextAreaProps } from './types'; const TextArea = ({ value, @@ -26,7 +13,7 @@ const TextArea = ({ onChange, onPaste, onKeyDown, -}: Props) => { +}: TextAreaProps) => { const textAreaRef = useRef(null); useEffect(() => { diff --git a/frontend/src/components/types/index.ts b/frontend/src/components/types/index.ts new file mode 100644 index 00000000..3cebc1c2 --- /dev/null +++ b/frontend/src/components/types/index.ts @@ -0,0 +1,23 @@ +export type TextAreaProps = { + value: string | string[] | number; + isAutoFocused: boolean; + id?: string; + maxLength?: number; + name?: string; + placeholder?: string; + className?: string; + children?: React.ReactElement; + onChange: ( + e: React.ChangeEvent, + ) => void; + onPaste?: ( + e: React.ClipboardEvent, + ) => void; + onKeyDown?: ( + e: React.KeyboardEvent, + ) => void; +}; + +export type InputProps = TextAreaProps & { + type: 'text' | 'number'; +}; From b21230c4d6899d921b3d95ed0973f1b924f59fba Mon Sep 17 00:00:00 2001 From: utin-francis-peter Date: Wed, 3 Jul 2024 12:34:13 +0100 Subject: [PATCH 07/13] chore: migrated to using custom Input component to address redundant twClasses --- frontend/src/components/Input.tsx | 39 +++++++++++++++++ frontend/src/components/types/index.ts | 3 +- frontend/src/preferences/APIKeyModal.tsx | 7 +-- frontend/src/preferences/PromptsModal.tsx | 15 ++++--- frontend/src/settings/APIKeys.tsx | 8 ++-- frontend/src/upload/Upload.tsx | 53 ++++++++++++----------- 6 files changed, 87 insertions(+), 38 deletions(-) create mode 100644 frontend/src/components/Input.tsx diff --git a/frontend/src/components/Input.tsx b/frontend/src/components/Input.tsx new file mode 100644 index 00000000..fe5cdba2 --- /dev/null +++ b/frontend/src/components/Input.tsx @@ -0,0 +1,39 @@ +import { InputProps } from './types'; + +const Input = ({ + id, + name, + type, + value, + isAutoFocused = false, + placeholder, + maxLength, + className, + hasSilverBorder, + children, + onChange, + onPaste, + onKeyDown, +}: InputProps) => { + return ( + + {children} + + ); +}; + +export default Input; diff --git a/frontend/src/components/types/index.ts b/frontend/src/components/types/index.ts index 3cebc1c2..3ee65ec4 100644 --- a/frontend/src/components/types/index.ts +++ b/frontend/src/components/types/index.ts @@ -1,6 +1,6 @@ export type TextAreaProps = { value: string | string[] | number; - isAutoFocused: boolean; + isAutoFocused?: boolean; id?: string; maxLength?: number; name?: string; @@ -20,4 +20,5 @@ export type TextAreaProps = { export type InputProps = TextAreaProps & { type: 'text' | 'number'; + hasSilverBorder?: boolean; }; diff --git a/frontend/src/preferences/APIKeyModal.tsx b/frontend/src/preferences/APIKeyModal.tsx index dd7a1b88..18cf53f3 100644 --- a/frontend/src/preferences/APIKeyModal.tsx +++ b/frontend/src/preferences/APIKeyModal.tsx @@ -4,6 +4,7 @@ import { ActiveState } from '../models/misc'; import { selectApiKey, setApiKey } from './preferenceSlice'; import { useMediaQuery, useOutsideAlerter } from './../hooks'; import Modal from '../modals'; +import Input from '../components/Input'; export default function APIKeyModal({ modalState, @@ -66,14 +67,14 @@ export default function APIKeyModal({ key for llm. Currently, we support only OpenAI but soon many more. You can find it here.

- setKey(e.target.value)} - /> + > ); }} diff --git a/frontend/src/preferences/PromptsModal.tsx b/frontend/src/preferences/PromptsModal.tsx index c16ddd2c..ce634c86 100644 --- a/frontend/src/preferences/PromptsModal.tsx +++ b/frontend/src/preferences/PromptsModal.tsx @@ -1,5 +1,6 @@ import { ActiveState } from '../models/misc'; import Exit from '../assets/exit.svg'; +import Input from '../components/Input'; function AddPrompt({ setModalState, @@ -34,13 +35,14 @@ function AddPrompt({ Add your custom prompt and save it to DocsGPT

- setNewPromptName(e.target.value)} - > + >
Prompt Name @@ -105,13 +107,14 @@ function EditPrompt({ Edit your custom prompt and save it to DocsGPT

- setEditPromptName(e.target.value)} - > + >
Prompt Name diff --git a/frontend/src/settings/APIKeys.tsx b/frontend/src/settings/APIKeys.tsx index 8264af08..dda68663 100644 --- a/frontend/src/settings/APIKeys.tsx +++ b/frontend/src/settings/APIKeys.tsx @@ -10,6 +10,7 @@ import { selectSourceDocs } from '../preferences/preferenceSlice'; import Exit from '../assets/exit.svg'; import Trash from '../assets/trash.svg'; import { useTranslation } from 'react-i18next'; +import Input from '../components/Input'; const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; const embeddingsName = import.meta.env.VITE_EMBEDDINGS_NAME || @@ -237,12 +238,13 @@ const CreateAPIKeyModal: React.FC = ({ {t('modals.createAPIKey.apiKeyName')} - setAPIKeyName(e.target.value)} - /> + >
) => { + const handleChange = ( + e: React.ChangeEvent, + ) => { const { name, value } = e.target; if (name === 'search_queries' && value.length > 0) { setRedditData({ @@ -323,12 +326,12 @@ function Upload({ {activeTab === 'file' && ( <> - setDocName(e.target.value)} - > + >
{t('modals.uploadDoc.name')} @@ -373,25 +376,25 @@ function Upload({ /> {urlType.label !== 'Reddit' ? ( <> - setUrlName(e.target.value)} - > + >
{t('modals.uploadDoc.name')}
- setUrl(e.target.value)} - > + >
{t('modals.uploadDoc.link')} @@ -400,66 +403,66 @@ function Upload({ ) : ( <> - + >
{t('modals.uploadDoc.reddit.id')}
- + >
{t('modals.uploadDoc.reddit.secret')}
- + >
{t('modals.uploadDoc.reddit.agent')}
- + >
{t('modals.uploadDoc.reddit.searchQueries')}
- + >
{t('modals.uploadDoc.reddit.numberOfPosts')} From a41519be637b57cf102ef126399b72558cba7b5d Mon Sep 17 00:00:00 2001 From: utin-francis-peter Date: Fri, 5 Jul 2024 11:41:12 +0100 Subject: [PATCH 08/13] fix: minor typo --- frontend/src/components/TextArea.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/TextArea.tsx b/frontend/src/components/TextArea.tsx index 311efe81..43e91e79 100644 --- a/frontend/src/components/TextArea.tsx +++ b/frontend/src/components/TextArea.tsx @@ -36,7 +36,7 @@ const TextArea = ({ return ( + >
{status === 'loading' ? ( Date: Tue, 16 Jul 2024 18:20:13 +0530 Subject: [PATCH 13/13] fix: removed unused TextArea component --- frontend/src/components/TextArea.tsx | 58 ---------------------------- 1 file changed, 58 deletions(-) delete mode 100644 frontend/src/components/TextArea.tsx diff --git a/frontend/src/components/TextArea.tsx b/frontend/src/components/TextArea.tsx deleted file mode 100644 index 43e91e79..00000000 --- a/frontend/src/components/TextArea.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React, { useEffect, useRef } from 'react'; -import { TextAreaProps } from './types'; - -const TextArea = ({ - value, - isAutoFocused, - id, - maxLength, - name, - placeholder, - className, - children, - onChange, - onPaste, - onKeyDown, -}: TextAreaProps) => { - const textAreaRef = useRef(null); - - useEffect(() => { - const autoResizeTextArea = () => { - if (textAreaRef.current) { - textAreaRef.current.style.height = 'auto'; - - const maxHeight = 96; - const currentContentHeight = textAreaRef.current.scrollHeight; - - const newHeight = Math.min(maxHeight, currentContentHeight); - - textAreaRef.current.style.height = `${newHeight}px`; - } - }; - - autoResizeTextArea(); - }, [value]); - - return ( - - ); -}; - -export default TextArea;