From 82fffbc54499530d3c575f01bd2ae04170991b29 Mon Sep 17 00:00:00 2001 From: Somi Date: Thu, 11 Jun 2026 21:50:54 +0100 Subject: [PATCH 1/4] feat: real-time notifications + UI fixes for industrial deployment New features: - Add useNotifications hook: real-time Socket.io notifications with Chainlit native auth - Add NotificationBell component: unread badge, popover list, type indicators - Add LeftSidebar/Search: thread history search functionality Bug fixes: - Fix Alert: use div instead of p to allow block-level children (Skeleton) - Fix AutoResizeTextarea: add forwardRef + useImperativeHandle to expose ref externally - Fix BlinkingCursor: replace static cursor with animated 3-dot typing indicator - Fix Logo/Avatar: resolve absolute URLs for reverse-proxy/standalone deployments - Fix ThreadList: use SidebarMenuAction to avoid invalid HTML nesting - Fix ThreadList: track active thread from URL params for correct highlight state - Fix ThreadList: add stopPropagation on delete/rename/share actions Compatibility: - Update shadcn/ui components to latest API (data-slot, React.ComponentProps) - Fix react-markdown PluggableList import for newer versions Tested in production: Chainlit + Next.js 14 + Nginx reverse proxy PFE ENSMR 2026 - Copilote Industriel OCP Jorf Lasfar --- frontend/src/components/Alert.tsx | 4 +- .../src/components/AutoResizeTextarea.tsx | 21 +- frontend/src/components/BlinkingCursor.tsx | 8 +- .../Elements/CustomElement/index.tsx | 2 +- .../src/components/Elements/Dataframe.tsx | 21 +- frontend/src/components/Elements/PDF.tsx | 317 +---------------- frontend/src/components/Elements/Plotly.tsx | 73 ++-- frontend/src/components/Elements/Text.tsx | 2 +- frontend/src/components/Elements/Video.tsx | 36 +- .../src/components/LeftSidebar/Search.tsx | 150 ++++++-- .../components/LeftSidebar/ThreadHistory.tsx | 81 ++++- .../src/components/LeftSidebar/ThreadList.tsx | 256 ++++++-------- .../components/LeftSidebar/ThreadOptions.tsx | 50 ++- frontend/src/components/LeftSidebar/index.tsx | 15 +- frontend/src/components/Logo.tsx | 13 +- frontend/src/components/Markdown.tsx | 327 +++++++----------- frontend/src/components/ThemeProvider.tsx | 47 ++- .../chat/MessageComposer/Attachment.tsx | 105 +++--- .../chat/MessageComposer/Attachments.tsx | 16 +- .../MessageComposer/CommandPopoverButton.tsx | 8 +- .../chat/MessageComposer/Mcp/AddForm.tsx | 2 +- .../chat/MessageComposer/Mcp/List.tsx | 2 +- .../chat/MessageComposer/Mcp/index.tsx | 26 +- .../chat/MessageComposer/SubmitButton.tsx | 2 +- .../chat/MessageComposer/UploadButton.tsx | 102 +++--- .../chat/MessageComposer/VoiceButton.tsx | 4 +- .../components/chat/MessageComposer/index.tsx | 110 +++--- .../Messages/Message/AskActionButtons.tsx | 2 +- .../chat/Messages/Message/AskFileButton.tsx | 4 +- .../chat/Messages/Message/Avatar.tsx | 20 +- .../Message/Buttons/Actions/ActionButton.tsx | 2 +- .../Message/Buttons/FeedbackButtons.tsx | 2 +- .../InlinedElements/InlinedImageList.tsx | 2 +- .../InlinedElements/InlinedVideoList.tsx | 2 +- .../components/chat/Messages/Message/Step.tsx | 2 +- .../chat/Messages/Message/UserMessage.tsx | 4 +- .../chat/Messages/Message/index.tsx | 52 +-- .../src/components/chat/Messages/index.tsx | 43 ++- .../chat/MessagesContainer/index.tsx | 23 +- frontend/src/components/chat/index.tsx | 50 ++- frontend/src/components/header/NewChat.tsx | 2 +- .../components/header/NotificationBell.tsx | 110 ++++++ .../src/components/header/SidebarTrigger.tsx | 79 ++--- .../src/components/header/ThemeToggle.tsx | 2 +- frontend/src/components/header/index.tsx | 2 +- frontend/src/components/i18n/Translator.tsx | 203 +++++++++-- frontend/src/components/ui/alert-dialog.tsx | 6 +- frontend/src/components/ui/button.tsx | 79 +++-- frontend/src/components/ui/calendar.tsx | 2 +- frontend/src/components/ui/card.tsx | 162 +++++---- frontend/src/components/ui/dialog.tsx | 127 ++++--- frontend/src/components/ui/dropdown-menu.tsx | 2 +- frontend/src/components/ui/input.tsx | 36 +- frontend/src/components/ui/label.tsx | 40 +-- frontend/src/components/ui/resizable.tsx | 27 +- frontend/src/components/ui/skeleton.tsx | 14 +- frontend/src/components/ui/tabs.tsx | 131 ++++--- frontend/src/contexts/MessageContext.tsx | 2 +- frontend/src/hooks/useNotifications.tsx | 81 +++++ frontend/src/hooks/useUpload.tsx | 2 +- frontend/src/lib/message.ts | 2 +- frontend/src/lib/utils.ts | 6 +- frontend/src/state/chat.ts | 2 +- 63 files changed, 1676 insertions(+), 1451 deletions(-) create mode 100644 frontend/src/components/header/NotificationBell.tsx create mode 100644 frontend/src/hooks/useNotifications.tsx diff --git a/frontend/src/components/Alert.tsx b/frontend/src/components/Alert.tsx index 8eb6e2d2d8..9b177b02df 100644 --- a/frontend/src/components/Alert.tsx +++ b/frontend/src/components/Alert.tsx @@ -1,3 +1,4 @@ + import { cn } from '@/lib/utils'; import React from 'react'; @@ -89,7 +90,8 @@ export const Alert: React.FC = ({
{icons[variant]}
-

{children}

+ {/* Correction : Changement de

vers

pour autoriser les composants block-level comme Skeleton */} +
{children}
diff --git a/frontend/src/components/AutoResizeTextarea.tsx b/frontend/src/components/AutoResizeTextarea.tsx index e585a5201d..c4b96383f9 100644 --- a/frontend/src/components/AutoResizeTextarea.tsx +++ b/frontend/src/components/AutoResizeTextarea.tsx @@ -1,6 +1,6 @@ + +import React, { forwardRef, useEffect, useRef, useState, useImperativeHandle } from 'react'; import { cn } from '@/lib/utils'; -import { useEffect, useRef, useState } from 'react'; - import { Textarea } from '@/components/ui/textarea'; interface Props extends Omit, 'onPaste'> { @@ -16,7 +16,7 @@ interface Props extends Omit, 'onPaste'> { ) => void; } -const AutoResizeTextarea = ({ +const AutoResizeTextarea = forwardRef(({ maxHeight, onPaste, onEnter, @@ -26,10 +26,13 @@ const AutoResizeTextarea = ({ onCompositionStart, onCompositionEnd, ...props -}: Props) => { +}, ref) => { const textareaRef = useRef(null); const [isComposing, setIsComposing] = useState(false); + // Synchronise la ref locale (utilisée pour les calculs de hauteur) avec la ref externe + useImperativeHandle(ref, () => textareaRef.current!); + useEffect(() => { const textarea = textareaRef.current; if (!textarea || !onPaste) return; @@ -44,18 +47,18 @@ const AutoResizeTextarea = ({ useEffect(() => { const textarea = textareaRef.current; if (!textarea || !maxHeight) return; + + // Réinitialisation temporaire pour calculer le scrollHeight réel textarea.style.height = '40px'; const newHeight = Math.min(textarea.scrollHeight, maxHeight); textarea.style.height = `${newHeight}px`; }, [props.value, maxHeight]); const handleKeyDown = (event: React.KeyboardEvent) => { - // Call the parent's onKeyDown first (this is Input's handler) if (onKeyDown) { onKeyDown(event); } - // Only handle our Enter logic if the event wasn't already handled if ( !event.defaultPrevented && event.key === 'Enter' && @@ -88,7 +91,7 @@ const AutoResizeTextarea = ({ return (