* feat: add conversations, desktop (Tauri), and offline sync Major new features: - conversations module: Slack-like channels, threads, reactions, pins - Tauri desktop app with local SQLite for offline-first operation - Hybrid logical clock sync engine with conflict resolution - DB provider abstraction (D1/Tauri/memory) with React context Conversations: - Text/voice/announcement channels with categories - Message threads, reactions, attachments, pinning - Real-time presence and typing indicators - Full-text search across messages Desktop (Tauri): - Local SQLite database with sync to cloud D1 - Offline mutation queue with automatic replay - Window management and keyboard shortcuts - Desktop shell with offline banner Sync infrastructure: - Vector clock implementation for causality tracking - Last-write-wins with semantic conflict resolution - Delta sync via checkpoints for bandwidth efficiency - Comprehensive test coverage Also adds e2e test setup with Playwright and CI workflows for desktop releases. * fix(tests): sync engine test schema and checkpoint logic - Add missing process_after column and sync_tombstone table to test schemas - Fix checkpoint update to save cursor even when records array is empty - Revert claude-code-review.yml workflow changes to match main --------- Co-authored-by: Nicholai <nicholaivogelfilms@gmail.com>
6.8 KiB
Conversations Module
A Slack-like messaging system for project teams. This module provides real-time chat with threads, reactions, and presence tracking.
What's Built
The conversations module is roughly 80% complete. The core messaging experience works end-to-end: channels, threads, search, and presence. What's missing is the periphery—attachments, voice, and some UI polish.
Working Features
Channels and Organization
- Text and announcement channels (public or private)
- Channel categories that collapse in the sidebar
- Join/leave public channels
- Channel membership with roles (owner, moderator, member)
Messaging
- Send messages with full markdown support (bold, italic, code, lists, links)
- Edit and soft-delete messages
- Threaded replies in a resizable side panel
- Pin important messages
- Message search with filters (by channel, user, date range)
Real-Time Updates
- Polling-based message updates (2.5s when visible, 10s when hidden)
- Typing indicators with 5-second timeout
- User presence (online, idle, do-not-disturb, offline)
- Automatic idle detection after 5 minutes of inactivity
Database and Performance
- 10 tables in
schema-conversations.ts - Indexed for production workloads (9 indexes added for common queries)
- Input validation (4000 char messages, 100 char status, emoji validation)
- LIKE query escaping to prevent pattern injection
What's Missing
The gaps fall into three categories: schema without implementation, UI not connected, and features not started.
Schema exists, no implementation:
| Feature | Status |
|---|---|
| Message attachments | Table defined, no upload/download actions |
| Voice channels | Type in schema, stub component only |
| Announcement channels | Type exists, no posting restrictions |
| Notification levels | Field in channelMembers, not exposed |
| Custom status messages | Field in userPresence, no UI |
Actions exist, UI incomplete:
| Feature | Status |
|---|---|
| Message reactions | addReaction/removeReaction work, emoji picker disabled |
| Pinned messages panel | Panel built, header button not wired |
| Unread badges | Read state tracked in DB, sidebar not always accurate |
Not implemented:
- @mentions and notifications
- Channel settings (edit, archive, delete)
- Member management (add/remove, role changes)
- Private channel invitations
- Offline sync integration (sync engine exists, not connected)
Architecture
Server Actions
Six action files handle all data mutations:
src/app/actions/
├── conversations.ts # Channel CRUD, join/leave
├── chat-messages.ts # Send, edit, delete, reactions, threads
├── conversations-realtime.ts # Polling updates, typing indicators
├── channel-categories.ts # Category management, channel reordering
├── message-search.ts # Full-text search, pin/unpin
└── presence.ts # Status updates, member presence
All actions return { success: true, data } or { success: false, error }. Authorization checks verify channel membership before allowing reactions or message operations.
Components
The UI is split between the sidebar navigation and the main channel view:
src/components/conversations/
├── channel-header.tsx # Name, description, member count, action buttons
├── message-list.tsx # Paginated messages grouped by date
├── message-item.tsx # Single message with toolbar
├── message-composer.tsx # TipTap editor with formatting
├── thread-panel.tsx # Resizable reply panel
├── member-sidebar.tsx # Members grouped by status
├── pinned-messages-panel.tsx # Sheet for pinned messages
├── search-dialog.tsx # Command dialog with filters
├── typing-indicator.tsx # Animated dots
├── create-channel-dialog.tsx # Full creation form
└── voice-channel-stub.tsx # Placeholder
The channel view at /dashboard/conversations/[channelId] combines these into a three-panel layout: sidebar (optional), messages, and thread panel (when open).
Real-Time Strategy
This module uses polling rather than WebSockets. The reasoning:
- Cloudflare Workers handles HTTP well; WebSocket support is newer
- Polling is simpler to debug and deploy
- 2.5s latency is acceptable for team chat
- Automatic backoff when tab is hidden reduces server load
If WebSocket requirements emerge (typing races, sub-second updates), the architecture can shift. The useRealtimeChannel hook abstracts the polling logic, so swapping implementations wouldn't require component changes.
Sync Infrastructure
A complete offline-first sync engine exists in src/lib/sync/ but isn't connected to conversations yet. The engine handles:
- Vector clocks for conflict detection
- Mutation queues for offline edits
- Delta sync with checkpoints
- Tombstones for deletions
This was built for the Tauri desktop app. When the mobile app needs offline messaging, this infrastructure is ready to connect.
Recent Fixes
February 2026 brought a comprehensive code review with 38 issues addressed:
Critical (5):
- Database indexes for production queries
- Edge runtime compatibility (replaced JSDOM with isomorphic-dompurify)
- Authorization bypasses in category/channel operations
Important (18):
- React.memo for message items
- Throttled presence updates
- Message length limits and input validation
- Accessibility (aria-labels, keyboard navigation)
Polish (15):
- Improved typing animation
- Sticky date separators
- Extracted duplicate query construction
What's Next
Priority order for completing the module:
-
Wire the disabled UI — Connect the emoji picker for reactions, wire the pinned messages button, fix unread badge accuracy. These are small changes with high user impact.
-
Attachments — The hardest missing piece. Requires file upload to R2, thumbnail generation, permissions, and a storage quota system. Start with images only.
-
Voice channels — Requires WebRTC or a third-party service. Consider LiveKit or Daily for the infrastructure layer.
-
Notifications — @mentions need a notification table, push integration, and preference settings. The schema doesn't support this yet.
-
Offline sync — Connect the existing sync engine to conversations. This unlocks the desktop app's full potential.
Files Reference
| Category | Files | Lines |
|---|---|---|
| Schema | schema-conversations.ts |
169 |
| Actions | 6 files in app/actions/ |
~2,200 |
| Components | 12 in components/conversations/ |
~2,300 |
| Pages | 3 in app/dashboard/conversations/ |
~160 |
| Hooks | use-realtime-channel.ts |
170 |
| Contexts | presence-context.tsx, conversations layout |
~320 |
| Sync | 9 files in lib/sync/ |
~1,800 |
Total: approximately 7,350 lines