.claude/skills/tauri-2/tauri-syntax/tauri-syntax-window/SKILL.md
Use when creating windows, handling window events, or managing multi-window layouts in Tauri 2. Prevents window label collisions and incorrect WebviewWindowBuilder usage that causes runtime panics. Covers WebviewWindowBuilder, window labels, window configuration, WindowEvent handling, and JavaScript Window class. Keywords: tauri window, WebviewWindowBuilder, window events, window label, Window class, monitor, multi-window.
npx skillsauth add OpenAEC-Foundation/OpenAEC-Workspace-Composer tauri-syntax-windowInstall 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.
app.windows[])| Property | Type | Default | Description |
|----------|------|---------|-------------|
| label | string | "main" | Unique window identifier |
| title | string | -- | Window title bar text |
| url | string | "/" | URL or path to load |
| width / height | number | -- | Dimensions in logical pixels |
| x / y | number | -- | Window position |
| minWidth / minHeight | number | -- | Minimum dimensions |
| maxWidth / maxHeight | number | -- | Maximum dimensions |
| resizable | boolean | true | Allow resizing |
| fullscreen | boolean | false | Start fullscreen |
| focus | boolean | true | Focus on creation |
| create | boolean | true | Create at startup (set false for programmatic creation) |
| transparent | boolean | false | Enable transparency |
| decorations | boolean | true | Show title bar / borders |
| alwaysOnTop | boolean | false | Stay above other windows |
| Variant | Fields | Description |
|---------|--------|-------------|
| Resized | PhysicalSize<u32> | Window client area resized |
| Moved | PhysicalPosition<i32> | Window position changed |
| CloseRequested | api: CloseRequestApi | Close requested; call api.prevent_close() to cancel |
| Destroyed | -- | Window has been destroyed |
| Focused | bool | true = gained focus, false = lost focus |
| ScaleFactorChanged | scale_factor: f64, new_inner_size: PhysicalSize<u32> | DPI/display scale changed |
| DragDrop | DragDropEvent | File drag-and-drop event |
| ThemeChanged | Theme | System theme changed (not supported on Linux) |
| Method | Callback Payload | Description |
|--------|-----------------|-------------|
| onCloseRequested(cb) | CloseRequestedEvent | Close requested (call event.preventDefault() to cancel) |
| onResized(cb) | PhysicalSize | Window resized |
| onMoved(cb) | PhysicalPosition | Window moved |
| onFocusChanged(cb) | boolean | Focus gained/lost |
| onThemeChanged(cb) | Theme | System theme changed |
| onScaleChanged(cb) | { scaleFactor, size } | DPI scale changed |
| onDragDropEvent(cb) | DragDropEvent | File drag-and-drop |
| Method | Returns | Description |
|--------|---------|-------------|
| innerSize() | Promise<PhysicalSize> | Client area size |
| outerSize() | Promise<PhysicalSize> | Total window size |
| innerPosition() | Promise<PhysicalPosition> | Client area position |
| outerPosition() | Promise<PhysicalPosition> | Window frame position |
| isMaximized() | Promise<boolean> | Maximized state |
| isMinimized() | Promise<boolean> | Minimized state |
| isVisible() | Promise<boolean> | Visibility state |
| isFullscreen() | Promise<boolean> | Fullscreen state |
| title() | Promise<string> | Current title |
| Function | Returns | Import |
|----------|---------|--------|
| availableMonitors() | Promise<Monitor[]> | @tauri-apps/api/window |
| currentMonitor() | Promise<Monitor \| null> | @tauri-apps/api/window |
| primaryMonitor() | Promise<Monitor \| null> | @tauri-apps/api/window |
| cursorPosition() | Promise<PhysicalPosition> | @tauri-apps/api/window |
| Type | Import | Description |
|------|--------|-------------|
| LogicalSize | @tauri-apps/api/dpi | Size in logical pixels (DPI-independent) |
| PhysicalSize | @tauri-apps/api/dpi | Size in physical pixels |
| LogicalPosition | @tauri-apps/api/dpi | Position in logical pixels |
| PhysicalPosition | @tauri-apps/api/dpi | Position in physical pixels |
NEVER use duplicate window labels -- each label MUST be unique across the application. Duplicate labels cause runtime panics in Rust and silent failures in JS.
NEVER call window.destroy() when window.close() suffices -- destroy() skips the CloseRequested event and prevents cleanup handlers from running.
NEVER forget to call api.prevent_close() inside CloseRequested if you want to cancel the close -- the window closes by default.
ALWAYS use app.get_webview_window("label") (not app.get_window("label")) in Tauri 2 -- get_window returns the raw Window, not the WebviewWindow.
ALWAYS check for None/null when retrieving windows by label -- the window may not exist yet or may have been closed.
use tauri::webview::WebviewWindowBuilder;
use tauri::WebviewUrl;
// In setup() or any context with Manager access
let window = WebviewWindowBuilder::new(
app,
"settings", // unique label
WebviewUrl::App("settings.html".into()), // URL to load
)
.title("Settings")
.inner_size(800.0, 600.0)
.center()
.resizable(true)
.build()?;
import { Window } from '@tauri-apps/api/window';
const webview = new Window('settings-window', {
url: '/settings',
title: 'Settings',
width: 600,
height: 400,
center: true,
decorations: true,
resizable: true,
});
import { getCurrentWindow, Window, getAllWindows } from '@tauri-apps/api/window';
const win = getCurrentWindow(); // current window
const settings = await Window.getByLabel('settings'); // by label (may be null)
const allWindows = await getAllWindows(); // all windows
const focused = await Window.getFocusedWindow(); // focused window (may be null)
.on_window_event(|window, event| {
match event {
WindowEvent::CloseRequested { api, .. } => {
api.prevent_close();
window.hide().unwrap();
}
WindowEvent::Focused(focused) => {
if *focused {
println!("{} gained focus", window.label());
}
}
_ => {}
}
})
const win = getCurrentWindow();
await win.onCloseRequested(async (event) => {
const confirmed = await confirm('Are you sure?');
if (!confirmed) {
event.preventDefault();
}
});
await win.onResized(({ payload: size }) => {
console.log('New size:', size.width, size.height);
});
// Show/hide from a command
#[tauri::command]
fn show_settings(app: tauri::AppHandle) {
if let Some(window) = app.get_webview_window("settings") {
window.show().unwrap();
window.set_focus().unwrap();
}
}
// JS: send event to a specific window
import { emitTo } from '@tauri-apps/api/event';
await emitTo('settings', 'settings-update-requested', {
key: 'theme',
value: 'dark',
});
const win = getCurrentWindow();
// Position and size
await win.center();
await win.setPosition(new LogicalPosition(100, 100));
await win.setSize(new LogicalSize(800, 600));
// Visibility and state
await win.show();
await win.hide();
await win.maximize();
await win.minimize();
await win.toggleMaximize();
await win.setFullscreen(true);
// Properties
await win.setTitle('My App - Document.txt');
await win.setDecorations(false);
await win.setAlwaysOnTop(true);
await win.setResizable(false);
// Lifecycle
await win.close(); // triggers CloseRequested event
await win.destroy(); // force-closes without events
import { availableMonitors, currentMonitor, primaryMonitor, cursorPosition } from '@tauri-apps/api/window';
const monitors = await availableMonitors();
const current = await currentMonitor(); // monitor containing the window
const primary = await primaryMonitor(); // primary display
const cursor = await cursorPosition(); // cursor PhysicalPosition
type Theme = 'light' | 'dark';
type TitleBarStyle = 'visible' | 'transparent' | 'overlay';
enum UserAttentionType { Critical = 1, Informational = 2 }
enum ProgressBarStatus { None, Normal, Indeterminate, Paused, Error }
// Window geometry
.inner_size(width: f64, height: f64)
.min_inner_size(w: f64, h: f64)
.max_inner_size(w: f64, h: f64)
.position(x: f64, y: f64)
.center()
// Window behavior
.title(title: impl Into<String>)
.visible(bool)
.focused(bool)
.maximized(bool)
.fullscreen(bool)
.resizable(bool)
.closable(bool)
.minimizable(bool)
.maximizable(bool)
.focusable(bool)
.transparent(bool)
// Webview configuration
.initialization_script(script: impl Into<String>)
.user_agent(ua: &str)
.devtools(enabled: bool)
.disable_javascript()
.zoom_hotkeys_enabled(bool)
// Event handlers
.on_page_load(F)
.on_navigation(F)
.on_web_resource_request(F)
.on_download(F)
.on_document_title_changed(F)
.on_new_window(F)
fn get_webview_window(&self, label: &str) -> Option<WebviewWindow<R>>
fn webview_windows(&self) -> HashMap<String, WebviewWindow<R>>
fn get_window(&self, label: &str) -> Option<Window<R>>
fn get_focused_window(&self) -> Option<Window<R>>
fn windows(&self) -> HashMap<String, Window<R>>
Window management requires core:window:default or specific permissions like core:window:allow-set-title, core:window:allow-close, core:window:allow-minimize, core:window:allow-maximize in the capability file.
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.