Skip to content
Merged
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
17 changes: 11 additions & 6 deletions frontend/src/components/ChatInterface.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ const ChatInterface = () => {
const handleMainContentClick = (e) => {
// Don't close sidebar if clicking on sidebar elements
if (e.target.closest('.sidebar')) {
e.stopPropagation();
return;
}

Expand All @@ -184,6 +185,10 @@ const ChatInterface = () => {
}
};

const handleSidebarClick = (e) => {
e.stopPropagation();
};

const handleCopyMessage = (text, messageId) => {
navigator.clipboard.writeText(text).then(() => {
setCopiedMessageId(messageId);
Expand Down Expand Up @@ -220,7 +225,7 @@ const ChatInterface = () => {

return (
<div className="chat-container">
<aside className={`sidebar ${sidebarOpen ? "open" : ""}`} onClick={(e) => e.stopPropagation()}>
<aside className={`sidebar ${sidebarOpen ? "open" : ""}`} onClick={handleSidebarClick}>
<div className="sidebar-header">
<div className="logo-container">
<LogoIcon />
Expand All @@ -230,13 +235,13 @@ const ChatInterface = () => {
<Icon path={<path d="M18 6L6 18M6 6l12 12" />} />
</button>
</div>
<div className="sidebar-top"> <nav className="main-nav"> <button className="new-thread-btn" onClick={(e) => { e.stopPropagation(); handleCreateNewChat(); }} disabled={!isAuthenticated}> <Icon path={<path d="M12 5v14m-7-7h14" />} /> <span>New Chat</span> </button> </nav> </div>
<div className="library">
<div className="sidebar-top" onClick={(e) => e.stopPropagation()}> <nav className="main-nav"> <button className="new-thread-btn" onClick={(e) => { e.stopPropagation(); handleCreateNewChat(); }} disabled={!isAuthenticated}> <Icon path={<path d="M12 5v14m-7-7h14" />} /> <span>New Chat</span> </button> </nav> </div>
<div className="library" onClick={(e) => e.stopPropagation()}>
<div className="library-header"> <h3>History</h3> </div>
{isCreatingNewChat && (<div className="new-chat-form-container" onClick={(e) => e.stopPropagation()}> <form onSubmit={handleNewChatSubmit} className={`new-chat-form ${titleError ? "error" : ""}`} > <input type="text" placeholder="New chat title..." value={newChatTitle} onChange={handleTitleChange} onBlur={() => !newChatTitle && setIsCreatingNewChat(false)} autoFocus /> <button type="submit" className="submit-new-chat-btn" disabled={!!titleError} > <Icon path={titleError ? (<path d="M18 6L6 18M6 6l12 12" />) : (<path d="M20 6L9 17l-5-5" />)} /> </button> </form> {titleError && (<p className="title-error-warning">{titleError}</p>)} </div>)}
<ul> {isAuthenticated ? (chats.length > 0 ? (chats.map((item) => (<li key={item._id} className={item._id === activeChatId ? "active" : ""} > <a href="#" onClick={(e) => { e.preventDefault(); handleHistoryClick(item._id); }}> <span>{item.title}</span> </a> </li>))) : (<li> <a href="#" className="no-chats"> <span>No chats found</span> </a> </li>)) : (<li> <a href="#" className="no-chats"> <span>Login to see history</span> </a> </li>)} </ul>
{isCreatingNewChat && (<div className="new-chat-form-container" onClick={(e) => e.stopPropagation()}> <form onSubmit={handleNewChatSubmit} className={`new-chat-form ${titleError ? "error" : ""}`} onClick={(e) => e.stopPropagation()}> <input type="text" placeholder="New chat title..." value={newChatTitle} onChange={handleTitleChange} onBlur={() => !newChatTitle && setIsCreatingNewChat(false)} autoFocus onClick={(e) => e.stopPropagation()} /> <button type="submit" className="submit-new-chat-btn" disabled={!!titleError} onClick={(e) => e.stopPropagation()}> <Icon path={titleError ? (<path d="M18 6L6 18M6 6l12 12" />) : (<path d="M20 6L9 17l-5-5" />)} /> </button> </form> {titleError && (<p className="title-error-warning">{titleError}</p>)} </div>)}
<ul> {isAuthenticated ? (chats.length > 0 ? (chats.map((item) => (<li key={item._id} className={item._id === activeChatId ? "active" : ""} > <a href="#" onClick={(e) => { e.preventDefault(); e.stopPropagation(); handleHistoryClick(item._id); }}> <span>{item.title}</span> </a> </li>))) : (<li> <a href="#" className="no-chats"> <span>No chats found</span> </a> </li>)) : (<li> <a href="#" className="no-chats"> <span>Login to see history</span> </a> </li>)} </ul>
</div>
<div className="sidebar-bottom"> <div className="user-profile"> <div className="user-info"> <Icon path={<> <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /> <circle cx="12" cy="7" r="4" /> </>} /> <span>{user?.fullName?.firstName || "Guest User"}</span> </div> <div className={`credits-container ${isCreditsVisible ? 'show-text' : ''} ${creditsLoading ? 'loading' : ''} ${credits === 0 ? 'zero-credits' : ''}`} onClick={handleCreditsClick} > <span>{creditsLoading ? <div className="loading-spinner"></div> : (isAuthenticated ? `Credits: ${credits}` : (isCreditsVisible ? 'Login First' : 'Credits: 0'))}</span> </div> </div> </div>
<div className="sidebar-bottom" onClick={(e) => e.stopPropagation()}> <div className="user-profile"> <div className="user-info"> <Icon path={<> <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /> <circle cx="12" cy="7" r="4" /> </>} /> <span>{user?.fullName?.firstName || "Guest User"}</span> </div> <div className={`credits-container ${isCreditsVisible ? 'show-text' : ''} ${creditsLoading ? 'loading' : ''} ${credits === 0 ? 'zero-credits' : ''}`} onClick={(e) => { e.stopPropagation(); handleCreditsClick(); }} > <span>{creditsLoading ? <div className="loading-spinner"></div> : (isAuthenticated ? `Credits: ${credits}` : (isCreditsVisible ? 'Login First' : 'Credits: 0'))}</span> </div> </div> </div>
</aside>

<main className="main-content" onClick={handleMainContentClick}>
Expand Down
21 changes: 21 additions & 0 deletions frontend/src/styles/ChatInterface.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
top: 0;
z-index: 1000;
pointer-events: auto;
isolation: isolate;
}

.sidebar a {
Expand Down Expand Up @@ -850,6 +851,16 @@ a:hover .icon {
transform: translateX(0);
box-shadow: 4px 0 15px rgba(0,0,0,0.2);
}

/* Prevent main content clicks when sidebar is open */
.sidebar.open ~ .main-content {
pointer-events: none;
}

/* Re-enable pointer events for header elements */
.sidebar.open ~ .main-content .main-header {
pointer-events: auto;
}
.header-hamburger { display: flex; }
.sidebar-close-btn { display: flex; }
.main-header { padding: 0 16px; }
Expand All @@ -875,17 +886,22 @@ a:hover .icon {
.first-chat-form-container {
padding: 0 8px;
margin-top: 20px;
overflow: hidden;
}

.first-chat-form {
min-width: 250px;
max-width: calc(100vw - 32px);
padding: 12px 45px 12px 12px;
overflow: hidden;
}

.first-chat-form input {
font-size: 16px; /* Prevents zoom on iOS */
min-height: 20px;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}

.submit-first-chat-btn {
Expand Down Expand Up @@ -1121,6 +1137,7 @@ a:hover .icon {
width: 100%;
padding: 0 16px;
box-sizing: border-box;
overflow: hidden;
}

.first-chat-form {
Expand All @@ -1135,6 +1152,7 @@ a:hover .icon {
width: 100%;
min-width: 280px;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
overflow: hidden;
}

.first-chat-form:focus-within {
Expand All @@ -1156,6 +1174,9 @@ a:hover .icon {
outline: none;
padding: 0;
margin-right: 0;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}

.first-chat-form input::placeholder {
Expand Down