diff --git a/apps/server/src/services/file_index.ts b/apps/server/src/services/file_index.ts index fb3ef38..d3afcc9 100644 --- a/apps/server/src/services/file_index.ts +++ b/apps/server/src/services/file_index.ts @@ -33,6 +33,7 @@ async function snapMtimes(root: string): Promise { const rootStat = await fs.stat(root); let gitHead: number | null = null; let gitIndex: number | null = null; + // best-effort; ignore failure because the project may not be a git repo try { gitHead = (await fs.stat(path.join(root, '.git', 'HEAD'))).mtimeMs; } catch {} try { gitIndex = (await fs.stat(path.join(root, '.git', 'index'))).mtimeMs; } catch {} return { root: rootStat.mtimeMs, gitHead, gitIndex }; diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index a408d7d..e488e28 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -18,7 +18,10 @@ function SessionRightRail() { function RightRailForSession({ sessionId }: { sessionId: string }) { const [projectId, setProjectId] = useState(null); useEffect(() => { - api.sessions.get(sessionId).then((s) => setProjectId(s.project_id)).catch(() => {}); + api.sessions + .get(sessionId) + .then((s) => setProjectId(s.project_id)) + .catch((err) => console.warn('RightRail: failed to fetch session', err)); }, [sessionId]); if (!projectId) return null; return ; diff --git a/apps/web/src/api/types.ts b/apps/web/src/api/types.ts index f56b379..4000aeb 100644 --- a/apps/web/src/api/types.ts +++ b/apps/web/src/api/types.ts @@ -191,6 +191,5 @@ export type WsFrame = finished_at?: string | null; } | { type: 'messages_deleted'; message_ids: string[]; chat_id?: string } - | { type: 'session_renamed'; session_id: string; name: string; chat_id?: string } | { type: 'chat_renamed'; chat_id: string; name: string } | { type: 'error'; message_id?: string; chat_id?: string; error: string }; diff --git a/apps/web/src/components/ProjectSidebar.tsx b/apps/web/src/components/ProjectSidebar.tsx index 92b5161..8c9159f 100644 --- a/apps/web/src/components/ProjectSidebar.tsx +++ b/apps/web/src/components/ProjectSidebar.tsx @@ -19,7 +19,6 @@ import { } from '@/components/ui/dialog'; import { AddProjectModal } from './AddProjectModal'; import { api } from '@/api/client'; -import { sessionEvents } from '@/hooks/sessionEvents'; import { useSidebar } from '@/hooks/useSidebar'; import type { SidebarProject } from '@/api/types'; import { giteaUrlFor } from '@/lib/projectUrls'; @@ -186,7 +185,8 @@ export function ProjectSidebar() { if (!trimmed) return; try { await api.sessions.update(sessionId, { name: trimmed }); - sessionEvents.emit({ type: 'session_renamed', session_id: sessionId, name: trimmed }); + // Server publishes session_renamed via broker.publishUser; useUserEvents + // forwards onto the bus. No local emit needed. } catch (err) { toast.error(err instanceof Error ? err.message : 'failed to rename session'); } diff --git a/apps/web/src/components/RightRail.tsx b/apps/web/src/components/RightRail.tsx index 3a8202d..ecdf026 100644 --- a/apps/web/src/components/RightRail.tsx +++ b/apps/web/src/components/RightRail.tsx @@ -35,6 +35,7 @@ export function RightRail({ projectId }: Props) { const [viewerFile, setViewerFile] = useState<{ path: string; content: string } | null>(null); useEffect(() => { + // best-effort; ignore failure because localStorage may be unavailable (quota, private mode) try { localStorage.setItem(`${STORAGE_KEY}.open`, String(open)); } catch {} }, [open]); diff --git a/apps/web/src/hooks/useSessionStream.ts b/apps/web/src/hooks/useSessionStream.ts index bda8b7f..78d9afd 100644 --- a/apps/web/src/hooks/useSessionStream.ts +++ b/apps/web/src/hooks/useSessionStream.ts @@ -2,6 +2,10 @@ import { useEffect, useRef, useState } from 'react'; import type { Message, WsFrame } from '@/api/types'; import { sessionEvents } from './sessionEvents'; +// session_renamed frame removed from WsFrame — it was declared but never +// published on the per-session WS channel (server publishes via broker.publishUser +// since v1.4). chat_renamed remains; auto_name.ts publishes it on session WS. + interface State { messages: Message[]; connected: boolean; @@ -118,14 +122,6 @@ function applyFrame(state: State, frame: WsFrame): State { messages: state.messages.filter((m) => !removeSet.has(m.id)), }; } - case 'session_renamed': { - sessionEvents.emit({ - type: 'session_renamed', - session_id: frame.session_id, - name: frame.name, - }); - return state; - } case 'chat_renamed': { sessionEvents.emit({ type: 'chat_updated', diff --git a/apps/web/src/hooks/useUserEvents.ts b/apps/web/src/hooks/useUserEvents.ts index 982626f..2fd5ae4 100644 --- a/apps/web/src/hooks/useUserEvents.ts +++ b/apps/web/src/hooks/useUserEvents.ts @@ -40,7 +40,8 @@ export function useUserEvents(): void { }; ws.onerror = () => { - // close handler will trigger reconnect + // close handler will trigger reconnect; best-effort, ignore failure + // because the socket may already be closing try { ws?.close(); } catch {} }; }; @@ -50,6 +51,7 @@ export function useUserEvents(): void { return () => { unmounted = true; if (reconnectTimer) clearTimeout(reconnectTimer); + // best-effort cleanup; ignore failure because the socket may already be closed if (ws) try { ws.close(); } catch {} }; }, []); diff --git a/apps/web/src/pages/Session.tsx b/apps/web/src/pages/Session.tsx index ddf8390..5d4f82b 100644 --- a/apps/web/src/pages/Session.tsx +++ b/apps/web/src/pages/Session.tsx @@ -38,9 +38,9 @@ export function Session() { if (cancelled) return; const p = projects.find((x) => x.id === s.project_id); if (p) setProject(p); - }).catch(() => {}); + }).catch((err) => console.warn('Session: failed to load project for breadcrumb', err)); }) - .catch(() => {}); + .catch((err) => console.warn('Session: failed to fetch session', err)); return () => { cancelled = true; };