Remove chat drag enter event handler
This commit is contained in:
177
src/App.jsx
177
src/App.jsx
@@ -1325,183 +1325,6 @@ async function regenerateFromIndex(index, overrideUserText = null) {
|
||||
setIsAttachmentMenuOpen(false)
|
||||
}, [activeSessionId, activeSidebarMode, canAttachImages])
|
||||
|
||||
// Persist the scrollTop of the session we are LEAVING (on chat change or when leaving the chat view)
|
||||
useEffect(() => {
|
||||
const leavingSessionId = activeSessionId;
|
||||
const leavingMode = activeSidebarMode;
|
||||
|
||||
return () => {
|
||||
if (leavingMode === 'chats' && leavingSessionId) {
|
||||
const top = typeof scrollTopsRef.current[leavingSessionId] === 'number'
|
||||
? scrollTopsRef.current[leavingSessionId]
|
||||
: (chatRef.current ? chatRef.current.scrollTop : 0);
|
||||
|
||||
setScrollPositions(prev => {
|
||||
const updated = { ...prev, [leavingSessionId]: top };
|
||||
window.electronAPI.updateSettings({ scrollPositions: updated });
|
||||
return updated;
|
||||
});
|
||||
}
|
||||
};
|
||||
}, [activeSessionId, activeSidebarMode]);
|
||||
|
||||
// Track scroll + whether user left bottom
|
||||
useEffect(() => {
|
||||
const chatDiv = chatRef.current;
|
||||
if (!chatDiv) return;
|
||||
|
||||
const handleScroll = () => {
|
||||
const { scrollTop, scrollHeight, clientHeight } = chatDiv;
|
||||
const isAtBottom = (scrollHeight - scrollTop - clientHeight) <= BOTTOM_EPSILON;
|
||||
|
||||
if (activeSessionId) {
|
||||
const prevScrollTop = prevScrollTopsRef.current[activeSessionId];
|
||||
const scrolledUp = typeof prevScrollTop === 'number' && scrollTop < prevScrollTop;
|
||||
|
||||
scrollTopsRef.current[activeSessionId] = scrollTop;
|
||||
|
||||
if (isAtBottom) {
|
||||
setUserScrolledUp(activeSessionId, false); // User is at bottom, enable autoscroll
|
||||
} else if (scrolledUp) {
|
||||
setUserScrolledUp(activeSessionId, true); // User scrolled up, disable autoscroll
|
||||
}
|
||||
// If user scrolled down but not to bottom, maintain current userScrolledUp state
|
||||
prevScrollTopsRef.current[activeSessionId] = scrollTop;
|
||||
}
|
||||
};
|
||||
|
||||
chatDiv.addEventListener('scroll', handleScroll);
|
||||
return () => chatDiv.removeEventListener('scroll', handleScroll);
|
||||
}, [activeSessionId, setUserScrolledUp]);
|
||||
|
||||
// Auto-hide the tip if user returns to bottom in the active chat
|
||||
useEffect(() => {
|
||||
const sid = activeSessionId;
|
||||
if (!sid) return;
|
||||
if (userScrolledUpState[sid] === false) {
|
||||
setNewMsgTip(prev => {
|
||||
if (!(sid in prev)) return prev;
|
||||
const rest = { ...prev };
|
||||
delete rest[sid];
|
||||
return rest;
|
||||
});
|
||||
}
|
||||
}, [activeSessionId, userScrolledUpState]);
|
||||
|
||||
// --- Robust restoration: do it before paint, exactly once per open ---
|
||||
useLayoutEffect(() => {
|
||||
if (activeSidebarMode !== 'chats' || !activeSessionId) return;
|
||||
|
||||
const div = chatRef.current;
|
||||
if (!div) return;
|
||||
|
||||
restoredForRef.current = null;
|
||||
|
||||
const applyRestore = () => {
|
||||
if (restoredForRef.current === activeSessionId) return;
|
||||
|
||||
const liveSaved = typeof scrollTopsRef.current[activeSessionId] === 'number'
|
||||
? scrollTopsRef.current[activeSessionId]
|
||||
: undefined;
|
||||
const saved = typeof liveSaved === 'number'
|
||||
? liveSaved
|
||||
: scrollPositions[activeSessionId];
|
||||
|
||||
if (typeof saved === 'number') {
|
||||
div.scrollTop = saved;
|
||||
restoredForRef.current = activeSessionId;
|
||||
return;
|
||||
}
|
||||
if (messages.length > 0) {
|
||||
// default: bottom when no saved position
|
||||
div.scrollTop = div.scrollHeight;
|
||||
restoredForRef.current = activeSessionId;
|
||||
}
|
||||
};
|
||||
|
||||
// Run immediately (pre-paint) and also schedule a fallback rAF
|
||||
applyRestore();
|
||||
const r0 = requestAnimationFrame(applyRestore);
|
||||
|
||||
// If content size/DOM changes after first paint, apply once
|
||||
const onDomChange = () => {
|
||||
if (restoredForRef.current !== activeSessionId) {
|
||||
requestAnimationFrame(applyRestore);
|
||||
}
|
||||
};
|
||||
|
||||
const mo = new MutationObserver(onDomChange);
|
||||
mo.observe(div, { childList: true, subtree: true });
|
||||
|
||||
const ro = new ResizeObserver(onDomChange);
|
||||
ro.observe(div);
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(r0);
|
||||
mo.disconnect();
|
||||
ro.disconnect();
|
||||
};
|
||||
}, [activeSessionId, activeSidebarMode, messages.length, scrollPositions]);
|
||||
|
||||
// If there is no saved scroll and content arrives later (e.g., on first app load),
|
||||
// default to bottom exactly once for this open chat.
|
||||
useEffect(() => {
|
||||
if (activeSidebarMode !== 'chats' || !activeSessionId) return;
|
||||
if (restoredForRef.current === activeSessionId) return; // already applied
|
||||
|
||||
const liveSaved = typeof scrollTopsRef.current[activeSessionId] === 'number'
|
||||
? scrollTopsRef.current[activeSessionId]
|
||||
: undefined;
|
||||
const savedScrollTop = typeof liveSaved === 'number'
|
||||
? liveSaved
|
||||
: scrollPositions[activeSessionId];
|
||||
|
||||
// Only when there is no saved position and we now have content
|
||||
if (typeof savedScrollTop !== 'number' && messages.length > 0) {
|
||||
requestAnimationFrame(() => {
|
||||
const div = chatRef.current;
|
||||
if (!div) return;
|
||||
div.scrollTop = div.scrollHeight;
|
||||
restoredForRef.current = activeSessionId;
|
||||
});
|
||||
}
|
||||
}, [messages.length, activeSessionId, activeSidebarMode, scrollPositions]);
|
||||
|
||||
// Session-aware scroll helpers
|
||||
const scrollToBottom = (behavior = 'smooth', sessionId = null) => {
|
||||
const chatDiv = chatRef.current;
|
||||
if (!chatDiv) return;
|
||||
const target = sessionId ?? activeSessionIdRef.current;
|
||||
if (activeSessionIdRef.current !== target) return;
|
||||
chatDiv.scrollTo({ top: chatDiv.scrollHeight, behavior });
|
||||
setUserScrolledUp(target, false);
|
||||
};
|
||||
|
||||
const scrollMessageToTop = (msgId, behavior = 'auto', sessionId = null) => {
|
||||
const chatDiv = chatRef.current;
|
||||
if (!chatDiv) return;
|
||||
const target = sessionId ?? activeSessionIdRef.current;
|
||||
if (activeSessionIdRef.current !== target) return;
|
||||
const el = document.getElementById(msgId);
|
||||
if (el) {
|
||||
const top = Math.max(0, el.offsetTop - TOP_ALIGN_OFFSET);
|
||||
chatDiv.scrollTo({ top, behavior });
|
||||
}
|
||||
};
|
||||
|
||||
// Handler for new message tip click
|
||||
const handleNewMsgTipClick = () => {
|
||||
const sid = activeSessionIdRef.current;
|
||||
const msgId = newMsgTip[sid];
|
||||
if (msgId) {
|
||||
scrollMessageToTop(msgId, 'smooth', sid);
|
||||
setNewMsgTip(prev => {
|
||||
const { [sid]: _omit, ...rest } = prev;
|
||||
return rest;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleChatDragEnter = (event) => {
|
||||
if (activeSidebarMode !== 'chats' || !hasFilePayload(event)) return
|
||||
event.preventDefault()
|
||||
|
||||
Reference in New Issue
Block a user