From 146c9ff19fd3964f3dbfa1aa4d8f61286977fc98 Mon Sep 17 00:00:00 2001
From: elvoisin <95469923+elvoisin@users.noreply.github.com>
Date: Tue, 14 Oct 2025 10:05:28 +0200
Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B(front)=20fix=20left=20panel=20stat?=
=?UTF-8?q?us=20+=20fix=20scroll=20(#65)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
fix scroll + fix left panel status + fix show sources
---
CHANGELOG.md | 1 +
.../src/features/chat/components/Chat.tsx | 165 ++++++++++--------
.../chat/stores/useChatPreferencesStore.ts | 6 +
.../components/ButtonToggleLeftPanel.tsx | 4 +-
.../header/components/ButtonTogglePanel.tsx | 4 +-
.../src/features/header/components/Header.tsx | 6 +-
.../left-panel/components/LeftPanel.tsx | 13 +-
.../left-panel/components/LeftPanelHeader.tsx | 7 +-
.../conversations/src/layouts/MainLayout.tsx | 4 +-
9 files changed, 116 insertions(+), 94 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 31fe8a9..3515f6c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ and this project adheres to
## [Unreleased]
+🐛(front) fix left panel status + fix scroll
-🐛(llm) add is_active field and persist chat preference
### Changed
diff --git a/src/frontend/apps/conversations/src/features/chat/components/Chat.tsx b/src/frontend/apps/conversations/src/features/chat/components/Chat.tsx
index 8e19799..966725e 100644
--- a/src/frontend/apps/conversations/src/features/chat/components/Chat.tsx
+++ b/src/frontend/apps/conversations/src/features/chat/components/Chat.tsx
@@ -119,7 +119,6 @@ export const Chat = ({
setChatContainerRef(chatContainerRef);
}, []);
- // Détecter si l'utilisateur scroll vers le haut
useEffect(() => {
const container = chatContainerRef.current;
if (!container) {
@@ -127,6 +126,11 @@ export const Chat = ({
}
const handleScroll = () => {
+ // Ignorer les scrolls automatiques
+ if (isAutoScrollingRef.current) {
+ return;
+ }
+
const { scrollTop, scrollHeight, clientHeight } = container;
const isAtBottom = scrollHeight - scrollTop - clientHeight < 50;
@@ -154,6 +158,7 @@ export const Chat = ({
number | null
>(null);
const [userScrolledUp, setUserScrolledUp] = useState(false);
+ const isAutoScrollingRef = useRef(false);
const { mutate: createChatConversation } = useCreateChatConversation();
@@ -195,10 +200,14 @@ export const Chat = ({
// Scroll to bottom when new messages arrive
const scrollToBottom = useCallback(() => {
if (chatContainerRef.current) {
+ isAutoScrollingRef.current = true;
chatContainerRef.current.scrollTo({
top: chatContainerRef.current.scrollHeight,
behavior: hasInitialized ? 'smooth' : 'auto',
});
+ setTimeout(() => {
+ isAutoScrollingRef.current = false;
+ }, 500);
}
}, [hasInitialized]);
@@ -314,10 +323,14 @@ export const Chat = ({
if (lastUserMessageElement) {
const messageTop = (lastUserMessageElement as HTMLElement)
.offsetTop;
+ isAutoScrollingRef.current = true;
chatContainerRef.current.scrollTo({
top: messageTop,
behavior: 'smooth',
});
+ setTimeout(() => {
+ isAutoScrollingRef.current = false;
+ }, 500);
}
}
}
@@ -394,10 +407,14 @@ export const Chat = ({
setTimeout(() => {
if (chatContainerRef.current) {
+ isAutoScrollingRef.current = true;
chatContainerRef.current.scrollTo({
top: chatContainerRef.current.scrollHeight,
behavior: 'auto',
});
+ setTimeout(() => {
+ isAutoScrollingRef.current = false;
+ }, 100);
}
}, 100);
}
@@ -638,7 +655,7 @@ export const Chat = ({
) : null,
)}
- {message.role !== 'user' && !isCurrentlyStreaming && (
+ {message.role !== 'user' && status !== 'streaming' && (
- copyToClipboard(message.content)}
- onKeyDown={(e) => {
- if (e.key === 'Enter' || e.key === ' ') {
- e.preventDefault();
- copyToClipboard(message.content);
- }
- }}
- role="button"
- tabIndex={0}
- >
-
- {!isMobile && (
-
- {t('Copy')}
-
- )}
-
- {message.parts?.some(
- (part) => part.type === 'source',
- ) &&
- (() => {
- const sourceCount =
- message.parts?.filter(
- (part) => part.type === 'source',
- ).length || 0;
- return (
- openSources(message.id)}
- onKeyDown={(e) => {
- if (e.key === 'Enter' || e.key === ' ') {
- e.preventDefault();
- openSources(message.id);
- }
- }}
- role="button"
- tabIndex={0}
- >
-
-
+ copyToClipboard(message.content)}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ copyToClipboard(message.content);
+ }
+ }}
+ role="button"
+ tabIndex={0}
+ >
+
+ {!isMobile && (
+
+ {t('Copy')}
+
+ )}
+
+ {message.parts?.some(
+ (part) => part.type === 'source',
+ ) &&
+ (() => {
+ const sourceCount =
+ message.parts?.filter(
+ (part) => part.type === 'source',
+ ).length || 0;
+ return (
+ openSources(message.id)}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ openSources(message.id);
+ }
+ }}
+ role="button"
+ tabIndex={0}
>
- {t('Show') +
- ` ${sourceCount} ` +
- t('sources')}
-
-
- );
- })()}
+
+
+ {t('Show') +
+ ` ${sourceCount} ` +
+ t('sources')}
+
+
+ );
+ })()}
+
{/* We should display the button, but disabled if no trace linked */}
{conversationId &&
diff --git a/src/frontend/apps/conversations/src/features/chat/stores/useChatPreferencesStore.ts b/src/frontend/apps/conversations/src/features/chat/stores/useChatPreferencesStore.ts
index c0cfb83..8efa664 100644
--- a/src/frontend/apps/conversations/src/features/chat/stores/useChatPreferencesStore.ts
+++ b/src/frontend/apps/conversations/src/features/chat/stores/useChatPreferencesStore.ts
@@ -4,8 +4,11 @@ import { persist } from 'zustand/middleware';
interface ChatPreferencesState {
selectedModelHrid: string | null;
forceWebSearch: boolean;
+ isPanelOpen: boolean;
setSelectedModelHrid: (hrid: string | null) => void;
toggleForceWebSearch: () => void;
+ setPanelOpen: (isOpen: boolean) => void;
+ togglePanel: () => void;
}
export const useChatPreferencesStore = create()(
@@ -13,9 +16,12 @@ export const useChatPreferencesStore = create()(
(set) => ({
selectedModelHrid: null,
forceWebSearch: false,
+ isPanelOpen: false,
setSelectedModelHrid: (hrid) => set({ selectedModelHrid: hrid }),
toggleForceWebSearch: () =>
set((state) => ({ forceWebSearch: !state.forceWebSearch })),
+ setPanelOpen: (isOpen) => set({ isPanelOpen: isOpen }),
+ togglePanel: () => set((state) => ({ isPanelOpen: !state.isPanelOpen })),
}),
{
name: 'chat-preferences',
diff --git a/src/frontend/apps/conversations/src/features/header/components/ButtonToggleLeftPanel.tsx b/src/frontend/apps/conversations/src/features/header/components/ButtonToggleLeftPanel.tsx
index 011c6d8..36b1d0c 100644
--- a/src/frontend/apps/conversations/src/features/header/components/ButtonToggleLeftPanel.tsx
+++ b/src/frontend/apps/conversations/src/features/header/components/ButtonToggleLeftPanel.tsx
@@ -2,11 +2,11 @@ import { Button } from '@openfun/cunningham-react';
import { useTranslation } from 'react-i18next';
import LeftPanelIcon from '@/assets/icons/left-panel-bold.svg';
-import { useLeftPanelStore } from '@/features/left-panel';
+import { useChatPreferencesStore } from '@/features/chat/stores/useChatPreferencesStore';
export const ButtonToggleLeftPanel = () => {
const { t } = useTranslation();
- const { isPanelOpen: _isPanelOpen, togglePanel } = useLeftPanelStore();
+ const { togglePanel } = useChatPreferencesStore();
return (