diff --git a/tauri2-docs/cheatsheet.md b/tauri2-docs/cheatsheet.md index 7ad95b0..0b75306 100644 --- a/tauri2-docs/cheatsheet.md +++ b/tauri2-docs/cheatsheet.md @@ -27,6 +27,8 @@ my-app/ ├── src-tauri/ # Backend environment │ ├── capabilities/ # NEW: Security capability JSON/TOML files │ │ └── default.json # Maps app windows to permissions +│ ├── gen/ +│ │ └── schemas/ # Generated JSON schemas (referenced by $schema in capabilities) │ ├── src/ │ │ ├── main.rs # Minimal platform entry-point │ │ └── lib.rs # Core Application setup & commands @@ -130,7 +132,7 @@ If you want your Tauri application to **act as a WebSocket Server** (e.g., expos [dependencies] tauri = { version = "2.0", features = [] } tokio = { version = "1", features = ["full"] } -tokio-tungstenite = "0.21" # Industry standard high-performance WS crate +tokio-tungstenite = "0.24" # Industry standard high-performance WS crate futures-util = "0.3" ``` @@ -147,7 +149,7 @@ async fn start_ws_server(port: &str) { .expect("Failed to bind port"); while let Ok((stream, _)) = listener.accept().await { - tokio::spawn(async move { + tauri::async_runtime::spawn(async move { if let Ok(mut ws_stream) = accept_async(stream).await { println!("New connection established to Tauri WS Server!"); while let Some(Ok(msg)) = ws_stream.next().await { @@ -165,8 +167,8 @@ async fn start_ws_server(port: &str) { pub fn run() { tauri::Builder::default() .setup(|app| { - // Spin up native server safely in an isolated async tokio environment - tokio::spawn(start_ws_server("8080")); + // Spin up native server safely using Tauri's async runtime (NOT tokio::spawn directly) + tauri::async_runtime::spawn(start_ws_server("8080")); Ok(()) }) .run(tauri::generate_context!()) @@ -263,10 +265,13 @@ fn create_extra_window(app: &tauri::AppHandle) { } ``` -### Single Instance Lock & Deep Linking -Ensures only one instance runs, piping external application schemas (e.g. `my-app://open?token=xyz`) into the active runtime. +### Single Instance Lock +Ensures only one instance runs, focusing the existing window when a second launch is attempted. ```rust +// src-tauri/Cargo.toml: tauri-plugin-single-instance = "2" +// src-tauri/capabilities/default.json: add "single-instance:default" to permissions + // src-tauri/src/lib.rs #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { @@ -282,3 +287,67 @@ pub fn run() { .expect("failed execution"); } ``` + +### Deep Linking with `tauri-plugin-deep-link` +Registers a custom URL scheme (e.g., `my-app://open?token=xyz`) so the OS routes external links into your running app. + +#### 1. Setup +```toml +# src-tauri/Cargo.toml +[dependencies] +tauri-plugin-deep-link = "2" +``` +```json +// src-tauri/capabilities/default.json — add permission +{ + "permissions": ["deep-link:default"] +} +``` +```json +// src-tauri/tauri.conf.json — register the scheme +{ + "app": { + "deepLink": { + "desktop": { + "schemes": ["my-app"] + }, + "mobile": { + "scheme": "myapp" + } + } + } +} +``` + +#### 2. Rust Handler +```rust +// src-tauri/src/lib.rs +use tauri_plugin_deep_link::DeepLinkExt; + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .plugin(tauri_plugin_deep_link::init()) + .setup(|app| { + // macOS / Linux / Windows desktop handler + #[cfg(desktop)] + app.deep_link().on_open_url(|url| { + println!("Deep link opened: {}", url); + // Parse URL and route to the appropriate handler + }); + Ok(()) + }) + .run(tauri::generate_context!()) + .expect("failed execution"); +} +``` + +#### 3. Frontend Listener (optional) +```typescript +import { listen } from '@tauri-apps/api/event'; + +// The deep-link plugin emits 'deep-link://new-url' events +await listen('deep-link://new-url', (event) => { + console.log('Received deep link:', event.payload); +}); +``` diff --git a/tauri2-docs/tauri2-links.md b/tauri2-docs/tauri2-links.md index 4837729..ba48b04 100644 --- a/tauri2-docs/tauri2-links.md +++ b/tauri2-docs/tauri2-links.md @@ -13,5 +13,35 @@ * Tauri 2.0 Official Configuration Files Layout Schema (`tauri.conf.json`): https://v2.tauri.app/develop/configuration-files/ -* Tauri 2.0 Rust API Documentation Engine (Official `tauri` Crate Source Docs): - https://docs.rs +* Tauri 2.0 Rust Crate API Documentation (Official `tauri` Crate Source Docs): + https://docs.rs/tauri/latest/tauri/ + +* Tauri 2.0 Plugin Index (All Official Plugins): + https://v2.tauri.app/plugin/ + +* Tauri 2.0 JavaScript API Reference: + https://v2.tauri.app/references/javascript/ + +* Tauri 2.0 Calling Rust from Frontend (`invoke` Commands): + https://v2.tauri.app/develop/calling-rust/ + +* Tauri 2.0 Calling Frontend from Rust (Events & Channels): + https://v2.tauri.app/develop/calling-frontend/ + +* Tauri 2.0 Architecture Overview: + https://v2.tauri.app/develop/architecture/ + +* Tauri 2.0 Security Overview: + https://v2.tauri.app/security/ + +* Tauri 2.0 Window Customization: + https://v2.tauri.app/develop/window-customization/ + +* Tauri 2.0 System Tray Guide: + https://v2.tauri.app/develop/tray/ + +* Tauri 2.0 Frontend Frameworks (Vite Integration): + https://v2.tauri.app/start/frontend/ + +* Tauri 2.0 Build Configuration: + https://v2.tauri.app/develop/building/ diff --git a/tauri2-docs/ws_server_client_inject_guide.md b/tauri2-docs/ws_server_client_inject_guide.md index a931649..68fe409 100644 --- a/tauri2-docs/ws_server_client_inject_guide.md +++ b/tauri2-docs/ws_server_client_inject_guide.md @@ -235,7 +235,11 @@ mainSocket.onerror = (err) => { // Sending data to other connected windows function sendToAllWindows(payload) { - mainSocket.send(JSON.stringify(payload)); + if (mainSocket.readyState === WebSocket.OPEN) { + mainSocket.send(JSON.stringify(payload)); + } else { + console.warn('WebSocket not open — cannot send. readyState:', mainSocket.readyState); + } } // Example: button click sends data diff --git a/tauri2-guide/agent-prompts.md b/tauri2-guide/agent-prompts.md index 43641a2..dd3a78c 100644 --- a/tauri2-guide/agent-prompts.md +++ b/tauri2-guide/agent-prompts.md @@ -21,6 +21,7 @@ 12. [Prompt: Full App Build (End-to-End)](#12-prompt-full-app-build-end-to-end) 13. [Verification Checklist for Agents](#13-verification-checklist-for-agents) 14. [Quick Reference: v1 vs v2 API Map](#14-quick-reference-v1-vs-v2-api-map) +15. [Prompt: Debug a Tauri 2.0 Build/Compile Error](#15-prompt-debug-a-tauri-20-buildcompile-error) --- @@ -507,3 +508,68 @@ Do NOT skip any file. Generate complete, production-ready code. | `tauri.updater` | `plugins.updater` | | `tauri.systemTray` | `app.trayIcon` | | `tauri.cli` | `plugins.cli` | + +--- + +## 15. Prompt: Debug a Tauri 2.0 Build/Compile Error + +``` +Diagnose the following Tauri 2.0 build or compile error. + +[PASTE FULL ERROR OUTPUT HERE] + +Follow this structured diagnosis process: + +1. READ THE ERROR MESSAGE CAREFULLY + - Identify the exact error code (e.g., E0432, E0599, E0757, E0277, E0255) + - Note which file and line number the error originates from + - Capture any "note" or "help" lines the compiler provides + +2. CROSS-REFERENCE AGAINST ERROR CATEGORIES + Consult the `error-resolution-guide.md` categories and match the error: + + - Category A (Rust Compilation Errors): Version mismatches, missing [lib], windres, strip failures + - Category B (v1→v2 Migration): tauri::api removals, Window→WebviewWindow, JS import paths + - Category C (Async & Thread Safety): Borrowed refs in async, Mutex not Send, Rc/RefCell in async + - Category D (Config Errors): V1 config keys, allowlist, tauri→app section, devUrl/frontendDist + - Category E (Capabilities & Permissions): Plugin not allowed, event permissions, path scope + - Category F (Plugin Integration): Plugin not registered in Builder, wrong plugin API usage + - Category G (IPC / Command Errors): Command not found, multiple invoke_handler, pub in lib.rs + - Category H (Webview & Window): WebView2 errors, blank screens, GDK main thread + - Category I (Event System): Events not received, emit vs emit_to confusion + - Category J (CSP Errors): IPC connection refused, inline styles blocked + - Category K (Frontend Integration): Vite port mismatch, Next.js static export, SvelteKit adapter + - Category L (WebSocket): Broken pipe, unstable connections + - Category M (Path & Asset): 403 on asset:// protocol + - Category O (Mobile Build): iOS/Android specific failures + +3. CHECK FOR COMMON PATTERNS + - [ ] v1 imports still present (tauri::api::*, @tauri-apps/api/tauri, @tauri-apps/api/window) + - [ ] Missing `pub` on commands in separate modules (or extra `pub` on commands in lib.rs) + - [ ] tauri/tauri-build version mismatch (one is "1" and the other is "2") + - [ ] Async command uses borrowed references (&str, &Path) without Result wrapping + - [ ] std::sync::Mutex held across .await (should be tokio::sync::Mutex) + - [ ] Plugin in Cargo.toml but not registered with .plugin() in Builder + - [ ] Missing capability permission for the plugin/command being used + - [ ] Multiple .invoke_handler() calls (only last one takes effect) + - [ ] tokio::spawn used instead of tauri::async_runtime::spawn inside setup() + - [ ] Window creation in async context without run_on_main_thread() + +4. OUTPUT STRUCTURED DIAGNOSIS + Format your response exactly as: + + ## Error Diagnosis + + **Error Category:** [Category letter + name from error-resolution-guide.md] + **Error Code:** [Rust error code if applicable, e.g., E0432] + **Root Cause:** [1-3 sentence explanation of WHY this error occurs] + **Fix Steps:** + 1. [Specific file to edit] + - [Exact change to make, with before/after code if applicable] + 2. [Additional files if needed] + - [Changes] + **Verification:** [How to confirm the fix works — what should the output look like after] + **Cross-Reference:** [Relevant section in error-resolution-guide.md] + + If the error doesn't match any known category, say so explicitly and provide your best analysis based on the error message, Rust compiler output, and Tauri 2.0 API knowledge. +``` diff --git a/tauri2-guide/definitive-guide.md b/tauri2-guide/definitive-guide.md index a7bd0da..9975feb 100644 --- a/tauri2-guide/definitive-guide.md +++ b/tauri2-guide/definitive-guide.md @@ -693,6 +693,7 @@ For streaming large amounts of data to the frontend: ```rust use tauri::ipc::Channel; +use tokio::io::AsyncReadExt; #[tauri::command] async fn stream_file(path: std::path::PathBuf, on_chunk: Channel>) { @@ -1048,7 +1049,7 @@ await newWindow.once('tauri://error', (e) => { | `tauri::WindowBuilder` | `tauri::WebviewWindowBuilder` | | `tauri::WindowUrl` | `tauri::WebviewUrl` | | `app.get_window("main")` | `app.get_webview_window("main")` | -| `WebviewUrl::App(path)` | `WebviewUrl::App(path.into())` | +| `tauri::WindowUrl::App(path)` | `tauri::WebviewUrl::App(path.into())` | | `WebviewUrl::External(url)` | `WebviewUrl::External(url)` | --- @@ -1092,9 +1093,9 @@ await newWindow.once('tauri://error', (e) => { | Window State | `tauri-plugin-window-state` | `@tauri-apps/plugin-window-state` | Win, Lin, Mac, Android, iOS | | Window Customization | `tauri-plugin-window-customization` | `@tauri-apps/plugin-window-customization` | Win, Lin, Mac | -### Adding a Plugin (3-Step Pattern) +### Adding a Plugin (4-Step Pattern) -Every plugin follows the same 3-step setup: +Every plugin follows the same 4-step setup: **Step 1: Add Rust dependency** ```toml diff --git a/tauri2-guide/error-resolution-guide.md b/tauri2-guide/error-resolution-guide.md index d0c15e1..8dfc891 100644 --- a/tauri2-guide/error-resolution-guide.md +++ b/tauri2-guide/error-resolution-guide.md @@ -88,6 +88,8 @@ crate-type = ["staticlib", "cdylib", "rlib"] ``` Create `src-tauri/src/lib.rs` with your core logic in `pub fn run()`, and a minimal `main.rs` that calls `my_app_lib::run()`. +> **Naming convention:** The `[lib]` `name` field must match the crate name used in `main.rs`'s function call (`crate_name::run()`). Rust replaces hyphens with underscores in crate identifiers, so a `name = "my-app-lib"` in Cargo.toml becomes `my_app_lib` in code — your `main.rs` must call `my_app_lib::run()`, not `my-app-lib::run()`. Keep this naming consistent across `[lib]`, `main.rs`, and any `mod` re-exports. + --- ### A4. `windres` Not Found (Windows) @@ -949,7 +951,7 @@ tauri::Builder::default() error[E0255]: the name '__cmd__my_command' is defined multiple times ``` -**Cause:** Commands in `lib.rs` cannot be `pub` due to glue code generation. +**Cause:** The `#[tauri::command]` macro generates a companion item named `__cmd__` in the same scope. When a command in `lib.rs` is marked `pub`, the generated `__cmd__` glue item is also `pub` — and since `lib.rs` itself re-exports everything at the crate root, the command's `__cmd__` symbol collides with a second copy that `generate_handler![]` produces internally. This can also happen when the same command function appears in multiple modules (e.g., defined in one module and `pub use`'d into another). **Solution:** Remove `pub` from commands in `lib.rs`, or move them to a separate module where they MUST be `pub`: ```rust @@ -1033,7 +1035,10 @@ Or `0x800700AA` ("The requested resource is in use"). **Cause:** Multiple possible causes: (1) WebView2 runtime not installed/corrupted, (2) Invalid `additional_browser_args`, (3) Another process holding a WebView2 lock, (4) Data folder creation failure. **Solution:** -1. Install/repair WebView2 runtime +1. Install/repair WebView2 runtime: + - **winget (recommended):** `winget install Microsoft.WebView2Runtime` + - **Direct download:** https://developer.microsoft.com/en-us/microsoft-edge/webview2/ + - **Evergreen Bootstrapper:** Download and run the bootstrapper from the link above to install/update to the latest version 2. Remove custom `additional_browser_args` from config 3. Kill old Tauri dev instances: `taskkill /f /im .exe` 4. For multi-webview support, ensure `features = ["unstable"]` in Cargo.toml @@ -1574,6 +1579,79 @@ No code signing certificates found. You must add one and set the certificate dev --- +### O9. Android SDK Not Found + +**Error:** +``` +Android SDK not found at ANDROID_HOME. Please set the ANDROID_HOME environment variable. +``` +Or Gradle fails with `SDK location not found`. + +**Cause:** The `ANDROID_HOME` (or `ANDROID_SDK_ROOT`) environment variable is not set, or points to a nonexistent directory. Tauri's Android build requires the Android SDK to be available. + +**Solution:** +1. Install Android Studio, which bundles the SDK +2. Set the environment variable: +```bash +# macOS/Linux — add to ~/.zshrc or ~/.bashrc +export ANDROID_HOME="$HOME/Android/Sdk" +export PATH="$ANDROID_HOME/platform-tools:$PATH" + +# Windows — set via System Properties → Environment Variables +ANDROID_HOME=C:\Users\\AppData\Local\Android\Sdk +``` +3. Verify: `echo $ANDROID_HOME` and `adb version` +4. Re-run `npx tauri android init` + +--- + +### O10. iOS Development Requires macOS + +**Error:** +``` +iOS is not supported on this platform +``` +Or `xcodebuild: command not found` when running on Linux or Windows. + +**Cause:** iOS builds require Xcode and the Apple toolchain, which are only available on macOS. Cross-compilation to iOS from Linux or Windows is not supported by Tauri. + +**Solution:** +1. Use a Mac (physical or CI runner like GitHub Actions `macos-latest`) +2. Install Xcode from the Mac App Store +3. Accept the Xcode license: `sudo xcodebuild -license accept` +4. Install CocoaPods: `sudo gem install cocoapods` (or `brew install cocoapods`) +5. For CI, use GitHub Actions with `macos-latest` runner + +--- + +### O11. `pod install` Failed (iOS) + +**Error:** +``` +Pod installation failed. CocoaPods could not find compatible versions for pod "TauriDriver" +``` +Or `pod: command not found`. + +**Cause:** CocoaPods is not installed, outdated, or the Podfile.lock is stale and conflicts with updated dependencies. + +**Solution:** +1. Ensure CocoaPods is installed: +```bash +sudo gem install cocoapods +# Or via Homebrew: +brew install cocoapods +``` +2. Clean and reinstall pods: +```bash +cd src-tauri/gen/apple +rm -rf Podfile.lock Pods +pod install --repo-update +``` +3. If version conflicts persist, update CocoaPods to the latest version: `sudo gem install cocoapods --pre` +4. Ensure your Xcode Command Line Tools are up to date: `xcode-select --install` + +--- + ## Category P: Platform-Specific Build Errors ### P1. Linux — Missing System Dependencies