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 (