Cliente nativo: enlaces externos al navegador, micrófono para notas de voz, descargas
La ventana se crea ahora en Rust (lib.rs) para poder: - Inyectar un script que manda los enlaces EXTERNOS (Drive, docs ajenos, target=_blank, window.open) al navegador del sistema, dejando dentro la navegación del propio servidor y el login por Authentik (que redirige a otro dominio y vuelve). - Permitir descargas (entregables/exportaciones) con el diálogo nativo de guardado. - SERVER_URL centralizado (antes en tauri.conf.json app.windows[0].url). macOS: Info.plist con NSMicrophoneUsageDescription/NSCameraUsageDescription y Entitlements.plist (audio-input, cámara, red, allow-jit) → las notas de voz y los adjuntos de foto/video piden permiso y funcionan. Referenciados en bundle.macOS. Se conserva el tray, cerrar=ocultar y el autostart. Verificado: build de macOS OK (.app + .dmg de 3,8 MB, icono del logo, textos de permiso fusionados en Info.plist). Los instaladores de Windows (.exe) y macOS (.dmg universal) los sigue produciendo GitHub Actions (.github/workflows/build.yml) en cada tag de versión. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Este commit está contenido en:
2
.gitignore
vendido
2
.gitignore
vendido
@@ -8,6 +8,8 @@ src-tauri/target/
|
|||||||
src-tauri/icons/*.png
|
src-tauri/icons/*.png
|
||||||
src-tauri/icons/*.ico
|
src-tauri/icons/*.ico
|
||||||
src-tauri/icons/*.icns
|
src-tauri/icons/*.icns
|
||||||
|
src-tauri/icons/android/
|
||||||
|
src-tauri/icons/ios/
|
||||||
!src-tauri/icons/.gitkeep
|
!src-tauri/icons/.gitkeep
|
||||||
|
|
||||||
# Esquemas/IPC generados por Tauri
|
# Esquemas/IPC generados por Tauri
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -8,8 +8,16 @@ que carga el servidor de Labstream OS que corre en el NAS
|
|||||||
No contiene la app: el backend, la base de datos, OnlyOffice, etc. siguen en el
|
No contiene la app: el backend, la base de datos, OnlyOffice, etc. siguen en el
|
||||||
NAS. Esto solo le da al equipo un ícono y una ventana propia en Windows y Mac.
|
NAS. Esto solo le da al equipo un ícono y una ventana propia en Windows y Mac.
|
||||||
|
|
||||||
El servidor objetivo se configura en `src-tauri/tauri.conf.json` →
|
Además del ícono y la ventana, el envoltorio añade comportamiento de cliente nativo:
|
||||||
`app.windows[0].url`.
|
|
||||||
|
- **Enlaces externos** (Drive, documentos ajenos…) abren en el **navegador del sistema**;
|
||||||
|
la navegación del propio servidor y el **login por Authentik** se quedan dentro.
|
||||||
|
- **Notas de voz / cámara**: pide permiso de micrófono y cámara (entitlements de macOS).
|
||||||
|
- **Descargas** (entregables, exportaciones) con el diálogo nativo de guardado.
|
||||||
|
- **Bandeja del sistema**: cerrar la ventana la oculta; la app sigue notificando.
|
||||||
|
- **Arranque automático** al iniciar sesión y **sesión persistente** (no re-loguea).
|
||||||
|
|
||||||
|
El servidor objetivo se configura en `src-tauri/src/lib.rs` → constante `SERVER_URL`.
|
||||||
|
|
||||||
Salidas:
|
Salidas:
|
||||||
|
|
||||||
@@ -152,11 +160,13 @@ labstream-desktop/
|
|||||||
├── dist/index.html pantalla de respaldo (requerida por Tauri)
|
├── dist/index.html pantalla de respaldo (requerida por Tauri)
|
||||||
├── package.json scripts de build
|
├── package.json scripts de build
|
||||||
├── src-tauri/
|
├── src-tauri/
|
||||||
│ ├── tauri.conf.json nombre, versión, URL del servidor, instaladores
|
│ ├── tauri.conf.json nombre, versión, iconos, instaladores, entitlements
|
||||||
│ ├── Cargo.toml dependencias Rust + versión
|
│ ├── Cargo.toml dependencias Rust + versión
|
||||||
|
│ ├── Info.plist textos de permiso (micrófono/cámara) en macOS
|
||||||
|
│ ├── Entitlements.plist entitlements de macOS (audio-input, cámara, red)
|
||||||
│ ├── build.rs
|
│ ├── build.rs
|
||||||
│ ├── capabilities/default.json
|
│ ├── capabilities/default.json
|
||||||
│ ├── icons/ se generan desde app-icon.png
|
│ ├── icons/ se generan desde app-icon.png
|
||||||
│ └── src/{main.rs,lib.rs}
|
│ └── src/{main.rs,lib.rs} lib.rs crea la ventana + SERVER_URL + tray
|
||||||
└── .github/workflows/build.yml build automático de .exe (Windows) y .dmg (macOS)
|
└── .github/workflows/build.yml build automático de .exe (Windows) y .dmg (macOS)
|
||||||
```
|
```
|
||||||
|
|||||||
247
package-lock.json
generado
Archivo normal
247
package-lock.json
generado
Archivo normal
@@ -0,0 +1,247 @@
|
|||||||
|
{
|
||||||
|
"name": "labstream-desktop",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "labstream-desktop",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"@tauri-apps/cli": "^2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tauri-apps/cli": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-bk3HemqvGRoy+5D/dVMUQHKMYLglD0jVnMm/0iGMH6ufZ+p8r14m6BpIixwij3PBvZdvORUp1YifTD8QxVZ1Nw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 OR MIT",
|
||||||
|
"bin": {
|
||||||
|
"tauri": "tauri.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/tauri"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@tauri-apps/cli-darwin-arm64": "2.11.2",
|
||||||
|
"@tauri-apps/cli-darwin-x64": "2.11.2",
|
||||||
|
"@tauri-apps/cli-linux-arm-gnueabihf": "2.11.2",
|
||||||
|
"@tauri-apps/cli-linux-arm64-gnu": "2.11.2",
|
||||||
|
"@tauri-apps/cli-linux-arm64-musl": "2.11.2",
|
||||||
|
"@tauri-apps/cli-linux-riscv64-gnu": "2.11.2",
|
||||||
|
"@tauri-apps/cli-linux-x64-gnu": "2.11.2",
|
||||||
|
"@tauri-apps/cli-linux-x64-musl": "2.11.2",
|
||||||
|
"@tauri-apps/cli-win32-arm64-msvc": "2.11.2",
|
||||||
|
"@tauri-apps/cli-win32-ia32-msvc": "2.11.2",
|
||||||
|
"@tauri-apps/cli-win32-x64-msvc": "2.11.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tauri-apps/cli-darwin-arm64": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-+4UZzLt+eOAEQCwgd+TqKgyUJMrvx+BgdXLLaqJYmPqzP+nE6YZr/hY6CWLYGQb8jFn99jEkmC6uA3tNvamA1w==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 OR MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tauri-apps/cli-darwin-x64": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-VjYYtZUPqDMLutSfJEyxFE3Bz+DPi7c8wC3imckgvciLDZLq4qwKJxBicg0BXGhXjJsl8vKWgWRFNMPELQ+Xyg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 OR MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-yMemD6f4i95AQriS8EazyOFzbE34yjnP16i3IOzpHGQvBoy2DjypFMFBq0NtPuITURv/cOGguRtHR5d79/9CSA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 OR MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tauri-apps/cli-linux-arm64-gnu": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-cgI91D2wL8GSgoWwZXDqt+DwnuZCP2/bz03QAE4TrhgAKIsrB4hX26W/H1EONPUUNkqrsgeCD0wU6pcNjV/5kw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0 OR MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tauri-apps/cli-linux-arm64-musl": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-X1rm0BERqAAggtYTESSgXrS3sz4Sb/OiPiz54UqISlXW+GkR3vNIGnsy/lejNmoXGVqri3Q53BCfQiclOIyRPw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"musl"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0 OR MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tauri-apps/cli-linux-riscv64-gnu": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-usbMLJbT3KtkOrBMDVeGYNM35aTHXx38SJSzTMSqqjeUIOQ+iVPjb2yAGNAE+KqmBbAx4FOFIyMeKXx2M/JKGQ==",
|
||||||
|
"cpu": [
|
||||||
|
"riscv64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0 OR MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tauri-apps/cli-linux-x64-gnu": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-Ru4gwJKPG0ctVGchRGpRup4Y4lW2SSfFnrbQcyHhCliKy4g8Qz97TrUgCur4CbWyAgKxvGh3SjrkA0LDYzDGiw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0 OR MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tauri-apps/cli-linux-x64-musl": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-eUm7T6clN1MMmNSRQ9gaWsQdyehQx2Gmn5hht/QUlqZQI/qcP2OJK5dnaxqwFzCr2HdsEo9ydxaqcS1oJzMvUw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"musl"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0 OR MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tauri-apps/cli-win32-arm64-msvc": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-HeeZW80jU+gVTOEX4X/hC6NVSAdDVXajwP5fxIZ/3z9WvUC7qrudX2GMTilYq6Dg0e0sk0XgsAJD1hZ5wPBXUA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 OR MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tauri-apps/cli-win32-ia32-msvc": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-YhjQNZcXfbkCLyazSv1nPnJ9iRFE1wm6kc51FDbU10/Dk09io+6PAGMLjkxnX2GdM0qMnDmTjstY8mTDVvtKeA==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 OR MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tauri-apps/cli-win32-x64-msvc": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-d2JchlFIpZevZVReyqhQOekJmb1UH3rhZ5VX6sH3ty9ETE0TKQavpihvoScUXfKKpW6HZC0MrFGRU0ZtD+w3gA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 OR MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5087
src-tauri/Cargo.lock
generado
Archivo normal
5087
src-tauri/Cargo.lock
generado
Archivo normal
La diferencia del archivo ha sido suprimido porque es demasiado grande
Cargar Diff
18
src-tauri/Entitlements.plist
Archivo normal
18
src-tauri/Entitlements.plist
Archivo normal
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<!-- Micrófono: notas de voz del chat (getUserMedia en el webview). -->
|
||||||
|
<key>com.apple.security.device.audio-input</key>
|
||||||
|
<true/>
|
||||||
|
<!-- Cámara: adjuntar foto/video en el chat. -->
|
||||||
|
<key>com.apple.security.device.camera</key>
|
||||||
|
<true/>
|
||||||
|
<!-- Cliente de red (conecta con os.labstreamsas.com). -->
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
<!-- Requerido por WKWebView (JIT de JavaScript) bajo hardened runtime. -->
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
11
src-tauri/Info.plist
Archivo normal
11
src-tauri/Info.plist
Archivo normal
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<!-- Textos que macOS muestra al pedir permiso. Tauri fusiona este Info.plist en el bundle. -->
|
||||||
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
|
<string>Labstream usa el micrófono para grabar notas de voz en el chat.</string>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>Labstream usa la cámara para adjuntar fotos y video en el chat.</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -6,14 +6,50 @@
|
|||||||
// - plugin `autostart`: la app arranca al iniciar sesión.
|
// - plugin `autostart`: la app arranca al iniciar sesión.
|
||||||
// - bandeja (tray) + "cerrar = ocultar": la app sigue corriendo y notificando
|
// - bandeja (tray) + "cerrar = ocultar": la app sigue corriendo y notificando
|
||||||
// aunque cierres la ventana.
|
// aunque cierres la ventana.
|
||||||
|
// - la ventana se crea AQUÍ (no en tauri.conf.json) para poder inyectar un script
|
||||||
|
// que manda los enlaces externos al navegador y para permitir descargas.
|
||||||
|
|
||||||
use tauri::{
|
use tauri::{
|
||||||
menu::{Menu, MenuItem},
|
menu::{Menu, MenuItem},
|
||||||
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
|
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
|
||||||
AppHandle, Manager, Runtime, WindowEvent,
|
AppHandle, Manager, Runtime, WebviewUrl, WebviewWindowBuilder, WindowEvent,
|
||||||
};
|
};
|
||||||
use tauri_plugin_autostart::{MacosLauncher, ManagerExt};
|
use tauri_plugin_autostart::{MacosLauncher, ManagerExt};
|
||||||
|
|
||||||
|
const SERVER_URL: &str = "https://os.labstreamsas.com";
|
||||||
|
|
||||||
|
// Script que corre en CADA carga de página del servidor. Reenvía al NAVEGADOR del sistema los
|
||||||
|
// enlaces a OTRO origen (Drive, documentos externos, etc.): clics en `<a>` externos, `target=_blank`
|
||||||
|
// externos y `window.open(url)` externos. NO toca la navegación del propio origen, así el login por
|
||||||
|
// Authentik —que redirige a otro dominio y vuelve— sigue funcionando. Usa el comando del plugin
|
||||||
|
// opener, permitido por la capability `opener:default` para este origen remoto.
|
||||||
|
const INIT_JS: &str = r#"
|
||||||
|
(function () {
|
||||||
|
if (window.__lsExternalLinks) return;
|
||||||
|
window.__lsExternalLinks = true;
|
||||||
|
var APP_ORIGIN = location.origin;
|
||||||
|
function toAbs(href) { try { return new URL(href, location.href); } catch (e) { return null; } }
|
||||||
|
function isExternal(u) { return !!u && (u.protocol === 'http:' || u.protocol === 'https:') && u.origin !== APP_ORIGIN; }
|
||||||
|
function openExternal(url) {
|
||||||
|
try { window.__TAURI__.core.invoke('plugin:opener|open_url', { url: url, with: null }); }
|
||||||
|
catch (e) { try { window.__TAURI__.opener.openUrl(url); } catch (_) {} }
|
||||||
|
}
|
||||||
|
document.addEventListener('click', function (e) {
|
||||||
|
if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
||||||
|
var a = e.target && e.target.closest ? e.target.closest('a[href]') : null;
|
||||||
|
if (!a) return;
|
||||||
|
var u = toAbs(a.getAttribute('href'));
|
||||||
|
if (isExternal(u)) { e.preventDefault(); openExternal(u.href); }
|
||||||
|
}, true);
|
||||||
|
var _open = window.open;
|
||||||
|
window.open = function (url, name, feats) {
|
||||||
|
var u = url ? toAbs(url) : null;
|
||||||
|
if (isExternal(u)) { openExternal(u.href); return null; }
|
||||||
|
return _open ? _open.call(window, url, name, feats) : null;
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
"#;
|
||||||
|
|
||||||
// Muestra y enfoca la ventana principal (desde el tray o su menú).
|
// Muestra y enfoca la ventana principal (desde el tray o su menú).
|
||||||
fn show_main<R: Runtime>(app: &AppHandle<R>) {
|
fn show_main<R: Runtime>(app: &AppHandle<R>) {
|
||||||
if let Some(w) = app.get_webview_window("main") {
|
if let Some(w) = app.get_webview_window("main") {
|
||||||
@@ -36,6 +72,23 @@ pub fn run() {
|
|||||||
// Arranque automático al iniciar sesión (idempotente).
|
// Arranque automático al iniciar sesión (idempotente).
|
||||||
let _ = app.autolaunch().enable();
|
let _ = app.autolaunch().enable();
|
||||||
|
|
||||||
|
// Ventana principal: carga el servidor del NAS. Se crea aquí (no en el config) para
|
||||||
|
// poder inyectar el script de enlaces externos y permitir las descargas.
|
||||||
|
WebviewWindowBuilder::new(
|
||||||
|
app,
|
||||||
|
"main",
|
||||||
|
WebviewUrl::External(SERVER_URL.parse().expect("URL del servidor válida")),
|
||||||
|
)
|
||||||
|
.title("Labstream OS")
|
||||||
|
.inner_size(1400.0, 900.0)
|
||||||
|
.min_inner_size(1024.0, 700.0)
|
||||||
|
.resizable(true)
|
||||||
|
.center()
|
||||||
|
.initialization_script(INIT_JS)
|
||||||
|
// Deja pasar las descargas (entregables, exportaciones) con el diálogo nativo de guardado.
|
||||||
|
.on_download(|_webview, _event| true)
|
||||||
|
.build()?;
|
||||||
|
|
||||||
// Icono en la bandeja del sistema con menú Abrir / Salir.
|
// Icono en la bandeja del sistema con menú Abrir / Salir.
|
||||||
let abrir = MenuItem::with_id(app, "abrir", "Abrir Labstream", true, None::<&str>)?;
|
let abrir = MenuItem::with_id(app, "abrir", "Abrir Labstream", true, None::<&str>)?;
|
||||||
let salir = MenuItem::with_id(app, "salir", "Salir", true, None::<&str>)?;
|
let salir = MenuItem::with_id(app, "salir", "Salir", true, None::<&str>)?;
|
||||||
|
|||||||
@@ -8,20 +8,7 @@
|
|||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"withGlobalTauri": true,
|
"withGlobalTauri": true,
|
||||||
"windows": [
|
"windows": [],
|
||||||
{
|
|
||||||
"label": "main",
|
|
||||||
"title": "Labstream OS",
|
|
||||||
"url": "https://os.labstreamsas.com",
|
|
||||||
"width": 1400,
|
|
||||||
"height": 900,
|
|
||||||
"minWidth": 1024,
|
|
||||||
"minHeight": 700,
|
|
||||||
"resizable": true,
|
|
||||||
"center": true,
|
|
||||||
"decorations": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"security": {
|
"security": {
|
||||||
"csp": null
|
"csp": null
|
||||||
}
|
}
|
||||||
@@ -32,7 +19,7 @@
|
|||||||
"publisher": "Labstream SAS",
|
"publisher": "Labstream SAS",
|
||||||
"category": "Productivity",
|
"category": "Productivity",
|
||||||
"shortDescription": "Espacio de trabajo de Labstream",
|
"shortDescription": "Espacio de trabajo de Labstream",
|
||||||
"longDescription": "Cliente de escritorio de Labstream OS para Windows. Abre el espacio de trabajo de Labstream en su propia ventana.",
|
"longDescription": "Cliente de escritorio de Labstream OS para Windows y macOS. Abre el espacio de trabajo de Labstream en su propia ventana.",
|
||||||
"icon": [
|
"icon": [
|
||||||
"icons/32x32.png",
|
"icons/32x32.png",
|
||||||
"icons/128x128.png",
|
"icons/128x128.png",
|
||||||
@@ -47,7 +34,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"macOS": {
|
"macOS": {
|
||||||
"minimumSystemVersion": "10.15"
|
"minimumSystemVersion": "10.15",
|
||||||
|
"entitlements": "Entitlements.plist"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Referencia en una nueva incidencia
Block a user