.claude/skills/tauri-2/tauri-syntax/tauri-syntax-plugins-api/SKILL.md
Use when using any official Tauri 2 plugin, accessing the file system, making HTTP requests, showing dialogs, or resolving app directories. Prevents missing plugin initialization in Builder, missing npm packages, and absent permission entries. Covers fs, dialog, http, notification, shell, clipboard, os, process, updater, store, opener, path API, and isTauri(). Keywords: tauri plugin, fs, dialog, http, notification, shell, clipboard, store, updater, BaseDirectory, convertFileSrc.
npx skillsauth add OpenAEC-Foundation/OpenAEC-Workspace-Composer tauri-syntax-plugins-apiInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| Plugin | Cargo Crate | npm Package | Default Permission |
|--------|-------------|-------------|-------------------|
| File System | tauri-plugin-fs = "2" | @tauri-apps/plugin-fs | fs:default |
| Dialog | tauri-plugin-dialog = "2" | @tauri-apps/plugin-dialog | dialog:default |
| HTTP | tauri-plugin-http = "2" | @tauri-apps/plugin-http | http:default |
| Notification | tauri-plugin-notification = "2" | @tauri-apps/plugin-notification | notification:default |
| Shell | tauri-plugin-shell = "2" | @tauri-apps/plugin-shell | shell:default |
| Clipboard | tauri-plugin-clipboard-manager = "2" | @tauri-apps/plugin-clipboard-manager | (none, explicit only) |
| OS | tauri-plugin-os = "2" | @tauri-apps/plugin-os | os:default |
| Process | tauri-plugin-process = "2" | @tauri-apps/plugin-process | process:default |
| Updater | tauri-plugin-updater = "2" | @tauri-apps/plugin-updater | updater:default |
| Store | tauri-plugin-store = "2" | @tauri-apps/plugin-store | store:default |
| Opener | tauri-plugin-opener = "2" | @tauri-apps/plugin-opener | opener:default |
For EVERY plugin, ALWAYS perform these three steps:
[dependencies] in src-tauri/Cargo.tomlnpm install @tauri-apps/plugin-<name>src-tauri/capabilities/default.jsonAdditionally, register the plugin in Rust:
// src-tauri/src/lib.rs
tauri::Builder::default()
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_dialog::init())
// ... other plugins
| Function | Description | Example (Windows) |
|----------|-------------|-------------------|
| appDataDir() | App-specific data | C:\Users\X\AppData\Roaming\com.app |
| appConfigDir() | App-specific config | C:\Users\X\AppData\Roaming\com.app |
| appLocalDataDir() | App-specific local data | C:\Users\X\AppData\Local\com.app |
| appCacheDir() | App-specific cache | C:\Users\X\AppData\Local\com.app |
| appLogDir() | App-specific logs | C:\Users\X\AppData\Roaming\com.app\logs |
| homeDir() | User home | C:\Users\X |
| desktopDir() | User desktop | C:\Users\X\Desktop |
| documentDir() | User documents | C:\Users\X\Documents |
| downloadDir() | User downloads | C:\Users\X\Downloads |
| tempDir() | System temp | C:\Users\X\AppData\Local\Temp |
| resourceDir() | Bundled resources | App install directory |
| Member | Value | Description |
|--------|-------|-------------|
| AppData | 14 | App-specific data directory |
| AppConfig | 13 | App-specific config directory |
| AppLocalData | 15 | App-specific local data |
| AppCache | 16 | App-specific cache |
| AppLog | 17 | App-specific log directory |
| Home | 21 | User home directory |
| Desktop | 18 | Desktop directory |
| Document | 6 | Documents directory |
| Download | 7 | Downloads directory |
| Resource | 11 | App bundled resources |
| Temp | 12 | System temp directory |
NEVER use a plugin without adding its permissions to the capability file -- all plugin API calls will fail with "command not allowed" at runtime.
NEVER use relative paths without a BaseDirectory in the fs plugin -- paths without a base directory fail security checks.
NEVER assume path functions are synchronous -- ALL path functions in Tauri return Promise<string>, unlike Node.js.
NEVER forget to install BOTH the Cargo crate AND the npm package -- plugins require both Rust and JS components.
ALWAYS use isTauri() to guard Tauri-specific API calls when the frontend may also run in a browser.
ALWAYS use convertFileSrc() to display local files in HTML -- direct file paths do not work in the webview.
ALWAYS register plugins in tauri::Builder with .plugin() -- installing the crate alone is not sufficient.
import { readTextFile, writeTextFile, readDir, mkdir, exists } from '@tauri-apps/plugin-fs';
import { BaseDirectory } from '@tauri-apps/api/path';
// Read text file
const content = await readTextFile('config.toml', {
baseDir: BaseDirectory.AppConfig,
});
// Write text file
await writeTextFile('config.json', JSON.stringify({ theme: 'dark' }), {
baseDir: BaseDirectory.AppConfig,
});
// Check existence
if (await exists('token.json', { baseDir: BaseDirectory.AppLocalData })) {
// file exists
}
// Create directory
await mkdir('cache/images', { baseDir: BaseDirectory.AppCache });
// Read directory
const entries = await readDir('data', { baseDir: BaseDirectory.AppData });
import { open, save, ask, confirm, message } from '@tauri-apps/plugin-dialog';
// File picker
const filePath = await open({
multiple: false,
directory: false,
filters: [{ name: 'Images', extensions: ['png', 'jpg', 'gif'] }],
});
// Directory picker
const dirPath = await open({ directory: true });
// Save dialog
const savePath = await save({
filters: [{ name: 'Documents', extensions: ['pdf', 'docx'] }],
});
// Message dialogs
await message('Operation completed!', { title: 'Success', kind: 'info' });
const yes = await ask('Delete this file?', { title: 'Confirm', kind: 'warning' });
const ok = await confirm('Proceed?', { title: 'Action' });
import { fetch } from '@tauri-apps/plugin-http';
const response = await fetch('https://api.example.com/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: 'value' }),
});
console.log(response.status);
const data = await response.json();
HTTP permission requires URL scoping:
{
"identifier": "http:default",
"allow": [{ "url": "https://api.example.com/*" }],
"deny": [{ "url": "https://api.example.com/admin/*" }]
}
import { isPermissionGranted, requestPermission, sendNotification } from '@tauri-apps/plugin-notification';
let granted = await isPermissionGranted();
if (!granted) {
const permission = await requestPermission();
granted = permission === 'granted';
}
if (granted) {
sendNotification({
title: 'Download Complete',
body: 'Your file has been downloaded successfully.',
});
}
import { Command } from '@tauri-apps/plugin-shell';
const result = await Command.create('exec-sh', ['-c', 'echo "Hello"']).execute();
console.log(result.stdout);
console.log(result.status.code);
Shell permission requires command whitelisting:
{
"identifier": "shell:allow-execute",
"allow": [{
"name": "exec-sh",
"cmd": "sh",
"args": ["-c", { "validator": "\\S+" }],
"sidecar": false
}]
}
import { writeText, readText } from '@tauri-apps/plugin-clipboard-manager';
await writeText('Hello, clipboard!');
const content = await readText();
import { platform, arch, type, version, hostname, locale, eol } from '@tauri-apps/plugin-os';
const os = await platform(); // "windows", "linux", "macos", "android", "ios"
const cpu = await arch(); // "x86_64", "aarch64", etc.
const ver = await version(); // e.g., "10.0.22621"
const host = await hostname();
const loc = await locale(); // e.g., "en-US"
const lineEnd = eol(); // "\r\n" on Windows, "\n" on Unix
import { exit, relaunch } from '@tauri-apps/plugin-process';
await exit(0); // exit with code
await relaunch(); // restart app
import { load } from '@tauri-apps/plugin-store';
const store = await load('settings.json', { autoSave: true });
await store.set('theme', 'dark');
await store.set('user', { name: 'Alice', prefs: { lang: 'en' } });
const theme = await store.get<string>('theme');
const hasKey = await store.has('theme');
const allKeys = await store.keys();
await store.delete('theme');
await store.clear();
await store.save(); // manual save (if autoSave is false)
await store.reload(); // reload from disk
import { appDataDir, join, resolve, dirname, basename, extname } from '@tauri-apps/api/path';
const dataPath = await appDataDir();
const full = await join(await appDataDir(), 'databases', 'app.db');
const dir = await dirname('/path/to/file.txt'); // "/path/to"
const base = await basename('/path/to/file.txt'); // "file.txt"
const ext = await extname('document.pdf'); // "pdf"
import { convertFileSrc } from '@tauri-apps/api/core';
import { appDataDir, join } from '@tauri-apps/api/path';
const imgSrc = convertFileSrc(
await join(await appDataDir(), 'photos', 'avatar.jpg')
);
document.getElementById('avatar').src = imgSrc;
// Returns: "https://asset.localhost/path/to/avatar.jpg"
import { isTauri } from '@tauri-apps/api/core';
import { invoke } from '@tauri-apps/api/core';
if (isTauri()) {
const data = await invoke('get_data');
} else {
const data = await fetch('/api/data').then(r => r.json());
}
import { check } from '@tauri-apps/plugin-updater';
import { relaunch } from '@tauri-apps/plugin-process';
const update = await check();
if (update) {
console.log(`Update available: ${update.version}`);
await update.downloadAndInstall((event) => {
switch (event.event) {
case 'Started':
console.log(`Downloading ${event.data.contentLength} bytes`);
break;
case 'Progress':
console.log(`Chunk: ${event.data.chunkLength} bytes`);
break;
case 'Finished':
console.log('Download complete');
break;
}
});
await relaunch();
}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Default capabilities for the main window",
"windows": ["main"],
"permissions": [
"core:default",
"core:window:default",
"core:app:default",
"fs:default",
"dialog:default",
"http:default",
"notification:default",
"shell:default",
"clipboard-manager:allow-read-text",
"clipboard-manager:allow-write-text",
"os:default",
"process:default",
"updater:default",
"store:default",
"opener:default"
]
}
For each plugin, auto-generated permissions follow this pattern:
<plugin>:default -- Safe default permissions<plugin>:allow-<command> -- Allow a specific command<plugin>:deny-<command> -- Deny a specific commandDeny rules ALWAYS take precedence over allow rules.
development
Use when integrating Vite with a backend framework, rendering Vite assets from server-side templates, or setting up dev/production HTML serving. Prevents incorrect manifest.json traversal and missing CSS chunk resolution in production. Covers build.manifest configuration, .vite/manifest.json structure, ManifestChunk properties, dev mode HTML setup, production rendering, CSS/JS chunk resolution, and modulepreload polyfill. Keywords: backend integration, manifest.json, ManifestChunk, Django, Laravel, Rails, modulepreload.
development
Use when encountering dev server startup failures, HMR issues, proxy errors, CORS blocks, or module not found errors during development. Prevents misconfiguring server.hmr behind reverse proxies and forgetting appType: 'custom' in middleware mode. Covers HMR full-reload debugging, proxy configuration, CORS setup, HTTPS certificates, server.fs.strict violations, port conflicts, WebSocket failures, file watcher issues, and middleware mode. Keywords: dev server, HMR, proxy, CORS, HTTPS, WebSocket, port conflict, server.fs.strict, middleware mode, file watcher.
development
Use when encountering pre-bundling errors, dependency resolution failures, stale cache issues, or slow development server startup. Prevents excluding CJS dependencies from pre-bundling (which breaks runtime module resolution) and misconfiguring optimizeDeps. Covers CJS/ESM conversion failures, missing dependency auto-discovery, optimizeDeps configuration, monorepo linked dependencies, cache invalidation, browser cache staleness, and large dependency tree performance. Keywords: pre-bundling, optimizeDeps, CJS, ESM, cache, dependency resolution, monorepo, node_modules/.vite.
development
Use when encountering Vite build failures, chunk size warnings, or version-specific build errors. Prevents the common mistake of using deprecated rollupOptions in v8 or misconfiguring build targets and minifiers. Covers Rolldown/Rollup bundling failures, CSS minification errors, sourcemap problems, library mode build failures, BundleError handling, and asset processing errors. Keywords: build error, Rolldown, chunk size, sourcemap, library mode, minify, BundleError, rollupOptions, build.target.