Enhance App.jsx with activeSidebarModeRef and cleanup listeners

This commit is contained in:
2026-04-17 08:01:23 +02:00
parent a83486107c
commit 0d1541d64c

View File

@@ -263,6 +263,7 @@ export default function App() {
const [chatSessions, setChatSessions] = useState([]) const [chatSessions, setChatSessions] = useState([])
const [activeSessionId, setActiveSessionId] = useState(null) const [activeSessionId, setActiveSessionId] = useState(null)
const [activeSidebarMode, setActiveSidebarMode] = useState('chats') // 'chats', 'dbs', 'settings' const [activeSidebarMode, setActiveSidebarMode] = useState('chats') // 'chats', 'dbs', 'settings'
const activeSidebarModeRef = useRef(activeSidebarMode)
const [activeSettingsSubmenu, setActiveSettingsSubmenu] = useState('General'); // 'General', 'Interface' const [activeSettingsSubmenu, setActiveSettingsSubmenu] = useState('General'); // 'General', 'Interface'
const [editingSessionId, setEditingSessionId] = useState(null); // ID of the session being edited const [editingSessionId, setEditingSessionId] = useState(null); // ID of the session being edited
const [editingLibrarySlug, setEditingLibrarySlug] = useState(null) const [editingLibrarySlug, setEditingLibrarySlug] = useState(null)
@@ -1017,6 +1018,10 @@ async function regenerateFromIndex(index, overrideUserText = null) {
// Collapse state per user message: { [msgKey]: boolean } — true means "collapsed" // Collapse state per user message: { [msgKey]: boolean } — true means "collapsed"
const [collapsedUserMsgs, setCollapsedUserMsgs] = useState({}); const [collapsedUserMsgs, setCollapsedUserMsgs] = useState({});
useEffect(() => {
activeSidebarModeRef.current = activeSidebarMode
}, [activeSidebarMode])
// Compute a stable key for collapse map (prefer id, else session:index) // Compute a stable key for collapse map (prefer id, else session:index)
const collapseKeyFor = (m, i, sessionId) => (m?.id ? m.id : `${sessionId}:${i}`); const collapseKeyFor = (m, i, sessionId) => (m?.id ? m.id : `${sessionId}:${i}`);
@@ -1114,9 +1119,11 @@ async function regenerateFromIndex(index, overrideUserText = null) {
React.useEffect(() => { React.useEffect(() => {
window.addEventListener('mousemove', resizeSidebar); window.addEventListener('mousemove', resizeSidebar);
window.addEventListener('mouseup', stopResizing); window.addEventListener('mouseup', stopResizing);
window.addEventListener('blur', stopResizing);
return () => { return () => {
window.removeEventListener('mousemove', resizeSidebar); window.removeEventListener('mousemove', resizeSidebar);
window.removeEventListener('mouseup', stopResizing); window.removeEventListener('mouseup', stopResizing);
window.removeEventListener('blur', stopResizing);
}; };
}, [resizeSidebar, stopResizing]); }, [resizeSidebar, stopResizing]);
@@ -1126,6 +1133,10 @@ async function regenerateFromIndex(index, overrideUserText = null) {
} else { } else {
document.body.classList.remove('no-select'); document.body.classList.remove('no-select');
} }
return () => {
document.body.classList.remove('no-select');
};
}, [isResizing]); }, [isResizing]);
React.useEffect(() => { React.useEffect(() => {
@@ -1153,9 +1164,11 @@ async function regenerateFromIndex(index, overrideUserText = null) {
}, []); }, []);
// Load settings on startup
useEffect(() => { useEffect(() => {
let cancelled = false
window.electronAPI.getSettings().then(settings => { window.electronAPI.getSettings().then(settings => {
if (cancelled) return
setBackendApiUrl(resolveBackendApiUrl(settings)); setBackendApiUrl(resolveBackendApiUrl(settings));
setColorScheme(settings.colorScheme || 'Default'); setColorScheme(settings.colorScheme || 'Default');
setModel(settings.chatModel || ''); // Load the selected model, with a fallback setModel(settings.chatModel || ''); // Load the selected model, with a fallback
@@ -1166,11 +1179,19 @@ async function regenerateFromIndex(index, overrideUserText = null) {
setScrollPositions(settings.scrollPositions || {}); // Load scroll positions setScrollPositions(settings.scrollPositions || {}); // Load scroll positions
applyColorScheme(settings.colorScheme || 'Default'); // Apply initial scheme applyColorScheme(settings.colorScheme || 'Default'); // Apply initial scheme
}).finally(() => { }).finally(() => {
setSettingsLoaded(true); if (!cancelled) {
setSettingsLoaded(true);
}
}); });
return () => {
cancelled = true
};
}, []);
useEffect(() => {
const handleFocus = () => { const handleFocus = () => {
if (activeSidebarMode === 'chats') { if (activeSidebarModeRef.current === 'chats') {
textareaRef.current?.focus(); textareaRef.current?.focus();
} }
}; };
@@ -1178,13 +1199,9 @@ async function regenerateFromIndex(index, overrideUserText = null) {
window.electronAPI.onWindowFocus(handleFocus); window.electronAPI.onWindowFocus(handleFocus);
return () => { return () => {
// Clean up the listener when the component unmounts window.electronAPI.offWindowFocus(handleFocus);
// This part is tricky with the current setup, as `onWindowFocus` uses `ipcRenderer.on`
// which doesn't return a cleanup function. A more robust implementation
// would involve `ipcRenderer.removeListener`. For now, we'll assume this is okay
// for the lifetime of the app.
}; };
}, [activeSidebarMode]); }, []);
useEffect(() => { useEffect(() => {
let cancelled = false let cancelled = false