diff --git a/CHANGELOG.md b/CHANGELOG.md index be2bca13..fc87a5ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.5] - 2026-06-18 + +### Fixed + +- 🔌 **Socket events no longer missed on slow connections.** Components that listen for real-time updates (sidebar chat list, chat panel, notifications) previously relied on polling to check if the socket was ready. If the socket connected slowly or reconnected, events could be silently dropped. Listeners are now registered centrally and automatically re-attached whenever the connection comes back. +- 🔄 **WebSocket connection is more resilient.** The socket now prefers a direct WebSocket connection and falls back to polling only when needed, reducing latency and improving reliability on unstable networks. + +### Changed + +- 📂 **Git status refreshes are debounced.** Rapid filesystem changes (like saving multiple files at once) no longer trigger a flood of git status checks. Updates are batched with a short delay so the UI stays responsive. + ## [0.5.4] - 2026-06-18 ### Added diff --git a/cptr/frontend/src/lib/components/Sidebar.svelte b/cptr/frontend/src/lib/components/Sidebar.svelte index 07226343..90aee35b 100644 --- a/cptr/frontend/src/lib/components/Sidebar.svelte +++ b/cptr/frontend/src/lib/components/Sidebar.svelte @@ -10,7 +10,7 @@ sidebarWidth, appVersion, showChangelog, - showSearch, + showSearch } from '$lib/stores'; import Sortable from 'sortablejs'; import Icon from './Icon.svelte'; @@ -41,6 +41,7 @@ let menuButtonEl: HTMLButtonElement | undefined = $state(); let wsListEl: HTMLDivElement | undefined = $state(); let sortable: Sortable | null = null; + let unbindSocketListener: (() => void) | null = null; // ── Per-workspace chat history ────────────────────────────── let expandedWorkspaces = $state>(new Set()); @@ -119,7 +120,13 @@ // Socket listener for chat events — invalidate cache when chats update const seenChatIds = new Set(); - function handleChatEvent(data: { chat_id: string; done?: boolean; title?: string; delta?: string; workspace?: string }) { + function handleChatEvent(data: { + chat_id: string; + done?: boolean; + title?: string; + delta?: string; + workspace?: string; + }) { // Re-fetch on done, title update, or first event of a new chat const isNew = !seenChatIds.has(data.chat_id); seenChatIds.add(data.chat_id); @@ -237,23 +244,13 @@ } // Bind socket listener for chat cache invalidation - const tryBind = () => { - const socket = socketStore.getSocket(); - if (!socket) { - setTimeout(tryBind, 200); - return; - } - socket.on('events:chat', handleChatEvent); - }; - tryBind(); + unbindSocketListener = socketStore.on('events:chat', handleChatEvent); }); onDestroy(() => { sortable?.destroy(); - const socket = socketStore.getSocket(); - if (socket) { - socket.off('events:chat', handleChatEvent); - } + unbindSocketListener?.(); + unbindSocketListener = null; }); @@ -307,26 +304,31 @@ {$t('search.search')} - + {#if $chatEnabled} - + {/if} @@ -349,10 +351,12 @@ {@const chats = wsChatsCache.get(ws.path)} {@const isLoading = wsChatsLoading.has(ws.path)}
- - -