- Display Goose dot avatar as main avatar for all assistant messages
- Add small user badge in bottom-right corner for collaborator messages
- Badge shows user's avatar if available, or initial letter on colored background
- Header shows 'Goose (username)' for collaborator messages
- This clearly indicates the message is from another user's Goose instance
- Check targetRoomId (not sessionId) to determine if in Matrix room context
- sessionId can be a backend session ID even when in a Matrix room
- Use isInMatrixRoomContext based on targetRoomId starting with '!'
- This correctly allows messages from other users in the same Matrix room
- Changed condition from (isMatrixRoom ? true : isSessionMatch) to (isMatrixRoom || isSessionMatch)
- For Matrix rooms, only room IDs need to match (not session IDs)
- Each user has their own backend session ID, so checking session match was incorrectly blocking messages
- This allows messages from other users in the same Matrix room to display properly
- Log full sender details (userId, displayName, avatarUrl) when processing messages
- Log complete message object being sent to onMessageSync
- This will help debug if sender info is properly flowing through to display
- Moved processedMessages.current.add() to AFTER onMessageSync call
- Prevents messages from being marked as processed when they fail to display
- Fixes issue where messages were skipped due to premature deduplication
- Added sentToMatrixRef to track sent messages and prevent duplicates
- Only send messages when chatState is Idle (streaming complete)
- Format messages as goose-session-message: for proper parsing by receivers
- Prevents 429 Too Many Requests errors from sending during streaming
Check message_count from backend session to determine if chat
has existing messages. Only show loading indicator if messages
need to be loaded. New chats (message_count = 0) show empty
state immediately with popular topics.
This prevents showing a loading spinner for brand new Matrix
chats that have no message history to load.
When switching to an existing Matrix tab that's still loading,
ensure the backend session is created and loading state is cleared.
Previously, clicking 'Open Chat' would switch to an existing tab
but leave it in a loading/blank state if the async session creation
hadn't completed yet.
- Fix Goose assistant response spam: suppress role='assistant' notifications
- Fix stale tab state: use fresh shouldSuppressNotification calls
- Remove buggy legacy /pair suppression logic that checked sender ID
- Fix 'Open Chat' to check existing tabs before navigating
- Add Matrix room ID display in SessionListView
- Optimize MatrixSessionService to defer heavy loading until needed
- Add loading state for Matrix chat initialization
- Improve loading indicator visibility in BaseChat2
Fixes notification spam, incorrect suppression, and blank chat states.
- Updated TabbedChatContainer to show pending invites in all tabs (not just Matrix tabs)
- Added getPendingInvitedRooms() method to MatrixService to fetch invites directly from Matrix server
- Exposed getPendingInvitedRooms() in MatrixContext for easy component access
- Updated PendingInvitesInHistory to fetch from Matrix API and merge with local storage
- Matrix server data now takes precedence over local storage for accuracy
Fixes issue where pending collaboration invites weren't showing in regular chat tabs
- Added detailed logging to useSessionSharing Matrix listener setup check
- Added critical debug logging to ChatInput before calling useSessionSharing
- Enhanced Matrix room detection logging with timestamps
- This will help identify why regular chats are still receiving Matrix messages
The issue appears to be that regular chat sessions might be getting
initialRoomId values when they shouldn't, causing Matrix listeners
to be set up for non-Matrix chats.
Key Changes:
- Fixed TabContext sanitization logic for hybrid approach
- Matrix tabs now only require matrixRoomId (not matrix_ sessionId format)
- Regular tabs are sanitized to remove Matrix properties (matrixRoomId, matrixRecipientId)
- Preserved actual backend session IDs during sanitization
- Applied fix to both initial load and restoreTabState functions
This prevents new chat tabs from inheriting Matrix properties after app reload,
which was causing Matrix messages to leak into regular chats. The hybrid approach
uses real backend session IDs while storing Matrix context in tab properties.
Key Changes:
- Matrix tabs now use actual backend session IDs, not matrix_ format
- Matrix context comes from tab properties (matrixRoomId, matrixRecipientId, type: 'matrix')
- SessionMappingService creates proper Matrix ↔ Backend session mappings
- ChatInput detects Matrix rooms via tab properties, not sessionId format
- useSessionSharing always gets real backend session ID for API calls
- Matrix room ID passed separately via initialRoomId parameter
- TabContext openMatrixChat creates backend sessions and proper mappings
- Matrix session restoration works via backend session metadata
This ensures Goose backend always gets proper session IDs while maintaining
Matrix functionality through the mapping service and tab properties.
- Store Matrix metadata in backend session descriptions during creation
- Add Matrix metadata extraction and restoration functionality to SessionMappingService
- Update TabContext to restore Matrix properties from backend sessions on load
- Ensure Matrix tabs maintain correct sessionId format and properties after reload
- Add comprehensive Matrix session detection and restoration logic
- Pass matrixRecipientId when creating Matrix session mappings
This fixes the issue where Matrix sessions lose their Matrix context
and become regular chats after app reload. Now Matrix sessions are
properly restored with their room ID, recipient ID, and Matrix properties.
- Add tab state sanitization during localStorage restoration
- Validate Matrix tab properties (must have matrixRoomId and matrix_ sessionId)
- Strip Matrix properties from regular chat tabs during restore
- Apply sanitization to both initial load and manual restoreTabState
- Generate new session IDs for corrupted tabs to prevent conflicts
This fixes the bug where reloading the app would cause regular 'New Chat'
tabs to inherit Matrix properties, leading to Matrix messages appearing
in non-Matrix chats.
- Add strict session isolation in useSessionSharing
- Only set up Matrix listeners for sessions that have a Matrix room ID
- Prevent regular chats from receiving Matrix messages
- Fix message filtering logic that was too permissive
This resolves the critical bug where Matrix messages were appearing
in non-Matrix chat sessions due to inadequate session isolation.
- Implement tab persistence to prevent component unmounting when switching tabs
- Remove key prop from BaseChat2 to maintain component state across tab switches
- Add conditional rendering for all tabs (visible/hidden instead of mount/unmount)
- Fix Matrix session ID consistency in TabContext.openMatrixChat
- Enhance ChatInput Matrix room detection with TabContext integration
- Add isTabActive prop to BaseChat2 for future optimizations
- Ensure Matrix connections persist when navigating between tabs
- Fix real-time message display in Matrix rooms when switching back to tabs
- Resolve notification suppression issues for multichat sessions
- Add extensive debugging logs for Matrix room detection and tab state
Key fixes:
* Matrix tabs no longer 'unmount' when switching away and back
* Real-time messages appear correctly in background Matrix tabs
* Notification suppression now works correctly with tab context
* Session ID format consistency (matrix_!roomId) maintained
* useSessionSharing connections persist across tab switches
This resolves the core issue where Matrix tabs would lose their state and
stop syncing messages after navigating to different tabs.
- Add function existence checks for showSaveDialog and showOpenDialog
- Provide clear error messages when IPC handlers are not available
- Add console logging for debugging dialog calls
- Graceful fallback when Electron app needs restart for new IPC handlers
- Add showSaveDialog and showOpenDialog to ElectronAPI interface
- Implement IPC calls for show-save-dialog and show-open-dialog handlers
- Fix TypeError: window.electron.showSaveDialog is not a function
- Enable Save As functionality in DocumentEditor persistence system
- Create complete persistence system with DocumentPersistence utility class
- Add usePersistence React hook for state management
- Enhance DocumentEditor with auto-save (30s intervals), Save As dialogs
- Support multiple file formats (HTML, Markdown, Plain Text) with smart conversion
- Add visual indicators for saving states (saving, auto-saving, unsaved)
- Implement backup/recovery system with localStorage session persistence
- Add keyboard shortcuts (Ctrl+S save, Ctrl+Shift+S save as)
- Create comprehensive error handling and user feedback
- Add IPC handlers for save/open dialogs in main.ts
Features:
- Auto-save every 30 seconds with visual feedback
- Save As with file type detection and format conversion
- Session persistence - drafts survive tab switches/app restarts
- Backup creation and restoration capabilities
- Multi-format support with intelligent content conversion
- Type-safe implementation with full TypeScript interfaces
- Fixed sessionId passing from BaseChat2 to useComments hook
- Moved comment loading to useState initializer for immediate loading
- Resolved useEffect dependency issues that prevented comment loading
- Comments now persist correctly across tab switches and page refreshes
- Cleaned up all debugging logs for production readiness
- Verified full persistence functionality working
The Google Docs-style commenting system is now complete and production-ready!
- Create CommentStorage utility class for localStorage management
- Comments now persist between app sessions with 90-day retention
- Automatic loading/saving tied to chat session IDs
- Updated useComments hook to integrate with storage system
- Session-scoped storage prevents comment cross-contamination
- Efficient JSON serialization with timestamp tracking
- Zero performance impact with background load/save operations
Comments now survive app restarts and session switching!
- Add text selection and commenting functionality to chat messages
- Comments appear in right margin with floating overlay design
- Support for threaded comment conversations with replies
- Visual text highlighting for commented sections
- Clickable highlights that focus corresponding comments
- Persistent comment storage with unique message identification
- Maintain tool call chaining functionality
- Components: CommentableMarkdown, MessageComments, CommentCard, CommentInput
- Custom useComments hook for state management
- TypeScript interfaces for comment data structures
- Add MultiPanelTabSidecar component for tab-level multi-panel layouts
- Implement drag-and-drop panel reordering with visual drop zones
- Support multiple layout modes: single, columns, rows, grid, custom
- Add drag handles to panel headers for intuitive rearrangement
- Auto-detect layout based on drop position (left/right for columns, top/bottom for rows, center for grid)
- Fix WebBrowser screenshot interference by disabling backdrop functionality
- Remove border strokes from all sidecar components for clean appearance
- Fix drop shadow clipping issues in ResizableSplitter
- Ensure proper rounded edges on all sidecar panels
- Maintain backward compatibility with single-panel TabSidecar
- Add comprehensive documentation in MULTI_PANEL_DEMO.md
Features:
- Drag panels by grip icon in headers
- Visual drop zones with color-coded feedback
- Smooth animations with Framer Motion
- Individual sidecar styling (no wrapper containers)
- Support for web, file, diff, editor, and localhost content types
- Automatic layout switching based on panel count
- Clean, modern UI without borders or clipped shadows
- Created WebBrowser component with proper browser controls
- Added address bar with URL input and search functionality
- Implemented navigation buttons (back, forward, refresh, home)
- Added loading states and status bar
- Uses Electron's native BrowserView for real web browsing instead of iframe
- Includes proper URL validation and search query handling
- Integrated into TabSidecar for web content type
- Maintains TipTap document editor for rich text editing
- Full browser functionality with navigation state polling
- Replaced simple textarea with full-featured DocumentEditor component
- Added RichDocumentEditor wrapper for sidecar integration
- Document editor now includes:
- Rich text formatting (bold, italic, underline, strikethrough)
- Headings (H1, H2, H3)
- Lists (bullet, ordered)
- Code blocks and blockquotes
- Undo/redo functionality
- Auto-save with Ctrl+S
- File format support (HTML, Markdown, plain text)
- Character and word count
- Unsaved changes indicator
- Maintains all original styling and features from the global document editor
- Added showWebViewer function to TabContext for web browsing
- Extended TabSidecarView interface to support 'web' content type
- Updated TabSidecar component to render web content with Monitor icon
- Fixed TabSidecarInvoker to use showWebViewer instead of showLocalhostViewer for web browsing
- All sidecar types (diff, localhost, web, file, editor) now properly supported
- Resolves 'Unknown content type' error for document editor and web viewer
- Changed TabSidecarView interface to use contentType/iconType strings instead of JSX elements
- Updated TabSidecar component to render components based on type rather than storing JSX
- Added component renderers for diff, localhost, file, and editor views
- Removed JSX elements from TabContext state to fix localStorage serialization
- This resolves the 'Objects are not valid as a React child' error
- Added TabSidecar component with proper drop shadow, rounded corners, and border styling
- Implemented ResizableSplitter for sidecar resizing functionality (30-80% width range)
- Added view mode toggle for diff viewers (split/unified)
- Extended TabContext with sidecar management functions (showDiffViewer, showLocalhostViewer, etc.)
- Created TabSidecarInvoker to replace global SidecarInvoker
- Updated TabbedChatContainer to use ResizableSplitter with floatingRight styling
- Removed global sidecar dependencies from BaseChat2
- Each tab now maintains independent sidecar state and content
- Changed popover zone from flex layout to absolute positioning
- Zone now positioned at -top-24 (96px above chat input)
- No longer affects the layout flow or pushes bottom controls up
- Popover still renders in dedicated space above chat input
- Removed popover zone from TabbedChatContainer (was too high up)
- Added popover zone directly above chat input in ChatInput component
- Zone positioned right before the chat input container for proper placement
- Height set to h-24 (96px) with same styling and portal target
- Popover should now appear directly above the input area where user is typing
- Changed popover zone height from h-16 to h-32 for more space
- Position popover at bottom of zone (bottom: 8px) instead of center
- Popover now appears closer to chat input while still being above it
- Only center horizontally, not vertically, for better UX
- Added mention-popover-zone div in TabbedChatContainer above the chat area
- Zone has fixed height (h-16) and high z-index (z-50) for proper layering
- Updated EnhancedMentionPopover to use React portal to render in this zone
- Popover now renders absolutely positioned within the dedicated zone
- Should appear above chat content in the reserved space
- Changed from percentage-based to fixed pixel positioning
- Popover now appears at top: 50px from the very top of screen
- Should be clearly visible at the top of the window
- Horizontally centered for easy access
- Changed from relative positioning to fixed center positioning
- Popover now appears at 50% left, 20% top with horizontal centering
- Should be clearly visible in upper portion of screen
- No longer dependent on chat input position calculations
- Changed positioning logic to use chatInputRef instead of bottomControlsRef
- Popover now positioned relative to the chat input container
- Should appear above the input area instead of at bottom of screen
- Updated position calculations to use chatInputRect.left and chatInputRect.top