[package] name = "labstream-desktop" version = "1.22.0" description = "Cliente de escritorio de Labstream OS" authors = ["Labstream SAS"] edition = "2021" rust-version = "1.77.2" [lib] name = "labstream_desktop_lib" crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] tauri-build = { version = "2", features = [] } [dependencies] # `unstable` habilita multiwebview: varios webviews (pestañas) dentro de UNA ventana. # `devtools` deja el inspector disponible TAMBIÉN en release (por defecto solo va en debug: # wry-0.55.1/src/lib.rs:834-837). Sin él, un fallo que solo aparece en la app instalada —como # el de OnlyOffice en Windows— no se puede mirar por dentro y hay que deducirlo leyendo # fuentes. Con él, clic derecho → Inspeccionar sobre el marco que falle y se acabó. tauri = { version = "2", features = ["tray-icon", "unstable", "devtools"] } tauri-plugin-opener = "2" tauri-plugin-clipboard-manager = "2" tauri-plugin-notification = "2" tauri-plugin-autostart = "2" tauri-plugin-updater = "2" tauri-plugin-single-instance = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" # ── Diálogo «Guardar como» de las descargas (on_download en create_tab, lib.rs) ── # rfd DIRECTO y no tauri-plugin-dialog: el manejador de descargas de wry exige la respuesta en # el mismo hilo y de forma síncrona (WebView2 DownloadStarting / WKDownload # decideDestination, ambos en el hilo principal). El `blocking_save_file()` del plugin monta # el diálogo ASÍNCRONO (rfd::AsyncFileDialog) y espera en un canal; en macOS ese diálogo se # presenta como hoja (beginSheetModalForWindow) y necesita que el run loop principal siga # girando… que es justo el hilo que está bloqueado esperando el canal → cuelgue. El # `rfd::FileDialog` síncrono corre un bucle modal anidado en el propio hilo # (IFileDialog::Show / NSSavePanel.runModal), que es lo que hace falta aquí. # Mismas features que usa el plugin oficial (common-controls-v6 = estilo moderno en Windows). rfd = { version = "0.17", default-features = false, features = ["common-controls-v6"] } # Para decodificar el último segmento de la URL cuando no hay nombre sugerido (macOS). percent-encoding = "2" # ── Rastreador de trabajo efectivo (src/tracker.rs) ── # active-win-pos-rs: la ventana al frente (app + título) en Win/Mac. # device_query: ¿hubo entrada? (huella de mouse/teclas presionadas; NUNCA qué se tecleó). # ureq: subir los lotes a /api/tracker (bloqueante, corre en su propio hilo). # gethostname: el nombre del equipo, que bautiza al device en el servidor. active-win-pos-rs = "0.11" ureq = { version = "2", features = ["json"] } gethostname = "0.5" # ── La IA del equipo (src/ia, 1.22) ── # sha2: la huella de lo que se baja (modelo y motor); zip: sacar las DLL del paquete del motor. sha2 = "0.10" zip = { version = "4", default-features = false, features = ["deflate"] } # device_query solo en Windows/Linux: la huella de entrada (posición del mouse + cuántas teclas # hay presionadas). En macOS NO se usa —leer el teclado global dispara el permiso de # Accesibilidad y el diálogo «… quiere controlar este ordenador»— y se sustituye por # CGEventSourceSecondsSinceLastEventType (idle_seconds_macos en tracker.rs), que no pide permiso. [target.'cfg(not(target_os = "macos"))'.dependencies] device_query = "4" # ── Excepción del video (Windows) ── # Para saber si «hay un video reproduciéndose» sin espiar el navegador: se mira el MEDIDOR de # la salida de audio por defecto (WASAPI, IAudioMeterInformation). Si al frente hay un navegador # y está saliendo sonido, es un video sonando → cuenta como navegador, no como inactividad. # Solo Windows: en macOS no hay API pública sencilla de nivel de audio por app (el equipo de # edición es Windows). El `windows` crate ya está en la caché por Tauri. [target.'cfg(windows)'.dependencies] # IA del equipo (1.22, src/ia/motor.rs): ONNX Runtime con DirectML sobre la tarjeta. `load-dynamic`: # nada se enlaza al compilar; la DLL (la de DirectML 1.24, de NuGet) se baja la primera vez y se # carga desde la carpeta de la IA. `api-24`: la versión de la API que trae esa DLL. ort = { version = "=2.0.0-rc.14", default-features = false, features = ["std", "load-dynamic", "directml", "api-24"] } # Pestañas en reposo (1.19): ICoreWebView2_3::TrySuspend. Es la misma versión que ya trae Tauri. webview2-com = "0.38" windows = { version = "0.61", features = [ "Win32_Foundation", "Win32_System_Com", # IMMDevice::Activate exige AMBAS (su firma lleva un PROPVARIANT): sin ellas el método # queda cfg'd out y «no existe». Verificado con cargo check --target windows-msvc. "Win32_System_Com_StructuredStorage", "Win32_System_Variant", "Win32_Media_Audio", "Win32_Media_Audio_Endpoints", # Salud (1.20): medir el árbol de procesos (exe + WebView2) y el reinicio automático de Windows. "Win32_System_Diagnostics_ToolHelp", "Win32_System_ProcessStatus", "Win32_System_Threading", "Win32_System_Recovery", # Memoria (1.20): ¿cuánto lleva el equipo sin tocar? (para reciclar el proceso gráfico sin que se note) "Win32_UI_Input_KeyboardAndMouse", "Win32_System_SystemInformation", # IA del equipo (1.22): ¿hay tarjeta NVIDIA? (los adaptadores de DXGI) "Win32_Graphics_Dxgi", # y cargar DirectML.dll por su ruta antes que ONNX Runtime (src/ia/motor.rs) "Win32_System_LibraryLoader", ] }