Comparar commits

..

1 Commits

Autor SHA1 Mensaje Fecha
Jonathan David Florez Salomón
d302591c25 v1.5.1: el shell dejaba de meterse dentro del editor de OnlyOffice en Windows
OnlyOffice fallaba al editar y al descargar SOLO en la app de escritorio de
Windows. En el navegador y en la app de macOS iba bien. La causa estaba en el
propio shell, no en el servidor de documentos.

En Windows, wry inyecta los scripts de inicio en TODOS los marcos e ignora
for_main_frame_only (wry-0.55.1/src/webview2/mod.rs:492-495, documentado en su
lib.rs:990); macOS si respeta la bandera (wkwebview/mod.rs:643-645). Y __TAURI__
viaja con ellos, asi que la guarda «if (!T || !T.event) return» no frenaba nada.
Resultado: INIT_JS_TPL corria DENTRO del iframe del Document Server con ORIGIN
apuntando todavia a la app; como alli location.href es el de docs.*, cada enlace
del editor se resolvia contra ese origen, daba isExt() = true y se desviaba al
navegador del sistema —que no tiene la sesion: «no se puede acceder»—, y
window.open devolvia null.

Cambios:

1. Guarda de marco y origen al principio del script inyectado: solo actua en el
   documento PRINCIPAL y solo en el origen de la app. En macOS es un no-op (alli
   nunca corrio en subframes). De paso arregla que en Windows el shell tambien
   secuestrara los enlaces de las paginas de SSO.

2. El manejador de atajos ignora e.altKey. AltGr enciende ctrlKey en Windows, asi
   que escribir [ o ] con teclado espanol disparaba history.back()/forward() en
   vez de escribir el caracter — en el editor y en cualquier campo de la app.

3. Aviso nativo al terminar una descarga. No es un adorno: wry trae un manejador
   de descargas por defecto (wry-0.55.1/src/lib.rs:830), de modo que en WebView2
   siempre se registra DownloadStarting y siempre responde SetHandled(true)
   (webview2/mod.rs:858-861), lo que apaga la interfaz de descargas de Windows.
   El archivo bajaba a Descargas pero nada lo decia y parecia roto. Quitar el
   manejador NO devuelve la interfaz nativa: el de wry sigue puesto.

4. Feature «devtools» de tauri: el inspector queda disponible tambien en release.
   Sin el, un fallo que solo aparece en la app instalada no se puede mirar por
   dentro.

Descartado a proposito: registrar on_new_window. En macOS el delegado ejecuta
webview.window().unwrap() y current_window.screen().unwrap() ANTES del match
(wry-0.55.1/src/wkwebview/class/wry_web_view_ui_delegate.rs:150-154), tambien en
la rama Deny; y este shell oculta la ventana al cerrar, con lo que NSWindow.screen
puede ser nil y la app entera abortaria. Cambiar un fallo de Windows por un crash
en macOS no compensa.

Verificado: cargo check limpio; sintaxis del script inyectado validada con node;
y simulacion de la guarda — marco principal + origen propio interviene, iframe de
OnlyOffice no interviene, origen ajeno no interviene.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 07:12:22 -05:00
Se han modificado 5 ficheros con 69 adiciones y 12 borrados

Ver fichero

@@ -1,6 +1,6 @@
{ {
"name": "labstream-desktop", "name": "labstream-desktop",
"version": "1.4.0", "version": "1.5.1",
"private": true, "private": true,
"description": "App de escritorio (Windows) que envuelve Labstream OS — cliente del servidor del NAS.", "description": "App de escritorio (Windows) que envuelve Labstream OS — cliente del servidor del NAS.",
"scripts": { "scripts": {

Ver fichero

@@ -2062,7 +2062,7 @@ dependencies = [
[[package]] [[package]]
name = "labstream-desktop" name = "labstream-desktop"
version = "1.5.0" version = "1.5.1"
dependencies = [ dependencies = [
"serde", "serde",
"serde_json", "serde_json",

Ver fichero

@@ -1,6 +1,6 @@
[package] [package]
name = "labstream-desktop" name = "labstream-desktop"
version = "1.5.0" version = "1.5.1"
description = "Cliente de escritorio de Labstream OS" description = "Cliente de escritorio de Labstream OS"
authors = ["Labstream SAS"] authors = ["Labstream SAS"]
edition = "2021" edition = "2021"
@@ -15,7 +15,11 @@ tauri-build = { version = "2", features = [] }
[dependencies] [dependencies]
# `unstable` habilita multiwebview: varios webviews (pestañas) dentro de UNA ventana. # `unstable` habilita multiwebview: varios webviews (pestañas) dentro de UNA ventana.
tauri = { version = "2", features = ["tray-icon", "unstable"] } # `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-opener = "2"
tauri-plugin-clipboard-manager = "2" tauri-plugin-clipboard-manager = "2"
tauri-plugin-notification = "2" tauri-plugin-notification = "2"

Ver fichero

@@ -39,7 +39,7 @@ use serde_json::json;
use tauri::{ use tauri::{
menu::{CheckMenuItem, Menu, MenuItem, PredefinedMenuItem, Submenu}, menu::{CheckMenuItem, Menu, MenuItem, PredefinedMenuItem, Submenu},
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent}, tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
webview::{PageLoadEvent, Webview, WebviewBuilder}, webview::{DownloadEvent, PageLoadEvent, Webview, WebviewBuilder},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
AppHandle, Emitter, EventTarget, Listener, LogicalPosition, LogicalSize, Manager, Runtime, AppHandle, Emitter, EventTarget, Listener, LogicalPosition, LogicalSize, Manager, Runtime,
Url, WebviewUrl, WindowEvent, Url, WebviewUrl, WindowEvent,
@@ -148,16 +148,33 @@ fn save_persisted<R: Runtime>(app: &AppHandle<R>) {
// - Reporta título/URL a la barra (observer del <title> + pushState del SPA). // - Reporta título/URL a la barra (observer del <title> + pushState del SPA).
// - Atajos de teclado (en macOS los consume antes el menú nativo; esto cubre Windows y extras) // - Atajos de teclado (en macOS los consume antes el menú nativo; esto cubre Windows y extras)
// y Ctrl+rueda para zoom. // y Ctrl+rueda para zoom.
// En páginas de OTRO origen (p. ej. el login de Authentik) __TAURI__ no está inyectado y el // SOLO debe gobernar el documento principal de NUESTRO origen; ver la guarda de abajo.
// script sale al principio sin tocar nada — la navegación del SSO sigue intacta.
const INIT_JS_TPL: &str = r#" const INIT_JS_TPL: &str = r#"
(function () { (function () {
var ORIGIN = "__ORIGIN__";
// ── GUARDA DE MARCO Y ORIGEN ──
// Antes aquí solo estaba `if (!T || !T.event) return;`, confiando en que __TAURI__ no
// existiría fuera del origen permitido. Eso es cierto en macOS y FALSO en Windows:
// · wry inyecta los scripts de inicio en TODOS los marcos e IGNORA for_main_frame_only
// (wry-0.55.1/src/webview2/mod.rs:492-495, documentado en su lib.rs:990), mientras que
// WKWebView sí la respeta (wry-0.55.1/src/wkwebview/mod.rs:643-645);
// · y __TAURI__ viaja con ellos (withGlobalTauri, tauri/src/manager/webview.rs:159-165).
// Consecuencia real: dentro del iframe del editor de OnlyOffice (docs.labstreamsas.com)
// este script SÍ corría en Windows, con ORIGIN apuntando todavía a la app. Como allí
// `location.href` es el del Document Server, abs() resolvía CADA enlace del editor contra
// docs.* → isExt() cierto → preventDefault() + mandarlo al navegador del sistema (que no
// tiene la sesión: «no se puede acceder»), y window.open devolvía null. Por eso editar y
// descargar fallaban solo en la app de Windows, y no en Mac ni en el navegador.
// En macOS estas dos líneas son un no-op: allí el script nunca ha corrido en subframes.
try { if (window.top !== window.self) return; } catch (e) { return; }
if (location.origin !== ORIGIN) return; // p. ej. el login del SSO: no intervenir
if (window.__lsShell) return; if (window.__lsShell) return;
window.__lsShell = true; window.__lsShell = true;
var T = window.__TAURI__; var T = window.__TAURI__;
if (!T || !T.event) return; // página fuera del origen permitido (p. ej. SSO): no intervenir if (!T || !T.event) return;
var LABEL = "__LABEL__"; var LABEL = "__LABEL__";
var ORIGIN = "__ORIGIN__";
function abs(h) { try { return new URL(h, location.href); } catch (e) { return null; } } function abs(h) { try { return new URL(h, location.href); } catch (e) { return null; } }
function isHttp(u) { return !!u && (u.protocol === 'http:' || u.protocol === 'https:'); } function isHttp(u) { return !!u && (u.protocol === 'http:' || u.protocol === 'https:'); }
@@ -224,6 +241,11 @@ const INIT_JS_TPL: &str = r#"
// Atajos de teclado. // Atajos de teclado.
addEventListener('keydown', function (e) { addEventListener('keydown', function (e) {
// AltGr enciende ctrlKey en Windows. Sin este filtro, escribir [ o ] con teclado
// español —que se hacen con AltGr— entraba por aquí y disparaba history.back() /
// history.forward() en vez de escribir el carácter: en el editor, en el chat, en una
// nota y en cualquier campo de la app. En macOS no aplica (Option no toca ctrlKey).
if (e.altKey) return;
var mod = e.metaKey || e.ctrlKey; var mod = e.metaKey || e.ctrlKey;
if (!mod) return; if (!mod) return;
var k = e.key; var k = e.key;
@@ -388,8 +410,39 @@ fn create_tab<R: Runtime>(app: &AppHandle<R>, url: Option<String>, activate: boo
let app_zoom = app.clone(); let app_zoom = app.clone();
let builder = WebviewBuilder::new(&label, WebviewUrl::External(parsed)) let builder = WebviewBuilder::new(&label, WebviewUrl::External(parsed))
.initialization_script(&init) .initialization_script(&init)
// Deja pasar las descargas (entregables, exportaciones) con el diálogo nativo de guardado. // Deja pasar las descargas (entregables, exportaciones) Y avisa al terminar.
.on_download(|_webview, _event| true) // El aviso no es un adorno: en WebView2 la interfaz de descargas queda SIEMPRE
// apagada. wry trae un manejador de descargas por defecto (wry-0.55.1/src/lib.rs:830),
// asi que DownloadStarting se registra siempre y siempre responde SetHandled(true)
// (wry-0.55.1/src/webview2/mod.rs:858-861), lo que le dice a WebView2 que del aviso
// se encarga la app: ni barra de progreso, ni globo de «descarga terminada». El
// archivo bajaba bien a la carpeta de Descargas, pero NADA lo decia y parecia roto.
// Quitar este manejador NO devuelve la interfaz nativa: el de wry sigue puesto.
.on_download(|webview, event| {
if let DownloadEvent::Finished { path, success, .. } = &event {
let cuerpo = if !*success {
"No se pudo completar la descarga.".to_string()
} else if let Some(p) = path {
// En macOS `path` llega siempre vacio (limite de WKDownload:
// wry-0.55.1/src/wkwebview/download.rs:98), asi que en la practica
// este brazo con nombre de archivo es el de Windows.
match p.file_name() {
Some(n) => format!("Descargado: {}", n.to_string_lossy()),
None => "Descarga terminada.".to_string(),
}
} else {
"Descarga terminada. Esta en tu carpeta de Descargas.".to_string()
};
let _ = webview
.app_handle()
.notification()
.builder()
.title("Labstream OS")
.body(cuerpo)
.show();
}
true
})
// El zoom guardado se re-aplica en cada carga (el factor no siempre sobrevive a la navegación). // El zoom guardado se re-aplica en cada carga (el factor no siempre sobrevive a la navegación).
.on_page_load(move |wv, payload| { .on_page_load(move |wv, payload| {
let cargando = matches!(payload.event(), PageLoadEvent::Started); let cargando = matches!(payload.event(), PageLoadEvent::Started);

Ver fichero

@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "Labstream OS", "productName": "Labstream OS",
"version": "1.5.0", "version": "1.5.1",
"identifier": "co.labstream.os", "identifier": "co.labstream.os",
"build": { "build": {
"frontendDist": "../dist" "frontendDist": "../dist"