From e94325b38ae34f80828a2e54fe058c2f9d842e18 Mon Sep 17 00:00:00 2001 From: Nicolas Varrot Date: Thu, 12 Feb 2026 16:57:18 +0000 Subject: [PATCH] feat: delete session from sidebar with confirmation dialog --- FEEDBACK.md | 2 +- src/App.tsx | 3 ++- src/components/Sidebar.tsx | 37 +++++++++++++++++++++++++++++++++++-- src/hooks/useGateway.ts | 20 +++++++++++++++++++- src/lib/i18n.ts | 6 ++++++ 5 files changed, 63 insertions(+), 5 deletions(-) diff --git a/FEEDBACK.md b/FEEDBACK.md index 03aa928..2cf13d8 100644 --- a/FEEDBACK.md +++ b/FEEDBACK.md @@ -268,7 +268,7 @@ ## Item #30 - **Date:** 2026-02-12 - **Priority:** medium -- **Status:** pending +- **Status:** in-progress - **Description:** Supprimer une session depuis la sidebar - **Details:** - Ajouter un bouton/action pour supprimer une session (clic droit ou icône) diff --git a/src/App.tsx b/src/App.tsx index 0035bc3..a44f2cf 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,7 +12,7 @@ const Chat = lazy(() => import('./components/Chat').then(m => ({ default: m.Chat export default function App() { const { status, messages, sessions, activeSession, isGenerating, isLoadingHistory, - sendMessage, abort, switchSession, + sendMessage, abort, switchSession, deleteSession, authenticated, login, logout, connectError, isConnecting, } = useGateway(); const [sidebarOpen, setSidebarOpen] = useState(false); @@ -79,6 +79,7 @@ export default function App() { sessions={sessions} activeSession={activeSession} onSwitch={switchSession} + onDelete={deleteSession} open={sidebarOpen} onClose={() => setSidebarOpen(false)} /> diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 6960a82..2f9fc58 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,5 +1,5 @@ import { useState, useMemo, useRef, useEffect, useCallback } from 'react'; -import { X, Sparkles, Search, Pin } from 'lucide-react'; +import { X, Sparkles, Search, Pin, Trash2 } from 'lucide-react'; import type { Session } from '../types'; import { useT } from '../hooks/useLocale'; import { SessionIcon } from './SessionIcon'; @@ -39,17 +39,19 @@ interface Props { sessions: Session[]; activeSession: string; onSwitch: (key: string) => void; + onDelete: (key: string) => void; open: boolean; onClose: () => void; } -export function Sidebar({ sessions, activeSession, onSwitch, open, onClose }: Props) { +export function Sidebar({ sessions, activeSession, onSwitch, onDelete, open, onClose }: Props) { const t = useT(); const [filter, setFilter] = useState(''); const [focusIdx, setFocusIdx] = useState(-1); const [pinned, setPinned] = useState(getPinnedSessions); const [width, setWidth] = useState(getSavedWidth); const [dragging, setDragging] = useState(false); + const [confirmDelete, setConfirmDelete] = useState(null); const searchRef = useRef(null); const listRef = useRef(null); const dragRef = useRef({ startX: 0, startW: 0 }); @@ -260,6 +262,14 @@ export function Sidebar({ sessions, activeSession, onSwitch, open, onClose }: Pr > + {s.messageCount != null && ( {s.messageCount} @@ -310,6 +320,29 @@ export function Sidebar({ sessions, activeSession, onSwitch, open, onClose }: Pr {/* Prevent text selection while dragging */} {dragging &&
} + {/* Delete confirmation dialog */} + {confirmDelete && ( + <> +
setConfirmDelete(null)} /> +
+

{t('sidebar.deleteConfirm')}

+
+ + +
+
+ + )} ); } diff --git a/src/hooks/useGateway.ts b/src/hooks/useGateway.ts index 6c1b745..2248bec 100644 --- a/src/hooks/useGateway.ts +++ b/src/hooks/useGateway.ts @@ -388,6 +388,24 @@ export function useGateway() { setupClient(url, token); }, [setupClient]); + const deleteSession = useCallback(async (key: string) => { + try { + await clientRef.current?.send('sessions.delete', { key, deleteTranscript: true }); + } catch { + // Ignore delete failures + } + // Remove from local state + setSessions(prev => prev.filter(s => s.key !== key)); + // If we deleted the active session, switch to main + if (activeSessionRef.current === key) { + const mainKey = 'agent:main:main'; + setActiveSession(mainKey); + activeSessionRef.current = mainKey; + setMessages([]); + loadHistory(mainKey); + } + }, [loadHistory]); + const logout = useCallback(() => { if (clientRef.current) { clientRef.current.disconnect(); @@ -416,7 +434,7 @@ export function useGateway() { return { status, messages, sessions: enrichedSessions, activeSession, isGenerating, isLoadingHistory, - sendMessage, abort, switchSession, loadSessions, + sendMessage, abort, switchSession, loadSessions, deleteSession, authenticated, login, logout, connectError, isConnecting, }; } diff --git a/src/lib/i18n.ts b/src/lib/i18n.ts index f1f8034..3236841 100644 --- a/src/lib/i18n.ts +++ b/src/lib/i18n.ts @@ -52,6 +52,9 @@ const en = { 'sidebar.pin': 'Pin session', 'sidebar.unpin': 'Unpin session', 'sidebar.pinned': 'Pinned', + 'sidebar.delete': 'Delete session', + 'sidebar.deleteConfirm': 'Delete this session? This cannot be undone.', + 'sidebar.deleteCancel': 'Cancel', // Thinking 'thinking.label': 'Thinking', @@ -134,6 +137,9 @@ const fr: Record = { 'sidebar.pin': 'Épingler la session', 'sidebar.unpin': 'Désépingler la session', 'sidebar.pinned': 'Épinglées', + 'sidebar.delete': 'Supprimer la session', + 'sidebar.deleteConfirm': 'Supprimer cette session ? Cette action est irréversible.', + 'sidebar.deleteCancel': 'Annuler', 'thinking.label': 'Réflexion',