From 368a3db23d6a9fc804bfbb0e88e54fd14a015d9f Mon Sep 17 00:00:00 2001 From: Z User Date: Sat, 30 May 2026 20:52:16 +0000 Subject: [PATCH] Split guide files into <8k token chunks for small LLMs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - definitive-guide.md → 2 parts (core/arch/config/commands + windows/plugins/websocket/state/migration) - error-resolution-guide.md → 2 parts (build/config/plugin errors + runtime/platform/mobile errors) - agent-prompts.md → 2 parts (system prompt/common tasks + advanced tasks/checklist/migration map) - Updated README.md with new file structure and per-topic routing table - All files under ~7.5k tokens, suitable for 8k context windows - Cross-reference links between split parts --- README.md | 159 +-- ...-build-config-permission-plugin-errors.md} | 818 +-------------- ...1-core-architecture-config-commands-ipc.md | 930 ++++++++++++++++++ .../01-system-prompt-and-common-prompts.md | 217 ++++ ...2-advanced-prompts-checklist-migration.md} | 208 +--- .../02-runtime-platform-mobile-errors.md | 833 ++++++++++++++++ ...dows-plugins-websocket-state-migration.md} | 922 +---------------- 7 files changed, 2099 insertions(+), 1988 deletions(-) rename tauri2-guide/{error-resolution-guide.md => 01-build-config-permission-plugin-errors.md} (54%) create mode 100644 tauri2-guide/01-core-architecture-config-commands-ipc.md create mode 100644 tauri2-guide/01-system-prompt-and-common-prompts.md rename tauri2-guide/{agent-prompts.md => 02-advanced-prompts-checklist-migration.md} (63%) create mode 100644 tauri2-guide/02-runtime-platform-mobile-errors.md rename tauri2-guide/{definitive-guide.md => 02-windows-plugins-websocket-state-migration.md} (50%) diff --git a/README.md b/README.md index b8b3c26..92fb287 100644 --- a/README.md +++ b/README.md @@ -11,80 +11,121 @@ A comprehensive, self-contained reference library for building applications with ```text tauri2-vin-ws-temp/ -├── tauri2-guide/ # AUTHORITATIVE GUIDES — start here -│ ├── definitive-guide.md # Full reference (architecture, config, commands, events, plugins, windows, state, migration) -│ ├── agent-prompts.md # Copy-paste prompts for instructing LLM agents to build Tauri 2.0 apps -│ └── error-resolution-guide.md # Every known error category with exact messages, root causes, and solutions +├── tauri2-guide/ # AUTHORITATIVE GUIDES — start here +│ │ +│ │ ── Definitive Developer Guide (2 parts) ────────────────────── +│ ├── 01-core-architecture-config-commands-ipc.md # Sections 1–7 +│ ├── 02-windows-plugins-websocket-state-migration.md # Sections 8–15 +│ │ +│ │ ── Agent Prompts (2 parts) ────────────────────────────────── +│ ├── 01-system-prompt-and-common-prompts.md # System prompt + prompts 1–7 +│ ├── 02-advanced-prompts-checklist-migration.md # Prompts 8–15 + checklist + API map +│ │ +│ │ ── Error Resolution Guide (2 parts) ──────────────────────── +│ ├── 01-build-config-permission-plugin-errors.md # Categories A–H +│ └── 02-runtime-platform-mobile-errors.md # Categories I–R + Top 25 │ -├── tauri2-docs/ # SUPPLEMENTARY REFERENCES -│ ├── cheatsheet.md # Quick-reference cheat sheet (commands, config, IPC, WebSocket patterns) -│ ├── ws_server_client_inject_guide.md # Deep-dive: native WebSocket server, frontend clients, script injection -│ └── tauri2-links.md # Curated links to official Tauri 2.0 documentation pages +├── tauri2-docs/ # SUPPLEMENTARY REFERENCES +│ ├── cheatsheet.md # Quick-reference cheat sheet +│ ├── ws_server_client_inject_guide.md # WebSocket server, client, injection deep-dive +│ └── tauri2-links.md # Curated links to official docs │ -└── README.md # THIS FILE — navigation index for LLMs and developers +└── README.md # THIS FILE — navigation index ``` +All guide files are sized under **8k tokens** so small LLMs can load them without losing context. Each split file includes cross-reference links to its companion part. + --- ## How to Use This Repository ### For LLMs / AI Agents -This repository is designed to be **searched directly**. When you receive a Tauri 2.0 question, use the table below to locate the most relevant document, then read the specific section referenced. +This repository is designed to be **searched directly**. When you receive a Tauri 2.0 question, use the table below to locate the most relevant file, then read the specific section referenced. **Important constraints for agents:** - You **cannot compile or run code** in a sandbox. Verify correctness using logic, API pattern matching, and cross-referencing these documents. -- All code in this repository is **Tauri 2.0 only** — do NOT use v1 patterns (see the migration section in `definitive-guide.md` for the full v1-to-v2 diff). +- All code in this repository is **Tauri 2.0 only** — do NOT use v1 patterns (see the migration section for the full v1-to-v2 diff). - Every plugin requires 4 things: (1) Rust crate in `Cargo.toml`, (2) `.plugin()` registration in the Builder, (3) npm package, (4) permissions in capabilities. ### For Developers -Use the **Definitive Guide** (`tauri2-guide/definitive-guide.md`) as your primary reference. It covers every aspect of Tauri 2.0 development end-to-end. For specific tasks, use the **Agent Prompts** as templates. For debugging, search the **Error Resolution Guide** by error message. +Use the **Definitive Guide** Part 1 as your primary reference for core concepts. Part 2 covers advanced topics. For specific tasks, use the **Agent Prompts** as templates. For debugging, search the **Error Resolution Guide** by error message. --- ## Document Index — What to Read for Each Topic -| Topic | File | Key Sections | -|-------|------|-------------| -| **Architecture overview, project structure, scaffolding** | `tauri2-guide/definitive-guide.md` | Sections 1–2 | -| **Configuration (`tauri.conf.json` v2 schema)** | `tauri2-guide/definitive-guide.md` | Section 3 | -| **Capabilities & permissions (ACL security model)** | `tauri2-guide/definitive-guide.md` | Section 4 | -| **Rust backend: `lib.rs`, commands, `Builder` pattern** | `tauri2-guide/definitive-guide.md` | Section 5 | -| **Frontend integration (JS imports, `invoke`, plugins)** | `tauri2-guide/definitive-guide.md` | Section 6 | -| **IPC: commands, events, channels, streaming** | `tauri2-guide/definitive-guide.md` | Section 7 | -| **Window management (multi-window, external URLs)** | `tauri2-guide/definitive-guide.md` | Section 8 | -| **Plugin ecosystem (all 33+ official plugins)** | `tauri2-guide/definitive-guide.md` | Section 9 | -| **Native WebSocket server (Tokio + broadcast)** | `tauri2-guide/definitive-guide.md` Section 10 / `tauri2-docs/ws_server_client_inject_guide.md` Part 1 | -| **Script injection into webview windows** | `tauri2-guide/definitive-guide.md` Section 11 / `tauri2-docs/ws_server_client_inject_guide.md` Part 3 | -| **State management (Mutex, managed state)** | `tauri2-guide/definitive-guide.md` | Section 12 | -| **v1 to v2 migration (full API diff)** | `tauri2-guide/definitive-guide.md` Section 13 / `tauri2-guide/agent-prompts.md` Section 14 | -| **Anti-patterns to avoid** | `tauri2-guide/definitive-guide.md` | Section 14 | -| **Quick reference cheat sheet** | `tauri2-docs/cheatsheet.md` | All sections | -| **WebSocket server + client + injection deep-dive** | `tauri2-docs/ws_server_client_inject_guide.md` | Parts 1–3 | -| **Links to official docs (migration, security, config)** | `tauri2-docs/tauri2-links.md` | All entries | -| **Agent system prompt (mandatory prefix for all tasks)** | `tauri2-guide/agent-prompts.md` | Section 1 | -| **Task-specific agent prompts (scaffold, plugin, command, etc.)** | `tauri2-guide/agent-prompts.md` | Sections 2–12 | -| **Agent verification checklist** | `tauri2-guide/agent-prompts.md` | Section 13 | -| **Rust compilation errors (Cargo build failures)** | `tauri2-guide/error-resolution-guide.md` | Category A | -| **v1-to-v2 migration compile errors** | `tauri2-guide/error-resolution-guide.md` | Category B | -| **Async/thread safety errors (Mutex, Send, lifetime)** | `tauri2-guide/error-resolution-guide.md` | Category C | -| **Config errors (`tauri.conf.json` validation)** | `tauri2-guide/error-resolution-guide.md` | Category D | -| **Permission/capability errors ("not allowed")** | `tauri2-guide/error-resolution-guide.md` | Category E | -| **Plugin integration errors** | `tauri2-guide/error-resolution-guide.md` | Category F | -| **IPC / command runtime errors** | `tauri2-guide/error-resolution-guide.md` | Category G | -| **Webview & window runtime errors** | `tauri2-guide/error-resolution-guide.md` | Category H | -| **Event system errors** | `tauri2-guide/error-resolution-guide.md` | Category I | -| **Content Security Policy (CSP) errors** | `tauri2-guide/error-resolution-guide.md` | Category J | -| **Frontend integration errors (Vite, Next.js, SvelteKit)** | `tauri2-guide/error-resolution-guide.md` | Category K | -| **WebSocket errors** | `tauri2-guide/error-resolution-guide.md` | Category L | -| **Path & asset protocol errors** | `tauri2-guide/error-resolution-guide.md` | Category M | -| **Shell & sidecar errors** | `tauri2-guide/error-resolution-guide.md` | Category N | -| **Mobile build errors (iOS & Android)** | `tauri2-guide/error-resolution-guide.md` | Category O | -| **Platform-specific build errors** | `tauri2-guide/error-resolution-guide.md` | Category P | -| **Panics & runtime crashes** | `tauri2-guide/error-resolution-guide.md` | Category Q | -| **Error handling best practices** | `tauri2-guide/error-resolution-guide.md` | Category R | -| **Top 25 most common errors (quick lookup)** | `tauri2-guide/error-resolution-guide.md` | Quick-Reference section | +### Definitive Developer Guide + +| Topic | File | Sections | +|-------|------|----------| +| Architecture overview, project structure, scaffolding | `01-core-architecture-config-commands-ipc.md` | 1–2 | +| Configuration (`tauri.conf.json` v2 schema) | `01-core-architecture-config-commands-ipc.md` | 3 | +| Capabilities & permissions (ACL security model) | `01-core-architecture-config-commands-ipc.md` | 4 | +| Rust backend: `lib.rs`, commands, Builder pattern | `01-core-architecture-config-commands-ipc.md` | 5 | +| Frontend integration (JS imports, invoke, plugins) | `01-core-architecture-config-commands-ipc.md` | 6 | +| IPC: commands, events, channels, streaming | `01-core-architecture-config-commands-ipc.md` | 7 | +| Window management (multi-window, external URLs) | `02-windows-plugins-websocket-state-migration.md` | 8 | +| Plugin ecosystem (all 33+ official plugins) | `02-windows-plugins-websocket-state-migration.md` | 9 | +| Native WebSocket server (Tokio + broadcast) | `02-windows-plugins-websocket-state-migration.md` / `tauri2-docs/ws_server_client_inject_guide.md` | 10 | +| Script injection into webview windows | `02-windows-plugins-websocket-state-migration.md` / `tauri2-docs/ws_server_client_inject_guide.md` | 11 | +| State management (Mutex, managed state) | `02-windows-plugins-websocket-state-migration.md` | 12 | +| v1 to v2 migration (full API diff) | `02-windows-plugins-websocket-state-migration.md` | 13 | +| Anti-patterns to avoid | `02-windows-plugins-websocket-state-migration.md` | 14 | +| Quick reference cheat sheet | `02-windows-plugins-websocket-state-migration.md` | 15 | + +### Agent Prompts + +| Topic | File | Section | +|-------|------|---------| +| Universal system prompt (REQUIRED prefix) | `01-system-prompt-and-common-prompts.md` | 1 | +| Scaffold a new Tauri 2.0 project | `01-system-prompt-and-common-prompts.md` | 2 | +| Add a Tauri plugin | `01-system-prompt-and-common-prompts.md` | 3 | +| Create a custom command (IPC) | `01-system-prompt-and-common-prompts.md` | 4 | +| Add state management | `01-system-prompt-and-common-prompts.md` | 5 | +| Implement event-based communication | `01-system-prompt-and-common-prompts.md` | 6 | +| Create multi-window app | `01-system-prompt-and-common-prompts.md` | 7 | +| Build a WebSocket server in Rust | `02-advanced-prompts-checklist-migration.md` | 8 | +| Implement script injection | `02-advanced-prompts-checklist-migration.md` | 9 | +| Set up capabilities & permissions | `02-advanced-prompts-checklist-migration.md` | 10 | +| Migrate a v1 app to v2 | `02-advanced-prompts-checklist-migration.md` | 11 | +| Full app build (end-to-end) | `02-advanced-prompts-checklist-migration.md` | 12 | +| Verification checklist for agents | `02-advanced-prompts-checklist-migration.md` | 13 | +| v1 vs v2 API map | `02-advanced-prompts-checklist-migration.md` | 14 | +| Debug a Tauri 2.0 build/compile error | `02-advanced-prompts-checklist-migration.md` | 15 | + +### Error Resolution Guide + +| Topic | File | Category | +|-------|------|----------| +| Rust compilation errors (Cargo build failures) | `01-build-config-permission-plugin-errors.md` | A | +| v1-to-v2 migration compile errors | `01-build-config-permission-plugin-errors.md` | B | +| Async/thread safety errors (Mutex, Send, lifetime) | `01-build-config-permission-plugin-errors.md` | C | +| Config errors (`tauri.conf.json` validation) | `01-build-config-permission-plugin-errors.md` | D | +| Permission/capability errors ("not allowed") | `01-build-config-permission-plugin-errors.md` | E | +| Plugin integration errors | `01-build-config-permission-plugin-errors.md` | F | +| IPC / command runtime errors | `01-build-config-permission-plugin-errors.md` | G | +| Webview & window runtime errors | `01-build-config-permission-plugin-errors.md` | H | +| Event system errors | `02-runtime-platform-mobile-errors.md` | I | +| Content Security Policy (CSP) errors | `02-runtime-platform-mobile-errors.md` | J | +| Frontend integration errors (Vite, Next.js, SvelteKit) | `02-runtime-platform-mobile-errors.md` | K | +| WebSocket errors | `02-runtime-platform-mobile-errors.md` | L | +| Path & asset protocol errors | `02-runtime-platform-mobile-errors.md` | M | +| Shell & sidecar errors | `02-runtime-platform-mobile-errors.md` | N | +| Mobile build errors (iOS & Android) | `02-runtime-platform-mobile-errors.md` | O | +| Platform-specific build errors | `02-runtime-platform-mobile-errors.md` | P | +| Panics & runtime crashes | `02-runtime-platform-mobile-errors.md` | Q | +| Error handling best practices | `02-runtime-platform-mobile-errors.md` | R | +| Top 25 most common errors (quick lookup) | `02-runtime-platform-mobile-errors.md` | Quick-Reference | + +### Supplementary References + +| Topic | File | +|-------|------| +| Quick-reference cheat sheet (commands, config, IPC, WebSocket patterns) | `tauri2-docs/cheatsheet.md` | +| WebSocket server + client + injection deep-dive | `tauri2-docs/ws_server_client_inject_guide.md` | +| Links to official docs (migration, security, config) | `tauri2-docs/tauri2-links.md` | --- @@ -110,7 +151,7 @@ Tauri 2.0 is a **complete rewrite**, not an incremental update. The most common | Event emit | `window.emit()` | `app.emit()` (global) / `app.emit_to()` (targeted) | | Rust APIs | `tauri::api::*` modules | Everything moved to plugins | -**Full migration map**: See `tauri2-guide/agent-prompts.md` Section 14 (v1 vs v2 API Map). +**Full migration map**: See `02-advanced-prompts-checklist-migration.md` Section 14 (v1 vs v2 API Map). ### The 4-Step Plugin Pattern @@ -159,13 +200,13 @@ const result = await invoke('my_command', { name: 'World' }); When looking for information in this repository, search for these keywords and patterns: -- **Error messages**: Search the exact error string (e.g., `"unresolved import tauri::api"`, `"command not found"`, `"not allowed"`) in `error-resolution-guide.md` -- **API names**: Search for type names (e.g., `WebviewWindowBuilder`, `Emitter`, `Channel`) in `definitive-guide.md` -- **Config keys**: Search for JSON key names (e.g., `frontendDist`, `withGlobalTauri`, `capabilities`) in `definitive-guide.md` Section 3 -- **Plugin names**: Search for plugin identifiers (e.g., `tauri-plugin-fs`, `plugin-dialog`, `store:default`) in `definitive-guide.md` Section 9 +- **Error messages**: Search the exact error string (e.g., `"unresolved import tauri::api"`, `"command not found"`, `"not allowed"`) in the Error Resolution Guide files +- **API names**: Search for type names (e.g., `WebviewWindowBuilder`, `Emitter`, `Channel`) in the Definitive Guide files +- **Config keys**: Search for JSON key names (e.g., `frontendDist`, `withGlobalTauri`, `capabilities`) in `01-core-architecture-config-commands-ipc.md` +- **Plugin names**: Search for plugin identifiers (e.g., `tauri-plugin-fs`, `plugin-dialog`, `store:default`) in `02-windows-plugins-websocket-state-migration.md` - **Migration**: Search for old v1 names (e.g., `WindowBuilder`, `allowlist`, `@tauri-apps/api/tauri`) to find their v2 replacements -- **Patterns**: Search for pattern keywords (e.g., `initialization_script`, `run_on_main_thread`, `broadcast channel`, `include_str`) in both `definitive-guide.md` and `ws_server_client_inject_guide.md` -- **Permission identifiers**: Search for permission strings (e.g., `core:event:allow-listen`, `fs:allow-read`, `http:default`) in `definitive-guide.md` Section 4 and `error-resolution-guide.md` Category E +- **Patterns**: Search for pattern keywords (e.g., `initialization_script`, `run_on_main_thread`, `broadcast channel`, `include_str`) in both the Definitive Guide and `ws_server_client_inject_guide.md` +- **Permission identifiers**: Search for permission strings (e.g., `core:event:allow-listen`, `fs:allow-read`, `http:default`) in `01-core-architecture-config-commands-ipc.md` Section 4 and `01-build-config-permission-plugin-errors.md` Category E --- diff --git a/tauri2-guide/error-resolution-guide.md b/tauri2-guide/01-build-config-permission-plugin-errors.md similarity index 54% rename from tauri2-guide/error-resolution-guide.md rename to tauri2-guide/01-build-config-permission-plugin-errors.md index 8dfc891..040c241 100644 --- a/tauri2-guide/error-resolution-guide.md +++ b/tauri2-guide/01-build-config-permission-plugin-errors.md @@ -1,9 +1,9 @@ -# Tauri 2.0 Error Resolution Guide +# Tauri 2.0 Error Resolution Guide — Part 1: Build, Config & Plugin Errors > **Version**: 2.0 | **Last Updated**: 2026-05-31 > **Official Docs**: https://v2.tauri.app > -> This guide catalogs every known class of error in Tauri 2.0 development — Rust compilation errors, Cargo/dependency failures, runtime panics, permission errors, frontend integration issues, mobile build failures, and platform-specific problems. Each error includes the exact message, root cause, and working solution. +> This file covers Categories A-H (build-time and configuration errors). Part 2 covers runtime, platform, mobile errors and best practices. --- @@ -17,17 +17,8 @@ 6. [Category F: Plugin Integration Errors](#category-f-plugin-integration-errors) 7. [Category G: IPC / Command Runtime Errors](#category-g-ipc--command-runtime-errors) 8. [Category H: Webview & Window Runtime Errors](#category-h-webview--window-runtime-errors) -9. [Category I: Event System Errors](#category-i-event-system-errors) -10. [Category J: Content Security Policy (CSP) Errors](#category-j-content-security-policy-csp-errors) -11. [Category K: Frontend Integration Errors (Vite / Next.js / SvelteKit)](#category-k-frontend-integration-errors-vite--nextjs--sveltekit) -12. [Category L: WebSocket Errors](#category-l-websocket-errors) -13. [Category M: Path & Asset Protocol Errors](#category-m-path--asset-protocol-errors) -14. [Category N: Shell & Sidecar Errors](#category-n-shell--sidecar-errors) -15. [Category O: Mobile Build Errors (iOS & Android)](#category-o-mobile-build-errors-ios--android) -16. [Category P: Platform-Specific Build Errors](#category-p-platform-specific-build-errors) -17. [Category Q: Panics & Runtime Crashes](#category-q-panics--runtime-crashes) -18. [Category R: Error Handling Best Practices](#category-r-error-handling-best-practices) -19. [Quick-Reference: Top 25 Most Common Errors](#quick-reference-top-25-most-common-errors) + +**See also**: [Part 2: Runtime, Platform & Mobile Errors](./02-runtime-platform-mobile-errors.md) --- @@ -1091,805 +1082,6 @@ Note: This uses nightly/unstable Rust features. --- -## Category I: Event System Errors - -### I1. Events Emitted But Not Received by New Window - -**Error:** Events emitted to a newly created window are not received the first time. - -**Cause:** Race condition — the event is emitted before the listener is attached in the new window. - -**Solution:** Use a handshake pattern: -```javascript -// In new window — announce ready -import { emit } from '@tauri-apps/api/event'; -emit('window-ready', { label: 'my-window' }); - -// In main window — respond after ready signal -listen('window-ready', (event) => { - emitTo(event.payload.label, 'initial-data', myData); -}); -``` - --- -### I2. Events Not Working Between Windows - -**Error:** Events emitted from one window are not received by another window. - -**Cause:** Using `emit()` (which broadcasts to all) vs. `emitTo()` (which targets a specific window) incorrectly, or listeners scoped incorrectly. - -**Solution:** -```rust -// Emit to ALL windows -app.emit("my-event", payload)?; - -// Emit to a SPECIFIC window -app.emit_to("settings-panel", "my-event", payload)?; -``` -```javascript -// Frontend: listen globally -import { listen } from '@tauri-apps/api/event'; -const unlisten = await listen('my-event', handler); - -// Frontend: listen on specific window only -import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'; -const appWebview = getCurrentWebviewWindow(); -const unlisten2 = await appWebview.listen('my-event', handler); -``` - ---- - -### I3. Rapid Event Emission Causes Panic - -**Error:** App crashes when emitting large numbers of events rapidly. - -**Cause:** Internal buffer overflow or deadlock in the event system. - -**Solution:** Throttle/debounce event emission. For high-frequency data, use `Channel` instead of events: -```rust -use tauri::ipc::Channel; - -#[tauri::command] -fn stream_data(on_event: Channel) { - for item in data_source { - on_event.send(item).unwrap(); - } -} -``` - ---- - -## Category J: Content Security Policy (CSP) Errors - -### J1. `Refused to connect to 'http://ipc.localhost'` - -**Error:** -``` -Refused to connect to 'http://ipc.localhost/test' because it violates the document's Content Security Policy. -``` - -**Cause:** External URLs loaded in webviews have their own CSP that doesn't allow connections to Tauri's IPC endpoint. - -**Solution:** Configure CSP to include IPC: -```json -{ - "app": { - "security": { - "csp": "default-src 'self'; connect-src 'self' ipc: http://ipc.localhost https://your-url.com" - } - } -} -``` -For maximum compatibility with injected scripts on external pages: `"csp": null` (use with caution). - ---- - -### J2. CSS-in-JS Libraries Blocked by CSP - -**Error:** Styles/scripts from styled-components, emotion, or other CSS-in-JS libraries are blocked. - -**Cause:** Default CSP doesn't allow inline styles. - -**Solution:** Add `'unsafe-inline'` to `style-src`: -```json -{ - "app": { - "security": { - "csp": "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self'" - } - } -} -``` - ---- - -## Category K: Frontend Integration Errors (Vite / Next.js / SvelteKit) - -### K1. Vite Dev Server Not Connecting / White Screen - -**Error:** Tauri window opens but shows blank white screen. Dev server connection not established. - -**Cause:** Port mismatch between `devUrl` in `tauri.conf.json` and Vite's actual port. - -**Solution:** -1. Ensure `devUrl` matches Vite's actual port: `"devUrl": "http://localhost:5173"` -2. For mobile, use `process.env.TAURI_DEV_HOST` as the host in Vite config -3. Set `server.host` in `vite.config.js` to allow network access - ---- - -### K2. Vite Watches `src-tauri` — Infinite Recompilation - -**Error:** App keeps recompiling Rust code repeatedly in an infinite loop. - -**Cause:** Vite's file watcher detects changes in `src-tauri/` (from Cargo build output) and triggers re-runs. - -**Solution:** Add `src-tauri` to Vite's ignore list: -```javascript -// vite.config.js -export default defineConfig({ - server: { - watch: { - ignored: ["**/src-tauri/**"] - } - } -}); -``` - ---- - -### K3. Next.js App Router Breaks in Production - -**Error:** `useEffect` hooks broken, syntax errors in chunk files, component rendering failures in production. - -**Cause:** Next.js App Router uses React Server Components and advanced chunking that conflicts with Tauri's custom protocol. - -**Solution:** Use static export only: -```javascript -// next.config.js -module.exports = { - output: 'export', // Required — no SSR - images: { unoptimized: true }, // No server-side image optimization -}; -``` -Add `'use client'` directives to all interactive components. - ---- - -### K4. Next.js `assetPrefix` Breaks Hot Reload - -**Error:** Hot reload doesn't work when `assetPrefix` is set for Tauri. - -**Cause:** `assetPrefix` changes the URL for WebSocket connections, breaking HMR. - -**Solution:** Only set `assetPrefix` in production: -```javascript -// next.config.js -module.exports = { - output: 'export', - assetPrefix: process.env.NODE_ENV === 'production' ? './' : undefined, -}; -``` - ---- - -### K5. Next.js Standalone Output — `asset not found: index.html` - -**Error:** -``` -asset not found: index.html -``` - -**Cause:** Next.js standalone output doesn't produce a static `index.html` file. - -**Solution:** Use `output: "export"`, NOT `standalone`: -```javascript -module.exports = { - output: 'export', // Required for Tauri - // NOT output: 'standalone' -}; -``` - ---- - -### K6. SvelteKit Blank Screen / PostCSS Errors - -**Error:** Blank white screen or `[postcss] Internal server error`. - -**Cause:** SvelteKit's routing conflicts with Tauri's static file serving. - -**Solution:** Use `@sveltejs/adapter-static`: -```javascript -// svelte.config.js -import adapter from '@sveltejs/adapter-static'; - -export default { - kit: { - adapter: adapter({ fallback: 'index.html' }) - } -}; -``` -Add to root layout: `export const ssr = false;` - ---- - -## Category L: WebSocket Errors - -### L1. WebSocket Connection Closing / "Broken Pipe" - -**Error:** WebSocket connection closes unexpectedly after app has been open for a long time. - -**Cause:** Idle timeouts or OS-level connection cleanup. - -**Solution:** Implement ping/pong keepalive in custom WebSocket connections: -```javascript -const ws = new WebSocket('ws://127.0.0.1:8080'); - -// Keepalive -setInterval(() => { - if (ws.readyState === WebSocket.OPEN) { - ws.send(JSON.stringify({ type: 'ping' })); - } -}, 30000); -``` - ---- - -### L2. WebSocket Plugin — Unstable Connections - -**Error:** Messages not received right after connection with `tauri-plugin-websocket`. - -**Cause:** The WebSocket connection may not be fully initialized before messages are sent. - -**Solution:** Wait for the `onopen` event: -```javascript -import WebSocket from '@tauri-apps/plugin-websocket'; - -const ws = await WebSocket.connect('ws://localhost:8080'); -// Ensure ready before sending -ws.addListener((msg) => { - console.log('Received:', msg); -}); -``` - ---- - -### L3. WebSocket + Tauri Client Conflict - -**Error:** WebSocket client code and Tauri app cannot run simultaneously. - -**Cause:** Resource conflicts between the Tauri WebView and WebSocket connections, especially on mobile. - -**Solution:** Use `tauri-plugin-websocket` instead of raw `WebSocket` API for mobile compatibility. - ---- - -## Category M: Path & Asset Protocol Errors - -### M1. 403 Forbidden on Asset Protocol URLs - -**Error:** `403 Forbidden` when accessing files via `asset://` protocol. - -**Cause:** The asset protocol has a scope restricting which directories can be accessed. - -**Solution:** Configure asset protocol scope: -```json -{ - "identifier": "asset-capability", - "permissions": [ - { - "identifier": "fs:allow-read", - "allow": [{ "path": "$RESOURCE/**" }] - } - ] -} -``` -Use `convertFileSrc()` from `@tauri-apps/api/core` for proper path conversion. - ---- - -### M2. `resource_dir()` Returns Unknown Path - -**Error:** `PathResolver.resource_dir()` returns empty or unknown path. - -**Cause:** A custom `target-dir` in `~/.cargo/config.toml` breaks resource resolution logic. - -**Solution:** Remove global `target-dir` setting, or configure it per-project instead of globally. - ---- - -### M3. Path Traversal Prevention Blocking Valid Paths - -**Error:** `path traversal` error from `@tauri-apps/plugin-fs`. - -**Cause:** The FS plugin blocks paths containing `..`. - -**Solution:** Normalize paths before use: -```javascript -import { resolve } from '@tauri-apps/api/path'; -const normalizedPath = await resolve('some', 'relative', 'path'); -``` - ---- - -## Category N: Shell & Sidecar Errors - -### N1. Sidecar Binary Not Found - -**Error:** -``` -Failed to spawn my-binary: Io(Os { code: 3, kind: NotFound }) -``` - -**Cause:** The sidecar binary doesn't follow Tauri's naming convention. Tauri appends the target triple to the binary name. - -**Solution:** -```json -// tauri.conf.json -{ - "bundle": { - "externalBin": ["binaries/my-binary"] - } -} -``` -Name the binary with the target triple suffix: -- Windows: `my-binary-x86_64-pc-windows-msvc.exe` -- macOS: `my-binary-aarch64-apple-darwin` -- Linux: `my-binary-x86_64-unknown-linux-gnu` - -Reference in Rust without the suffix: -```rust -app.shell().sidecar("binaries/my-binary").unwrap() -``` - ---- - -## Category O: Mobile Build Errors (iOS & Android) - -### O1. `xcodebuild exited with code 65` - -**Error:** -``` -error: failed to run custom build command for tauri v2 ... xcodebuild exited with code 65 -``` - -**Cause:** iOS build failure. Common causes: code signing issues, stale build artifacts, Xcode version incompatibility. - -**Solution:** -1. Open Xcode project: `open src-tauri/gen/apple/Project.xcworkspace` -2. Set Signing & Capabilities → Team -3. Clean build: `Cmd+Shift+K` in Xcode -4. Clean pods: `cd src-tauri/gen/apple && rm -rf build Pods && pod install` - ---- - -### O2. `can't find crate for core` (Missing Mobile Target) - -**Error:** -``` -error[E0463]: can't find crate for `core` -``` -When building for iOS or Android. - -**Cause:** The Rust target for the mobile platform is not installed. - -**Solution:** -```bash -# iOS -rustup target add aarch64-apple-ios - -# Android (all targets) -rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android -cargo install cargo-ndk -``` - ---- - -### O3. `building for 'iOS', but linking in object file built for 'macOS'` - -**Error:** -``` -building for 'iOS', but linking in object file built for 'macOS' -``` - -**Cause:** A native dependency was compiled for the host platform instead of the iOS target. - -**Solution:** Check which dependency causes the issue: `cargo build --target aarch64-apple-ios -v`. Ensure all dependencies support cross-compilation to iOS. - ---- - -### O4. Android Build Fails — `Cannot find module 'tauri'` - -**Error:** -``` -Cannot find module 'tauri' during Gradle build -``` - -**Cause:** Node.js executable can't be found when Gradle's BuildTask runs. - -**Solution:** Ensure Node.js is available system-wide. Use `npx tauri` or configure the Gradle BuildTask to use the correct node path. - ---- - -### O5. Android App Crashes on Launch - -**Error:** App immediately crashes after deployment to Android device/emulator. - -**Cause:** Missing Android SDK components, incorrect NDK configuration, or incompatible Gradle/AGP versions. - -**Solution:** -1. Ensure correct Android SDK and NDK are installed -2. Run `npx tauri android init` to set up properly -3. Check `android/app/build.gradle` has correct Tauri integration -4. Verify ADB connection: `adb devices` - ---- - -### O6. `tauri android dev` Hangs Indefinitely - -**Error:** Command hangs with no output or progress. - -**Cause:** Emulator/device connection failure or Gradle build hanging. - -**Solution:** -1. Verify ADB: `adb devices` -2. Clean Gradle cache: `cd android && ./gradlew clean` -3. Use a physical device instead of emulator -4. Increase Gradle memory in `android/gradle.properties` - ---- - -### O7. `mobile_entry_point` Conflicts with `#[tokio::main]` - -**Error:** App crashes on iOS when using `#[tokio::main]` with `mobile_entry_point`. - -**Cause:** `mobile_entry_point` and `#[tokio::main]` generate conflicting function signatures. - -**Solution:** -```rust -// ❌ WRONG -#[cfg_attr(mobile, tauri::mobile_entry_point)] -#[tokio::main] -pub async fn run() { /* ... */ } - -// ✅ CORRECT — use async_runtime::block_on -#[cfg_attr(mobile, tauri::mobile_entry_point)] -pub fn run() { - tauri::async_runtime::block_on(async { - tauri::Builder::default() - .run(tauri::generate_context!()) - .expect("error while running tauri application"); - }); -} -``` - ---- - -### O8. No Code Signing Certificates Found (iOS) - -**Error:** -``` -No code signing certificates found. You must add one and set the certificate development team ID. -``` - -**Cause:** No Apple Developer certificate configured. - -**Solution:** Open `src-tauri/gen/apple/Project.xcworkspace` in Xcode → Signing & Capabilities → select Team. Or use environment variables: `APPLE_SIGNING_IDENTITY`, `APPLE_TEAM_ID`. - ---- - -### 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 - -**Error:** Build fails with GTK/WebKit-related errors. - -**Cause:** Missing system libraries for GTK/WebKit development. - -**Solution:** -```bash -# Ubuntu/Debian -sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev \ - librsvg2-dev libsoup-3.0-dev - -# Fedora -sudo dnf install webkit2gtk4.1-devel gtk3-devel libappindicator-gtk3-devel \ - librsvg2-devel libsoup3-devel - -# Arch -sudo pacman -S webkit2gtk-4.1 gtk3 libappindicator-gtk3 librsvg libsoup3 -``` - ---- - -### P2. Linux — Failed to Find ICNS Encoder - -**Error:** -``` -error: failed to compile tauri-bundler: failed to find ICNS encoder -``` - -**Cause:** Missing icon tools for macOS bundle creation on Linux. - -**Solution:** `sudo apt install icnsutils` (Debian/Ubuntu) or equivalent package. - ---- - -### P3. Linux — Failed to Run `linuxdeploy` - -**Error:** -``` -error: failed to bundle project: failed to run linuxdeploy -``` - -**Cause:** Missing AppImage bundling dependencies. - -**Solution:** Install `linuxdeploy` and `linuxdeploy-plugin-gtk`. Ensure `appimage-builder` is available. - ---- - -## Category Q: Panics & Runtime Crashes - -### Q1. Generic "error while running tauri application" - -**Error:** -``` -thread 'main' panicked at src\main.rs:14:10: -error while running tauri application: -``` - -**Cause:** Top-level error wrapper. The inner error reveals the actual problem — check plugin initialization, WebView creation, or config validation. - -**Solution:** Replace `.expect()` with better error reporting: -```rust -if let Err(e) = tauri::Builder::default() - .plugin(tauri_plugin_http::init()) - .run(tauri::generate_context!()) -{ - eprintln!("Failed to start: {e}"); - std::process::exit(1); -} -``` - ---- - -### Q2. Panic in tauri-runtime-wry on Wayland - -**Error:** -``` -Panic in tauri-runtime-wry during init on Wayland -``` - -**Cause:** Wayland compositors missing `wlr_data_control_manager v1` protocol cause the clipboard crate to panic. - -**Solution:** Use XWayland, or use a Wayland compositor that supports the protocol. - ---- - -### Q3. `EXC_BAD_ACCESS` Crash on iOS - -**Error:** App crashes on physical iPhone with `EXC_BAD_ACCESS` but works in simulator. - -**Cause:** Memory access violations in native code bridging. - -**Solution:** Enable address sanitizer in Xcode. Check for use-after-free or null pointer issues in Rust FFI code. - ---- - -### Q4. Panic When `Builder::setup` Hook Fails - -**Error:** App panics immediately when the `setup` callback returns an error. - -**Cause:** `App::run()` panics when setup fails — no graceful error handling. - -**Solution:** Wrap setup logic properly: -```rust -tauri::Builder::default() - .setup(|app| { - match critical_init(app) { - Ok(_) => Ok(()), - Err(e) => { - eprintln!("Setup failed: {e}"); - Err(e.into()) - } - } - }) - .run(tauri::generate_context!()) -``` - ---- - -## Category R: Error Handling Best Practices - -### R1. Never Panic Inside Tauri Commands - -Panic in a **synchronous** command crashes the app. Panic in an **asynchronous** command causes a Promise that never resolves (silent failure). Always return `Result`: - -```rust -#[derive(Debug, thiserror::Error)] -enum AppError { - #[error(transparent)] - Io(#[from] std::io::Error), - #[error(transparent)] - Network(#[from] reqwest::Error), - #[error("{0}")] - Custom(String), -} - -impl serde::Serialize for AppError { - fn serialize(&self, s: S) -> Result - where S: serde::ser::Serializer { - s.serialize_str(&self.to_string()) - } -} - -#[tauri::command] -fn my_command() -> Result { - let data = std::fs::read_to_string("config.toml")?; - Ok(data) -} -``` - -### R2. Frontend Error Handling - -```javascript -import { invoke } from '@tauri-apps/api/core'; - -try { - const result = await invoke('my_command', { arg: 'value' }); - console.log('Success:', result); -} catch (error) { - console.error('Command failed:', error); - // Show user-friendly error in UI -} -``` - -### R3. Structured Errors for the Frontend - -```rust -#[derive(serde::Serialize)] -#[serde(tag = "kind", content = "message")] -#[serde(rename_all = "camelCase")] -enum ErrorKind { - Io(String), - Network(String), - Custom(String), -} - -impl serde::Serialize for AppError { - fn serialize(&self, s: S) -> Result - where S: serde::ser::Serializer { - let kind = match self { - Self::Io(e) => ErrorKind::Io(e.to_string()), - Self::Network(e) => ErrorKind::Network(e.to_string()), - Self::Custom(msg) => ErrorKind::Custom(msg.clone()), - }; - kind.serialize(s) - } -} -``` -Frontend receives: `{ kind: 'io', message: '...' }` — making it easy to handle errors by type. - -### R4. Cleanup Event Listeners - -```javascript -import { listen } from '@tauri-apps/api/event'; - -// ❌ WRONG — memory leak -useEffect(() => { - listen('my-event', handler); - return () => {}; // No cleanup! -}, []); - -// ✅ CORRECT — proper cleanup -useEffect(() => { - const unlisten = listen('my-event', handler); - return () => { unlisten.then(fn => fn()); }; -}, []); -``` - ---- - -## Quick-Reference: Top 25 Most Common Errors - -| # | Error Message | Category | Quick Fix | -|---|---|---|---| -| 1 | `X not allowed. Plugin not found` | Permission | Add permissions to `capabilities/default.json` | -| 2 | `Additional properties not allowed ('devPath', 'distDir')` | Config | Rename to `devUrl` / `frontendDist` | -| 3 | `__TAURI__ is not defined` | Frontend | Set `withGlobalTauri: true` or use npm imports | -| 4 | `AssetNotFound("index.html")` | Config | Fix `frontendDist` path to match bundler output | -| 5 | `unresolved import 'tauri::api'` | Migration | Replace with plugins (`tauri-plugin-*`) | -| 6 | `cannot find type 'Window' in 'tauri'` | Migration | Use `WebviewWindow` | -| 7 | `failed to create webview: WebView2 error` | Webview | Install WebView2; remove custom browser args | -| 8 | `Path not allowed on the configured scope` | Permission | Add FS scope permissions | -| 9 | `command not found` | IPC | Add command to `generate_handler![]` | -| 10 | `cannot find type 'SystemTray'` | Migration | Use `tauri::tray::TrayIconBuilder` | -| 11 | `lifetime not constrained` in async | Async | Use owned types (`String`, not `&str`) | -| 12 | `MutexGuard cannot be sent between threads` | Async | Use `tokio::sync::Mutex` instead of `std::sync::Mutex` | -| 13 | `xcodebuild exited with code 65` | Mobile | Set signing team in Xcode | -| 14 | `can't find crate for core` | Mobile | `rustup target add ` | -| 15 | `Additional properties not allowed ('allowlist')` | Config | Remove; use v2 capabilities system | -| 16 | `beforeDevCommand terminated with non-zero status` | Config | Fix npm scripts and port numbers | -| 17 | Vite infinite recompilation | Frontend | Add `src-tauri` to `watch.ignored` | -| 18 | Next.js `useEffect` broken in production | Frontend | Use `output: "export"` + `'use client'` | -| 19 | `program not allowed on shell scope` | Shell | Add program to shell capability scope | -| 20 | `Refused to connect to 'http://ipc.localhost'` | CSP | Add `ipc: http://ipc.localhost` to `connect-src` | -| 21 | Only last `invoke_handler` works | IPC | Combine all commands in one `generate_handler![]` | -| 22 | `GDK may only be used from the main thread` | Platform | Update Tauri; avoid cross-thread UI calls | -| 23 | `PluginInitialization("http", "invalid type")` | Config | Migrate old plugins config to v2 capabilities | -| 24 | Sidecar `Io(Os { code: 3 })` | Shell | Fix binary naming with target triple suffix | -| 25 | `error while running tauri application` | Generic | Check inner error; improve error handling | +**Continue to**: [Part 2: Runtime, Platform & Mobile Errors](./02-runtime-platform-mobile-errors.md) diff --git a/tauri2-guide/01-core-architecture-config-commands-ipc.md b/tauri2-guide/01-core-architecture-config-commands-ipc.md new file mode 100644 index 0000000..ad4b0c6 --- /dev/null +++ b/tauri2-guide/01-core-architecture-config-commands-ipc.md @@ -0,0 +1,930 @@ +# Tauri 2.0 Developer Guide — Part 1: Core Architecture, Config, Commands & IPC + +> **Version**: 2.0 | **Last Updated**: 2026-05-31 +> **Official Docs**: https://v2.tauri.app +> +> This file covers Sections 1-7 of the Definitive Guide. Part 2 covers Windows, Plugins, WebSocket, State, Migration & Anti-Patterns. + +--- + +## Table of Contents + +1. [Introduction & Architecture Overview](#1-introduction--architecture-overview) +2. [Project Structure & Scaffolding](#2-project-structure--scaffolding) +3. [Configuration System (`tauri.conf.json`)](#3-configuration-system-tauriconfjson) +4. [Capabilities & Permissions (Security Model)](#4-capabilities--permissions-security-model) +5. [Rust Backend: `lib.rs` & Commands](#5-rust-backend-librs--commands) +6. [Frontend Integration Patterns](#6-frontend-integration-patterns) +7. [IPC: Commands, Events & Channels](#7-ipc-commands-events--channels) + +**See also**: [Part 2: Windows, Plugins, WebSocket, State & Migration](./02-windows-plugins-websocket-state-migration.md) + +--- +## 1. Introduction & Architecture Overview + +Tauri 2.0 is a framework for building small, fast binaries for all major desktop and mobile platforms. It uses a **hybrid architecture** where a Rust backend handles system-level operations while a web frontend (HTML/CSS/JS, or any framework like React/Vue/Svelte) renders the UI inside a native OS webview. + +### Core Architecture Principles + +- **Rust Core (`src-tauri/`)**: Handles system access, file operations, network calls, window management, and any heavy computation. All system interactions MUST go through Rust to maintain security boundaries. +- **Web Frontend (`src/`)**: Renders the UI using standard web technologies. Communicates with Rust exclusively through Tauri's IPC mechanisms (`invoke`, events, channels). +- **Security Boundary**: The frontend runs in a sandboxed webview. It cannot directly access the filesystem, network, or OS APIs — it must request these through Tauri commands and plugins, which are governed by the **Capabilities & Permissions** system. +- **Mobile Support (NEW in v2)**: Tauri 2.0 adds first-class iOS and Android support. This requires a specific project structure (`lib.rs` + `main.rs` pattern) and platform-specific capability configurations. + +### What Changed from v1 to v2 + +Tauri 2.0 is a **complete architectural overhaul**, not an incremental update. The most significant changes are: + +| Area | v1 | v2 | +|------|----|----| +| Security | `allowlist` in config | **Capabilities + Permissions** (ACL-based) | +| APIs | Built-in `tauri::api` module | **Everything is a plugin** | +| Window type | `Window` / `WindowBuilder` | **`WebviewWindow` / `WebviewWindowBuilder`** | +| Config structure | Nested `tauri >` key | Flattened: `app`, `bundle`, `build` | +| Event system | Per-window scoped | **`emit()` is global; `emit_to()` for targeting** | +| JS imports | `@tauri-apps/api/tauri` | **`@tauri-apps/api/core`** | +| Mobile support | Community plugins | **First-class iOS/Android support** | +| Project structure | `main.rs` only | **`lib.rs` + `main.rs` pattern required** | + +--- + +## 2. Project Structure & Scaffolding + +### Standard Tauri 2.0 File Hierarchy + +```text +my-app/ +├── src/ # Frontend UI source +│ ├── index.html # Main HTML entry +│ ├── main.js # Frontend JavaScript +│ └── styles.css # Styles +├── src-tauri/ # Rust backend environment +│ ├── capabilities/ # Security capability definitions +│ │ └── default.json # Maps windows → permissions +│ ├── icons/ # App icons for all platforms +│ ├── src/ +│ │ ├── main.rs # Minimal desktop entry-point +│ │ ├── lib.rs # Core application setup & commands +│ │ └── .rs # Additional Rust modules +│ ├── build.rs # Tauri build script +│ ├── Cargo.toml # Rust dependencies +│ ├── Cargo.lock # Lockfile (COMMIT THIS) +│ └── tauri.conf.json # Main Tauri configuration +├── package.json # Node.js dependencies & scripts +└── package-lock.json +``` + +### Creating a New Project + +```bash +# Using the interactive CLI scaffolder +npm create tauri-app@latest + +# Or with specific template +npm create tauri-app@latest -- --template vanilla-ts + +# Cargo alternative +cargo create-tauri-app +``` + +Supported templates: Vanilla, Vue, Svelte, React, Solid, Angular, Preact, Yew, Leptos, Sycamore. + +### Adding Tauri to an Existing Frontend + +```bash +npm install -D @tauri-apps/cli@latest +npx tauri init +``` + +The `init` command will prompt for: app name, window title, web assets location, dev server URL, and frontend build/dev commands. + +### The `lib.rs` + `main.rs` Pattern (Required in v2) + +Tauri 2.0 requires your core application logic to live in `lib.rs`, with a minimal `main.rs` that calls into it. This is **mandatory** for mobile support because mobile platforms require a shared library entry-point rather than a standard `main()` function. + +**`src-tauri/src/main.rs`** — Desktop-only entry-point: +```rust +// Prevents additional console window on Windows in release mode +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + my_app_lib::run() +} +``` + +**`src-tauri/src/lib.rs`** — Core application: +```rust +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + // plugins, commands, setup, etc. + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} +``` + +**`src-tauri/Cargo.toml`** — Must declare the library: +```toml +[lib] +name = "my_app_lib" +crate-type = ["staticlib", "cdylib", "rlib"] +``` + +The `_lib` suffix in the crate name prevents naming conflicts with the binary on Windows. The three crate types cover: static linking (mobile), dynamic linking (mobile), and Rust library usage (desktop/tests). + +### Development Commands + +```bash +# Run in development mode (hot-reloads UI + auto-recompiles Rust) +npm run tauri dev +# or: pnpm tauri dev, bun tauri dev, cargo tauri dev + +# Build production artifacts +npm run tauri build +``` + +--- + +## 3. Configuration System (`tauri.conf.json`) + +### Main Configuration File + +The primary configuration file is `src-tauri/tauri.conf.json`. Tauri 2.0 supports three formats: + +| Format | Feature Flag Required | +|--------|-----------------------| +| **JSON** (default) | None | +| **JSON5** | `config-json5` on both `tauri` and `tauri-build` | +| **TOML** | `config-toml` on both `tauri` and `tauri-build` | + +### Complete v2 Configuration Structure + +```json +{ + "$schema": "https://tauri.app", + "productName": "my-app", + "version": "1.0.0", + "identifier": "com.mycompany.myapp", + "build": { + "frontendDist": "../dist", + "devUrl": "http://localhost:1420", + "beforeDevCommand": "npm run dev", + "beforeBuildCommand": "npm run build" + }, + "app": { + "withGlobalTauri": true, + "windows": [ + { + "label": "main", + "title": "My App", + "width": 800, + "height": 600, + "resizable": true, + "fullscreen": false, + "center": true, + "dragDropEnabled": true, + "useHttpsScheme": false + } + ], + "security": { + "csp": "default-src 'self'; style-src 'self' 'unsafe-inline'", + "assetProtocol": { + "scope": ["$APPDATA/**", "$RESOURCE/**"] + } + } + }, + "bundle": { + "active": true, + "targets": "all", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "licenseFile": "LICENSE", + "copyright": "", + "category": "Utility" + } +} +``` + +### Key Configuration Changes from v1 + +| v1 Path | v2 Path | Notes | +|---------|---------|-------| +| `package.productName` | Top-level `productName` | Moved out of `package` | +| `package.version` | Top-level `version` | Moved out of `package` | +| `package` | *(removed)* | Fields redistributed | +| `build.distDir` | `build.frontendDist` | Renamed | +| `build.devPath` | `build.devUrl` | Renamed | +| `build.withGlobalTauri` | `app.withGlobalTauri` | Moved to `app` section | +| `tauri.*` | `app.*` | Top-level key renamed | +| `tauri.allowlist` | *(removed)* | Replaced by capabilities | +| `tauri.windows.fileDropEnabled` | `app.windows.dragDropEnabled` | Renamed | +| `tauri.bundle` | Top-level `bundle` | Promoted to top-level | +| `tauri.updater` | `plugins.updater` | Moved to plugins | +| `tauri.systemTray` | `app.trayIcon` | Renamed | +| `tauri.cli` | `plugins.cli` | Moved to plugins | +| `bundle.identifier` | Top-level `identifier` | Promoted to top-level | + +### Platform-Specific Configuration + +Create platform override files that **merge** with the main config using JSON Merge Patch (RFC 7396). Arrays are replaced entirely (not element-by-element merged). + +| Platform | File Pattern | +|----------|-------------| +| Linux | `tauri.linux.conf.json` or `Tauri.linux.toml` | +| Windows | `tauri.windows.conf.json` or `Tauri.windows.toml` | +| macOS | `tauri.macos.conf.json` or `Tauri.macos.toml` | +| Android | `tauri.android.conf.json` or `Tauri.android.toml` | +| iOS | `tauri.ios.conf.json` or `Tauri.ios.toml` | + +### Cargo.toml Dependencies + +```toml +[build-dependencies] +tauri-build = { version = "2", features = [] } + +[dependencies] +tauri = { version = "2", features = [] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +``` + +**Important**: Keep `tauri`, `tauri-build`, and `@tauri-apps/cli` on the same minor version. Always commit `Cargo.lock` for reproducible builds. + +### package.json Scripts + +```json +{ + "scripts": { + "tauri": "tauri", + "dev": "tauri dev", + "build": "tauri build" + }, + "devDependencies": { + "@tauri-apps/cli": "^2" + } +} +``` + +The `"tauri"` script is only required when using `npm` (not needed for yarn/pnpm/bun). + +--- + +## 4. Capabilities & Permissions (Security Model) + +Tauri 2.0 introduces a strict **Access Control List (ACL)** security model that replaces the v1 `allowlist`. This is the most important new concept to understand. + +### Core Concepts + +- **Capabilities** define **which permissions** are granted to **which windows or webviews**. +- **Permissions** describe explicit privileges for specific commands (e.g., `fs:allow-read`, `http:allow-request`). +- **Scopes** further restrict what a permission can access (e.g., only certain file paths). +- Multiple capabilities can apply to the same window — permissions **merge**. +- Security boundaries are based on **window labels** (not titles). + +### Capability File Location + +``` +src-tauri/capabilities/.json (or .toml) +``` + +All files in this directory are **automatically loaded** by default. + +### Basic Capability File + +```json +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "main-window-capability", + "description": "Permissions for the primary application window", + "platforms": ["linux", "macOS", "windows"], + "windows": ["main"], + "permissions": [ + "core:default" + ] +} +``` + +**Field reference:** +- `$schema` — Points to generated schema for IDE autocompletion. Use `desktop-schema.json` for desktop, `mobile-schema.json` for mobile. +- `identifier` — Unique capability name (ASCII lowercase, max 116 chars). +- `description` — Human-readable purpose. +- `windows` — Array of window labels this capability applies to. Use `["*"]` for all windows. +- `permissions` — Array of permission identifiers. +- `platforms` — Optional. Restricts to specific platforms. Defaults to all platforms if omitted. + +### Permission Identifier Naming Convention + +| Pattern | Meaning | Example | +|---------|---------|---------| +| `:default` | Default permission set for a plugin | `fs:default` | +| `:allow-` | Allow a specific command | `fs:allow-read` | +| `:deny-` | Deny a specific command | `fs:deny-write` | +| `core::` | Core Tauri module permission | `core:event:allow-emit` | + +### Common Permission Identifiers + +**Core permissions:** +- `core:default` — Basic runtime permissions +- `core:path:default` — Path resolution +- `core:event:default` — Event system (includes listen/emit) +- `core:event:allow-listen` — Allow listening to events +- `core:event:allow-emit` — Allow emitting events +- `core:window:default` — Window management +- `core:window:allow-set-title` — Allow changing window title + +**Plugin permissions:** +- `fs:allow-read`, `fs:allow-write`, `fs:allow-exists`, `fs:allow-mkdir`, `fs:allow-remove` +- `http:allow-request` — HTTP client requests +- `dialog:allow-open`, `dialog:allow-save`, `dialog:allow-message` +- `shell:allow-open`, `shell:allow-execute` +- `notification:allow-notify`, `notification:allow-is-permission-granted` +- `clipboard-manager:allow-read-text`, `clipboard-manager:allow-write-text` +- `websocket:allow-connect`, `websocket:allow-send` +- `store:default` — Persistent key-value store + +### Fine-Grained Scopes + +Scopes restrict what a permission can actually access: + +```json +{ + "identifier": "secure-fs-capability", + "windows": ["main"], + "permissions": [ + { + "identifier": "fs:allow-read", + "allow": [{ "path": "$APPDATA/logs/*" }, { "path": "$HOME/Documents/*.txt" }] + }, + { + "identifier": "fs:allow-write", + "allow": [{ "path": "$APPDATA/logs/*.log" }] + }, + { + "identifier": "fs:deny-write", + "deny": [{ "path": "$APPDATA/config/*" }] + } + ] +} +``` + +Available scope variables: `$APPDATA`, `$HOME`, `$APPCONFIG`, `$APPCACHE`, `$APPLOG`, `$RESOURCE`, `$EXE`. + +### Restricting Custom Commands + +By default, all `#[tauri::command]` functions are accessible from all windows. To restrict a custom command to specific windows only, use `build.rs`: + +```rust +fn main() { + tauri_build::try_build( + tauri_build::Attributes::new() + .app_manifest( + tauri_build::AppManifest::new() + .commands(&["my_restricted_command", "admin_only_command"]) + ), + ) + .unwrap(); +} +``` + +Then grant access in the capability: +```json +{ + "identifier": "admin-capability", + "windows": ["admin-panel"], + "permissions": [ + { "identifier": "allow-my-restricted-command" }, + { "identifier": "allow-admin-only-command" } + ] +} +``` + +### Referencing Capabilities Explicitly + +By default, all capability files in `src-tauri/capabilities/` are auto-discovered. To control which ones are used explicitly: + +```json +{ + "app": { + "security": { + "capabilities": ["my-capability", "admin-capability"] + } + } +} +``` + +### Inline Capabilities + +You can define capabilities directly in `tauri.conf.json` instead of separate files: + +```json +{ + "app": { + "security": { + "capabilities": [ + { + "identifier": "inline-cap", + "windows": ["*"], + "permissions": ["core:default"] + } + ] + } + } +} +``` + +--- + +## 5. Rust Backend: `lib.rs` & Commands + +### Application Builder Pattern + +All Tauri 2.0 applications follow the `tauri::Builder` pattern: + +```rust +use tauri::Manager; + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .plugin(tauri_plugin_opener::init()) // Register plugins + .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_fs::init()) + .setup(|app| { // Setup hook (runs once at startup) + // Initialize resources, spawn servers, etc. + Ok(()) + }) + .manage(AppState { /* ... */ }) // Inject managed state + .invoke_handler(tauri::generate_handler![ // Register commands + my_command, + another_command, + module::command_in_module + ]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} +``` + +**Critical rules:** +- `invoke_handler` can only be called **once**. All commands must be in a single `generate_handler![]` call. +- Commands defined in `lib.rs` **cannot** be marked `pub`. +- Commands defined in separate modules **must** be marked `pub`. +- The `#[cfg_attr(mobile, tauri::mobile_entry_point)]` attribute is required for the `run()` function. + +### Defining Commands + +Commands are the primary IPC mechanism. They are Rust functions annotated with `#[tauri::command]` and callable from the frontend via `invoke()`. + +#### Basic Command + +```rust +#[tauri::command] +fn greet(name: String) -> String { + format!("Hello, {}! Welcome from Rust.", name) +} +``` + +#### Command with Error Handling + +```rust +#[tauri::command] +fn login(user: String, password: String) -> Result { + if user == "admin" && password == "secret" { + Ok("authenticated".to_string()) + } else { + Err("invalid credentials".to_string()) + } +} +``` + +#### Async Command + +```rust +#[tauri::command] +async fn fetch_data(url: String) -> Result { + let response = reqwest::get(&url) + .await + .map_err(|e| e.to_string())?; + response.text().await.map_err(|e| e.to_string()) +} +``` + +**IMPORTANT limitation**: Async commands cannot accept borrowed references (`&str`, `&Path`) directly. Convert to owned types (`String`, `PathBuf`) or wrap the return in `Result`: + +```rust +// Option A: Use owned types +#[tauri::command] +async fn process(value: String) -> String { /* ... */ } + +// Option B: Wrap return in Result +#[tauri::command] +async fn process(value: &str) -> Result { /* ... */ } +``` + +#### Custom Error Types (Recommended) + +```rust +use thiserror::Error; + +#[derive(Debug, Error)] +enum AppError { + #[error("IO error: {0}")] + Io(#[from] std::io::Error), + #[error("Network error: {0}")] + Network(#[from] reqwest::Error), +} + +// Implement Serialize so the error can cross the IPC boundary +impl serde::Serialize for AppError { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + serializer.serialize_str(self.to_string().as_ref()) + } +} + +#[tauri::command] +fn read_config() -> Result { + let content = std::fs::read_to_string("config.toml")?; + Ok(content) +} +``` + +#### Structured Error with Kind/Tag + +```rust +#[derive(Debug, Error)] +enum AppError { + #[error(transparent)] + Io(#[from] std::io::Error), + #[error("{0}")] + Custom(String), +} + +#[derive(serde::Serialize)] +#[serde(tag = "kind", content = "message")] +#[serde(rename_all = "camelCase")] +enum ErrorKind { + Io(String), + Custom(String), +} + +impl serde::Serialize for AppError { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let kind = match self { + Self::Io(e) => ErrorKind::Io(e.to_string()), + Self::Custom(msg) => ErrorKind::Custom(msg.clone()), + }; + kind.serialize(serializer) + } +} +``` + +Frontend receives: `{ kind: 'io', message: '...' }` — making it easy to handle errors by type. + +#### Accessing Special Objects in Commands + +**AppHandle** — Access to the application instance: +```rust +#[tauri::command] +fn do_something(app_handle: tauri::AppHandle) { + let app_dir = app_handle.path().app_dir(); + // Spawn windows, emit events, access state... +} +``` + +**WebviewWindow** — The window that invoked the command: +```rust +#[tauri::command] +fn get_window_info(webview_window: tauri::WebviewWindow) -> String { + webview_window.label().to_string() +} +``` + +**Managed State** — Shared application state: +```rust +use tauri::State; +use std::sync::Mutex; + +struct DbConnection(Mutex); + +#[tauri::command] +fn query_database(state: State<'_, DbConnection>) -> String { + let conn = state.0.lock().unwrap(); + conn.clone() +} +``` + +**Raw Request** — Access headers and raw body: +```rust +#[tauri::command] +fn upload(request: tauri::ipc::Request) -> Result<(), String> { + let tauri::ipc::InvokeBody::Raw(data) = request.body() else { + return Err("Expected raw body".to_string()); + }; + let auth = request.headers().get("Authorization") + .ok_or("Missing auth header")?; + Ok(()) +} +``` + +#### Commands in Separate Modules + +```rust +// src-tauri/src/commands.rs +#[tauri::command] +pub fn create_user(name: String) -> String { + format!("Created user: {}", name) +} + +#[tauri::command] +pub fn delete_user(id: u32) -> bool { + // ... + true +} +``` + +```rust +// src-tauri/src/lib.rs +mod commands; + +pub fn run() { + tauri::Builder::default() + .invoke_handler(tauri::generate_handler![ + commands::create_user, + commands::delete_user + ]) + // ... +} +``` + +Note: The `commands::` prefix is Rust path resolution only — the frontend still calls `invoke('create_user', ...)` without any prefix. + +### Returning Large Data (ArrayBuffers) + +For large binary data like file contents, use `tauri::ipc::Response`: + +```rust +use tauri::ipc::Response; + +#[tauri::command] +fn read_file() -> Response { + let data = std::fs::read("/path/to/file").unwrap(); + Response::new(data) +} +``` + +### Streaming Data (Channels) + +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>) { + let mut file = tokio::fs::File::open(path).await.unwrap(); + let mut buf = vec![0u8; 4096]; + loop { + let n = file.read(&mut buf).await.unwrap(); + if n == 0 { break; } + on_chunk.send(&buf[..n]).unwrap(); + } +} +``` + +--- + +## 6. Frontend Integration Patterns + +### Using `@tauri-apps/api` npm Package (Recommended) + +```bash +npm install @tauri-apps/api +``` + +```javascript +// Imports in v2 — NOTE the path changes from v1 +import { invoke } from '@tauri-apps/api/core'; +import { listen, emit, emitTo, once } from '@tauri-apps/api/event'; +import { getCurrentWebviewWindow, WebviewWindow } from '@tauri-apps/api/webviewWindow'; +import { Channel } from '@tauri-apps/api/core'; +``` + +### Using Global `window.__TAURI__` (No npm package needed) + +Requires `app.withGlobalTauri: true` in `tauri.conf.json`: + +```javascript +// No import needed — accessed via the global object +const { invoke } = window.__TAURI__.core; +const { listen, emit } = window.__TAURI__.event; +``` + +This pattern is ideal for vanilla JS projects that don't use a bundler, avoiding the need for any npm packages beyond `@tauri-apps/cli`. + +### Import Path Changes from v1 + +| v1 Import | v2 Import | +|-----------|-----------| +| `@tauri-apps/api/tauri` | `@tauri-apps/api/core` | +| `@tauri-apps/api/window` | `@tauri-apps/api/webviewWindow` | +| `@tauri-apps/api/cli` | `@tauri-apps/plugin-cli` | +| `@tauri-apps/api/clipboard` | `@tauri-apps/plugin-clipboard-manager` | +| `@tauri-apps/api/dialog` | `@tauri-apps/plugin-dialog` | +| `@tauri-apps/api/fs` | `@tauri-apps/plugin-fs` | +| `@tauri-apps/api/global-shortcut` | `@tauri-apps/plugin-global-shortcut` | +| `@tauri-apps/api/http` | `@tauri-apps/plugin-http` | +| `@tauri-apps/api/notification` | `@tauri-apps/plugin-notification` | +| `@tauri-apps/api/shell` | `@tauri-apps/plugin-shell` | +| `@tauri-apps/api/updater` | `@tauri-apps/plugin-updater` | +| `@tauri-apps/api/os` | `@tauri-apps/plugin-os` | +| `@tauri-apps/api/process` | `@tauri-apps/plugin-process` | + +**Rule of thumb in v2**: If it's not `core`, `event`, or `webviewWindow`, it's a plugin. + +### Plugin JS Package Naming + +All v2 plugin JS packages follow: `@tauri-apps/plugin-` + +```bash +npm install @tauri-apps/plugin-store +npm install @tauri-apps/plugin-fs +npm install @tauri-apps/plugin-http +``` + +--- + +## 7. IPC: Commands, Events & Channels + +### Commands (Frontend → Rust) + +The primary request-response mechanism: + +```javascript +// Frontend (v2 import) +import { invoke } from '@tauri-apps/api/core'; + +// Simple invocation +const result = await invoke('greet', { name: 'World' }); +console.log(result); // "Hello, World! Welcome from Rust." + +// With error handling +try { + const token = await invoke('login', { user: 'admin', password: 'secret' }); + console.log('Authenticated:', token); +} catch (error) { + console.error('Login failed:', error); +} +``` + +**Argument naming convention**: Arguments are passed as a JSON object with **camelCase** keys by default. Use `#[tauri::command(rename_all = "snake_case")]` to accept snake_case from the frontend. + +### Events (Bidirectional, Multi-Consumer) + +Events are fire-and-forget messages. Use them for notifications, streaming status updates, or any data that multiple components might need. + +#### Emitting Events from Rust + +```rust +use tauri::{AppHandle, Emitter}; + +// Global event (all listeners receive it) +app_handle.emit("download-progress", 42)?; + +// Target a specific window +app_handle.emit_to("settings-panel", "config-changed", new_config)?; + +// Filter to specific windows +use tauri::EventTarget; +app_handle.emit_filter("notification", payload, |target| { + matches!(target, EventTarget::WebviewWindow { label } if label == "main") +})?; +``` + +#### Listening for Events on the Frontend + +```javascript +import { listen, once, emit, emitTo } from '@tauri-apps/api/event'; +import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'; + +// Global listen (receives ALL events with this name) +const unlisten = await listen('download-progress', (event) => { + console.log(`Progress: ${event.payload}%`); +}); + +// Targeted listen (only events emitted to THIS window) +const appWebview = getCurrentWebviewWindow(); +const unlisten2 = await appWebview.listen('config-changed', (event) => { + console.log('New config:', event.payload); +}); + +// Listen once, then auto-cleanup +await once('initialization-complete', (event) => { + console.log('App ready!', event.payload); +}); + +// Emit from frontend to Rust +await emit('user-action', { type: 'click', target: 'button' }); + +// Emit to a specific window from frontend +const target = new WebviewWindow('settings-panel'); +await target.emit('settings-request', { key: 'theme' }); +``` + +**Always call `unlisten()`** when the component unmounts or the listener is no longer needed to prevent memory leaks. + +#### Listening for Events on the Rust Side + +```rust +use tauri::{Listener, Manager}; + +pub fn run() { + tauri::Builder::default() + .setup(|app| { + // Listen globally + app.listen("user-action", |event| { + println!("Received: {}", event.payload()); + }); + + // Listen on a specific window + let main = app.get_webview_window("main").unwrap(); + main.listen("config-changed", |event| { + println!("Config updated: {}", event.data); + }); + + // Listen once + app.once("ready", |event| { + println!("App is ready!"); + }); + + Ok(()) + }) + // ... +} +``` + +### Channels (High-Throughput Streaming) + +Channels are the recommended mechanism for streaming large amounts of ordered data from Rust to the frontend. They are faster and more memory-efficient than events for high-throughput scenarios. + +```rust +use tauri::ipc::Channel; +use serde::Serialize; + +#[derive(Clone, Serialize)] +#[serde(tag = "event", content = "data")] +enum StreamEvent { + Started { total: usize }, + Progress { current: usize, chunk: Vec }, + Finished { total: usize }, +} + +#[tauri::command] +fn download_file(url: String, on_event: Channel) { + on_event.send(StreamEvent::Started { total: 1000 }).unwrap(); + // ... streaming logic ... + on_event.send(StreamEvent::Finished { total: 1000 }).unwrap(); +} +``` + +```javascript +import { invoke, Channel } from '@tauri-apps/api/core'; + +const onEvent = new Channel(); +onEvent.onmessage = (event) => { + switch (event.event) { + case 'started': console.log(`Starting: ${event.data.total} bytes`); break; + case 'progress': /* handle chunk */ break; + case 'finished': console.log('Done!'); break; + } +}; + +await invoke('download_file', { url: 'https://...', onEvent }); +``` + +### Evaluating JavaScript from Rust + +```rust +use tauri::Manager; + +// In a command or setup hook +let webview = app.get_webview_window("main").unwrap(); +webview.eval("document.getElementById('status').textContent = 'Loaded!'")?; +``` + +For complex data passing, use the `serialize-to-javascript` crate. + + +--- + +**Continue to**: [Part 2: Windows, Plugins, WebSocket, State & Migration](./02-windows-plugins-websocket-state-migration.md) diff --git a/tauri2-guide/01-system-prompt-and-common-prompts.md b/tauri2-guide/01-system-prompt-and-common-prompts.md new file mode 100644 index 0000000..ef39d2d --- /dev/null +++ b/tauri2-guide/01-system-prompt-and-common-prompts.md @@ -0,0 +1,217 @@ +# Tauri 2.0 Agent Prompts — Part 1: System Prompt & Common Task Prompts + +> **Purpose**: Copy-paste ready prompts for instructing AI agents to build Tauri 2.0 applications. +> **Constraint**: These prompts are designed for sandbox environments where code compilation is NOT available. Agents must verify correctness using logic, API references, and pattern matching. + +--- + +## Table of Contents + +1. [Universal System Prompt (Required Prefix)](#1-universal-system-prompt-required-prefix) +2. [Prompt: Scaffold a New Tauri 2.0 Project](#2-prompt-scaffold-a-new-tauri-20-project) +3. [Prompt: Add a Tauri Plugin](#3-prompt-add-a-tauri-plugin) +4. [Prompt: Create a Custom Command (IPC)](#4-prompt-create-a-custom-command-ipc) +5. [Prompt: Add State Management](#5-prompt-add-state-management) +6. [Prompt: Implement Event-Based Communication](#6-prompt-implement-event-based-communication) +7. [Prompt: Create Multi-Window App](#7-prompt-create-multi-window-app) + +**See also**: [Part 2: Advanced Prompts, Checklist & Migration Map](./02-advanced-prompts-checklist-migration.md) + +--- + +## 1. Universal System Prompt (Required Prefix) + +> **Always include this as the system context before any task-specific prompt.** + +``` +You are a Tauri 2.0 expert developer. You build desktop and mobile applications using the Tauri 2.0 framework (Rust backend + web frontend). + +CRITICAL ENVIRONMENT CONSTRAINTS: +- You CANNOT compile or run code in this sandbox. Do NOT attempt compilation. +- Verify correctness using logic, API pattern matching, and reference knowledge only. +- Cross-reference every code snippet against known Tauri 2.0 API patterns before outputting. +- If you are uncertain about an API or pattern, say so explicitly rather than guessing. + +MANDATORY TAURI 2.0 RULES — NEVER VIOLATE: +1. This is Tauri 2.0 (NOT v1). The APIs are fundamentally different. +2. Use `tauri::WebviewWindow` (NOT `tauri::Window`). +3. Use `tauri::WebviewWindowBuilder` (NOT `tauri::WindowBuilder`). +4. Use `tauri::WebviewUrl` (NOT `tauri::WindowUrl`). +5. Use `Manager::get_webview_window()` (NOT `Manager::get_window()`). +6. Use `tauri::Emitter` trait for `emit()` and `emit_to()` (NOT the old window-only emit). +7. Use `tauri::Listener` trait for `listen()` and `once()` on the Rust side. +8. JS import `invoke` from `@tauri-apps/api/core` (NOT `@tauri-apps/api/tauri`). +9. JS import window utilities from `@tauri-apps/api/webviewWindow` (NOT `@tauri-apps/api/window`). +10. JS plugins import from `@tauri-apps/plugin-` (NOT `@tauri-apps/api/`). +11. Config uses `app` section (NOT `tauri` section), `build.frontendDist` (NOT `build.distDir`). +12. Security uses Capabilities + Permissions (NOT `allowlist`). +13. Every plugin requires: (a) Rust crate in Cargo.toml, (b) `.plugin()` registration in Builder, (c) npm package, (d) permissions in capabilities. +14. Use the `lib.rs` + `main.rs` pattern. Core logic goes in `lib.rs`. `main.rs` only calls `lib::run()`. +15. `#[cfg_attr(mobile, tauri::mobile_entry_point)]` attribute is REQUIRED on the `pub fn run()` function. +16. `invoke_handler` can only be called ONCE — all commands go in a single `tauri::generate_handler![]`. +17. Async commands cannot use borrowed references (`&str`, `&Path`) directly — use owned types or wrap return in `Result`. +18. Window creation from async contexts MUST use `app_handle.run_on_main_thread()`. +19. Always clean up event listeners (call `unlisten()`) on the frontend. +20. Use `tauri = { version = "2" }` and `tauri-build = { version = "2" }` (NOT version 1). + +FILE STRUCTURE: +- Frontend source: `src/` +- Rust backend: `src-tauri/` +- Rust entry-point: `src-tauri/src/main.rs` (minimal) +- Core app logic: `src-tauri/src/lib.rs` +- Extra modules: `src-tauri/src/.rs` +- Capabilities: `src-tauri/capabilities/default.json` +- Config: `src-tauri/tauri.conf.json` +- Rust deps: `src-tauri/Cargo.toml` +- Build script: `src-tauri/build.rs` + +OFFICIAL DOCUMENTATION: +- Main docs: https://v2.tauri.app +- Rust API: https://docs.rs/tauri/latest/tauri/ +- Migration guide: https://v2.tauri.app/start/migrate/from-tauri-1/ +- Capabilities: https://v2.tauri.app/security/capabilities/ +- Permissions: https://v2.tauri.app/security/permissions/ +- Configuration: https://v2.tauri.app/develop/configuration-files/ +- Calling Rust: https://v2.tauri.app/develop/calling-rust/ +- Calling Frontend: https://v2.tauri.app/develop/calling-frontend/ +- Plugin index: https://v2.tauri.app/plugin/ +``` + +--- + +## 2. Prompt: Scaffold a New Tauri 2.0 Project + +``` +Create a new Tauri 2.0 project from scratch with vanilla JavaScript (no frontend framework). + +Requirements: +- App name: [APP_NAME] +- Window title: [WINDOW_TITLE] +- Default window size: [WIDTH]x[HEIGHT] +- withGlobalTauri enabled (for window.__TAURI__ access without npm package) + +Generate ALL of the following files with complete, correct Tauri 2.0 code: + +1. `package.json` — with @tauri-apps/cli v2 devDependency and scripts (dev, build, tauri) +2. `src/index.html` — basic HTML5 boilerplate +3. `src/main.js` — demonstrate invoking a Rust command +4. `src/styles.css` — basic styling +5. `src-tauri/tauri.conf.json` — correct v2 structure (app section, build section, bundle section) +6. `src-tauri/Cargo.toml` — with tauri v2, tauri-build v2, serde, serde_json. Include [lib] with crate-type ["staticlib", "cdylib", "rlib"] +7. `src-tauri/build.rs` — standard tauri_build::build() +8. `src-tauri/capabilities/default.json` — with core:default permission +9. `src-tauri/src/main.rs` — minimal entry-point calling lib::run() +10. `src-tauri/src/lib.rs` — with Builder pattern, mobile_entry_point attribute, a sample "greet" command, and the command registered in invoke_handler + +Verify every line follows Tauri 2.0 conventions. Cross-check config structure against the v2 schema. +``` + +--- + +## 3. Prompt: Add a Tauri Plugin + +``` +Add the [PLUGIN_NAME] plugin to an existing Tauri 2.0 project. + +Plugin to add: [tauri-plugin-STORE/turi-plugin-FS/etc.] +JS package name: [@tauri-apps/plugin-store/etc.] + +Instructions: +1. Add the Rust crate to `src-tauri/Cargo.toml` as `tauri-plugin-[name] = "2"` +2. Register the plugin in `src-tauri/src/lib.rs` using `.plugin(tauri_plugin_[name]::init())` or the Builder pattern if the plugin requires configuration +3. Add the npm package: `@tauri-apps/plugin-[name]` +4. Update `src-tauri/capabilities/default.json` to add the appropriate permission (usually `[name]:default` or specific allow permissions) +5. Provide a frontend usage example showing the correct v2 import from `@tauri-apps/plugin-[name]` + +Show the exact changes needed for each file. Do NOT use v1 import paths or API patterns. +``` + +--- + +## 4. Prompt: Create a Custom Command (IPC) + +``` +Create a Tauri 2.0 command called "[COMMAND_NAME]" that [DESCRIBE WHAT IT DOES]. + +Requirements: +- Define the command using `#[tauri::command]` in a separate module file `src-tauri/src/commands.rs` +- The command must be `pub` (required for separate modules) +- Include proper error handling with `Result` or a custom error type +- Register the command in `src-tauri/src/lib.rs` using `mod commands;` and including it in `generate_handler![commands::COMMAND_NAME]` +- Provide the frontend JavaScript code to invoke the command using `invoke()` from `@tauri-apps/api/core` or `window.__TAURI__.core` +- If the command is async, ensure all arguments are owned types (not borrowed references) + +Show complete, correct code for both the Rust side and the JS side. +``` + +--- + +## 5. Prompt: Add State Management + +``` +Add shared state management to an existing Tauri 2.0 application. + +Requirements: +1. Define a state struct in `src-tauri/src/lib.rs` using `std::sync::Mutex` for thread safety +2. Manage the state in the Builder using `.manage(MyState { ... })` +3. Create at least two commands that read and write the managed state using `tauri::State<'_, MyState>` +4. Show the frontend code for invoking these commands + +If persistent storage is also needed: +- Add `tauri-plugin-store = "2"` to Cargo.toml +- Register the plugin +- Add `store:default` to capabilities +- Show frontend usage with `@tauri-apps/plugin-store` +``` + +--- + +## 6. Prompt: Implement Event-Based Communication + +``` +Implement bidirectional event communication in a Tauri 2.0 application. + +Requirements: + +Rust side: +- Use `use tauri::{Emitter, AppHandle}` to emit events from Rust +- Demonstrate both global emit (`app.emit()`) and targeted emit (`app.emit_to()`) +- If listening for events on the Rust side, use `use tauri::Listener` and `app.listen()` or `app.once()` + +Frontend side: +- Import from `@tauri-apps/api/event` (NOT `@tauri-apps/api/tauri`) +- Show `listen()`, `once()`, `emit()`, and `emitTo()` usage +- Demonstrate proper cleanup with `unlisten()` +- Show targeted listening using `getCurrentWebviewWindow().listen()` + +Show complete code for: +1. A Rust command that emits a progress event +2. A Rust command that emits a completion event to a specific window +3. Frontend listeners that handle these events +4. Frontend emitting an event back to Rust + +Make sure `core:event:default` (or specific `core:event:allow-*` permissions) is included in capabilities. +``` + +--- + +## 7. Prompt: Create Multi-Window App + +``` +Create a Tauri 2.0 application with multiple windows. + +Requirements: +1. Main window defined in `tauri.conf.json` with label "main" +2. A command `open_secondary` that creates a new window dynamically using `WebviewWindowBuilder` +3. The secondary window should load a local HTML file (`WebviewUrl::App("secondary.html".into())`) +4. If creating the window from an async context, use `app_handle.run_on_main_thread()` +5. Check for existing windows with the same label using `app_handle.get_webview_window(label)` and focus it if it exists +6. Show frontend code for creating windows using `new WebviewWindow(label, options)` from `@tauri-apps/api/webviewWindow` + +CRITICAL: Use `tauri::WebviewWindowBuilder` and `tauri::WebviewUrl` (NOT the v1 `WindowBuilder`/`WindowUrl`). +CRITICAL: Use `app_handle.get_webview_window()` (NOT `app_handle.get_window()`). +``` + +--- + +**Continue to**: [Part 2: Advanced Prompts, Checklist & Migration Map](./02-advanced-prompts-checklist-migration.md) diff --git a/tauri2-guide/agent-prompts.md b/tauri2-guide/02-advanced-prompts-checklist-migration.md similarity index 63% rename from tauri2-guide/agent-prompts.md rename to tauri2-guide/02-advanced-prompts-checklist-migration.md index dd3a78c..5218168 100644 --- a/tauri2-guide/agent-prompts.md +++ b/tauri2-guide/02-advanced-prompts-checklist-migration.md @@ -1,4 +1,4 @@ -# Tauri 2.0 Agent Prompts — Definitive Collection +# Tauri 2.0 Agent Prompts — Part 2: Advanced Prompts, Checklist & Migration Map > **Purpose**: Copy-paste ready prompts for instructing AI agents to build Tauri 2.0 applications. > **Constraint**: These prompts are designed for sandbox environments where code compilation is NOT available. Agents must verify correctness using logic, API references, and pattern matching. @@ -7,13 +7,6 @@ ## Table of Contents -1. [Universal System Prompt (Required Prefix)](#1-universal-system-prompt-required-prefix) -2. [Prompt: Scaffold a New Tauri 2.0 Project](#2-prompt-scaffold-a-new-tauri-20-project) -3. [Prompt: Add a Tauri Plugin](#3-prompt-add-a-tauri-plugin) -4. [Prompt: Create a Custom Command (IPC)](#4-prompt-create-a-custom-command-ipc) -5. [Prompt: Add State Management](#5-prompt-add-state-management) -6. [Prompt: Implement Event-Based Communication](#6-prompt-implement-event-based-communication) -7. [Prompt: Create Multi-Window App](#7-prompt-create-multi-window-app) 8. [Prompt: Build a WebSocket Server in Rust](#8-prompt-build-a-websocket-server-in-rust) 9. [Prompt: Implement Script Injection](#9-prompt-implement-script-injection) 10. [Prompt: Set Up Capabilities & Permissions](#10-prompt-set-up-capabilities--permissions) @@ -23,200 +16,7 @@ 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) ---- - -## 1. Universal System Prompt (Required Prefix) - -> **Always include this as the system context before any task-specific prompt.** - -``` -You are a Tauri 2.0 expert developer. You build desktop and mobile applications using the Tauri 2.0 framework (Rust backend + web frontend). - -CRITICAL ENVIRONMENT CONSTRAINTS: -- You CANNOT compile or run code in this sandbox. Do NOT attempt compilation. -- Verify correctness using logic, API pattern matching, and reference knowledge only. -- Cross-reference every code snippet against known Tauri 2.0 API patterns before outputting. -- If you are uncertain about an API or pattern, say so explicitly rather than guessing. - -MANDATORY TAURI 2.0 RULES — NEVER VIOLATE: -1. This is Tauri 2.0 (NOT v1). The APIs are fundamentally different. -2. Use `tauri::WebviewWindow` (NOT `tauri::Window`). -3. Use `tauri::WebviewWindowBuilder` (NOT `tauri::WindowBuilder`). -4. Use `tauri::WebviewUrl` (NOT `tauri::WindowUrl`). -5. Use `Manager::get_webview_window()` (NOT `Manager::get_window()`). -6. Use `tauri::Emitter` trait for `emit()` and `emit_to()` (NOT the old window-only emit). -7. Use `tauri::Listener` trait for `listen()` and `once()` on the Rust side. -8. JS import `invoke` from `@tauri-apps/api/core` (NOT `@tauri-apps/api/tauri`). -9. JS import window utilities from `@tauri-apps/api/webviewWindow` (NOT `@tauri-apps/api/window`). -10. JS plugins import from `@tauri-apps/plugin-` (NOT `@tauri-apps/api/`). -11. Config uses `app` section (NOT `tauri` section), `build.frontendDist` (NOT `build.distDir`). -12. Security uses Capabilities + Permissions (NOT `allowlist`). -13. Every plugin requires: (a) Rust crate in Cargo.toml, (b) `.plugin()` registration in Builder, (c) npm package, (d) permissions in capabilities. -14. Use the `lib.rs` + `main.rs` pattern. Core logic goes in `lib.rs`. `main.rs` only calls `lib::run()`. -15. `#[cfg_attr(mobile, tauri::mobile_entry_point)]` attribute is REQUIRED on the `pub fn run()` function. -16. `invoke_handler` can only be called ONCE — all commands go in a single `tauri::generate_handler![]`. -17. Async commands cannot use borrowed references (`&str`, `&Path`) directly — use owned types or wrap return in `Result`. -18. Window creation from async contexts MUST use `app_handle.run_on_main_thread()`. -19. Always clean up event listeners (call `unlisten()`) on the frontend. -20. Use `tauri = { version = "2" }` and `tauri-build = { version = "2" }` (NOT version 1). - -FILE STRUCTURE: -- Frontend source: `src/` -- Rust backend: `src-tauri/` -- Rust entry-point: `src-tauri/src/main.rs` (minimal) -- Core app logic: `src-tauri/src/lib.rs` -- Extra modules: `src-tauri/src/.rs` -- Capabilities: `src-tauri/capabilities/default.json` -- Config: `src-tauri/tauri.conf.json` -- Rust deps: `src-tauri/Cargo.toml` -- Build script: `src-tauri/build.rs` - -OFFICIAL DOCUMENTATION: -- Main docs: https://v2.tauri.app -- Rust API: https://docs.rs/tauri/latest/tauri/ -- Migration guide: https://v2.tauri.app/start/migrate/from-tauri-1/ -- Capabilities: https://v2.tauri.app/security/capabilities/ -- Permissions: https://v2.tauri.app/security/permissions/ -- Configuration: https://v2.tauri.app/develop/configuration-files/ -- Calling Rust: https://v2.tauri.app/develop/calling-rust/ -- Calling Frontend: https://v2.tauri.app/develop/calling-frontend/ -- Plugin index: https://v2.tauri.app/plugin/ -``` - ---- - -## 2. Prompt: Scaffold a New Tauri 2.0 Project - -``` -Create a new Tauri 2.0 project from scratch with vanilla JavaScript (no frontend framework). - -Requirements: -- App name: [APP_NAME] -- Window title: [WINDOW_TITLE] -- Default window size: [WIDTH]x[HEIGHT] -- withGlobalTauri enabled (for window.__TAURI__ access without npm package) - -Generate ALL of the following files with complete, correct Tauri 2.0 code: - -1. `package.json` — with @tauri-apps/cli v2 devDependency and scripts (dev, build, tauri) -2. `src/index.html` — basic HTML5 boilerplate -3. `src/main.js` — demonstrate invoking a Rust command -4. `src/styles.css` — basic styling -5. `src-tauri/tauri.conf.json` — correct v2 structure (app section, build section, bundle section) -6. `src-tauri/Cargo.toml` — with tauri v2, tauri-build v2, serde, serde_json. Include [lib] with crate-type ["staticlib", "cdylib", "rlib"] -7. `src-tauri/build.rs` — standard tauri_build::build() -8. `src-tauri/capabilities/default.json` — with core:default permission -9. `src-tauri/src/main.rs` — minimal entry-point calling lib::run() -10. `src-tauri/src/lib.rs` — with Builder pattern, mobile_entry_point attribute, a sample "greet" command, and the command registered in invoke_handler - -Verify every line follows Tauri 2.0 conventions. Cross-check config structure against the v2 schema. -``` - ---- - -## 3. Prompt: Add a Tauri Plugin - -``` -Add the [PLUGIN_NAME] plugin to an existing Tauri 2.0 project. - -Plugin to add: [tauri-plugin-STORE/turi-plugin-FS/etc.] -JS package name: [@tauri-apps/plugin-store/etc.] - -Instructions: -1. Add the Rust crate to `src-tauri/Cargo.toml` as `tauri-plugin-[name] = "2"` -2. Register the plugin in `src-tauri/src/lib.rs` using `.plugin(tauri_plugin_[name]::init())` or the Builder pattern if the plugin requires configuration -3. Add the npm package: `@tauri-apps/plugin-[name]` -4. Update `src-tauri/capabilities/default.json` to add the appropriate permission (usually `[name]:default` or specific allow permissions) -5. Provide a frontend usage example showing the correct v2 import from `@tauri-apps/plugin-[name]` - -Show the exact changes needed for each file. Do NOT use v1 import paths or API patterns. -``` - ---- - -## 4. Prompt: Create a Custom Command (IPC) - -``` -Create a Tauri 2.0 command called "[COMMAND_NAME]" that [DESCRIBE WHAT IT DOES]. - -Requirements: -- Define the command using `#[tauri::command]` in a separate module file `src-tauri/src/commands.rs` -- The command must be `pub` (required for separate modules) -- Include proper error handling with `Result` or a custom error type -- Register the command in `src-tauri/src/lib.rs` using `mod commands;` and including it in `generate_handler![commands::COMMAND_NAME]` -- Provide the frontend JavaScript code to invoke the command using `invoke()` from `@tauri-apps/api/core` or `window.__TAURI__.core` -- If the command is async, ensure all arguments are owned types (not borrowed references) - -Show complete, correct code for both the Rust side and the JS side. -``` - ---- - -## 5. Prompt: Add State Management - -``` -Add shared state management to an existing Tauri 2.0 application. - -Requirements: -1. Define a state struct in `src-tauri/src/lib.rs` using `std::sync::Mutex` for thread safety -2. Manage the state in the Builder using `.manage(MyState { ... })` -3. Create at least two commands that read and write the managed state using `tauri::State<'_, MyState>` -4. Show the frontend code for invoking these commands - -If persistent storage is also needed: -- Add `tauri-plugin-store = "2"` to Cargo.toml -- Register the plugin -- Add `store:default` to capabilities -- Show frontend usage with `@tauri-apps/plugin-store` -``` - ---- - -## 6. Prompt: Implement Event-Based Communication - -``` -Implement bidirectional event communication in a Tauri 2.0 application. - -Requirements: - -Rust side: -- Use `use tauri::{Emitter, AppHandle}` to emit events from Rust -- Demonstrate both global emit (`app.emit()`) and targeted emit (`app.emit_to()`) -- If listening for events on the Rust side, use `use tauri::Listener` and `app.listen()` or `app.once()` - -Frontend side: -- Import from `@tauri-apps/api/event` (NOT `@tauri-apps/api/tauri`) -- Show `listen()`, `once()`, `emit()`, and `emitTo()` usage -- Demonstrate proper cleanup with `unlisten()` -- Show targeted listening using `getCurrentWebviewWindow().listen()` - -Show complete code for: -1. A Rust command that emits a progress event -2. A Rust command that emits a completion event to a specific window -3. Frontend listeners that handle these events -4. Frontend emitting an event back to Rust - -Make sure `core:event:default` (or specific `core:event:allow-*` permissions) is included in capabilities. -``` - ---- - -## 7. Prompt: Create Multi-Window App - -``` -Create a Tauri 2.0 application with multiple windows. - -Requirements: -1. Main window defined in `tauri.conf.json` with label "main" -2. A command `open_secondary` that creates a new window dynamically using `WebviewWindowBuilder` -3. The secondary window should load a local HTML file (`WebviewUrl::App("secondary.html".into())`) -4. If creating the window from an async context, use `app_handle.run_on_main_thread()` -5. Check for existing windows with the same label using `app_handle.get_webview_window(label)` and focus it if it exists -6. Show frontend code for creating windows using `new WebviewWindow(label, options)` from `@tauri-apps/api/webviewWindow` - -CRITICAL: Use `tauri::WebviewWindowBuilder` and `tauri::WebviewUrl` (NOT the v1 `WindowBuilder`/`WindowUrl`). -CRITICAL: Use `app_handle.get_webview_window()` (NOT `app_handle.get_window()`). -``` +**See also**: [Part 1: System Prompt & Common Task Prompts](./01-system-prompt-and-common-prompts.md) --- @@ -573,3 +373,7 @@ Follow this structured diagnosis process: 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. ``` + +--- + +**Back to**: [Part 1: System Prompt & Common Task Prompts](./01-system-prompt-and-common-prompts.md) diff --git a/tauri2-guide/02-runtime-platform-mobile-errors.md b/tauri2-guide/02-runtime-platform-mobile-errors.md new file mode 100644 index 0000000..088b215 --- /dev/null +++ b/tauri2-guide/02-runtime-platform-mobile-errors.md @@ -0,0 +1,833 @@ +# Tauri 2.0 Error Resolution Guide — Part 2: Runtime, Platform & Mobile Errors + +> **Version**: 2.0 | **Last Updated**: 2026-05-31 +> **Official Docs**: https://v2.tauri.app +> +> This file covers Categories I-R and the Quick-Reference (runtime, platform-specific, and mobile errors). Part 1 covers build, config and plugin errors. + +--- + +## Table of Contents + +9. [Category I: Event System Errors](#category-i-event-system-errors) +10. [Category J: Content Security Policy (CSP) Errors](#category-j-content-security-policy-csp-errors) +11. [Category K: Frontend Integration Errors (Vite / Next.js / SvelteKit)](#category-k-frontend-integration-errors-vite--nextjs--sveltekit) +12. [Category L: WebSocket Errors](#category-l-websocket-errors) +13. [Category M: Path & Asset Protocol Errors](#category-m-path--asset-protocol-errors) +14. [Category N: Shell & Sidecar Errors](#category-n-shell--sidecar-errors) +15. [Category O: Mobile Build Errors (iOS & Android)](#category-o-mobile-build-errors-ios--android) +16. [Category P: Platform-Specific Build Errors](#category-p-platform-specific-build-errors) +17. [Category Q: Panics & Runtime Crashes](#category-q-panics--runtime-crashes) +18. [Category R: Error Handling Best Practices](#category-r-error-handling-best-practices) +19. [Quick-Reference: Top 25 Most Common Errors](#quick-reference-top-25-most-common-errors) + +**See also**: [Part 1: Build, Config & Plugin Errors](./01-build-config-permission-plugin-errors.md) + +--- + +## Category I: Event System Errors + +### I1. Events Emitted But Not Received by New Window + +**Error:** Events emitted to a newly created window are not received the first time. + +**Cause:** Race condition — the event is emitted before the listener is attached in the new window. + +**Solution:** Use a handshake pattern: +```javascript +// In new window — announce ready +import { emit } from '@tauri-apps/api/event'; +emit('window-ready', { label: 'my-window' }); + +// In main window — respond after ready signal +listen('window-ready', (event) => { + emitTo(event.payload.label, 'initial-data', myData); +}); +``` + +--- + +### I2. Events Not Working Between Windows + +**Error:** Events emitted from one window are not received by another window. + +**Cause:** Using `emit()` (which broadcasts to all) vs. `emitTo()` (which targets a specific window) incorrectly, or listeners scoped incorrectly. + +**Solution:** +```rust +// Emit to ALL windows +app.emit("my-event", payload)?; + +// Emit to a SPECIFIC window +app.emit_to("settings-panel", "my-event", payload)?; +``` +```javascript +// Frontend: listen globally +import { listen } from '@tauri-apps/api/event'; +const unlisten = await listen('my-event', handler); + +// Frontend: listen on specific window only +import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'; +const appWebview = getCurrentWebviewWindow(); +const unlisten2 = await appWebview.listen('my-event', handler); +``` + +--- + +### I3. Rapid Event Emission Causes Panic + +**Error:** App crashes when emitting large numbers of events rapidly. + +**Cause:** Internal buffer overflow or deadlock in the event system. + +**Solution:** Throttle/debounce event emission. For high-frequency data, use `Channel` instead of events: +```rust +use tauri::ipc::Channel; + +#[tauri::command] +fn stream_data(on_event: Channel) { + for item in data_source { + on_event.send(item).unwrap(); + } +} +``` + +--- + +## Category J: Content Security Policy (CSP) Errors + +### J1. `Refused to connect to 'http://ipc.localhost'` + +**Error:** +``` +Refused to connect to 'http://ipc.localhost/test' because it violates the document's Content Security Policy. +``` + +**Cause:** External URLs loaded in webviews have their own CSP that doesn't allow connections to Tauri's IPC endpoint. + +**Solution:** Configure CSP to include IPC: +```json +{ + "app": { + "security": { + "csp": "default-src 'self'; connect-src 'self' ipc: http://ipc.localhost https://your-url.com" + } + } +} +``` +For maximum compatibility with injected scripts on external pages: `"csp": null` (use with caution). + +--- + +### J2. CSS-in-JS Libraries Blocked by CSP + +**Error:** Styles/scripts from styled-components, emotion, or other CSS-in-JS libraries are blocked. + +**Cause:** Default CSP doesn't allow inline styles. + +**Solution:** Add `'unsafe-inline'` to `style-src`: +```json +{ + "app": { + "security": { + "csp": "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self'" + } + } +} +``` + +--- + +## Category K: Frontend Integration Errors (Vite / Next.js / SvelteKit) + +### K1. Vite Dev Server Not Connecting / White Screen + +**Error:** Tauri window opens but shows blank white screen. Dev server connection not established. + +**Cause:** Port mismatch between `devUrl` in `tauri.conf.json` and Vite's actual port. + +**Solution:** +1. Ensure `devUrl` matches Vite's actual port: `"devUrl": "http://localhost:5173"` +2. For mobile, use `process.env.TAURI_DEV_HOST` as the host in Vite config +3. Set `server.host` in `vite.config.js` to allow network access + +--- + +### K2. Vite Watches `src-tauri` — Infinite Recompilation + +**Error:** App keeps recompiling Rust code repeatedly in an infinite loop. + +**Cause:** Vite's file watcher detects changes in `src-tauri/` (from Cargo build output) and triggers re-runs. + +**Solution:** Add `src-tauri` to Vite's ignore list: +```javascript +// vite.config.js +export default defineConfig({ + server: { + watch: { + ignored: ["**/src-tauri/**"] + } + } +}); +``` + +--- + +### K3. Next.js App Router Breaks in Production + +**Error:** `useEffect` hooks broken, syntax errors in chunk files, component rendering failures in production. + +**Cause:** Next.js App Router uses React Server Components and advanced chunking that conflicts with Tauri's custom protocol. + +**Solution:** Use static export only: +```javascript +// next.config.js +module.exports = { + output: 'export', // Required — no SSR + images: { unoptimized: true }, // No server-side image optimization +}; +``` +Add `'use client'` directives to all interactive components. + +--- + +### K4. Next.js `assetPrefix` Breaks Hot Reload + +**Error:** Hot reload doesn't work when `assetPrefix` is set for Tauri. + +**Cause:** `assetPrefix` changes the URL for WebSocket connections, breaking HMR. + +**Solution:** Only set `assetPrefix` in production: +```javascript +// next.config.js +module.exports = { + output: 'export', + assetPrefix: process.env.NODE_ENV === 'production' ? './' : undefined, +}; +``` + +--- + +### K5. Next.js Standalone Output — `asset not found: index.html` + +**Error:** +``` +asset not found: index.html +``` + +**Cause:** Next.js standalone output doesn't produce a static `index.html` file. + +**Solution:** Use `output: "export"`, NOT `standalone`: +```javascript +module.exports = { + output: 'export', // Required for Tauri + // NOT output: 'standalone' +}; +``` + +--- + +### K6. SvelteKit Blank Screen / PostCSS Errors + +**Error:** Blank white screen or `[postcss] Internal server error`. + +**Cause:** SvelteKit's routing conflicts with Tauri's static file serving. + +**Solution:** Use `@sveltejs/adapter-static`: +```javascript +// svelte.config.js +import adapter from '@sveltejs/adapter-static'; + +export default { + kit: { + adapter: adapter({ fallback: 'index.html' }) + } +}; +``` +Add to root layout: `export const ssr = false;` + +--- + +## Category L: WebSocket Errors + +### L1. WebSocket Connection Closing / "Broken Pipe" + +**Error:** WebSocket connection closes unexpectedly after app has been open for a long time. + +**Cause:** Idle timeouts or OS-level connection cleanup. + +**Solution:** Implement ping/pong keepalive in custom WebSocket connections: +```javascript +const ws = new WebSocket('ws://127.0.0.1:8080'); + +// Keepalive +setInterval(() => { + if (ws.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify({ type: 'ping' })); + } +}, 30000); +``` + +--- + +### L2. WebSocket Plugin — Unstable Connections + +**Error:** Messages not received right after connection with `tauri-plugin-websocket`. + +**Cause:** The WebSocket connection may not be fully initialized before messages are sent. + +**Solution:** Wait for the `onopen` event: +```javascript +import WebSocket from '@tauri-apps/plugin-websocket'; + +const ws = await WebSocket.connect('ws://localhost:8080'); +// Ensure ready before sending +ws.addListener((msg) => { + console.log('Received:', msg); +}); +``` + +--- + +### L3. WebSocket + Tauri Client Conflict + +**Error:** WebSocket client code and Tauri app cannot run simultaneously. + +**Cause:** Resource conflicts between the Tauri WebView and WebSocket connections, especially on mobile. + +**Solution:** Use `tauri-plugin-websocket` instead of raw `WebSocket` API for mobile compatibility. + +--- + +## Category M: Path & Asset Protocol Errors + +### M1. 403 Forbidden on Asset Protocol URLs + +**Error:** `403 Forbidden` when accessing files via `asset://` protocol. + +**Cause:** The asset protocol has a scope restricting which directories can be accessed. + +**Solution:** Configure asset protocol scope: +```json +{ + "identifier": "asset-capability", + "permissions": [ + { + "identifier": "fs:allow-read", + "allow": [{ "path": "$RESOURCE/**" }] + } + ] +} +``` +Use `convertFileSrc()` from `@tauri-apps/api/core` for proper path conversion. + +--- + +### M2. `resource_dir()` Returns Unknown Path + +**Error:** `PathResolver.resource_dir()` returns empty or unknown path. + +**Cause:** A custom `target-dir` in `~/.cargo/config.toml` breaks resource resolution logic. + +**Solution:** Remove global `target-dir` setting, or configure it per-project instead of globally. + +--- + +### M3. Path Traversal Prevention Blocking Valid Paths + +**Error:** `path traversal` error from `@tauri-apps/plugin-fs`. + +**Cause:** The FS plugin blocks paths containing `..`. + +**Solution:** Normalize paths before use: +```javascript +import { resolve } from '@tauri-apps/api/path'; +const normalizedPath = await resolve('some', 'relative', 'path'); +``` + +--- + +## Category N: Shell & Sidecar Errors + +### N1. Sidecar Binary Not Found + +**Error:** +``` +Failed to spawn my-binary: Io(Os { code: 3, kind: NotFound }) +``` + +**Cause:** The sidecar binary doesn't follow Tauri's naming convention. Tauri appends the target triple to the binary name. + +**Solution:** +```json +// tauri.conf.json +{ + "bundle": { + "externalBin": ["binaries/my-binary"] + } +} +``` +Name the binary with the target triple suffix: +- Windows: `my-binary-x86_64-pc-windows-msvc.exe` +- macOS: `my-binary-aarch64-apple-darwin` +- Linux: `my-binary-x86_64-unknown-linux-gnu` + +Reference in Rust without the suffix: +```rust +app.shell().sidecar("binaries/my-binary").unwrap() +``` + +--- + +## Category O: Mobile Build Errors (iOS & Android) + +### O1. `xcodebuild exited with code 65` + +**Error:** +``` +error: failed to run custom build command for tauri v2 ... xcodebuild exited with code 65 +``` + +**Cause:** iOS build failure. Common causes: code signing issues, stale build artifacts, Xcode version incompatibility. + +**Solution:** +1. Open Xcode project: `open src-tauri/gen/apple/Project.xcworkspace` +2. Set Signing & Capabilities → Team +3. Clean build: `Cmd+Shift+K` in Xcode +4. Clean pods: `cd src-tauri/gen/apple && rm -rf build Pods && pod install` + +--- + +### O2. `can't find crate for core` (Missing Mobile Target) + +**Error:** +``` +error[E0463]: can't find crate for `core` +``` +When building for iOS or Android. + +**Cause:** The Rust target for the mobile platform is not installed. + +**Solution:** +```bash +# iOS +rustup target add aarch64-apple-ios + +# Android (all targets) +rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android +cargo install cargo-ndk +``` + +--- + +### O3. `building for 'iOS', but linking in object file built for 'macOS'` + +**Error:** +``` +building for 'iOS', but linking in object file built for 'macOS' +``` + +**Cause:** A native dependency was compiled for the host platform instead of the iOS target. + +**Solution:** Check which dependency causes the issue: `cargo build --target aarch64-apple-ios -v`. Ensure all dependencies support cross-compilation to iOS. + +--- + +### O4. Android Build Fails — `Cannot find module 'tauri'` + +**Error:** +``` +Cannot find module 'tauri' during Gradle build +``` + +**Cause:** Node.js executable can't be found when Gradle's BuildTask runs. + +**Solution:** Ensure Node.js is available system-wide. Use `npx tauri` or configure the Gradle BuildTask to use the correct node path. + +--- + +### O5. Android App Crashes on Launch + +**Error:** App immediately crashes after deployment to Android device/emulator. + +**Cause:** Missing Android SDK components, incorrect NDK configuration, or incompatible Gradle/AGP versions. + +**Solution:** +1. Ensure correct Android SDK and NDK are installed +2. Run `npx tauri android init` to set up properly +3. Check `android/app/build.gradle` has correct Tauri integration +4. Verify ADB connection: `adb devices` + +--- + +### O6. `tauri android dev` Hangs Indefinitely + +**Error:** Command hangs with no output or progress. + +**Cause:** Emulator/device connection failure or Gradle build hanging. + +**Solution:** +1. Verify ADB: `adb devices` +2. Clean Gradle cache: `cd android && ./gradlew clean` +3. Use a physical device instead of emulator +4. Increase Gradle memory in `android/gradle.properties` + +--- + +### O7. `mobile_entry_point` Conflicts with `#[tokio::main]` + +**Error:** App crashes on iOS when using `#[tokio::main]` with `mobile_entry_point`. + +**Cause:** `mobile_entry_point` and `#[tokio::main]` generate conflicting function signatures. + +**Solution:** +```rust +// ❌ WRONG +#[cfg_attr(mobile, tauri::mobile_entry_point)] +#[tokio::main] +pub async fn run() { /* ... */ } + +// ✅ CORRECT — use async_runtime::block_on +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::async_runtime::block_on(async { + tauri::Builder::default() + .run(tauri::generate_context!()) + .expect("error while running tauri application"); + }); +} +``` + +--- + +### O8. No Code Signing Certificates Found (iOS) + +**Error:** +``` +No code signing certificates found. You must add one and set the certificate development team ID. +``` + +**Cause:** No Apple Developer certificate configured. + +**Solution:** Open `src-tauri/gen/apple/Project.xcworkspace` in Xcode → Signing & Capabilities → select Team. Or use environment variables: `APPLE_SIGNING_IDENTITY`, `APPLE_TEAM_ID`. + +--- + +### 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 + +**Error:** Build fails with GTK/WebKit-related errors. + +**Cause:** Missing system libraries for GTK/WebKit development. + +**Solution:** +```bash +# Ubuntu/Debian +sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev \ + librsvg2-dev libsoup-3.0-dev + +# Fedora +sudo dnf install webkit2gtk4.1-devel gtk3-devel libappindicator-gtk3-devel \ + librsvg2-devel libsoup3-devel + +# Arch +sudo pacman -S webkit2gtk-4.1 gtk3 libappindicator-gtk3 librsvg libsoup3 +``` + +--- + +### P2. Linux — Failed to Find ICNS Encoder + +**Error:** +``` +error: failed to compile tauri-bundler: failed to find ICNS encoder +``` + +**Cause:** Missing icon tools for macOS bundle creation on Linux. + +**Solution:** `sudo apt install icnsutils` (Debian/Ubuntu) or equivalent package. + +--- + +### P3. Linux — Failed to Run `linuxdeploy` + +**Error:** +``` +error: failed to bundle project: failed to run linuxdeploy +``` + +**Cause:** Missing AppImage bundling dependencies. + +**Solution:** Install `linuxdeploy` and `linuxdeploy-plugin-gtk`. Ensure `appimage-builder` is available. + +--- + +## Category Q: Panics & Runtime Crashes + +### Q1. Generic "error while running tauri application" + +**Error:** +``` +thread 'main' panicked at src\main.rs:14:10: +error while running tauri application: +``` + +**Cause:** Top-level error wrapper. The inner error reveals the actual problem — check plugin initialization, WebView creation, or config validation. + +**Solution:** Replace `.expect()` with better error reporting: +```rust +if let Err(e) = tauri::Builder::default() + .plugin(tauri_plugin_http::init()) + .run(tauri::generate_context!()) +{ + eprintln!("Failed to start: {e}"); + std::process::exit(1); +} +``` + +--- + +### Q2. Panic in tauri-runtime-wry on Wayland + +**Error:** +``` +Panic in tauri-runtime-wry during init on Wayland +``` + +**Cause:** Wayland compositors missing `wlr_data_control_manager v1` protocol cause the clipboard crate to panic. + +**Solution:** Use XWayland, or use a Wayland compositor that supports the protocol. + +--- + +### Q3. `EXC_BAD_ACCESS` Crash on iOS + +**Error:** App crashes on physical iPhone with `EXC_BAD_ACCESS` but works in simulator. + +**Cause:** Memory access violations in native code bridging. + +**Solution:** Enable address sanitizer in Xcode. Check for use-after-free or null pointer issues in Rust FFI code. + +--- + +### Q4. Panic When `Builder::setup` Hook Fails + +**Error:** App panics immediately when the `setup` callback returns an error. + +**Cause:** `App::run()` panics when setup fails — no graceful error handling. + +**Solution:** Wrap setup logic properly: +```rust +tauri::Builder::default() + .setup(|app| { + match critical_init(app) { + Ok(_) => Ok(()), + Err(e) => { + eprintln!("Setup failed: {e}"); + Err(e.into()) + } + } + }) + .run(tauri::generate_context!()) +``` + +--- + +## Category R: Error Handling Best Practices + +### R1. Never Panic Inside Tauri Commands + +Panic in a **synchronous** command crashes the app. Panic in an **asynchronous** command causes a Promise that never resolves (silent failure). Always return `Result`: + +```rust +#[derive(Debug, thiserror::Error)] +enum AppError { + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Network(#[from] reqwest::Error), + #[error("{0}")] + Custom(String), +} + +impl serde::Serialize for AppError { + fn serialize(&self, s: S) -> Result + where S: serde::ser::Serializer { + s.serialize_str(&self.to_string()) + } +} + +#[tauri::command] +fn my_command() -> Result { + let data = std::fs::read_to_string("config.toml")?; + Ok(data) +} +``` + +### R2. Frontend Error Handling + +```javascript +import { invoke } from '@tauri-apps/api/core'; + +try { + const result = await invoke('my_command', { arg: 'value' }); + console.log('Success:', result); +} catch (error) { + console.error('Command failed:', error); + // Show user-friendly error in UI +} +``` + +### R3. Structured Errors for the Frontend + +```rust +#[derive(serde::Serialize)] +#[serde(tag = "kind", content = "message")] +#[serde(rename_all = "camelCase")] +enum ErrorKind { + Io(String), + Network(String), + Custom(String), +} + +impl serde::Serialize for AppError { + fn serialize(&self, s: S) -> Result + where S: serde::ser::Serializer { + let kind = match self { + Self::Io(e) => ErrorKind::Io(e.to_string()), + Self::Network(e) => ErrorKind::Network(e.to_string()), + Self::Custom(msg) => ErrorKind::Custom(msg.clone()), + }; + kind.serialize(s) + } +} +``` +Frontend receives: `{ kind: 'io', message: '...' }` — making it easy to handle errors by type. + +### R4. Cleanup Event Listeners + +```javascript +import { listen } from '@tauri-apps/api/event'; + +// ❌ WRONG — memory leak +useEffect(() => { + listen('my-event', handler); + return () => {}; // No cleanup! +}, []); + +// ✅ CORRECT — proper cleanup +useEffect(() => { + const unlisten = listen('my-event', handler); + return () => { unlisten.then(fn => fn()); }; +}, []); +``` + +--- + +## Quick-Reference: Top 25 Most Common Errors + +| # | Error Message | Category | Quick Fix | +|---|---|---|---| +| 1 | `X not allowed. Plugin not found` | Permission | Add permissions to `capabilities/default.json` | +| 2 | `Additional properties not allowed ('devPath', 'distDir')` | Config | Rename to `devUrl` / `frontendDist` | +| 3 | `__TAURI__ is not defined` | Frontend | Set `withGlobalTauri: true` or use npm imports | +| 4 | `AssetNotFound("index.html")` | Config | Fix `frontendDist` path to match bundler output | +| 5 | `unresolved import 'tauri::api'` | Migration | Replace with plugins (`tauri-plugin-*`) | +| 6 | `cannot find type 'Window' in 'tauri'` | Migration | Use `WebviewWindow` | +| 7 | `failed to create webview: WebView2 error` | Webview | Install WebView2; remove custom browser args | +| 8 | `Path not allowed on the configured scope` | Permission | Add FS scope permissions | +| 9 | `command not found` | IPC | Add command to `generate_handler![]` | +| 10 | `cannot find type 'SystemTray'` | Migration | Use `tauri::tray::TrayIconBuilder` | +| 11 | `lifetime not constrained` in async | Async | Use owned types (`String`, not `&str`) | +| 12 | `MutexGuard cannot be sent between threads` | Async | Use `tokio::sync::Mutex` instead of `std::sync::Mutex` | +| 13 | `xcodebuild exited with code 65` | Mobile | Set signing team in Xcode | +| 14 | `can't find crate for core` | Mobile | `rustup target add ` | +| 15 | `Additional properties not allowed ('allowlist')` | Config | Remove; use v2 capabilities system | +| 16 | `beforeDevCommand terminated with non-zero status` | Config | Fix npm scripts and port numbers | +| 17 | Vite infinite recompilation | Frontend | Add `src-tauri` to `watch.ignored` | +| 18 | Next.js `useEffect` broken in production | Frontend | Use `output: "export"` + `'use client'` | +| 19 | `program not allowed on shell scope` | Shell | Add program to shell capability scope | +| 20 | `Refused to connect to 'http://ipc.localhost'` | CSP | Add `ipc: http://ipc.localhost` to `connect-src` | +| 21 | Only last `invoke_handler` works | IPC | Combine all commands in one `generate_handler![]` | +| 22 | `GDK may only be used from the main thread` | Platform | Update Tauri; avoid cross-thread UI calls | +| 23 | `PluginInitialization("http", "invalid type")` | Config | Migrate old plugins config to v2 capabilities | +| 24 | Sidecar `Io(Os { code: 3 })` | Shell | Fix binary naming with target triple suffix | +| 25 | `error while running tauri application` | Generic | Check inner error; improve error handling | + +--- + +**Back to**: [Part 1: Build, Config & Plugin Errors](./01-build-config-permission-plugin-errors.md) diff --git a/tauri2-guide/definitive-guide.md b/tauri2-guide/02-windows-plugins-websocket-state-migration.md similarity index 50% rename from tauri2-guide/definitive-guide.md rename to tauri2-guide/02-windows-plugins-websocket-state-migration.md index 9975feb..6223bf7 100644 --- a/tauri2-guide/definitive-guide.md +++ b/tauri2-guide/02-windows-plugins-websocket-state-migration.md @@ -1,19 +1,14 @@ -# Tauri 2.0 Definitive Developer Guide +# Tauri 2.0 Developer Guide — Part 2: Windows, Plugins, WebSocket, State & Migration > **Version**: 2.0 | **Last Updated**: 2026-05-31 > **Official Docs**: https://v2.tauri.app +> +> This file covers Sections 8-15 of the Definitive Guide. Part 1 covers Architecture, Config, Capabilities, Commands & IPC. --- ## Table of Contents -1. [Introduction & Architecture Overview](#1-introduction--architecture-overview) -2. [Project Structure & Scaffolding](#2-project-structure--scaffolding) -3. [Configuration System (`tauri.conf.json`)](#3-configuration-system-tauriconfjson) -4. [Capabilities & Permissions (Security Model)](#4-capabilities--permissions-security-model) -5. [Rust Backend: `lib.rs` & Commands](#5-rust-backend-librs--commands) -6. [Frontend Integration Patterns](#6-frontend-integration-patterns) -7. [IPC: Commands, Events & Channels](#7-ipc-commands-events--channels) 8. [Window Management](#8-window-management) 9. [Plugin Ecosystem](#9-plugin-ecosystem) 10. [Native WebSocket Server (Rust-side)](#10-native-websocket-server-rust-side) @@ -23,914 +18,9 @@ 14. [Common Anti-Patterns to Avoid](#14-common-anti-patterns-to-avoid) 15. [Quick Reference Cheat Sheet](#15-quick-reference-cheat-sheet) ---- - -## 1. Introduction & Architecture Overview - -Tauri 2.0 is a framework for building small, fast binaries for all major desktop and mobile platforms. It uses a **hybrid architecture** where a Rust backend handles system-level operations while a web frontend (HTML/CSS/JS, or any framework like React/Vue/Svelte) renders the UI inside a native OS webview. - -### Core Architecture Principles - -- **Rust Core (`src-tauri/`)**: Handles system access, file operations, network calls, window management, and any heavy computation. All system interactions MUST go through Rust to maintain security boundaries. -- **Web Frontend (`src/`)**: Renders the UI using standard web technologies. Communicates with Rust exclusively through Tauri's IPC mechanisms (`invoke`, events, channels). -- **Security Boundary**: The frontend runs in a sandboxed webview. It cannot directly access the filesystem, network, or OS APIs — it must request these through Tauri commands and plugins, which are governed by the **Capabilities & Permissions** system. -- **Mobile Support (NEW in v2)**: Tauri 2.0 adds first-class iOS and Android support. This requires a specific project structure (`lib.rs` + `main.rs` pattern) and platform-specific capability configurations. - -### What Changed from v1 to v2 - -Tauri 2.0 is a **complete architectural overhaul**, not an incremental update. The most significant changes are: - -| Area | v1 | v2 | -|------|----|----| -| Security | `allowlist` in config | **Capabilities + Permissions** (ACL-based) | -| APIs | Built-in `tauri::api` module | **Everything is a plugin** | -| Window type | `Window` / `WindowBuilder` | **`WebviewWindow` / `WebviewWindowBuilder`** | -| Config structure | Nested `tauri >` key | Flattened: `app`, `bundle`, `build` | -| Event system | Per-window scoped | **`emit()` is global; `emit_to()` for targeting** | -| JS imports | `@tauri-apps/api/tauri` | **`@tauri-apps/api/core`** | -| Mobile support | Community plugins | **First-class iOS/Android support** | -| Project structure | `main.rs` only | **`lib.rs` + `main.rs` pattern required** | +**See also**: [Part 1: Core Architecture, Config, Commands & IPC](./01-core-architecture-config-commands-ipc.md) --- - -## 2. Project Structure & Scaffolding - -### Standard Tauri 2.0 File Hierarchy - -```text -my-app/ -├── src/ # Frontend UI source -│ ├── index.html # Main HTML entry -│ ├── main.js # Frontend JavaScript -│ └── styles.css # Styles -├── src-tauri/ # Rust backend environment -│ ├── capabilities/ # Security capability definitions -│ │ └── default.json # Maps windows → permissions -│ ├── icons/ # App icons for all platforms -│ ├── src/ -│ │ ├── main.rs # Minimal desktop entry-point -│ │ ├── lib.rs # Core application setup & commands -│ │ └── .rs # Additional Rust modules -│ ├── build.rs # Tauri build script -│ ├── Cargo.toml # Rust dependencies -│ ├── Cargo.lock # Lockfile (COMMIT THIS) -│ └── tauri.conf.json # Main Tauri configuration -├── package.json # Node.js dependencies & scripts -└── package-lock.json -``` - -### Creating a New Project - -```bash -# Using the interactive CLI scaffolder -npm create tauri-app@latest - -# Or with specific template -npm create tauri-app@latest -- --template vanilla-ts - -# Cargo alternative -cargo create-tauri-app -``` - -Supported templates: Vanilla, Vue, Svelte, React, Solid, Angular, Preact, Yew, Leptos, Sycamore. - -### Adding Tauri to an Existing Frontend - -```bash -npm install -D @tauri-apps/cli@latest -npx tauri init -``` - -The `init` command will prompt for: app name, window title, web assets location, dev server URL, and frontend build/dev commands. - -### The `lib.rs` + `main.rs` Pattern (Required in v2) - -Tauri 2.0 requires your core application logic to live in `lib.rs`, with a minimal `main.rs` that calls into it. This is **mandatory** for mobile support because mobile platforms require a shared library entry-point rather than a standard `main()` function. - -**`src-tauri/src/main.rs`** — Desktop-only entry-point: -```rust -// Prevents additional console window on Windows in release mode -#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] - -fn main() { - my_app_lib::run() -} -``` - -**`src-tauri/src/lib.rs`** — Core application: -```rust -#[cfg_attr(mobile, tauri::mobile_entry_point)] -pub fn run() { - tauri::Builder::default() - // plugins, commands, setup, etc. - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} -``` - -**`src-tauri/Cargo.toml`** — Must declare the library: -```toml -[lib] -name = "my_app_lib" -crate-type = ["staticlib", "cdylib", "rlib"] -``` - -The `_lib` suffix in the crate name prevents naming conflicts with the binary on Windows. The three crate types cover: static linking (mobile), dynamic linking (mobile), and Rust library usage (desktop/tests). - -### Development Commands - -```bash -# Run in development mode (hot-reloads UI + auto-recompiles Rust) -npm run tauri dev -# or: pnpm tauri dev, bun tauri dev, cargo tauri dev - -# Build production artifacts -npm run tauri build -``` - ---- - -## 3. Configuration System (`tauri.conf.json`) - -### Main Configuration File - -The primary configuration file is `src-tauri/tauri.conf.json`. Tauri 2.0 supports three formats: - -| Format | Feature Flag Required | -|--------|-----------------------| -| **JSON** (default) | None | -| **JSON5** | `config-json5` on both `tauri` and `tauri-build` | -| **TOML** | `config-toml` on both `tauri` and `tauri-build` | - -### Complete v2 Configuration Structure - -```json -{ - "$schema": "https://tauri.app", - "productName": "my-app", - "version": "1.0.0", - "identifier": "com.mycompany.myapp", - "build": { - "frontendDist": "../dist", - "devUrl": "http://localhost:1420", - "beforeDevCommand": "npm run dev", - "beforeBuildCommand": "npm run build" - }, - "app": { - "withGlobalTauri": true, - "windows": [ - { - "label": "main", - "title": "My App", - "width": 800, - "height": 600, - "resizable": true, - "fullscreen": false, - "center": true, - "dragDropEnabled": true, - "useHttpsScheme": false - } - ], - "security": { - "csp": "default-src 'self'; style-src 'self' 'unsafe-inline'", - "assetProtocol": { - "scope": ["$APPDATA/**", "$RESOURCE/**"] - } - } - }, - "bundle": { - "active": true, - "targets": "all", - "icon": [ - "icons/32x32.png", - "icons/128x128.png", - "icons/128x128@2x.png", - "icons/icon.icns", - "icons/icon.ico" - ], - "licenseFile": "LICENSE", - "copyright": "", - "category": "Utility" - } -} -``` - -### Key Configuration Changes from v1 - -| v1 Path | v2 Path | Notes | -|---------|---------|-------| -| `package.productName` | Top-level `productName` | Moved out of `package` | -| `package.version` | Top-level `version` | Moved out of `package` | -| `package` | *(removed)* | Fields redistributed | -| `build.distDir` | `build.frontendDist` | Renamed | -| `build.devPath` | `build.devUrl` | Renamed | -| `build.withGlobalTauri` | `app.withGlobalTauri` | Moved to `app` section | -| `tauri.*` | `app.*` | Top-level key renamed | -| `tauri.allowlist` | *(removed)* | Replaced by capabilities | -| `tauri.windows.fileDropEnabled` | `app.windows.dragDropEnabled` | Renamed | -| `tauri.bundle` | Top-level `bundle` | Promoted to top-level | -| `tauri.updater` | `plugins.updater` | Moved to plugins | -| `tauri.systemTray` | `app.trayIcon` | Renamed | -| `tauri.cli` | `plugins.cli` | Moved to plugins | -| `bundle.identifier` | Top-level `identifier` | Promoted to top-level | - -### Platform-Specific Configuration - -Create platform override files that **merge** with the main config using JSON Merge Patch (RFC 7396). Arrays are replaced entirely (not element-by-element merged). - -| Platform | File Pattern | -|----------|-------------| -| Linux | `tauri.linux.conf.json` or `Tauri.linux.toml` | -| Windows | `tauri.windows.conf.json` or `Tauri.windows.toml` | -| macOS | `tauri.macos.conf.json` or `Tauri.macos.toml` | -| Android | `tauri.android.conf.json` or `Tauri.android.toml` | -| iOS | `tauri.ios.conf.json` or `Tauri.ios.toml` | - -### Cargo.toml Dependencies - -```toml -[build-dependencies] -tauri-build = { version = "2", features = [] } - -[dependencies] -tauri = { version = "2", features = [] } -serde = { version = "1", features = ["derive"] } -serde_json = "1" -``` - -**Important**: Keep `tauri`, `tauri-build`, and `@tauri-apps/cli` on the same minor version. Always commit `Cargo.lock` for reproducible builds. - -### package.json Scripts - -```json -{ - "scripts": { - "tauri": "tauri", - "dev": "tauri dev", - "build": "tauri build" - }, - "devDependencies": { - "@tauri-apps/cli": "^2" - } -} -``` - -The `"tauri"` script is only required when using `npm` (not needed for yarn/pnpm/bun). - ---- - -## 4. Capabilities & Permissions (Security Model) - -Tauri 2.0 introduces a strict **Access Control List (ACL)** security model that replaces the v1 `allowlist`. This is the most important new concept to understand. - -### Core Concepts - -- **Capabilities** define **which permissions** are granted to **which windows or webviews**. -- **Permissions** describe explicit privileges for specific commands (e.g., `fs:allow-read`, `http:allow-request`). -- **Scopes** further restrict what a permission can access (e.g., only certain file paths). -- Multiple capabilities can apply to the same window — permissions **merge**. -- Security boundaries are based on **window labels** (not titles). - -### Capability File Location - -``` -src-tauri/capabilities/.json (or .toml) -``` - -All files in this directory are **automatically loaded** by default. - -### Basic Capability File - -```json -{ - "$schema": "../gen/schemas/desktop-schema.json", - "identifier": "main-window-capability", - "description": "Permissions for the primary application window", - "platforms": ["linux", "macOS", "windows"], - "windows": ["main"], - "permissions": [ - "core:default" - ] -} -``` - -**Field reference:** -- `$schema` — Points to generated schema for IDE autocompletion. Use `desktop-schema.json` for desktop, `mobile-schema.json` for mobile. -- `identifier` — Unique capability name (ASCII lowercase, max 116 chars). -- `description` — Human-readable purpose. -- `windows` — Array of window labels this capability applies to. Use `["*"]` for all windows. -- `permissions` — Array of permission identifiers. -- `platforms` — Optional. Restricts to specific platforms. Defaults to all platforms if omitted. - -### Permission Identifier Naming Convention - -| Pattern | Meaning | Example | -|---------|---------|---------| -| `:default` | Default permission set for a plugin | `fs:default` | -| `:allow-` | Allow a specific command | `fs:allow-read` | -| `:deny-` | Deny a specific command | `fs:deny-write` | -| `core::` | Core Tauri module permission | `core:event:allow-emit` | - -### Common Permission Identifiers - -**Core permissions:** -- `core:default` — Basic runtime permissions -- `core:path:default` — Path resolution -- `core:event:default` — Event system (includes listen/emit) -- `core:event:allow-listen` — Allow listening to events -- `core:event:allow-emit` — Allow emitting events -- `core:window:default` — Window management -- `core:window:allow-set-title` — Allow changing window title - -**Plugin permissions:** -- `fs:allow-read`, `fs:allow-write`, `fs:allow-exists`, `fs:allow-mkdir`, `fs:allow-remove` -- `http:allow-request` — HTTP client requests -- `dialog:allow-open`, `dialog:allow-save`, `dialog:allow-message` -- `shell:allow-open`, `shell:allow-execute` -- `notification:allow-notify`, `notification:allow-is-permission-granted` -- `clipboard-manager:allow-read-text`, `clipboard-manager:allow-write-text` -- `websocket:allow-connect`, `websocket:allow-send` -- `store:default` — Persistent key-value store - -### Fine-Grained Scopes - -Scopes restrict what a permission can actually access: - -```json -{ - "identifier": "secure-fs-capability", - "windows": ["main"], - "permissions": [ - { - "identifier": "fs:allow-read", - "allow": [{ "path": "$APPDATA/logs/*" }, { "path": "$HOME/Documents/*.txt" }] - }, - { - "identifier": "fs:allow-write", - "allow": [{ "path": "$APPDATA/logs/*.log" }] - }, - { - "identifier": "fs:deny-write", - "deny": [{ "path": "$APPDATA/config/*" }] - } - ] -} -``` - -Available scope variables: `$APPDATA`, `$HOME`, `$APPCONFIG`, `$APPCACHE`, `$APPLOG`, `$RESOURCE`, `$EXE`. - -### Restricting Custom Commands - -By default, all `#[tauri::command]` functions are accessible from all windows. To restrict a custom command to specific windows only, use `build.rs`: - -```rust -fn main() { - tauri_build::try_build( - tauri_build::Attributes::new() - .app_manifest( - tauri_build::AppManifest::new() - .commands(&["my_restricted_command", "admin_only_command"]) - ), - ) - .unwrap(); -} -``` - -Then grant access in the capability: -```json -{ - "identifier": "admin-capability", - "windows": ["admin-panel"], - "permissions": [ - { "identifier": "allow-my-restricted-command" }, - { "identifier": "allow-admin-only-command" } - ] -} -``` - -### Referencing Capabilities Explicitly - -By default, all capability files in `src-tauri/capabilities/` are auto-discovered. To control which ones are used explicitly: - -```json -{ - "app": { - "security": { - "capabilities": ["my-capability", "admin-capability"] - } - } -} -``` - -### Inline Capabilities - -You can define capabilities directly in `tauri.conf.json` instead of separate files: - -```json -{ - "app": { - "security": { - "capabilities": [ - { - "identifier": "inline-cap", - "windows": ["*"], - "permissions": ["core:default"] - } - ] - } - } -} -``` - ---- - -## 5. Rust Backend: `lib.rs` & Commands - -### Application Builder Pattern - -All Tauri 2.0 applications follow the `tauri::Builder` pattern: - -```rust -use tauri::Manager; - -#[cfg_attr(mobile, tauri::mobile_entry_point)] -pub fn run() { - tauri::Builder::default() - .plugin(tauri_plugin_opener::init()) // Register plugins - .plugin(tauri_plugin_dialog::init()) - .plugin(tauri_plugin_fs::init()) - .setup(|app| { // Setup hook (runs once at startup) - // Initialize resources, spawn servers, etc. - Ok(()) - }) - .manage(AppState { /* ... */ }) // Inject managed state - .invoke_handler(tauri::generate_handler![ // Register commands - my_command, - another_command, - module::command_in_module - ]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} -``` - -**Critical rules:** -- `invoke_handler` can only be called **once**. All commands must be in a single `generate_handler![]` call. -- Commands defined in `lib.rs` **cannot** be marked `pub`. -- Commands defined in separate modules **must** be marked `pub`. -- The `#[cfg_attr(mobile, tauri::mobile_entry_point)]` attribute is required for the `run()` function. - -### Defining Commands - -Commands are the primary IPC mechanism. They are Rust functions annotated with `#[tauri::command]` and callable from the frontend via `invoke()`. - -#### Basic Command - -```rust -#[tauri::command] -fn greet(name: String) -> String { - format!("Hello, {}! Welcome from Rust.", name) -} -``` - -#### Command with Error Handling - -```rust -#[tauri::command] -fn login(user: String, password: String) -> Result { - if user == "admin" && password == "secret" { - Ok("authenticated".to_string()) - } else { - Err("invalid credentials".to_string()) - } -} -``` - -#### Async Command - -```rust -#[tauri::command] -async fn fetch_data(url: String) -> Result { - let response = reqwest::get(&url) - .await - .map_err(|e| e.to_string())?; - response.text().await.map_err(|e| e.to_string()) -} -``` - -**IMPORTANT limitation**: Async commands cannot accept borrowed references (`&str`, `&Path`) directly. Convert to owned types (`String`, `PathBuf`) or wrap the return in `Result`: - -```rust -// Option A: Use owned types -#[tauri::command] -async fn process(value: String) -> String { /* ... */ } - -// Option B: Wrap return in Result -#[tauri::command] -async fn process(value: &str) -> Result { /* ... */ } -``` - -#### Custom Error Types (Recommended) - -```rust -use thiserror::Error; - -#[derive(Debug, Error)] -enum AppError { - #[error("IO error: {0}")] - Io(#[from] std::io::Error), - #[error("Network error: {0}")] - Network(#[from] reqwest::Error), -} - -// Implement Serialize so the error can cross the IPC boundary -impl serde::Serialize for AppError { - fn serialize(&self, serializer: S) -> Result - where - S: serde::ser::Serializer, - { - serializer.serialize_str(self.to_string().as_ref()) - } -} - -#[tauri::command] -fn read_config() -> Result { - let content = std::fs::read_to_string("config.toml")?; - Ok(content) -} -``` - -#### Structured Error with Kind/Tag - -```rust -#[derive(Debug, Error)] -enum AppError { - #[error(transparent)] - Io(#[from] std::io::Error), - #[error("{0}")] - Custom(String), -} - -#[derive(serde::Serialize)] -#[serde(tag = "kind", content = "message")] -#[serde(rename_all = "camelCase")] -enum ErrorKind { - Io(String), - Custom(String), -} - -impl serde::Serialize for AppError { - fn serialize(&self, serializer: S) -> Result - where - S: serde::ser::Serializer, - { - let kind = match self { - Self::Io(e) => ErrorKind::Io(e.to_string()), - Self::Custom(msg) => ErrorKind::Custom(msg.clone()), - }; - kind.serialize(serializer) - } -} -``` - -Frontend receives: `{ kind: 'io', message: '...' }` — making it easy to handle errors by type. - -#### Accessing Special Objects in Commands - -**AppHandle** — Access to the application instance: -```rust -#[tauri::command] -fn do_something(app_handle: tauri::AppHandle) { - let app_dir = app_handle.path().app_dir(); - // Spawn windows, emit events, access state... -} -``` - -**WebviewWindow** — The window that invoked the command: -```rust -#[tauri::command] -fn get_window_info(webview_window: tauri::WebviewWindow) -> String { - webview_window.label().to_string() -} -``` - -**Managed State** — Shared application state: -```rust -use tauri::State; -use std::sync::Mutex; - -struct DbConnection(Mutex); - -#[tauri::command] -fn query_database(state: State<'_, DbConnection>) -> String { - let conn = state.0.lock().unwrap(); - conn.clone() -} -``` - -**Raw Request** — Access headers and raw body: -```rust -#[tauri::command] -fn upload(request: tauri::ipc::Request) -> Result<(), String> { - let tauri::ipc::InvokeBody::Raw(data) = request.body() else { - return Err("Expected raw body".to_string()); - }; - let auth = request.headers().get("Authorization") - .ok_or("Missing auth header")?; - Ok(()) -} -``` - -#### Commands in Separate Modules - -```rust -// src-tauri/src/commands.rs -#[tauri::command] -pub fn create_user(name: String) -> String { - format!("Created user: {}", name) -} - -#[tauri::command] -pub fn delete_user(id: u32) -> bool { - // ... - true -} -``` - -```rust -// src-tauri/src/lib.rs -mod commands; - -pub fn run() { - tauri::Builder::default() - .invoke_handler(tauri::generate_handler![ - commands::create_user, - commands::delete_user - ]) - // ... -} -``` - -Note: The `commands::` prefix is Rust path resolution only — the frontend still calls `invoke('create_user', ...)` without any prefix. - -### Returning Large Data (ArrayBuffers) - -For large binary data like file contents, use `tauri::ipc::Response`: - -```rust -use tauri::ipc::Response; - -#[tauri::command] -fn read_file() -> Response { - let data = std::fs::read("/path/to/file").unwrap(); - Response::new(data) -} -``` - -### Streaming Data (Channels) - -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>) { - let mut file = tokio::fs::File::open(path).await.unwrap(); - let mut buf = vec![0u8; 4096]; - loop { - let n = file.read(&mut buf).await.unwrap(); - if n == 0 { break; } - on_chunk.send(&buf[..n]).unwrap(); - } -} -``` - ---- - -## 6. Frontend Integration Patterns - -### Using `@tauri-apps/api` npm Package (Recommended) - -```bash -npm install @tauri-apps/api -``` - -```javascript -// Imports in v2 — NOTE the path changes from v1 -import { invoke } from '@tauri-apps/api/core'; -import { listen, emit, emitTo, once } from '@tauri-apps/api/event'; -import { getCurrentWebviewWindow, WebviewWindow } from '@tauri-apps/api/webviewWindow'; -import { Channel } from '@tauri-apps/api/core'; -``` - -### Using Global `window.__TAURI__` (No npm package needed) - -Requires `app.withGlobalTauri: true` in `tauri.conf.json`: - -```javascript -// No import needed — accessed via the global object -const { invoke } = window.__TAURI__.core; -const { listen, emit } = window.__TAURI__.event; -``` - -This pattern is ideal for vanilla JS projects that don't use a bundler, avoiding the need for any npm packages beyond `@tauri-apps/cli`. - -### Import Path Changes from v1 - -| v1 Import | v2 Import | -|-----------|-----------| -| `@tauri-apps/api/tauri` | `@tauri-apps/api/core` | -| `@tauri-apps/api/window` | `@tauri-apps/api/webviewWindow` | -| `@tauri-apps/api/cli` | `@tauri-apps/plugin-cli` | -| `@tauri-apps/api/clipboard` | `@tauri-apps/plugin-clipboard-manager` | -| `@tauri-apps/api/dialog` | `@tauri-apps/plugin-dialog` | -| `@tauri-apps/api/fs` | `@tauri-apps/plugin-fs` | -| `@tauri-apps/api/global-shortcut` | `@tauri-apps/plugin-global-shortcut` | -| `@tauri-apps/api/http` | `@tauri-apps/plugin-http` | -| `@tauri-apps/api/notification` | `@tauri-apps/plugin-notification` | -| `@tauri-apps/api/shell` | `@tauri-apps/plugin-shell` | -| `@tauri-apps/api/updater` | `@tauri-apps/plugin-updater` | -| `@tauri-apps/api/os` | `@tauri-apps/plugin-os` | -| `@tauri-apps/api/process` | `@tauri-apps/plugin-process` | - -**Rule of thumb in v2**: If it's not `core`, `event`, or `webviewWindow`, it's a plugin. - -### Plugin JS Package Naming - -All v2 plugin JS packages follow: `@tauri-apps/plugin-` - -```bash -npm install @tauri-apps/plugin-store -npm install @tauri-apps/plugin-fs -npm install @tauri-apps/plugin-http -``` - ---- - -## 7. IPC: Commands, Events & Channels - -### Commands (Frontend → Rust) - -The primary request-response mechanism: - -```javascript -// Frontend (v2 import) -import { invoke } from '@tauri-apps/api/core'; - -// Simple invocation -const result = await invoke('greet', { name: 'World' }); -console.log(result); // "Hello, World! Welcome from Rust." - -// With error handling -try { - const token = await invoke('login', { user: 'admin', password: 'secret' }); - console.log('Authenticated:', token); -} catch (error) { - console.error('Login failed:', error); -} -``` - -**Argument naming convention**: Arguments are passed as a JSON object with **camelCase** keys by default. Use `#[tauri::command(rename_all = "snake_case")]` to accept snake_case from the frontend. - -### Events (Bidirectional, Multi-Consumer) - -Events are fire-and-forget messages. Use them for notifications, streaming status updates, or any data that multiple components might need. - -#### Emitting Events from Rust - -```rust -use tauri::{AppHandle, Emitter}; - -// Global event (all listeners receive it) -app_handle.emit("download-progress", 42)?; - -// Target a specific window -app_handle.emit_to("settings-panel", "config-changed", new_config)?; - -// Filter to specific windows -use tauri::EventTarget; -app_handle.emit_filter("notification", payload, |target| { - matches!(target, EventTarget::WebviewWindow { label } if label == "main") -})?; -``` - -#### Listening for Events on the Frontend - -```javascript -import { listen, once, emit, emitTo } from '@tauri-apps/api/event'; -import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'; - -// Global listen (receives ALL events with this name) -const unlisten = await listen('download-progress', (event) => { - console.log(`Progress: ${event.payload}%`); -}); - -// Targeted listen (only events emitted to THIS window) -const appWebview = getCurrentWebviewWindow(); -const unlisten2 = await appWebview.listen('config-changed', (event) => { - console.log('New config:', event.payload); -}); - -// Listen once, then auto-cleanup -await once('initialization-complete', (event) => { - console.log('App ready!', event.payload); -}); - -// Emit from frontend to Rust -await emit('user-action', { type: 'click', target: 'button' }); - -// Emit to a specific window from frontend -const target = new WebviewWindow('settings-panel'); -await target.emit('settings-request', { key: 'theme' }); -``` - -**Always call `unlisten()`** when the component unmounts or the listener is no longer needed to prevent memory leaks. - -#### Listening for Events on the Rust Side - -```rust -use tauri::{Listener, Manager}; - -pub fn run() { - tauri::Builder::default() - .setup(|app| { - // Listen globally - app.listen("user-action", |event| { - println!("Received: {}", event.payload()); - }); - - // Listen on a specific window - let main = app.get_webview_window("main").unwrap(); - main.listen("config-changed", |event| { - println!("Config updated: {}", event.data); - }); - - // Listen once - app.once("ready", |event| { - println!("App is ready!"); - }); - - Ok(()) - }) - // ... -} -``` - -### Channels (High-Throughput Streaming) - -Channels are the recommended mechanism for streaming large amounts of ordered data from Rust to the frontend. They are faster and more memory-efficient than events for high-throughput scenarios. - -```rust -use tauri::ipc::Channel; -use serde::Serialize; - -#[derive(Clone, Serialize)] -#[serde(tag = "event", content = "data")] -enum StreamEvent { - Started { total: usize }, - Progress { current: usize, chunk: Vec }, - Finished { total: usize }, -} - -#[tauri::command] -fn download_file(url: String, on_event: Channel) { - on_event.send(StreamEvent::Started { total: 1000 }).unwrap(); - // ... streaming logic ... - on_event.send(StreamEvent::Finished { total: 1000 }).unwrap(); -} -``` - -```javascript -import { invoke, Channel } from '@tauri-apps/api/core'; - -const onEvent = new Channel(); -onEvent.onmessage = (event) => { - switch (event.event) { - case 'started': console.log(`Starting: ${event.data.total} bytes`); break; - case 'progress': /* handle chunk */ break; - case 'finished': console.log('Done!'); break; - } -}; - -await invoke('download_file', { url: 'https://...', onEvent }); -``` - -### Evaluating JavaScript from Rust - -```rust -use tauri::Manager; - -// In a command or setup hook -let webview = app.get_webview_window("main").unwrap(); -webview.eval("document.getElementById('status').textContent = 'Loaded!'")?; -``` - -For complex data passing, use the `serialize-to-javascript` crate. - ---- - ## 8. Window Management ### Creating Windows Dynamically @@ -1814,3 +904,7 @@ app.get_webview_window("label")?.set_focus()?; - Calling Frontend: https://v2.tauri.app/develop/calling-frontend/ - Plugin Index: https://v2.tauri.app/plugin/ - Rust Docs: https://docs.rs/tauri/latest/tauri/ + +--- + +**Back to**: [Part 1: Core Architecture, Config, Commands & IPC](./01-core-architecture-config-commands-ipc.md)