Compare commits

..
Author SHA1 Message Date
goatoush aa2d127de4 fix(frontend): remove jitter when scrolling up during chat autoscroll (#646)
The chat area re-armed autoscroll whenever the user was within 100px of the bottom, so scrolling up during a streaming response fought the incoming content ticks and produced jitter.

Autoscroll now disengages on any upward scroll (direction-based, no distance threshold), re-engages when scrolled back within 2px of the bottom (tolerating sub-pixel rounding at fractional zoom levels, where the at-bottom residual can reach 1px), and ignores sub-1px upward movement so macOS elastic-bounce settling does not disengage it. Sending a message pins the view to the bottom even if the user had scrolled up to read earlier messages.
2026-07-20 13:45:12 -07:00
github-actions[bot] 87f6238338 chore: update clone traffic data [skip ci] 2026-07-20 08:53:13 +00:00
github-actions[bot] 452bcc38cf chore: update clone traffic data [skip ci] 2026-07-19 08:12:04 +00:00
goatoush b35a4c8113 fix(desktop): preserve active chat when switching models (#648)
Switching models from the command palette called createConversation() on every change, creating a persisted empty "New chat" entry and pulling the user out of their active conversation. Because updateLastAssistant writes the visible messages array without checking the active conversation, a mid-stream switch could also clobber the new chat's view with the old conversation's messages.

Remove the conversation-creation side effect. Model switching now preserves the active chat (matching the pull-completion and delete-fallback paths, which already switched silently); the next request uses the newly selected model with the current conversation context. Preloading, loading state, and logging are unchanged.
2026-07-18 12:51:38 -07:00
github-actions[bot] f001e3b0ca chore: update clone traffic data [skip ci] 2026-07-18 07:44:44 +00:00
4 changed files with 31 additions and 8 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"schemaVersion": 1,
"label": "Git Clones",
"message": "161,819",
"message": "166,579",
"color": "green",
"namedLogo": "git"
}
+6 -3
View File
@@ -1,6 +1,6 @@
{
"total_clones": 161819,
"last_updated": "2026-07-17T08:05:34Z",
"total_clones": 166579,
"last_updated": "2026-07-20T08:53:12Z",
"daily": {
"2026-03-27": 2189,
"2026-03-28": 1874,
@@ -113,6 +113,9 @@
"2026-07-13": 2102,
"2026-07-14": 2337,
"2026-07-15": 2362,
"2026-07-16": 2497
"2026-07-16": 2497,
"2026-07-17": 1773,
"2026-07-18": 1542,
"2026-07-19": 1445
}
}
+23 -2
View File
@@ -22,6 +22,8 @@ export function ChatArea() {
const navigate = useNavigate();
const listRef = useRef<HTMLDivElement>(null);
const shouldAutoScroll = useRef(true);
const wasStreaming = useRef(false);
const lastScrollTop = useRef(0);
// Check if any data sources are connected
const [hasConnectedSources, setHasConnectedSources] = useState<boolean | null>(null);
@@ -34,15 +36,34 @@ export function ChatArea() {
}, []);
useEffect(() => {
// Sending a message always pins the view to the bottom, even if the
// user had scrolled up to read earlier messages.
if (streamState.isStreaming && !wasStreaming.current) {
shouldAutoScroll.current = true;
}
wasStreaming.current = streamState.isStreaming;
if (shouldAutoScroll.current && listRef.current) {
listRef.current.scrollTop = listRef.current.scrollHeight;
}
}, [messages, streamState.content]);
}, [messages, streamState.content, streamState.isStreaming]);
const handleScroll = () => {
if (!listRef.current) return;
const { scrollTop, scrollHeight, clientHeight } = listRef.current;
shouldAutoScroll.current = scrollHeight - scrollTop - clientHeight < 100;
const distance = scrollHeight - scrollTop - clientHeight;
const scrolledUp = scrollTop < lastScrollTop.current;
lastScrollTop.current = scrollTop;
if (scrolledUp && distance >= 1) {
// Any upward scroll away from the bottom stops autoscroll immediately,
// so streaming content never fights the user (no jitter). Sub-1px
// upward movement (elastic bounce settling at the bottom) is ignored.
shouldAutoScroll.current = false;
} else if (!scrolledUp) {
// Re-engage when scrolled back to the bottom. < 2 rather than < 1:
// at fractional zoom levels the at-bottom residual can reach 1px,
// which would otherwise leave autoscroll permanently disengaged.
shouldAutoScroll.current = distance < 2;
}
};
const isEmpty = messages.length === 0 && !streamState.isStreaming;
+1 -2
View File
@@ -149,8 +149,7 @@ export function CommandPalette() {
setCommandPaletteOpen(false);
if (modelId !== previousModel) {
const { createConversation, setModelLoading, addLogEntry } = useAppStore.getState();
createConversation(modelId);
const { setModelLoading, addLogEntry } = useAppStore.getState();
setModelLoading(true);
addLogEntry({ timestamp: Date.now(), level: 'info', category: 'model', message: `Switching to ${modelId}...` });
try {