* 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>
128 lines
4.4 KiB
Rust
128 lines
4.4 KiB
Rust
//! Compass Desktop - Tauri v2 Backend
|
|
//!
|
|
//! Provides native desktop support for Compass construction project management
|
|
//! with SQLite database, sync capabilities, and cross-platform compatibility.
|
|
|
|
mod commands;
|
|
mod error;
|
|
|
|
use tauri::Manager;
|
|
use tauri_plugin_sql::{Migration, MigrationKind};
|
|
|
|
pub use error::{AppError, Result};
|
|
|
|
/// Application state shared across all commands
|
|
pub struct AppState {
|
|
pub db_path: std::sync::Mutex<Option<String>>,
|
|
pub sync_status: std::sync::Mutex<SyncStatus>,
|
|
}
|
|
|
|
/// Current sync status with remote server
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
|
pub struct SyncStatus {
|
|
pub last_sync: Option<String>,
|
|
pub pending_changes: u64,
|
|
pub is_syncing: bool,
|
|
pub error: Option<String>,
|
|
}
|
|
|
|
impl Default for AppState {
|
|
fn default() -> Self {
|
|
Self {
|
|
db_path: std::sync::Mutex::new(None),
|
|
sync_status: std::sync::Mutex::new(SyncStatus::default()),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Initialize the Tauri application
|
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
|
pub fn run() {
|
|
// Apply Linux/Wayland/NVIDIA compatibility fixes before anything else
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
let session_type = std::env::var("XDG_SESSION_TYPE").unwrap_or_default();
|
|
let is_wayland = session_type == "wayland";
|
|
|
|
// Check for NVIDIA driver via /proc/driver/nvidia
|
|
let has_nvidia = std::path::Path::new("/proc/driver/nvidia").exists();
|
|
|
|
if is_wayland && has_nvidia {
|
|
// NVIDIA explicit sync disable - better performance on newer drivers (545+)
|
|
// Only set if user hasn't already configured it
|
|
if std::env::var("__NV_DISABLE_EXPLICIT_SYNC").is_err() {
|
|
std::env::set_var("__NV_DISABLE_EXPLICIT_SYNC", "1");
|
|
}
|
|
// Note: We don't set WEBKIT_DISABLE_DMABUF_RENDERER here because it forces
|
|
// software rendering which is very slow. If explicit sync doesn't work,
|
|
// users can manually set: WEBKIT_DISABLE_DMABUF_RENDERER=1 bun tauri:dev
|
|
}
|
|
}
|
|
|
|
let migrations = vec![
|
|
Migration {
|
|
version: 1,
|
|
description: "Initial schema",
|
|
sql: include_str!("../migrations/initial.sql"),
|
|
kind: MigrationKind::Up,
|
|
},
|
|
];
|
|
|
|
tauri::Builder::default()
|
|
.plugin(tauri_plugin_shell::init())
|
|
.plugin(
|
|
tauri_plugin_sql::Builder::default()
|
|
.add_migrations("sqlite:compass.db", migrations)
|
|
.build(),
|
|
)
|
|
.plugin(tauri_plugin_http::init())
|
|
.plugin(tauri_plugin_window_state::Builder::default().build())
|
|
.plugin(tauri_plugin_updater::Builder::new().build())
|
|
.plugin(tauri_plugin_fs::init())
|
|
.plugin(tauri_plugin_dialog::init())
|
|
.manage(AppState::default())
|
|
.invoke_handler(tauri::generate_handler![
|
|
// Database commands
|
|
commands::database::db_query,
|
|
commands::database::db_execute,
|
|
commands::database::db_init,
|
|
// Sync commands
|
|
commands::sync::get_sync_status,
|
|
commands::sync::trigger_sync,
|
|
commands::sync::cancel_sync,
|
|
// Platform commands
|
|
commands::platform::get_platform_info,
|
|
commands::platform::get_display_server,
|
|
])
|
|
.setup(|app| {
|
|
// Get the main window
|
|
let window = app.get_webview_window("main").expect("no main window");
|
|
|
|
// Log startup info
|
|
#[cfg(debug_assertions)]
|
|
{
|
|
println!("Compass Desktop starting up...");
|
|
println!("Platform: {}", std::env::consts::OS);
|
|
}
|
|
|
|
// On Wayland, disable decorations since the compositor handles window chrome
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
let session_type = std::env::var("XDG_SESSION_TYPE").unwrap_or_default();
|
|
let is_wayland = session_type == "wayland";
|
|
|
|
if is_wayland {
|
|
#[cfg(debug_assertions)]
|
|
println!("Wayland detected - disabling CSD decorations");
|
|
|
|
// Set decorations to false for Wayland
|
|
let _ = window.set_decorations(false);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
})
|
|
.run(tauri::generate_context!())
|
|
.expect("error while running tauri application");
|
|
}
|