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

Prompts #229

Merged
merged 37 commits into from
Mar 27, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
8098838
init prompts
mckaywrigley Mar 26, 2023
a5967a8
Merge branch 'main' of https://github.com/mckaywrigley/chatbot-ui-pro…
mckaywrigley Mar 26, 2023
40e05e2
fix
mckaywrigley Mar 26, 2023
2877f4b
style tweak
mckaywrigley Mar 26, 2023
705ed00
massive type overhaul
mckaywrigley Mar 26, 2023
97203f9
prompt utils
mckaywrigley Mar 26, 2023
118c6d3
modal working
mckaywrigley Mar 26, 2023
d2ac266
Merge branch 'main' of https://github.com/mckaywrigley/chatbot-ui-pro…
mckaywrigley Mar 26, 2023
20f3f09
save
mckaywrigley Mar 26, 2023
30f3ed8
Merge branch 'main' of https://github.com/mckaywrigley/chatbot-ui-pro…
mckaywrigley Mar 26, 2023
b7acbfe
save
mckaywrigley Mar 26, 2023
ce50b52
fix
mckaywrigley Mar 26, 2023
b518a9a
more
mckaywrigley Mar 26, 2023
6dc93df
Merge branch 'main' of https://github.com/mckaywrigley/chatbot-ui-pro…
mckaywrigley Mar 26, 2023
60d82e0
save
mckaywrigley Mar 26, 2023
8ba6dd2
save
mckaywrigley Mar 26, 2023
ea8c64b
save
mckaywrigley Mar 26, 2023
dbee2cf
stash
mckaywrigley Mar 26, 2023
ce06bc8
Merge branch 'main' of https://github.com/mckaywrigley/chatbot-ui-pro…
mckaywrigley Mar 26, 2023
7897e9c
working
mckaywrigley Mar 27, 2023
2afe397
save
mckaywrigley Mar 27, 2023
4c8a8ff
toggles
mckaywrigley Mar 27, 2023
81532fa
test
mckaywrigley Mar 27, 2023
84fd8f1
style tweak
mckaywrigley Mar 27, 2023
0f4d24a
folders
mckaywrigley Mar 27, 2023
7c7fd4c
save
mckaywrigley Mar 27, 2023
add6ea4
Merge branch 'main' of https://github.com/mckaywrigley/chatbot-ui-pro…
mckaywrigley Mar 27, 2023
52b052b
save
mckaywrigley Mar 27, 2023
d8ee062
save
mckaywrigley Mar 27, 2023
c1824b0
fix drag
mckaywrigley Mar 27, 2023
69b173a
Merge branch 'main' of https://github.com/mckaywrigley/chatbot-ui-pro…
mckaywrigley Mar 27, 2023
985f4d4
save
mckaywrigley Mar 27, 2023
bf577a8
fix import
mckaywrigley Mar 27, 2023
2084b38
done
mckaywrigley Mar 27, 2023
4587bb3
remove log
mckaywrigley Mar 27, 2023
bb317da
merge
mckaywrigley Mar 27, 2023
b901382
merge
mckaywrigley Mar 27, 2023
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
Prev Previous commit
Next Next commit
save
  • Loading branch information
mckaywrigley committed Mar 26, 2023
commit ea8c64b722c337f70fd54ee6bf3d4d7f914ec4d9
2 changes: 1 addition & 1 deletion components/Chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const Chat: FC<Props> = ({
if (chatContainerRef.current) {
const { scrollTop, scrollHeight, clientHeight } =
chatContainerRef.current;
const bottomTolerance = 5;
const bottomTolerance = 30;

if (scrollTop + clientHeight < scrollHeight - bottomTolerance) {
setAutoScrollEnabled(false);
Expand Down
70 changes: 52 additions & 18 deletions components/Chat/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
useRef,
useState,
} from 'react';
import { VariableModal } from './VariableModal';

interface Props {
messageIsStreaming: boolean;
Expand All @@ -35,11 +36,14 @@ export const ChatInput: FC<Props> = ({
textareaRef,
}) => {
const { t } = useTranslation('chat');

const [content, setContent] = useState<string>();
const [isTyping, setIsTyping] = useState<boolean>(false);
const [showPromptList, setShowPromptList] = useState(false);
const [activePromptIndex, setActivePromptIndex] = useState(0);
const [promptInputValue, setPromptInputValue] = useState('');
const [variables, setVariables] = useState<string[]>([]);
const [isModalVisible, setIsModalVisible] = useState(false);

const promptListRef = useRef<HTMLUListElement | null>(null);

Expand All @@ -62,11 +66,6 @@ export const ChatInput: FC<Props> = ({
}

setContent(value);

if (value) {
setIsTyping(true);
}

updatePromptListVisibility(value);
};

Expand Down Expand Up @@ -132,14 +131,22 @@ export const ChatInput: FC<Props> = ({
} else {
setActivePromptIndex(0);
}
} else if (e.key === 'Enter' && !isMobile() && !e.shiftKey) {
e.preventDefault();
handleSend();
}
};

if (!isTyping) {
if (e.key === 'Enter' && !e.shiftKey && !isMobile()) {
e.preventDefault();
handleSend();
}
const parseVariables = (content: string) => {
const regex = /{{(.*?)}}/g;
const foundVariables = [];
let match;

while ((match = regex.exec(content)) !== null) {
foundVariables.push(match[1]);
}

return foundVariables;
};

const updatePromptListVisibility = useCallback((text: string) => {
Expand All @@ -153,6 +160,32 @@ export const ChatInput: FC<Props> = ({
}
}, []);

const handlePromptSelect = (promptText: string) => {
const parsedVariables = parseVariables(promptText);
setVariables(parsedVariables);

if (parsedVariables.length > 0) {
setIsModalVisible(true);
} else {
setContent((prevContent) => prevContent?.replace(/\/\w*$/, promptText));
updatePromptListVisibility(promptText);
}
};

const handleSubmit = (updatedVariables: string[]) => {
let newContent = content?.replace(/\/\w*$/, variables[0]);

variables.forEach((variable, index) => {
newContent = newContent?.replace(
`{{${variable}}}`,
updatedVariables[index],
);
});

setContent(newContent);
setIsModalVisible(false);
};

useEffect(() => {
if (promptListRef.current) {
promptListRef.current.scrollTop = activePromptIndex * 30;
Expand Down Expand Up @@ -244,14 +277,7 @@ export const ChatInput: FC<Props> = ({
: ''
} cursor-pointer px-3 py-2 text-sm text-black hover:bg-gray-200 dark:text-white dark:hover:bg-gray-200`}
onClick={() => {
setContent((prevContent) => {
const newContent = prevContent?.replace(
/\/\w*$/,
prompt.content,
);
updatePromptListVisibility(newContent || '');
return newContent;
});
handlePromptSelect(prompt.content);
}}
onMouseEnter={() => setActivePromptIndex(index)}
>
Expand All @@ -260,6 +286,14 @@ export const ChatInput: FC<Props> = ({
))}
</ul>
)}

{isModalVisible && (
<VariableModal
variables={variables}
onSubmit={handleSubmit}
onClose={() => setIsModalVisible(false)}
/>
)}
</div>
</div>
<div className="px-3 pt-2 pb-3 text-center text-[12px] text-black/50 dark:text-white/50 md:px-4 md:pt-3 md:pb-6">
Expand Down
106 changes: 106 additions & 0 deletions components/Chat/VariableModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { FC, KeyboardEvent, useEffect, useRef, useState } from 'react';

interface Props {
variables: string[];
onSubmit: (updatedVariables: string[]) => void;
onClose: () => void;
}

export const VariableModal: FC<Props> = ({ variables, onSubmit, onClose }) => {
const [updatedVariables, setUpdatedVariables] = useState<string[]>(
Array(variables.length).fill(''),
);

const modalRef = useRef<HTMLDivElement>(null);
const nameInputRef = useRef<HTMLInputElement>(null);

const handleChange = (index: number, value: string) => {
setUpdatedVariables((prev) => {
const newUpdatedVariables = [...prev];
newUpdatedVariables[index] = value;
return newUpdatedVariables;
});
};

const handleSubmit = () => {
onSubmit(updatedVariables);
};

const handleEnter = (e: KeyboardEvent<HTMLDivElement>) => {
if (e.key === 'Enter' && !e.shiftKey) {
handleSubmit();
}
};

useEffect(() => {
const handleOutsideClick = (e: MouseEvent) => {
if (modalRef.current && !modalRef.current.contains(e.target as Node)) {
onClose();
}
};

window.addEventListener('click', handleOutsideClick);

return () => {
window.removeEventListener('click', handleOutsideClick);
};
}, [onClose]);

useEffect(() => {
nameInputRef.current?.focus();
}, []);

return (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50"
onKeyDown={handleEnter}
>
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex min-h-screen items-center justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
<div
className="hidden sm:inline-block sm:h-screen sm:align-middle"
aria-hidden="true"
/>

<div
ref={modalRef}
className="dark:border-netural-400 inline-block transform overflow-hidden rounded-lg border border-gray-300 bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all dark:bg-[#202123] sm:my-8 sm:w-full sm:max-w-lg sm:p-6 sm:align-middle"
role="dialog"
>
{/* <input
ref={nameInputRef}
className="w-full rounded-lg border border-neutral-500 px-4 py-2 text-neutral-900 shadow focus:outline-none dark:border-neutral-800 dark:border-opacity-50 dark:bg-[#40414F] dark:text-neutral-100"
value={name}
onChange={(e) => setName(e.target.value)}
/>

<textarea
className="mt-6 w-full rounded-lg border border-neutral-500 px-4 py-2 text-neutral-900 shadow focus:outline-none dark:border-neutral-800 dark:border-opacity-50 dark:bg-[#40414F] dark:text-neutral-100"
style={{ resize: 'none' }}
value={content}
onChange={(e) => setContent(e.target.value)}
rows={10}
/>

<button
type="button"
className="mt-6 w-full rounded-lg border border-neutral-500 px-4 py-2 text-neutral-900 shadow hover:bg-neutral-100 focus:outline-none dark:border-neutral-800 dark:border-opacity-50 dark:bg-white dark:text-black dark:hover:bg-neutral-300"
onClick={() => {
const updatedPrompt = {
...prompt,
name,
content: content.trim(),
};

onUpdatePrompt(updatedPrompt);
onClose();
}}
>
Save
</button> */}
</div>
</div>
</div>
</div>
);
};
1 change: 0 additions & 1 deletion components/Promptbar/Prompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ export const PromptComponent: FC<Props> = ({
{showModal && (
<PromptModal
prompt={prompt}
isOpen={showModal}
onClose={() => setShowModal(false)}
onUpdatePrompt={onUpdatePrompt}
/>
Expand Down
104 changes: 50 additions & 54 deletions components/Promptbar/PromptModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ import { FC, KeyboardEvent, useEffect, useRef, useState } from 'react';

interface Props {
prompt: Prompt;
isOpen: boolean;
onClose: () => void;
onUpdatePrompt: (prompt: Prompt) => void;
}

export const PromptModal: FC<Props> = ({
prompt,
isOpen,

onClose,
onUpdatePrompt,
}) => {
Expand All @@ -22,7 +21,7 @@ export const PromptModal: FC<Props> = ({

const handleEnter = (e: KeyboardEvent<HTMLDivElement>) => {
if (e.key === 'Enter' && !e.shiftKey) {
onUpdatePrompt({ ...prompt, name, content });
onUpdatePrompt({ ...prompt, name, content: content.trim() });
onClose();
}
};
Expand All @@ -34,71 +33,68 @@ export const PromptModal: FC<Props> = ({
}
};

if (isOpen) {
window.addEventListener('click', handleOutsideClick);
}
window.addEventListener('click', handleOutsideClick);

return () => {
window.removeEventListener('click', handleOutsideClick);
};
}, [isOpen, onClose]);
}, [onClose]);

useEffect(() => {
if (isOpen) {
nameInputRef.current?.focus();
}
}, [isOpen]);
nameInputRef.current?.focus();
}, []);

return (
<div className="relative" onKeyDown={handleEnter}>
{isOpen && (
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex min-h-screen items-center justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
<div
className="hidden sm:inline-block sm:h-screen sm:align-middle"
aria-hidden="true"
/>
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50"
onKeyDown={handleEnter}
>
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex min-h-screen items-center justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
<div
className="hidden sm:inline-block sm:h-screen sm:align-middle"
aria-hidden="true"
/>

<div
ref={modalRef}
className="dark:border-netural-400 inline-block transform overflow-hidden rounded-lg border border-gray-300 bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all dark:bg-[#202123] sm:my-8 sm:w-full sm:max-w-lg sm:p-6 sm:align-middle"
role="dialog"
>
<input
ref={nameInputRef}
className="w-full rounded-lg border border-neutral-500 px-4 py-2 text-neutral-900 shadow focus:outline-none dark:border-neutral-800 dark:border-opacity-50 dark:bg-[#40414F] dark:text-neutral-100"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<div
ref={modalRef}
className="dark:border-netural-400 inline-block transform overflow-hidden rounded-lg border border-gray-300 bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all dark:bg-[#202123] sm:my-8 sm:w-full sm:max-w-lg sm:p-6 sm:align-middle"
role="dialog"
>
<input
ref={nameInputRef}
className="w-full rounded-lg border border-neutral-500 px-4 py-2 text-neutral-900 shadow focus:outline-none dark:border-neutral-800 dark:border-opacity-50 dark:bg-[#40414F] dark:text-neutral-100"
value={name}
onChange={(e) => setName(e.target.value)}
/>

<textarea
className="mt-6 w-full rounded-lg border border-neutral-500 px-4 py-2 text-neutral-900 shadow focus:outline-none dark:border-neutral-800 dark:border-opacity-50 dark:bg-[#40414F] dark:text-neutral-100"
style={{ resize: 'none' }}
value={content}
onChange={(e) => setContent(e.target.value)}
rows={5}
/>
<textarea
className="mt-6 w-full rounded-lg border border-neutral-500 px-4 py-2 text-neutral-900 shadow focus:outline-none dark:border-neutral-800 dark:border-opacity-50 dark:bg-[#40414F] dark:text-neutral-100"
style={{ resize: 'none' }}
value={content}
onChange={(e) => setContent(e.target.value)}
rows={10}
/>

<button
type="button"
className="mt-6 w-full rounded-lg border border-neutral-500 px-4 py-2 text-neutral-900 shadow hover:bg-neutral-100 focus:outline-none dark:border-neutral-800 dark:border-opacity-50 dark:bg-white dark:text-black dark:hover:bg-neutral-300"
onClick={() => {
const updatedPrompt = {
...prompt,
name,
content,
};
<button
type="button"
className="mt-6 w-full rounded-lg border border-neutral-500 px-4 py-2 text-neutral-900 shadow hover:bg-neutral-100 focus:outline-none dark:border-neutral-800 dark:border-opacity-50 dark:bg-white dark:text-black dark:hover:bg-neutral-300"
onClick={() => {
const updatedPrompt = {
...prompt,
name,
content: content.trim(),
};

onUpdatePrompt(updatedPrompt);
onClose();
}}
>
Save
</button>
</div>
onUpdatePrompt(updatedPrompt);
onClose();
}}
>
Save
</button>
</div>
</div>
)}
</div>
</div>
);
};
Loading