-
Notifications
You must be signed in to change notification settings - Fork 3
Refactor login page #38
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
Changes from all commits
7909476
d5686b2
6fca281
8285ab9
48bb045
529e4a4
69e7803
822a2f4
5af5de7
13015f1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,5 @@ | ||||||||||||
| 'use client'; | ||||||||||||
|
|
||||||||||||
| import React, { useState, useEffect } from 'react'; | ||||||||||||
| import { Button } from '@/components/ui/button'; | ||||||||||||
| import { Input } from '@/components/ui/input'; | ||||||||||||
|
|
@@ -18,6 +19,12 @@ const LoginPage = () => { | |||||||||||
| password: '', | ||||||||||||
| }); | ||||||||||||
| const [error, setError] = useState<string | null>(null); | ||||||||||||
| const [mounted, setMounted] = useState(false); | ||||||||||||
| const router = useRouter(); | ||||||||||||
|
|
||||||||||||
| useEffect(() => { | ||||||||||||
| setMounted(true); | ||||||||||||
| }, []); | ||||||||||||
|
|
||||||||||||
| const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||||||||||||
| const { name, value } = e.target; | ||||||||||||
|
|
@@ -27,7 +34,6 @@ const LoginPage = () => { | |||||||||||
| })); | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| const router = useRouter(); | ||||||||||||
| const handleSubmit = async (e: React.FormEvent) => { | ||||||||||||
| e.preventDefault(); | ||||||||||||
| setError(null); | ||||||||||||
|
|
@@ -57,16 +63,21 @@ const LoginPage = () => { | |||||||||||
|
|
||||||||||||
| return ( | ||||||||||||
| <div className="flex items-center justify-center min-h-screen bg-light-background dark:bg-dark-background"> | ||||||||||||
| <div className="w-full max-w-md px-8"> | ||||||||||||
| <div | ||||||||||||
| className={`w-full max-w-md px-8 transition-all duration-1000 ease-in-out | ||||||||||||
| ${mounted ? 'translate-y-0 opacity-100' : 'translate-y-4 opacity-0'}`} | ||||||||||||
| > | ||||||||||||
| <div className="space-y-2 mb-8"> | ||||||||||||
| <h1 className="text-2xl font-semibold">Sign In</h1> | ||||||||||||
| <h1 className="text-2xl font-semibold text-light-text-primary dark:text-dark-text-primary"> | ||||||||||||
| Sign In | ||||||||||||
| </h1> | ||||||||||||
| <p className="text-gray-500"> | ||||||||||||
| Enter credentials to login to your account | ||||||||||||
| </p> | ||||||||||||
| </div> | ||||||||||||
|
|
||||||||||||
| {error && ( | ||||||||||||
| <div className="mb-4 p-3 text-sm text-red-500 bg-red-50 rounded-lg"> | ||||||||||||
| <div className="mb-4 p-3 text-sm text-red-500 bg-red-50 dark:bg-red-900/20 rounded-lg"> | ||||||||||||
| {error} | ||||||||||||
| </div> | ||||||||||||
| )} | ||||||||||||
|
|
@@ -83,7 +94,10 @@ const LoginPage = () => { | |||||||||||
| value={formData.username} | ||||||||||||
| onChange={handleChange} | ||||||||||||
| required | ||||||||||||
| className="h-12 rounded-lg border-gray-200 focus:border-gray-300 focus:ring-0" | ||||||||||||
| className="h-12 rounded-lg border-light-border dark:border-dark-border | ||||||||||||
| bg-light-surface dark:bg-dark-surface | ||||||||||||
| text-light-text-primary dark:text-dark-text-primary | ||||||||||||
| focus:outline-none focus:ring-2 focus:ring-primary-400 dark:focus:ring-primary-500 focus:border-transparent" | ||||||||||||
| placeholder="Enter your username" | ||||||||||||
| disabled={isLoading} | ||||||||||||
| /> | ||||||||||||
|
|
@@ -100,15 +114,19 @@ const LoginPage = () => { | |||||||||||
| value={formData.password} | ||||||||||||
| onChange={handleChange} | ||||||||||||
| required | ||||||||||||
| className="h-12 rounded-lg border-gray-200 focus:border-gray-300 focus:ring-0" | ||||||||||||
| className="h-12 rounded-lg border-light-border dark:border-dark-border | ||||||||||||
| bg-light-surface dark:bg-dark-surface | ||||||||||||
| text-light-text-primary dark:text-dark-text-primary | ||||||||||||
| focus:outline-none focus:ring-2 focus:ring-primary-400 dark:focus:ring-primary-500 focus:border-transparent" | ||||||||||||
| placeholder="Enter your password" | ||||||||||||
| disabled={isLoading} | ||||||||||||
| /> | ||||||||||||
| </div> | ||||||||||||
|
|
||||||||||||
| <Button | ||||||||||||
| type="submit" | ||||||||||||
| className="w-full h-12 bg-primary-500 hover:bg-primary-600 text-white rounded-lg font-medium" | ||||||||||||
| className="w-full h-12 bg-primary-500 hover:bg-primary-600 dark:bg-primary-600 dark:hover:bg-primary-700 | ||||||||||||
| text-white rounded-lg font-medium" | ||||||||||||
|
Comment on lines
+128
to
+129
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add focus-visible styles for keyboard navigation The button should have visible focus indicators for keyboard users. - className="w-full h-12 bg-primary-500 hover:bg-primary-600 dark:bg-primary-600 dark:hover:bg-primary-700
- text-white rounded-lg font-medium"
+ className="w-full h-12 bg-primary-500 hover:bg-primary-600 dark:bg-primary-600 dark:hover:bg-primary-700
+ text-white rounded-lg font-medium focus-visible:ring-2 focus-visible:ring-offset-2
+ focus-visible:ring-primary-500 dark:focus-visible:ring-primary-400"📝 Committable suggestion
Suggested change
|
||||||||||||
| disabled={isLoading} | ||||||||||||
| > | ||||||||||||
| {isLoading ? 'Signing In...' : 'Sign In'} | ||||||||||||
|
|
||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,98 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'use client'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import React, { useEffect, useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ResizableHandle, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ResizablePanel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ResizablePanelGroup, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from '@/components/ui/resizable'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { cn } from '@/lib/utils'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { usePathname } from 'next/navigation'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import Sidebar from '@/components/sidebar'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useChatList } from '../hooks/useChatList'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default function MainLayout({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| children, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| children: React.ReactNode; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [isCollapsed, setIsCollapsed] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [isMobile, setIsMobile] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const defaultLayout = [30, 160]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const navCollapsedSize = 10; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const pathname = usePathname(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const currentChatId = pathname.split('/')[1] || ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| chats, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| loading, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| chatListUpdated, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setChatListUpdated, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| refetchChats, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } = useChatList(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+24
to
+34
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add path validation and error handling. The current pathname splitting implementation could be unsafe if the path structure changes. Consider applying this safer approach: - const currentChatId = pathname.split('/')[1] || '';
+ const currentChatId = (() => {
+ const parts = pathname.split('/');
+ if (parts.length < 2) return '';
+ const id = parts[1];
+ return /^[a-zA-Z0-9-_]+$/.test(id) ? id : '';
+ })();📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const checkScreenWidth = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setIsMobile(window.innerWidth <= 1023); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| checkScreenWidth(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.addEventListener('resize', checkScreenWidth); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.removeEventListener('resize', checkScreenWidth); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, []); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+36
to
+45
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Optimize resize handling and align breakpoints with Tailwind. The resize handler could benefit from debouncing, and the breakpoint should match Tailwind's Consider applying these improvements: +import { debounce } from 'lodash';
+
useEffect(() => {
- const checkScreenWidth = () => {
+ const checkScreenWidth = debounce(() => {
setIsMobile(window.innerWidth <= 1023);
- };
+ }, 250);
checkScreenWidth();
window.addEventListener('resize', checkScreenWidth);
return () => {
window.removeEventListener('resize', checkScreenWidth);
+ checkScreenWidth.cancel();
};
}, []);📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <main className="flex h-[calc(100dvh)] flex-col items-center"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ResizablePanelGroup | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| direction="horizontal" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onLayout={(sizes: number[]) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| document.cookie = `react-resizable-panels:layout=${JSON.stringify(sizes)}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="h-screen items-stretch" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ResizablePanel | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defaultSize={defaultLayout[0]} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| collapsedSize={navCollapsedSize} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| collapsible={true} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| minSize={isMobile ? 0 : 12} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| maxSize={isMobile ? 0 : 16} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onCollapse={() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setIsCollapsed(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| document.cookie = `react-resizable-panels:collapsed=${JSON.stringify(true)}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onExpand={() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setIsCollapsed(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| document.cookie = `react-resizable-panels:collapsed=${JSON.stringify(false)}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className={cn( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isCollapsed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? 'min-w-[50px] md:min-w-[70px] transition-all duration-300 ease-in-out' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : 'hidden md:block' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Sidebar | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isCollapsed={isCollapsed} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isMobile={isMobile} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| currentChatId={currentChatId} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| chatListUpdated={chatListUpdated} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setChatListUpdated={setChatListUpdated} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| chats={chats} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| loading={loading} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error={error} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onRefetch={refetchChats} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </ResizablePanel> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ResizableHandle className={cn('hidden md:flex')} withHandle /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ResizablePanel | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="h-full w-full flex justify-center" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defaultSize={defaultLayout[1]} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {children} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </ResizablePanel> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </ResizablePanelGroup> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </main> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+47
to
+98
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance maintainability and accessibility of the layout. Consider the following improvements:
Here's a suggested implementation for the cookie handling hook: // hooks/useLayoutPreferences.ts
export const useLayoutPreferences = () => {
const [layout, setLayout] = useState<number[]>([30, 160]);
const [isCollapsed, setIsCollapsed] = useState(false);
const updateLayout = (sizes: number[]) => {
setLayout(sizes);
document.cookie = `react-resizable-panels:layout=${JSON.stringify(sizes)}`;
};
const updateCollapsed = (collapsed: boolean) => {
setIsCollapsed(collapsed);
document.cookie = `react-resizable-panels:collapsed=${JSON.stringify(collapsed)}`;
};
return { layout, isCollapsed, updateLayout, updateCollapsed };
};And update the ResizablePanel with accessibility attributes: <ResizablePanel
+ role="complementary"
+ aria-label="Sidebar navigation"
defaultSize={defaultLayout[0]}
collapsedSize={navCollapsedSize}
collapsible={true} |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,63 @@ | ||||||||||||||||||||||||||
| 'use client'; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| import { useEffect, useRef, useState } from 'react'; | ||||||||||||||||||||||||||
| import { useParams } from 'next/navigation'; | ||||||||||||||||||||||||||
| import { Message } from '@/components/types'; | ||||||||||||||||||||||||||
| import { useModels } from '@/app/hooks/useModels'; | ||||||||||||||||||||||||||
| import ChatContent from '@/components/chat/chat'; | ||||||||||||||||||||||||||
| import { useChatStream } from '../../hooks/useChatStream'; | ||||||||||||||||||||||||||
| import { useQuery } from '@apollo/client'; | ||||||||||||||||||||||||||
| import { GET_CHAT_HISTORY } from '@/graphql/request'; | ||||||||||||||||||||||||||
| import { toast } from 'sonner'; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| export default function ChatPage() { | ||||||||||||||||||||||||||
| const params = useParams(); | ||||||||||||||||||||||||||
| const chatId = params.id as string; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
Comment on lines
+14
to
+16
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add validation for chatId parameter. The chatId is directly used from URL params without validation. Consider adding validation to handle invalid or missing chatId cases. const params = useParams();
-const chatId = params.id as string;
+const chatId = params.id;
+if (!chatId || typeof chatId !== 'string') {
+ throw new Error('Invalid chat ID');
+}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||
| // Core message states | ||||||||||||||||||||||||||
| const [messages, setMessages] = useState<Message[]>([]); | ||||||||||||||||||||||||||
| const [input, setInput] = useState(''); | ||||||||||||||||||||||||||
| const formRef = useRef<HTMLFormElement>(null); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const { models } = useModels(); | ||||||||||||||||||||||||||
| const [selectedModel, setSelectedModel] = useState<string>( | ||||||||||||||||||||||||||
| models[0] || 'Loading models' | ||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||
|
Comment on lines
+22
to
+25
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle empty models array case. The current initialization of selectedModel could result in 'Loading models' being used as an actual model identifier. -const [selectedModel, setSelectedModel] = useState<string>(
- models[0] || 'Loading models'
-);
+const [selectedModel, setSelectedModel] = useState<string | null>(null);
+
+useEffect(() => {
+ if (models.length > 0) {
+ setSelectedModel(models[0]);
+ }
+}, [models]);📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| useQuery(GET_CHAT_HISTORY, { | ||||||||||||||||||||||||||
| variables: { chatId: params.id }, | ||||||||||||||||||||||||||
| onCompleted: (data) => { | ||||||||||||||||||||||||||
| if (data?.getChatHistory) { | ||||||||||||||||||||||||||
| setMessages(data.getChatHistory); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| onError: (error) => { | ||||||||||||||||||||||||||
| toast.error('Failed to load chat history'); | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const { loadingSubmit, handleSubmit, handleInputChange, stop } = | ||||||||||||||||||||||||||
| useChatStream({ | ||||||||||||||||||||||||||
| chatId, | ||||||||||||||||||||||||||
| input, | ||||||||||||||||||||||||||
| setInput, | ||||||||||||||||||||||||||
| setMessages, | ||||||||||||||||||||||||||
| selectedModel, | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||
| <ChatContent | ||||||||||||||||||||||||||
| chatId={chatId} | ||||||||||||||||||||||||||
| setSelectedModel={setSelectedModel} | ||||||||||||||||||||||||||
| messages={messages} | ||||||||||||||||||||||||||
| input={input} | ||||||||||||||||||||||||||
| handleInputChange={handleInputChange} | ||||||||||||||||||||||||||
| handleSubmit={handleSubmit} | ||||||||||||||||||||||||||
| loadingSubmit={loadingSubmit} | ||||||||||||||||||||||||||
| stop={stop} | ||||||||||||||||||||||||||
| formRef={formRef} | ||||||||||||||||||||||||||
| setInput={setInput} | ||||||||||||||||||||||||||
| setMessages={setMessages} | ||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add aria-live for better accessibility
Error messages should be announced by screen readers when they appear.
📝 Committable suggestion