diff --git a/apps/frontend/src/main/insights-service.ts b/apps/frontend/src/main/insights-service.ts index 59fe3cb2..43f47751 100644 --- a/apps/frontend/src/main/insights-service.ts +++ b/apps/frontend/src/main/insights-service.ts @@ -182,6 +182,9 @@ export class InsightsService extends EventEmitter { session.messages.push(assistantMessage); session.updatedAt = new Date(); this.sessionManager.saveSession(projectPath, session); + + // Emit session-updated event for real-time UI updates + this.emit('session-updated', projectId, session); } catch (error) { // Error already emitted by executor console.error('[InsightsService] Error executing insights:', error); diff --git a/apps/frontend/src/main/ipc-handlers/insights-handlers.ts b/apps/frontend/src/main/ipc-handlers/insights-handlers.ts index 4f1336cc..d050a6ed 100644 --- a/apps/frontend/src/main/ipc-handlers/insights-handlers.ts +++ b/apps/frontend/src/main/ipc-handlers/insights-handlers.ts @@ -371,4 +371,9 @@ export function registerInsightsHandlers(getMainWindow: () => BrowserWindow | nu insightsService.on("sdk-rate-limit", (rateLimitInfo: unknown) => { safeSendToRenderer(getMainWindow, IPC_CHANNELS.CLAUDE_SDK_RATE_LIMIT, rateLimitInfo); }); + + // Forward session-updated events to renderer for real-time UI updates + insightsService.on("session-updated", (projectId: string, session: unknown) => { + safeSendToRenderer(getMainWindow, IPC_CHANNELS.INSIGHTS_SESSION_UPDATED, projectId, session); + }); } diff --git a/apps/frontend/src/preload/api/modules/insights-api.ts b/apps/frontend/src/preload/api/modules/insights-api.ts index 0dc258f8..f65d60c2 100644 --- a/apps/frontend/src/preload/api/modules/insights-api.ts +++ b/apps/frontend/src/preload/api/modules/insights-api.ts @@ -42,6 +42,9 @@ export interface InsightsAPI { onInsightsError: ( callback: (projectId: string, error: string) => void ) => IpcListenerCleanup; + onInsightsSessionUpdated: ( + callback: (projectId: string, session: InsightsSession) => void + ) => IpcListenerCleanup; } /** @@ -98,5 +101,10 @@ export const createInsightsAPI = (): InsightsAPI => ({ onInsightsError: ( callback: (projectId: string, error: string) => void ): IpcListenerCleanup => - createIpcListener(IPC_CHANNELS.INSIGHTS_ERROR, callback) + createIpcListener(IPC_CHANNELS.INSIGHTS_ERROR, callback), + + onInsightsSessionUpdated: ( + callback: (projectId: string, session: InsightsSession) => void + ): IpcListenerCleanup => + createIpcListener(IPC_CHANNELS.INSIGHTS_SESSION_UPDATED, callback) }); diff --git a/apps/frontend/src/renderer/lib/mocks/insights-mock.ts b/apps/frontend/src/renderer/lib/mocks/insights-mock.ts index 09c6d923..8f619f0c 100644 --- a/apps/frontend/src/renderer/lib/mocks/insights-mock.ts +++ b/apps/frontend/src/renderer/lib/mocks/insights-mock.ts @@ -107,5 +107,6 @@ export const insightsMock = { onInsightsStreamChunk: () => () => {}, onInsightsStatus: () => () => {}, - onInsightsError: () => () => {} + onInsightsError: () => () => {}, + onInsightsSessionUpdated: () => () => {} }; diff --git a/apps/frontend/src/renderer/stores/insights-store.ts b/apps/frontend/src/renderer/stores/insights-store.ts index 09ee971c..05df0e90 100644 --- a/apps/frontend/src/renderer/stores/insights-store.ts +++ b/apps/frontend/src/renderer/stores/insights-store.ts @@ -415,10 +415,26 @@ export function setupInsightsListeners(): () => void { }); }); + // Listen for session updates (e.g., after assistant message saved with auto-generated title) + const unsubSessionUpdated = window.electronAPI.onInsightsSessionUpdated( + (_projectId, session: InsightsSession) => { + // Update current session if it matches + const currentSession = store().session; + if (currentSession?.id === session.id) { + store().setSession(session); + } + // Also refresh sessions list for sidebar + loadInsightsSessions(session.projectId).catch((err) => { + console.error('Failed to refresh sessions list after update:', err); + }); + } + ); + // Return cleanup function return () => { unsubStreamChunk(); unsubStatus(); unsubError(); + unsubSessionUpdated(); }; } diff --git a/apps/frontend/src/shared/constants/ipc.ts b/apps/frontend/src/shared/constants/ipc.ts index 9f4a9151..c8bc37c5 100644 --- a/apps/frontend/src/shared/constants/ipc.ts +++ b/apps/frontend/src/shared/constants/ipc.ts @@ -487,6 +487,7 @@ export const IPC_CHANNELS = { INSIGHTS_STREAM_CHUNK: 'insights:streamChunk', INSIGHTS_STATUS: 'insights:status', INSIGHTS_ERROR: 'insights:error', + INSIGHTS_SESSION_UPDATED: 'insights:sessionUpdated', // Event: session updated (main -> renderer) // File explorer operations FILE_EXPLORER_LIST: 'fileExplorer:list', diff --git a/apps/frontend/src/shared/types/ipc.ts b/apps/frontend/src/shared/types/ipc.ts index 2a8af16c..87663c64 100644 --- a/apps/frontend/src/shared/types/ipc.ts +++ b/apps/frontend/src/shared/types/ipc.ts @@ -747,6 +747,9 @@ export interface ElectronAPI { onInsightsError: ( callback: (projectId: string, error: string) => void ) => () => void; + onInsightsSessionUpdated: ( + callback: (projectId: string, session: InsightsSession) => void + ) => () => void; // Task logs operations getTaskLogs: (projectId: string, specId: string) => Promise>;