diff --git a/autoqa/scripts/setup-android-env.sh b/autoqa/scripts/setup-android-env.sh index a327c9e2b..62adc079f 100755 --- a/autoqa/scripts/setup-android-env.sh +++ b/autoqa/scripts/setup-android-env.sh @@ -9,24 +9,53 @@ export PATH="$HOME/.cargo/bin:$PATH" export JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home export PATH="/opt/homebrew/opt/openjdk@17/bin:$PATH" -export ANDROID_HOME=~/Library/Android/sdk -export ANDROID_NDK_ROOT=~/Library/Android/sdk/ndk/29.0.14033849 -export NDK_HOME=~/Library/Android/sdk/ndk/29.0.14033849 +export ANDROID_HOME="$HOME/Library/Android/sdk" +export ANDROID_NDK_ROOT="$HOME/Library/Android/sdk/ndk/29.0.14033849" +export NDK_HOME="$HOME/Library/Android/sdk/ndk/29.0.14033849" # Add Android tools to PATH export PATH=$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/emulator:$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin # Set up CC and CXX for Android compilation -export CC_aarch64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android21-clang -export CXX_aarch64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android21-clang++ -export AR_aarch64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-ar -export RANLIB_aarch64_linux_android=$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-ranlib +export CC_aarch64_linux_android="$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android21-clang" +export CXX_aarch64_linux_android="$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android21-clang++" +export AR_aarch64_linux_android="$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-ar" +export RANLIB_aarch64_linux_android="$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-ranlib" + +# Additional environment variables for Rust cross-compilation +export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android21-clang" + +# Only set global CC and AR for Android builds (when TAURI_ANDROID_BUILD is set) +if [ "$TAURI_ANDROID_BUILD" = "true" ]; then + export CC="$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android21-clang" + export AR="$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-ar" + echo "Global CC and AR set for Android build" +fi # Create symlinks for Android tools if they don't exist mkdir -p ~/.local/bin if [ ! -f ~/.local/bin/aarch64-linux-android-ranlib ]; then ln -sf $NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-ranlib ~/.local/bin/aarch64-linux-android-ranlib fi +if [ ! -f ~/.local/bin/aarch64-linux-android-clang ]; then + ln -sf $NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android21-clang ~/.local/bin/aarch64-linux-android-clang +fi +if [ ! -f ~/.local/bin/aarch64-linux-android-clang++ ]; then + ln -sf $NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android21-clang++ ~/.local/bin/aarch64-linux-android-clang++ +fi + +# Fix the broken clang symlinks by ensuring base clang is available +if [ ! -f ~/.local/bin/clang ]; then + ln -sf $NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang ~/.local/bin/clang +fi +if [ ! -f ~/.local/bin/clang++ ]; then + ln -sf $NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++ ~/.local/bin/clang++ +fi + +# Create symlinks for target-specific ar tools +if [ ! -f ~/.local/bin/aarch64-linux-android-ar ]; then + ln -sf $NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-ar ~/.local/bin/aarch64-linux-android-ar +fi export PATH="$HOME/.local/bin:$PATH" echo "Android environment configured:" diff --git a/package.json b/package.json index 7e56913d8..99747edb0 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "scripts": { "lint": "yarn workspace @janhq/web-app lint", "dev": "yarn dev:tauri", + "ios": "yarn tauri ios dev", + "android": "yarn tauri android dev", "build": "yarn build:web && yarn build:tauri", "test": "vitest run", "test:watch": "vitest", @@ -24,9 +26,9 @@ "serve:web-app": "yarn workspace @janhq/web-app serve:web", "build:serve:web-app": "yarn build:web-app && yarn serve:web-app", "dev:tauri": "yarn build:icon && yarn copy:assets:tauri && cross-env IS_CLEAN=true tauri dev", - "dev:ios": "yarn copy:assets:mobile && RUSTC_WRAPPER= yarn tauri ios dev", - "dev:android": "yarn copy:assets:mobile && yarn tauri android dev", - "build:android": "yarn build && yarn copy:assets:mobile && yarn tauri android build --no-default-features --features mobile", + "dev:ios": "yarn copy:assets:mobile && RUSTC_WRAPPER= yarn tauri ios dev --features mobile", + "dev:android": "yarn copy:assets:mobile && TAURI_ANDROID_BUILD=true yarn tauri android dev --features mobile", + "build:android": "yarn build && yarn copy:assets:mobile && TAURI_ANDROID_BUILD=true yarn tauri android build --no-default-features --features mobile", "build:ios": "yarn build && yarn copy:assets:mobile && yarn tauri ios build --no-default-features --features mobile", "build:ios:device": "yarn build && yarn copy:assets:mobile && yarn tauri ios build --no-default-features --features mobile --export-method debugging", "copy:assets:tauri": "cpx \"pre-install/*.tgz\" \"src-tauri/resources/pre-install/\" && cpx \"LICENSE\" \"src-tauri/resources/\"", diff --git a/src-tauri/.cargo/config.toml b/src-tauri/.cargo/config.toml index 978a02b7b..9089e8115 100644 --- a/src-tauri/.cargo/config.toml +++ b/src-tauri/.cargo/config.toml @@ -7,6 +7,7 @@ ENABLE_SYSTEM_TRAY_ICON = "false" [target.aarch64-linux-android] linker = "aarch64-linux-android21-clang" ar = "llvm-ar" +rustflags = ["-C", "link-arg=-fuse-ld=lld"] [target.armv7-linux-androideabi] linker = "armv7a-linux-androideabi21-clang" @@ -19,5 +20,3 @@ ar = "llvm-ar" [target.i686-linux-android] linker = "i686-linux-android21-clang" ar = "llvm-ar" - - diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 07272b7bf..562d9c67e 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -23,10 +23,19 @@ default = [ "tauri/tray-icon", "tauri/test", "tauri/custom-protocol", - "hardware", + "desktop", ] hardware = ["dep:tauri-plugin-hardware"] -mobile = [] +deep-link = ["dep:tauri-plugin-deep-link"] +desktop = [ + "deep-link", + "hardware" +] +mobile = [ + "tauri/protocol-asset", + "tauri/test", + "tauri/wry", +] test-tauri = [ "tauri/wry", "tauri/x11", @@ -63,11 +72,11 @@ serde_json = "1.0" serde_yaml = "0.9.34" tar = "0.4" zip = "0.6" -tauri-plugin-deep-link = { version = "2.3.4" } tauri-plugin-dialog = "2.2.1" +tauri-plugin-deep-link = { version = "2", optional = true } tauri-plugin-hardware = { path = "./plugins/tauri-plugin-hardware", optional = true } -tauri-plugin-http = { version = "2", features = ["unsafe-headers"] } tauri-plugin-llamacpp = { path = "./plugins/tauri-plugin-llamacpp" } +tauri-plugin-http = { version = "2", features = ["unsafe-headers"] } tauri-plugin-log = "2.0.0-rc" tauri-plugin-opener = "2.2.7" tauri-plugin-os = "2.2.1" @@ -97,8 +106,16 @@ windows-sys = { version = "0.60.2", features = ["Win32_Storage_FileSystem"] } [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] tauri-plugin-updater = "2" once_cell = "1.18" +tauri-plugin-single-instance = { version = "2", features = ["deep-link"] } -tauri-plugin-single-instance = { version = "2.3.4", features = ["deep-link"] } +[target.'cfg(any(target_os = "android", target_os = "ios"))'.dependencies] +tauri-plugin-dialog = { version = "2.2.1", default-features = false } +tauri-plugin-http = { version = "2", default-features = false } +tauri-plugin-log = { version = "2.0.0-rc", default-features = false } +tauri-plugin-opener = { version = "2.2.7", default-features = false } +tauri-plugin-os = { version = "2.2.1", default-features = false } +tauri-plugin-shell = { version = "2.2.0", default-features = false } +tauri-plugin-store = { version = "2", default-features = false } # Release profile optimizations for minimal binary size [profile.release] diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 432de21d9..8587a460a 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -19,7 +19,6 @@ "opener:default", "log:default", "dialog:default", - "deep-link:default", "core:webview:allow-create-webview-window", "opener:allow-open-url", { @@ -55,7 +54,6 @@ ] }, "store:default", - "llamacpp:default", - "hardware:default" + "llamacpp:default" ] } diff --git a/src-tauri/capabilities/desktop.json b/src-tauri/capabilities/desktop.json new file mode 100644 index 000000000..41be646d3 --- /dev/null +++ b/src-tauri/capabilities/desktop.json @@ -0,0 +1,63 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "desktop", + "description": "enables the default permissions for desktop platforms", + "windows": ["main"], + "remote": { + "urls": ["http://*"] + }, + "platforms": ["linux", "macOS", "windows"], + "permissions": [ + "core:default", + "core:webview:allow-set-webview-zoom", + "core:window:allow-start-dragging", + "core:window:allow-set-theme", + "shell:allow-spawn", + "shell:allow-open", + "core:app:allow-set-app-theme", + "core:window:allow-set-focus", + "os:default", + "opener:default", + "log:default", + "dialog:default", + "core:webview:allow-create-webview-window", + "opener:allow-open-url", + "store:default", + "llamacpp:default", + "deep-link:default", + "hardware:default", + + { + "identifier": "http:default", + "allow": [ + { + "url": "https://*:*" + }, + { + "url": "http://*:*" + } + ], + "deny": [] + }, + { + "identifier": "shell:allow-execute", + "allow": [] + }, + { + "identifier": "opener:allow-open-url", + "description": "opens the default permissions for the core module", + "windows": ["*"], + "allow": [ + { + "url": "https://*" + }, + { + "url": "http://127.0.0.1:*" + }, + { + "url": "http://0.0.0.0:*" + } + ] + } + ] +} \ No newline at end of file diff --git a/src-tauri/capabilities/mobile.json b/src-tauri/capabilities/mobile.json new file mode 100644 index 000000000..fdbda476a --- /dev/null +++ b/src-tauri/capabilities/mobile.json @@ -0,0 +1,58 @@ +{ + "$schema": "../gen/schemas/mobile-schema.json", + "identifier": "mobile", + "description": "enables the default permissions for mobile platforms", + "windows": ["main"], + "remote": { + "urls": ["http://*"] + }, + "permissions": [ + "core:default", + "core:webview:allow-set-webview-zoom", + "core:window:allow-start-dragging", + "core:window:allow-set-theme", + "shell:allow-spawn", + "shell:allow-open", + "core:app:allow-set-app-theme", + "core:window:allow-set-focus", + "os:default", + "opener:default", + "log:default", + "dialog:default", + "core:webview:allow-create-webview-window", + "opener:allow-open-url", + "store:default", + { + "identifier": "http:default", + "allow": [ + { + "url": "https://*:*" + }, + { + "url": "http://*:*" + } + ], + "deny": [] + }, + { + "identifier": "shell:allow-execute", + "allow": [] + }, + { + "identifier": "opener:allow-open-url", + "description": "opens the default permissions for the core module", + "windows": ["*"], + "allow": [ + { + "url": "https://*" + }, + { + "url": "http://127.0.0.1:*" + }, + { + "url": "http://0.0.0.0:*" + } + ] + } + ] +} \ No newline at end of file diff --git a/src-tauri/capabilities/system-monitor-window.json b/src-tauri/capabilities/system-monitor-window.json index 740bb82cc..68a75e9fb 100644 --- a/src-tauri/capabilities/system-monitor-window.json +++ b/src-tauri/capabilities/system-monitor-window.json @@ -3,6 +3,7 @@ "identifier": "system-monitor-window", "description": "enables permissions for the system monitor window", "windows": ["system-monitor-window"], + "platforms": ["linux", "macOS", "windows"], "permissions": [ "core:default", "core:window:allow-start-dragging", diff --git a/src-tauri/gen/android/app/src/main/assets/resources/LICENSE b/src-tauri/gen/android/app/src/main/assets/resources/LICENSE new file mode 100644 index 000000000..d614b967f --- /dev/null +++ b/src-tauri/gen/android/app/src/main/assets/resources/LICENSE @@ -0,0 +1,19 @@ +Jan + +Copyright 2025 Menlo Research + +This product includes software developed by Menlo Research (https://menlo.ai). + +Licensed under the Apache License, Version 2.0 (the "License"); +You may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Attribution is requested in user-facing documentation and materials, where appropriate. \ No newline at end of file diff --git a/src-tauri/plugins/tauri-plugin-hardware/src/vendor/vulkan.rs b/src-tauri/plugins/tauri-plugin-hardware/src/vendor/vulkan.rs index cad21895d..d911e7013 100644 --- a/src-tauri/plugins/tauri-plugin-hardware/src/vendor/vulkan.rs +++ b/src-tauri/plugins/tauri-plugin-hardware/src/vendor/vulkan.rs @@ -45,7 +45,6 @@ pub fn get_vulkan_gpus(lib_path: &str) -> Vec { } } -#[cfg(not(any(target_os = "android", target_os = "ios")))] fn parse_c_string_u8(buf: &[u8]) -> String { unsafe { std::ffi::CStr::from_ptr(buf.as_ptr() as *const std::ffi::c_char) } .to_str() diff --git a/src-tauri/src/core/app/commands.rs b/src-tauri/src/core/app/commands.rs index ba3e493b3..0d9c66c12 100644 --- a/src-tauri/src/core/app/commands.rs +++ b/src-tauri/src/core/app/commands.rs @@ -58,8 +58,8 @@ pub fn get_app_configurations(app_handle: tauri::AppHandle) -> Ap } #[tauri::command] -pub fn update_app_configuration( - app_handle: tauri::AppHandle, +pub fn update_app_configuration( + app_handle: tauri::AppHandle, configuration: AppConfiguration, ) -> Result<(), String> { let configuration_file = get_configuration_file_path(app_handle); @@ -155,13 +155,13 @@ pub fn default_data_folder_path(app_handle: tauri::AppHandle) -> } #[tauri::command] -pub fn get_user_home_path(app: AppHandle) -> String { +pub fn get_user_home_path(app: AppHandle) -> String { return get_app_configurations(app.clone()).data_folder; } #[tauri::command] -pub fn change_app_data_folder( - app_handle: tauri::AppHandle, +pub fn change_app_data_folder( + app_handle: tauri::AppHandle, new_data_folder: String, ) -> Result<(), String> { // Get current data folder path diff --git a/src-tauri/src/core/downloads/commands.rs b/src-tauri/src/core/downloads/commands.rs index f2187046a..6d50ed1a3 100644 --- a/src-tauri/src/core/downloads/commands.rs +++ b/src-tauri/src/core/downloads/commands.rs @@ -3,12 +3,12 @@ use super::models::DownloadItem; use crate::core::app::commands::get_jan_data_folder_path; use crate::core::state::AppState; use std::collections::HashMap; -use tauri::State; +use tauri::{Runtime, State}; use tokio_util::sync::CancellationToken; #[tauri::command] -pub async fn download_files( - app: tauri::AppHandle, +pub async fn download_files( + app: tauri::AppHandle, state: State<'_, AppState>, items: Vec, task_id: &str, diff --git a/src-tauri/src/core/downloads/helpers.rs b/src-tauri/src/core/downloads/helpers.rs index 137bbdd3d..ab6979575 100644 --- a/src-tauri/src/core/downloads/helpers.rs +++ b/src-tauri/src/core/downloads/helpers.rs @@ -6,7 +6,7 @@ use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; use std::collections::HashMap; use std::path::Path; use std::time::Duration; -use tauri::Emitter; +use tauri::{Emitter, Runtime}; use tokio::fs::File; use tokio::io::AsyncWriteExt; use tokio_util::sync::CancellationToken; @@ -25,7 +25,7 @@ pub fn err_to_string(e: E) -> String { async fn validate_downloaded_file( item: &DownloadItem, save_path: &Path, - app: &tauri::AppHandle, + app: &tauri::AppHandle, cancel_token: &CancellationToken, ) -> Result<(), String> { // Skip validation if no verification data is provided @@ -298,7 +298,7 @@ pub async fn _get_file_size( /// Downloads multiple files in parallel with individual progress tracking pub async fn _download_files_internal( - app: tauri::AppHandle, + app: tauri::AppHandle, items: &[DownloadItem], headers: &HashMap, task_id: &str, @@ -423,7 +423,7 @@ pub async fn _download_files_internal( /// Downloads a single file without blocking other downloads async fn download_single_file( - app: tauri::AppHandle, + app: tauri::AppHandle, item: &DownloadItem, header_map: &HeaderMap, save_path: &std::path::Path, diff --git a/src-tauri/src/core/extensions/commands.rs b/src-tauri/src/core/extensions/commands.rs index 784c71f46..4c5a44a53 100644 --- a/src-tauri/src/core/extensions/commands.rs +++ b/src-tauri/src/core/extensions/commands.rs @@ -1,24 +1,24 @@ use std::fs; use std::path::PathBuf; -use tauri::AppHandle; +use tauri::{AppHandle, Runtime}; use crate::core::app::commands::get_jan_data_folder_path; use crate::core::setup; #[tauri::command] -pub fn get_jan_extensions_path(app_handle: tauri::AppHandle) -> PathBuf { +pub fn get_jan_extensions_path(app_handle: tauri::AppHandle) -> PathBuf { get_jan_data_folder_path(app_handle).join("extensions") } #[tauri::command] -pub fn install_extensions(app: AppHandle) { +pub fn install_extensions(app: AppHandle) { if let Err(err) = setup::install_extensions(app, true) { log::error!("Failed to install extensions: {}", err); } } #[tauri::command] -pub fn get_active_extensions(app: AppHandle) -> Vec { +pub fn get_active_extensions(app: AppHandle) -> Vec { let mut path = get_jan_extensions_path(app); path.push("extensions.json"); log::info!("get jan extensions, path: {:?}", path); diff --git a/src-tauri/src/core/filesystem/commands.rs b/src-tauri/src/core/filesystem/commands.rs index 7cf7803c6..20af5cded 100644 --- a/src-tauri/src/core/filesystem/commands.rs +++ b/src-tauri/src/core/filesystem/commands.rs @@ -124,7 +124,7 @@ pub fn readdir_sync( #[tauri::command] pub fn write_yaml( - app: tauri::AppHandle, + app: tauri::AppHandle, data: serde_json::Value, save_path: &str, ) -> Result<(), String> { @@ -145,7 +145,7 @@ pub fn write_yaml( } #[tauri::command] -pub fn read_yaml(app: tauri::AppHandle, path: &str) -> Result { +pub fn read_yaml(app: tauri::AppHandle, path: &str) -> Result { let jan_data_folder = crate::core::app::commands::get_jan_data_folder_path(app.clone()); let path = jan_utils::normalize_path(&jan_data_folder.join(path)); if !path.starts_with(&jan_data_folder) { @@ -162,7 +162,7 @@ pub fn read_yaml(app: tauri::AppHandle, path: &str) -> Result Result<(), String> { +pub fn decompress(app: tauri::AppHandle, path: &str, output_dir: &str) -> Result<(), String> { let jan_data_folder = crate::core::app::commands::get_jan_data_folder_path(app.clone()); let path_buf = jan_utils::normalize_path(&jan_data_folder.join(path)); diff --git a/src-tauri/src/core/mcp/commands.rs b/src-tauri/src/core/mcp/commands.rs index 3bef12149..a86db598e 100644 --- a/src-tauri/src/core/mcp/commands.rs +++ b/src-tauri/src/core/mcp/commands.rs @@ -80,7 +80,7 @@ pub async fn deactivate_mcp_server(state: State<'_, AppState>, name: String) -> } #[tauri::command] -pub async fn restart_mcp_servers(app: AppHandle, state: State<'_, AppState>) -> Result<(), String> { +pub async fn restart_mcp_servers(app: AppHandle, state: State<'_, AppState>) -> Result<(), String> { let servers = state.mcp_servers.clone(); // Stop the servers stop_mcp_servers(state.mcp_servers.clone()).await?; @@ -119,7 +119,7 @@ pub async fn reset_mcp_restart_count( #[tauri::command] pub async fn get_connected_servers( - _app: AppHandle, + _app: AppHandle, state: State<'_, AppState>, ) -> Result, String> { let servers = state.mcp_servers.clone(); @@ -293,7 +293,7 @@ pub async fn cancel_tool_call( } #[tauri::command] -pub async fn get_mcp_configs(app: AppHandle) -> Result { +pub async fn get_mcp_configs(app: AppHandle) -> Result { let mut path = get_jan_data_folder_path(app); path.push("mcp_config.json"); @@ -308,7 +308,7 @@ pub async fn get_mcp_configs(app: AppHandle) -> Result { } #[tauri::command] -pub async fn save_mcp_configs(app: AppHandle, configs: String) -> Result<(), String> { +pub async fn save_mcp_configs(app: AppHandle, configs: String) -> Result<(), String> { let mut path = get_jan_data_folder_path(app); path.push("mcp_config.json"); log::info!("save mcp configs, path: {:?}", path); diff --git a/src-tauri/src/core/server/proxy.rs b/src-tauri/src/core/server/proxy.rs index 504afe72d..12398ac02 100644 --- a/src-tauri/src/core/server/proxy.rs +++ b/src-tauri/src/core/server/proxy.rs @@ -767,7 +767,7 @@ pub async fn start_server( }); *handle_guard = Some(server_task); - let actual_port = actual_addr.port(); + let actual_port = addr.port(); log::info!("Jan API server started successfully on port {}", actual_port); Ok(actual_port) } diff --git a/src-tauri/src/core/setup.rs b/src-tauri/src/core/setup.rs index 112fe9902..301378695 100644 --- a/src-tauri/src/core/setup.rs +++ b/src-tauri/src/core/setup.rs @@ -6,7 +6,7 @@ use std::{ }; use tar::Archive; use tauri::{ - App, Emitter, Manager, + App, Emitter, Manager, Runtime, }; #[cfg(desktop)] @@ -25,7 +25,7 @@ use super::{ mcp::helpers::run_mcp_commands, state::AppState, }; -pub fn install_extensions(app: tauri::AppHandle, force: bool) -> Result<(), String> { +pub fn install_extensions(app: tauri::AppHandle, force: bool) -> Result<(), String> { let mut store_path = get_jan_data_folder_path(app.clone()); store_path.push("store.json"); let store = app.store(store_path).expect("Store not initialized"); @@ -201,10 +201,10 @@ pub fn extract_extension_manifest( Ok(None) } -pub fn setup_mcp(app: &App) { +pub fn setup_mcp(app: &App) { let state = app.state::(); let servers = state.mcp_servers.clone(); - let app_handle: tauri::AppHandle = app.handle().clone(); + let app_handle = app.handle().clone(); tauri::async_runtime::spawn(async move { if let Err(e) = run_mcp_commands(&app_handle, servers).await { log::error!("Failed to run mcp commands: {}", e); diff --git a/src-tauri/src/core/system/commands.rs b/src-tauri/src/core/system/commands.rs index aaf3c76ec..f5e9d7618 100644 --- a/src-tauri/src/core/system/commands.rs +++ b/src-tauri/src/core/system/commands.rs @@ -1,6 +1,6 @@ use std::fs; use std::path::PathBuf; -use tauri::{AppHandle, Manager, State}; +use tauri::{AppHandle, Manager, Runtime, State}; use tauri_plugin_llamacpp::cleanup_llama_processes; use crate::core::app::commands::{ @@ -11,7 +11,7 @@ use crate::core::mcp::helpers::clean_up_mcp_servers; use crate::core::state::AppState; #[tauri::command] -pub fn factory_reset(app_handle: tauri::AppHandle, state: State<'_, AppState>) { +pub fn factory_reset(app_handle: tauri::AppHandle, state: State<'_, AppState>) { // close window (not available on mobile platforms) #[cfg(not(any(target_os = "ios", target_os = "android")))] { @@ -49,12 +49,12 @@ pub fn factory_reset(app_handle: tauri::AppHandle, state: State<'_, AppState>) { } #[tauri::command] -pub fn relaunch(app: AppHandle) { +pub fn relaunch(app: AppHandle) { app.restart() } #[tauri::command] -pub fn open_app_directory(app: AppHandle) { +pub fn open_app_directory(app: AppHandle) { let app_path = app.path().app_data_dir().unwrap(); if cfg!(target_os = "windows") { std::process::Command::new("explorer") @@ -96,7 +96,7 @@ pub fn open_file_explorer(path: String) { } #[tauri::command] -pub async fn read_logs(app: AppHandle) -> Result { +pub async fn read_logs(app: AppHandle) -> Result { let log_path = get_jan_data_folder_path(app).join("logs").join("app.log"); if log_path.exists() { let content = fs::read_to_string(log_path).map_err(|e| e.to_string())?; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 5b351b593..169ee5b19 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -8,54 +8,23 @@ use core::{ }; use jan_utils::generate_app_token; use std::{collections::HashMap, sync::Arc}; -#[cfg(not(any(target_os = "ios", target_os = "android")))] -use tauri_plugin_deep_link::DeepLinkExt; use tauri::{Emitter, Manager, RunEvent}; use tauri_plugin_llamacpp::cleanup_llama_processes; use tokio::sync::Mutex; -#[cfg(desktop)] -use crate::core::setup::setup_tray; - -#[cfg_attr(mobile, tauri::mobile_entry_point)] +#[cfg_attr(all(mobile, any(target_os = "android", target_os = "ios")), tauri::mobile_entry_point)] pub fn run() { - #[cfg(desktop)] let mut builder = tauri::Builder::default(); - #[cfg(mobile)] - let builder = tauri::Builder::default(); #[cfg(desktop)] { builder = builder.plugin(tauri_plugin_single_instance::init(|_app, argv, _cwd| { println!("a new app instance was opened with {argv:?} and the deep link event was already triggered"); // when defining deep link schemes at runtime, you must also check `argv` here - let arg = argv.iter().find(|arg| arg.starts_with("jan://")); - if let Some(deep_link) = arg { - println!("deep link: {deep_link}"); - // handle the deep link, e.g., emit an event to the webview - _app.app_handle().emit("deep-link", deep_link).unwrap(); - if let Some(window) = _app.app_handle().get_webview_window("main") { - let _ = window.set_focus(); - } - } })); } - #[cfg(feature = "hardware")] - let app = builder + let mut app_builder = builder .plugin(tauri_plugin_os::init()) - .plugin(tauri_plugin_deep_link::init()) - .plugin(tauri_plugin_dialog::init()) - .plugin(tauri_plugin_opener::init()) - .plugin(tauri_plugin_http::init()) - .plugin(tauri_plugin_store::Builder::new().build()) - .plugin(tauri_plugin_shell::init()) - .plugin(tauri_plugin_llamacpp::init()) - .plugin(tauri_plugin_hardware::init()); - - #[cfg(not(feature = "hardware"))] - let app = builder - .plugin(tauri_plugin_os::init()) - .plugin(tauri_plugin_deep_link::init()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_http::init()) @@ -63,7 +32,17 @@ pub fn run() { .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_llamacpp::init()); - let app = app + #[cfg(feature = "deep-link")] + { + app_builder = app_builder.plugin(tauri_plugin_deep_link::init()); + } + + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + app_builder = app_builder.plugin(tauri_plugin_hardware::init()); + } + + let app = app_builder .invoke_handler(tauri::generate_handler![ // FS commands - Deperecate soon core::filesystem::commands::join_path, @@ -138,24 +117,6 @@ pub fn run() { server_handle: Arc::new(Mutex::new(None)), tool_call_cancellations: Arc::new(Mutex::new(HashMap::new())), }) - .on_window_event(|window, event| match event { - tauri::WindowEvent::CloseRequested { api, .. } => { - if option_env!("ENABLE_SYSTEM_TRAY_ICON").unwrap_or("false") == "true" { - #[cfg(target_os = "macos")] - window - .app_handle() - .set_activation_policy(tauri::ActivationPolicy::Accessory) - .unwrap(); - - #[cfg(not(any(target_os = "ios", target_os = "android")))] - window.hide().unwrap(); - #[cfg(any(target_os = "ios", target_os = "android"))] - let _ = window; // Use window to avoid unused variable warning - api.prevent_close(); - } - } - _ => {} - }) .setup(|app| { app.handle().plugin( tauri_plugin_log::Builder::default() @@ -177,15 +138,11 @@ pub fn run() { log::error!("Failed to install extensions: {}", e); } - #[cfg(desktop)] - if option_env!("ENABLE_SYSTEM_TRAY_ICON").unwrap_or("false") == "true" { - log::info!("Enabling system tray icon"); - let _ = setup_tray(app); - } - - #[cfg(all(not(any(target_os = "ios", target_os = "android")), any(windows, target_os = "linux")))] + #[cfg(all(feature = "deep-link", any(windows, target_os = "linux")))] { - app.deep_link().register_all()?; + use tauri_plugin_deep_link::DeepLinkExt; + // Register the deep-link scheme programmatically + app.deep_link().register("jan")?; } setup_mcp(app); Ok(()) @@ -199,13 +156,6 @@ pub fn run() { // This is called when the app is actually exiting (e.g., macOS dock quit) // We can't prevent this, so run cleanup quickly let app_handle = app.clone(); - // Hide window immediately - if let Some(window) = app_handle.get_webview_window("main") { - #[cfg(not(any(target_os = "ios", target_os = "android")))] - let _ = window.hide(); - #[cfg(any(target_os = "ios", target_os = "android"))] - let _ = window; // Use window to avoid unused variable warning - } tokio::task::block_in_place(|| { tauri::async_runtime::block_on(async { // Hide window immediately (not available on mobile platforms) diff --git a/src-tauri/tauri.android.conf.json b/src-tauri/tauri.android.conf.json index a77961841..a5136b851 100644 --- a/src-tauri/tauri.android.conf.json +++ b/src-tauri/tauri.android.conf.json @@ -1,13 +1,15 @@ { + "identifier": "jan.ai.app", "build": { "devUrl": null, "frontendDist": "../web-app/dist" }, "app": { "security": { - "capabilities": ["default"] + "capabilities": ["mobile"] } }, + "plugins": {}, "bundle": { "resources": ["resources/LICENSE", "resources/pre-install"], "externalBin": [] diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 6aaa66bb7..90e87b824 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -72,8 +72,7 @@ "windows": { "installMode": "passive" } - }, - "deep-link": { "schemes": ["jan"] } + } }, "bundle": { "active": true, diff --git a/src-tauri/tauri.ios.conf.json b/src-tauri/tauri.ios.conf.json index fa201d467..53b1cff49 100644 --- a/src-tauri/tauri.ios.conf.json +++ b/src-tauri/tauri.ios.conf.json @@ -3,11 +3,13 @@ "devUrl": null, "frontendDist": "../web-app/dist" }, + "identifier": "jan.ai.app.ios", "app": { "security": { - "capabilities": ["default"] + "capabilities": ["mobile"] } }, + "plugins": {}, "bundle": { "active": true, "iOS": { diff --git a/src-tauri/tauri.linux.conf.json b/src-tauri/tauri.linux.conf.json index 80e7446ff..552217384 100644 --- a/src-tauri/tauri.linux.conf.json +++ b/src-tauri/tauri.linux.conf.json @@ -1,8 +1,12 @@ { + "app": { + "security": { + "capabilities": ["default", "system-monitor-window"] + } + }, "bundle": { "targets": ["deb", "appimage"], - "resources": ["resources/pre-install/**/*", "resources/LICENSE"], - "externalBin": ["resources/bin/uv"], + "resources": ["resources/LICENSE"], "linux": { "appimage": { "bundleMediaFramework": false, diff --git a/src-tauri/tauri.macos.conf.json b/src-tauri/tauri.macos.conf.json index d7d80f669..79f165df7 100644 --- a/src-tauri/tauri.macos.conf.json +++ b/src-tauri/tauri.macos.conf.json @@ -1,7 +1,11 @@ { + "app": { + "security": { + "capabilities": ["default", "system-monitor-window"] + } + }, "bundle": { "targets": ["app", "dmg"], - "resources": ["resources/pre-install/**/*", "resources/LICENSE"], - "externalBin": ["resources/bin/bun", "resources/bin/uv"] + "resources": ["resources/LICENSE"] } } diff --git a/src-tauri/tauri.windows.conf.json b/src-tauri/tauri.windows.conf.json index 16cb9b10a..1030563ee 100644 --- a/src-tauri/tauri.windows.conf.json +++ b/src-tauri/tauri.windows.conf.json @@ -1,4 +1,10 @@ { + "app": { + "security": { + "capabilities": ["default"] + } + }, + "bundle": { "targets": ["nsis"], "resources": ["resources/pre-install/**/*", "resources/lib/vulkan-1.dll", "resources/lib/vc_redist.x64.exe", "resources/LICENSE"],