Skip to content

Latest commit

 

History

History
204 lines (152 loc) · 7.68 KB

File metadata and controls

204 lines (152 loc) · 7.68 KB

Messages Tab Fix Documentation

Overview

This document details the fix implemented for the Messages tab in Crystal's session window. The Messages tab was intended to show JSON output from Claude Code for debugging purposes but was broken and not displaying any data.

Problem Description

The Messages tab in the session view was supposed to display JSON messages from Claude Code instances for debugging, but it was showing "No messages yet" even when sessions had active Claude Code conversations. The issue was that there was no mechanism to load and populate the JSON messages data.

Root Cause Analysis

  1. Missing Data Flow: The JsonMessageView component expected activeSession.jsonMessages to be populated, but this array was always empty
  2. No Loading Mechanism: There was no IPC handler or frontend logic to fetch JSON messages separately from regular output
  3. Data Transformation Issue: The existing sessions:get-output handler was transforming JSON messages to formatted stdout for the Output tab, losing the original JSON structure

Solution Architecture

The fix implements a complete end-to-end data flow:

Frontend (Messages Tab) → API Layer → IPC Handler → Session Manager → Database

Key Components

  1. New IPC Handler: sessions:get-json-messages
  2. Frontend API Method: API.sessions.getJsonMessages()
  3. Session Store Method: setSessionJsonMessages()
  4. Auto-loading Logic: Effect hook that loads JSON messages when switching to Messages view

Implementation Details

1. Backend IPC Handler (main/src/ipc/session.ts)

ipcMain.handle('sessions:get-json-messages', async (_event, sessionId: string) => {
  try {
    const outputs = await sessionManager.getSessionOutputs(sessionId);
    
    // Filter to only JSON messages and include timestamp
    const jsonMessages = outputs
      .filter(output => output.type === 'json')
      .map(output => ({
        ...output.data,
        timestamp: output.timestamp.toISOString()
      }));
    
    return { success: true, data: jsonMessages };
  } catch (error) {
    return { success: false, error: 'Failed to get JSON messages' };
  }
});

Purpose: Extracts and returns only JSON-type outputs from session data, preserving the original JSON structure with timestamps.

2. Preload Script Update (main/src/preload.ts)

getJsonMessages: (sessionId: string): Promise<IPCResponse> => 
  ipcRenderer.invoke('sessions:get-json-messages', sessionId),

Purpose: Exposes the new IPC handler to the frontend through the secure preload script.

3. Frontend API Layer (frontend/src/utils/api.ts)

async getJsonMessages(sessionId: string) {
  if (!isElectron()) throw new Error('Electron API not available');
  return window.electronAPI.sessions.getJsonMessages(sessionId);
}

Purpose: Provides a consistent API interface with error handling for the frontend to use.

4. Session Store Enhancement (frontend/src/stores/sessionStore.ts)

setSessionJsonMessages: (sessionId, jsonMessages) => set((state) => {
  // Update both regular sessions and activeMainRepoSession
  const updatedSessions = state.sessions.map(session => {
    if (session.id === sessionId) {
      return { ...session, jsonMessages };
    }
    return session;
  });
  
  let updatedActiveMainRepoSession = state.activeMainRepoSession;
  if (state.activeMainRepoSession?.id === sessionId) {
    updatedActiveMainRepoSession = { ...state.activeMainRepoSession, jsonMessages };
  }
  
  return { ...state, sessions: updatedSessions, activeMainRepoSession: updatedActiveMainRepoSession };
})

Purpose: Updates session state with JSON messages while maintaining consistency between regular sessions and main repo sessions.

5. Session View Hook (frontend/src/hooks/useSessionView.ts)

// Load JSON messages for the Messages tab
const loadJsonMessages = useCallback(async (sessionId: string) => {
  try {
    const response = await API.sessions.getJsonMessages(sessionId);
    if (response.success) {
      const jsonMessages = response.data || [];
      useSessionStore.getState().setSessionJsonMessages(sessionId, jsonMessages);
    }
  } catch (error) {
    console.error(`Error loading JSON messages:`, error);
  }
}, []);

// Load JSON messages when switching to messages view
useEffect(() => {
  if (!activeSession || viewMode !== 'messages') return;
  loadJsonMessages(activeSession.id);
}, [activeSession?.id, viewMode, loadJsonMessages]);

Purpose: Automatically loads JSON messages when the user switches to the Messages tab, ensuring data is available when needed.

6. TypeScript Definitions (frontend/src/types/electron.d.ts)

interface ElectronAPI {
  sessions: {
    // ... other methods
    getJsonMessages: (sessionId: string) => Promise<IPCResponse>;
    // ... other methods
  };
}

Purpose: Provides type safety for the new IPC method in TypeScript.

Data Flow

  1. User Action: User clicks on "Messages" tab in session view
  2. View Mode Change: viewMode state changes to 'messages'
  3. Effect Trigger: useEffect hook detects the view mode change
  4. API Call: loadJsonMessages() function calls API.sessions.getJsonMessages()
  5. IPC Communication: Frontend calls the sessions:get-json-messages IPC handler
  6. Data Retrieval: Backend fetches session outputs and filters for JSON messages
  7. Response: Filtered JSON messages returned to frontend
  8. State Update: setSessionJsonMessages() updates the session store
  9. UI Update: JsonMessageView component re-renders with the new data

Message Structure

JSON messages are formatted with the following structure:

interface JsonMessage {
  type: string;           // e.g., 'system', 'user', 'assistant'
  subtype?: string;       // e.g., 'init', 'result'
  content?: string;       // Message content
  data?: any;            // Additional structured data
  timestamp: string;     // ISO timestamp
  [key: string]: any;    // Other fields from Claude Code
}

Benefits

  1. Debugging Capability: Developers can now inspect raw JSON messages from Claude Code
  2. Performance: JSON messages are only loaded when the Messages tab is viewed
  3. Real-time: Messages are loaded fresh each time the tab is accessed
  4. Type Safety: Full TypeScript support for the new functionality
  5. Consistency: Follows existing patterns in the codebase

Testing

The fix was tested by:

  1. Type Checking: pnpm typecheck passes without errors
  2. Compilation: pnpm build:main succeeds
  3. Runtime Testing: Application starts successfully in development mode
  4. Integration: IPC handlers are properly registered and accessible

Files Modified

  • main/src/ipc/session.ts - Added new IPC handler
  • main/src/preload.ts - Exposed new method to frontend
  • frontend/src/utils/api.ts - Added API method
  • frontend/src/stores/sessionStore.ts - Added state management method
  • frontend/src/hooks/useSessionView.ts - Added loading logic
  • frontend/src/types/electron.d.ts - Added TypeScript definitions

Future Enhancements

Potential improvements could include:

  1. Caching: Cache JSON messages to avoid repeated API calls
  2. Real-time Updates: Listen for new JSON messages and update the tab live
  3. Filtering: Add filtering/search capabilities within the Messages tab
  4. Export: Allow exporting JSON messages for external debugging tools

Conclusion

The Messages tab fix provides a complete solution for debugging Claude Code sessions by making the raw JSON messages accessible through a clean, type-safe interface. The implementation follows Crystal's architectural patterns and maintains consistency with existing code.