From 7825f5d07b445058166ae37031a2307cd183d008 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Nov 2025 21:49:25 +0000 Subject: [PATCH 1/3] fix: uniformiser la taille et l'alignement des boutons flottants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Retirer size="lg" du bouton QuickCapture - Aligner les deux boutons sur right-4 (au lieu de right-6 pour QuickCapture) - Ajouter les mêmes effets hover et transition aux deux boutons - Les boutons ont maintenant exactement la même taille (h-14 w-14) - Les icônes ont la même taille (h-6 w-6) --- src/components/QuickCaptureWidget.tsx | 3 +-- src/pages/Index.tsx | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/QuickCaptureWidget.tsx b/src/components/QuickCaptureWidget.tsx index eb7457f..75a17d1 100644 --- a/src/components/QuickCaptureWidget.tsx +++ b/src/components/QuickCaptureWidget.tsx @@ -68,8 +68,7 @@ export function QuickCaptureWidget({ onNoteCaptured }: QuickCaptureWidgetProps) <> {/* Floating Action Button */} From 08bdbc389a35050108545416261d2927649381af Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Nov 2025 21:54:07 +0000 Subject: [PATCH 2/3] fix: maintenir le timer Pomodoro actif quand le modal se ferme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Utiliser useCallback pour mémoriser handleTimerComplete - Retirer timeLeft des dépendances du useEffect qui gère l'intervalle - Le useEffect ne dépend plus que de isRunning et handleTimerComplete - L'intervalle continue de tourner même après démontage/remontage du composant - Nettoyage propre de l'intervalle avec intervalRef.current = null --- src/components/PomodoroTimer.tsx | 53 ++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/components/PomodoroTimer.tsx b/src/components/PomodoroTimer.tsx index 0aaa4a8..da420d9 100644 --- a/src/components/PomodoroTimer.tsx +++ b/src/components/PomodoroTimer.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from "react"; +import { useState, useEffect, useRef, useCallback } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; @@ -105,27 +105,7 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) { } }, [isRunning, timeLeft, mode, onStateChange]); - useEffect(() => { - if (isRunning && timeLeft > 0) { - intervalRef.current = setInterval(() => { - setTimeLeft(prev => { - if (prev <= 1) { - handleTimerComplete(); - return 0; - } - return prev - 1; - }); - }, 1000); - } else if (intervalRef.current) { - clearInterval(intervalRef.current); - } - - return () => { - if (intervalRef.current) clearInterval(intervalRef.current); - }; - }, [isRunning, timeLeft]); - - const handleTimerComplete = () => { + const handleTimerComplete = useCallback(() => { setIsRunning(false); // Play notification sound @@ -151,7 +131,34 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) { setTimeLeft(TIMER_DURATIONS.work); showSuccess('Pause terminée ! Prêt pour une nouvelle session ?'); } - }; + }, [mode, sessionsCompleted]); + + // Manage timer interval + useEffect(() => { + if (isRunning) { + intervalRef.current = setInterval(() => { + setTimeLeft(prev => { + if (prev <= 1) { + handleTimerComplete(); + return 0; + } + return prev - 1; + }); + }, 1000); + } else { + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + } + + return () => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + }; + }, [isRunning, handleTimerComplete]); const handleStart = () => { setIsRunning(true); From ca0c60a13bae3a2aff60ca4256202a0ceda78ca5 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Nov 2025 21:56:24 +0000 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20am=C3=A9liorer=20l'assistant=20IA?= =?UTF-8?q?=20avec=20modal=20agrandi=20et=20z-index?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ajouter z-50 à la Card du chat pour qu'elle passe devant le bouton QuickCapture - Ajouter un bouton 'Agrandir' avec l'icône LayoutGrid - Créer un modal agrandi (max-w-4xl, h-80vh) pour l'assistant IA - Améliorer l'affichage des messages avec whitespace-pre-wrap - Ajouter des tooltips sur les boutons du chat --- src/pages/Index.tsx | 164 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 163 insertions(+), 1 deletion(-) diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index 43f0aba..916b75d 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -217,6 +217,7 @@ const Index = () => { // Chatbox states const [chatOpen, setChatOpen] = useState(false); + const [chatExpandedModal, setChatExpandedModal] = useState(false); const [chatMessages, setChatMessages] = useState>([]); const [chatInput, setChatInput] = useState(""); const [chatLoading, setChatLoading] = useState(false); @@ -2988,7 +2989,7 @@ const Index = () => { ) : ( - +
@@ -2996,11 +2997,23 @@ const Index = () => { Assistant IA
+ @@ -3008,6 +3021,7 @@ const Index = () => { variant="ghost" size="sm" onClick={() => setChatOpen(false)} + title="Fermer" > @@ -3176,6 +3190,154 @@ const Index = () => { )} + {/* Expanded Chat Modal */} + + + +
+ + + Assistant IA + +
+ +
+
+
+ + + + + + + + Aucun modèle trouvé. + + + {openRouterModels.map((model) => ( + { + setSelectedModel(currentValue); + setModelSelectorOpen(false); + }} + > + +
+ {model.name} + {model.id} +
+
+ ))} +
+
+
+
+
+
+
+ +
+ {chatMessages.length === 0 ? ( +
+ +

Bienvenue dans l'Assistant IA

+

+ Posez vos questions et obtenez des réponses intelligentes grâce à l'IA. + {openRouterModels.length === 0 && user?.is_admin && ( + Configurez une clé API OpenRouter dans les paramètres. + )} +

+
+ ) : ( + chatMessages.map((msg, idx) => ( +
+
+
{msg.content}
+
+
+ )) + )} + {chatLoading && ( +
+
+
+ ● + ● + ● +
+
+
+ )} +
+
+ +
+
{ + e.preventDefault(); + handleSendMessage(); + }} + className="flex gap-2" + > + setChatInput(e.target.value)} + placeholder="Posez une question..." + disabled={chatLoading || !selectedModel} + className="flex-1" + /> + +
+
+ +
+ {/* Image modal for fullscreen view */} {selectedImage && (