diff --git a/apps/desktop/src-tauri/Cargo.lock b/apps/desktop/src-tauri/Cargo.lock index 8a2010ae..34cbb131 100644 --- a/apps/desktop/src-tauri/Cargo.lock +++ b/apps/desktop/src-tauri/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "TrixtyIDE" -version = "0.0.0" +version = "1.1.5" dependencies = [ "dirs 5.0.1", "grep-regex", @@ -397,6 +397,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.11.1" @@ -2037,7 +2043,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" dependencies = [ "byteorder", - "png", + "png 0.17.16", ] [[package]] @@ -2170,6 +2176,19 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "png 0.18.1", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -2663,6 +2682,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + [[package]] name = "muda" version = "0.17.2" @@ -2678,7 +2707,7 @@ dependencies = [ "objc2-core-foundation", "objc2-foundation", "once_cell", - "png", + "png 0.17.16", "serde", "thiserror 2.0.18", "windows-sys 0.60.2", @@ -3486,6 +3515,19 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.11.1", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "portable-pty" version = "0.9.0" @@ -3651,6 +3693,12 @@ dependencies = [ "psl-types", ] +[[package]] +name = "pxfm" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" + [[package]] name = "quick-xml" version = "0.38.4" @@ -5163,6 +5211,7 @@ dependencies = [ "gtk", "heck 0.5.0", "http", + "image", "jni", "libc", "log", @@ -5230,7 +5279,7 @@ dependencies = [ "ico", "json-patch", "plist", - "png", + "png 0.17.16", "proc-macro2", "quote", "semver", @@ -6008,7 +6057,7 @@ dependencies = [ "objc2-core-graphics", "objc2-foundation", "once_cell", - "png", + "png 0.17.16", "serde", "thiserror 2.0.18", "windows-sys 0.60.2", diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml index 13bde4fd..8a50d07f 100644 --- a/apps/desktop/src-tauri/Cargo.toml +++ b/apps/desktop/src-tauri/Cargo.toml @@ -37,7 +37,7 @@ tauri-build = { version = "2.5.6", features = [] } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } log = "0.4" -tauri = { version = "2.10.3", features = [] } +tauri = { version = "2.10.3", features = [ "image-ico", "image-png", "tray-icon"] } tauri-plugin-log = "2" tauri-plugin-shell = "2.0.0" tauri-plugin-store = "2.0.0" diff --git a/apps/desktop/src-tauri/capabilities/default.json b/apps/desktop/src-tauri/capabilities/default.json index bb43535a..4afa90ee 100644 --- a/apps/desktop/src-tauri/capabilities/default.json +++ b/apps/desktop/src-tauri/capabilities/default.json @@ -23,6 +23,16 @@ "core:window:allow-start-resize-dragging", "core:window:allow-show", "core:window:allow-hide", + "core:window:allow-create", + "core:window:allow-set-size", + "core:window:allow-set-position", + "core:webview:default", + "core:webview:allow-create-webview", + "core:webview:allow-webview-close", + "core:webview:allow-set-webview-position", + "core:webview:allow-set-webview-size", + "core:webview:allow-webview-show", + "core:webview:allow-webview-hide", "positioner:default", "window-state:default", diff --git a/apps/desktop/src-tauri/src/discord_rpc.rs b/apps/desktop/src-tauri/src/discord_rpc.rs index e88a6c9b..73e3c820 100644 --- a/apps/desktop/src-tauri/src/discord_rpc.rs +++ b/apps/desktop/src-tauri/src/discord_rpc.rs @@ -2,6 +2,10 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use log::{error, info, warn}; +#[cfg(unix)] +use std::env; +#[cfg(unix)] +use std::path::PathBuf; use tauri::{AppHandle, Emitter}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; #[cfg(unix)] diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index 36e45ea5..fbc08608 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -21,8 +21,10 @@ use scraper::{Html, Selector}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs; +use std::net::TcpStream; use std::path::PathBuf; use std::sync::{Arc, Mutex}; +use std::time::Duration; use sysinfo::System; use tauri::Manager; use tauri_plugin_store::StoreExt; @@ -41,7 +43,11 @@ use tracing_subscriber::prelude::*; /// supervisor lazily the first time you spawn. #[inline] fn silent_command(program: &str) -> Command { + #[cfg(target_os = "windows")] let mut cmd = Command::new(program); + #[cfg(not(target_os = "windows"))] + let cmd = Command::new(program); + #[cfg(target_os = "windows")] { // `creation_flags` and `raw_arg` are inherent methods on @@ -627,6 +633,15 @@ async fn get_system_health( }) } +#[tauri::command] +async fn check_port(port: u16) -> bool { + TcpStream::connect_timeout( + &format!("127.0.0.1:{}", port).parse().unwrap(), + Duration::from_millis(150), + ) + .is_ok() +} + #[tauri::command] async fn git_init(path: String) -> Result { let output = silent_command("git") @@ -2685,6 +2700,23 @@ fn delete_path(path: String, workspace: tauri::State<'_, WorkspaceState>) -> Res } } +#[tauri::command] +async fn open_browser_window(app: tauri::AppHandle, url: String) -> Result<(), String> { + // Generate a unique label so we can open multiple browser windows + let label = format!("browser-{}", uuid::Uuid::new_v4()); + let _ = tauri::WebviewWindowBuilder::new( + &app, + &label, + tauri::WebviewUrl::External(url.parse::().map_err(|e| e.to_string())?), + ) + .title("Trixty Browser") + .inner_size(1200.0, 800.0) + .resizable(true) + .build() + .map_err(|e| e.to_string())?; + Ok(()) +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { // Initialize Sentry @@ -2775,6 +2807,7 @@ pub fn run() { .manage::(new_initial_join_secret(initial_join_secret)) .manage(DiscordState(Arc::new(tokio::sync::Mutex::new(discord_rpc::DiscordRpc::new())))) .invoke_handler(tauri::generate_handler![ + check_port, read_directory, read_file, write_file, @@ -2848,7 +2881,8 @@ pub fn run() { set_discord_activity, accept_discord_join_request, reject_discord_join_request, - get_initial_join_secret + get_initial_join_secret, + open_browser_window ]) .setup(|app| { // Main window is required — failing fast with a structured error beats @@ -2992,6 +3026,9 @@ pub fn run() { let _ = main_window.set_focus(); }); + + + // Start Discord RPC background task let discord_state = app.handle().state::(); let mut discord_rpc = discord_state.0.blocking_lock(); diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index 8d2aa739..52902ec4 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -43,7 +43,7 @@ ], "withGlobalTauri": false, "security": { - "csp": "default-src 'self'; img-src 'self' data: asset: https://asset.localhost https://raw.githubusercontent.com; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' ipc: http://ipc.localhost https://ipc.localhost; font-src 'self' data:;" + "csp": "default-src 'self'; img-src 'self' data: asset: https://asset.localhost https://raw.githubusercontent.com; script-src 'self' blob:; style-src 'self' 'unsafe-inline'; connect-src 'self' ipc: http://ipc.localhost https://ipc.localhost; font-src 'self' data:; frame-src *; worker-src 'self' blob:;" } }, "plugins": { diff --git a/apps/desktop/src/api/builtin.l10n.ts b/apps/desktop/src/api/builtin.l10n.ts index 012f5d22..171d0461 100644 --- a/apps/desktop/src/api/builtin.l10n.ts +++ b/apps/desktop/src/api/builtin.l10n.ts @@ -94,6 +94,16 @@ export function registerBuiltinTranslations() { 'marketplace.view_install': 'View & Install', 'marketplace.installed_badge': 'Installed', + 'browser.title': 'Browser', + 'browser.back': 'Back', + 'browser.forward': 'Forward', + 'browser.reload': 'Reload', + 'browser.address_placeholder': 'Enter URL...', + 'browser.open_external': 'Open in External Browser', + 'browser.open_native': 'Open in Native Window', + 'browser.native_mode_active': 'Native Engine Active (No Restrictions)', + 'browser.security_note': 'Some sites may block embedding for security.', + 'git.no_repo': 'No Repository', 'git.no_repo_desc': 'Initialize a Git repository to begin', 'git.init_button': 'git init', @@ -613,6 +623,16 @@ export function registerBuiltinTranslations() { 'marketplace.view_install': 'Ver e Instalar', 'marketplace.installed_badge': 'Instalada', + 'browser.title': 'Navegador', + 'browser.back': 'Atrás', + 'browser.forward': 'Adelante', + 'browser.reload': 'Recargar', + 'browser.address_placeholder': 'Ingrese URL...', + 'browser.open_external': 'Abrir en navegador externo', + 'browser.open_native': 'Abrir en ventana nativa', + 'browser.native_mode_active': 'Motor nativo activo (sin restricciones)', + 'browser.security_note': 'Algunos sitios pueden bloquear su inserción por seguridad.', + 'git.no_repo': 'Sin Repositorio', 'git.no_repo_desc': 'Inicializa un repositorio Git para empezar', 'git.init_button': 'git init', diff --git a/apps/desktop/src/components/ActivityBar.tsx b/apps/desktop/src/components/ActivityBar.tsx index a9aa6470..36ad21c0 100644 --- a/apps/desktop/src/components/ActivityBar.tsx +++ b/apps/desktop/src/components/ActivityBar.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { Settings, Package } from "lucide-react"; +import { Settings, Package, Globe } from "lucide-react"; import { useUI } from "@/context/UIContext"; import { useFiles } from "@/context/FilesContext"; import { trixty, WebviewView } from "@/api/trixty"; @@ -39,6 +39,11 @@ const ActivityBar: React.FC = () => { return; } + if (id === "browser") { + openFile("virtual://browser", t('browser.title'), "", "virtual"); + return; + } + if (id === "settings") { setSettingsOpen(true); return; @@ -87,6 +92,18 @@ const ActivityBar: React.FC = () => { ); })}
+ + +
+

Press ENTER to launch browser

+ + + )} + + {/* Browser Toolbar */} +
+
+ + +
+ +
+ +
+ http://localhost: + handleToolbarPortChange(e.target.value)} + placeholder="----" + className="bg-transparent border-none outline-none text-white/40 flex-1 ml-0.5 placeholder:text-white/5" + /> +
+ {isChecking &&
} +
+ + +
+ + {/* Main Content Area */} +
+ {(url && isServerUp) ? ( +