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

Improve chat input #472

Merged
merged 9 commits into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
9 changes: 8 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,14 @@ const App: React.FC<AppProps> = () => {
return (
<div className="max-h-screen bg-neutral-800 font-sans">
<Portal>
<ToastContainer className="mt-4" />
<ToastContainer
theme="dark"
position="bottom-right"
autoClose={3000}
hideProgressBar={false}
closeOnClick
pauseOnHover
/>{' '}
Copy link

Choose a reason for hiding this comment

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

syntax: unnecessary trailing whitespace after ToastContainer closing tag

</Portal>
{!userHasConfiguredSettingsForIndexing && (
<InitialSetupSinglePage readyForIndexing={handleAllInitialSettingsAreReady} />
Expand Down
72 changes: 35 additions & 37 deletions src/components/Chat/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,55 @@ import React from 'react'

import { PiPaperPlaneRight } from 'react-icons/pi'
import { LoadingState } from '../../lib/llm/types'
import { Button } from '../ui/button'
import LLMSelectOrButton from '../Settings/LLMSettings/LLMSelectOrButton'

interface ChatInputProps {
userTextFieldInput: string
setUserTextFieldInput: (value: string) => void
handleSubmitNewMessage: () => void
loadingState: LoadingState
selectedLLM: string | undefined
setSelectedLLM: (value: string | undefined) => void
}

const ChatInput: React.FC<ChatInputProps> = ({
userTextFieldInput,
setUserTextFieldInput,
handleSubmitNewMessage,
loadingState,
selectedLLM,
setSelectedLLM,
}) => (
<div className="flex h-titlebar w-full items-center justify-center p-10">
<div className=" relative bottom-5 flex w-full max-w-3xl">
<div className="w-full rounded-lg border-2 border-solid border-neutral-700 p-3 focus-within:ring-1 focus-within:ring-[#8c8c8c]">
<div className="flex h-full pr-8">
<textarea
onKeyDown={(e) => {
if (userTextFieldInput && !e.shiftKey && e.key === 'Enter') {
e.preventDefault()
handleSubmitNewMessage()
}
}}
onChange={(e) => setUserTextFieldInput(e.target.value)}
value={userTextFieldInput}
className="mr-2 max-h-[50px] w-full resize-none overflow-y-auto border-0 bg-gray-300 focus:outline-none"
name="Outlined"
placeholder="Follow up..."
rows={1}
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus
style={{
backgroundColor: 'rgba(255, 255, 255, 0)',
color: 'rgb(212 212 212)',
border: 'none',
}}
onInput={(e) => {
const target = e.target as HTMLTextAreaElement // Prevent TS inferring type error
target.style.height = 'auto'
target.style.height = `${Math.min(target.scrollHeight, 160)}px`
}}
/>
<div className="absolute right-3 top-1/2 -translate-y-1/2">
<PiPaperPlaneRight
color={userTextFieldInput && loadingState !== 'idle' ? 'white' : 'gray'}
onClick={userTextFieldInput ? handleSubmitNewMessage : undefined}
className={userTextFieldInput ? 'cursor-pointer' : ''}
/>
</div>
<div className=" flex w-full ">
Copy link

Choose a reason for hiding this comment

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

syntax: Extra space in className string (' flex' and trailing space)

<div className="z-50 flex w-full flex-col overflow-hidden rounded border-2 border-solid border-border bg-background focus-within:ring-1 focus-within:ring-ring">
<textarea
value={userTextFieldInput}
onKeyDown={(e) => {
if (!e.shiftKey && e.key === 'Enter') {
e.preventDefault()
handleSubmitNewMessage()
}
Copy link

Choose a reason for hiding this comment

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

logic: Submit message is now triggered even when input is empty, unlike previous version which checked userTextFieldInput

}}
className="h-[100px] w-[600px] resize-none border-0 bg-transparent p-4 text-foreground caret-current focus:outline-none"
placeholder="What can Reor help you with today?"
onChange={(e) => setUserTextFieldInput(e.target.value)}
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus
/>
<div className="mx-auto h-px w-[96%] bg-background/20" />
<div className="flex h-10 flex-col items-center justify-between gap-2 py-2 md:flex-row md:gap-4">
Copy link

Choose a reason for hiding this comment

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

syntax: Extra space in gap-2 py-2

<div className="flex flex-col items-center justify-between rounded-md border-0 py-2 md:flex-row">
<LLMSelectOrButton selectedLLM={selectedLLM} setSelectedLLM={setSelectedLLM} />
</div>
<div className="flex items-center">
<Button
className="m-2 flex items-center justify-between gap-2 bg-transparent text-primary hover:bg-transparent hover:text-accent-foreground"
onClick={handleSubmitNewMessage}
disabled={loadingState !== 'idle'}
>
<PiPaperPlaneRight className="size-4" />
</Button>
</div>
</div>
</div>
Expand Down
23 changes: 16 additions & 7 deletions src/components/Chat/ChatMessages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import AssistantMessage from './MessageComponents/AssistantMessage'
import SystemMessage from './MessageComponents/SystemMessage'
import ChatSources from './MessageComponents/ChatSources'
import LoadingDots from '@/lib/animations'
import useLLMConfigs from '@/lib/hooks/use-llm-configs'
import useAgentConfig from '@/lib/hooks/use-agent-configs'

interface MessageProps {
message: ReorChatMessage
Expand Down Expand Up @@ -41,7 +43,7 @@ interface ChatMessagesProps {
currentChat: Chat | undefined
setCurrentChat: React.Dispatch<React.SetStateAction<Chat | undefined>>
loadingState: LoadingState
handleNewChatMessage: (userTextFieldInput?: string, agentConfig?: AgentConfig) => void
handleNewChatMessage: (llmName: string, userTextFieldInput?: string, agentConfig?: AgentConfig) => void
}

const ChatMessages: React.FC<ChatMessagesProps> = ({
Expand All @@ -50,11 +52,18 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({
handleNewChatMessage,
loadingState,
}) => {
const { agentConfig } = useAgentConfig()
const [userTextFieldInput, setUserTextFieldInput] = useState<string | undefined>()
const { defaultLLM, setDefaultLLM } = useLLMConfigs()
const [selectedLLM, setSelectedLLM] = useState<string>()
const [shouldAutoScroll, setShouldAutoScroll] = useState(true)
const chatContainerRef = useRef<HTMLDivElement>(null)
const lastMessageRef = useRef<HTMLDivElement>(null)

useEffect(() => {
setSelectedLLM(defaultLLM)
}, [defaultLLM])

const scrollToBottom = useCallback(() => {
if (chatContainerRef.current) {
const { scrollHeight, clientHeight } = chatContainerRef.current
Expand All @@ -78,13 +87,11 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({

const handleSubmitNewMessage = async () => {
if (userTextFieldInput) {
// this for v1 could just use the default agent config...
const agentConfigs = await window.electronStore.getAgentConfigs()
if (agentConfigs && agentConfigs.length > 0) {
handleNewChatMessage(userTextFieldInput, agentConfigs[0])
} else {
handleNewChatMessage(userTextFieldInput)
if (!selectedLLM) {
throw new Error('Select an LLM.')
}
Comment on lines 88 to 92
Copy link

Choose a reason for hiding this comment

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

logic: Throwing an error directly will crash the UI. Should handle this gracefully with toast/alert instead.

setDefaultLLM(selectedLLM)
handleNewChatMessage(selectedLLM, userTextFieldInput, agentConfig)
Comment on lines +93 to +94
Copy link

Choose a reason for hiding this comment

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

logic: setDefaultLLM is async but not awaited, could cause race conditions with message sending

setUserTextFieldInput('')
setShouldAutoScroll(true)
}
Expand Down Expand Up @@ -134,6 +141,8 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({
setUserTextFieldInput={setUserTextFieldInput}
handleSubmitNewMessage={handleSubmitNewMessage}
loadingState={loadingState}
selectedLLM={selectedLLM}
setSelectedLLM={setSelectedLLM}
/>
</div>
</div>
Expand Down
Loading
Loading