tauri-connector
A Tauri v2 plugin with embedded MCP server + Rust CLI for deep inspection and interaction with Tauri desktop applications. Drop-in replacement for tauri-plugin-mcp-bridge that fixes the __TAURI__ not available bug on macOS.
The Problem
tauri-plugin-mcp-bridge injects JavaScript into the webview that relies on window.__TAURI__ to send execution results back to Rust. On macOS with WKWebView, the injected scripts run in an isolated content world where window.__TAURI__ doesn't exist -- causing all JS-based tools (execute_js, dom_snapshot, console logs) to time out.
The Fix
tauri-connector uses a dual-path JS execution strategy:
-
WS Bridge (primary) -- A small JS client injected into the webview connects back to the plugin via
ws://127.0.0.1:{port}. Scripts and results flow through this dedicated WebSocket channel. -
Eval+Event fallback -- If the WS bridge times out (2s), the plugin falls back to injecting JS via Tauri's
window.eval()and receiving results through Tauri's event system. This path requireswithGlobalTauri: true.
The fallback is transparent -- callers get the same result regardless of which path succeeds. The MCP server runs inside the plugin -- when your Tauri app starts, it starts automatically.
Frontend JS (app context)
|-- invoke('plugin:connector|push_dom') --> Rust state (cached DOM)
|-- invoke('plugin:connector|push_logs') -> Rust state (cached logs)
'-- WebSocket ws://127.0.0.1:9300 --------> Bridge (JS execution, path 1)
Plugin (Rust)
|-- bridge.execute_js()
| |-- try WS bridge (2s timeout) --------> webview JS via WebSocket
| '-- fallback: window.eval() + event ---> webview JS via Tauri IPC
|-- xcap native capture (cross-platform) --> PNG/JPEG/WebP with resize
'-- snapdom fallback ---------------------> DOM-to-image via @zumer/snapdom
Claude Code -------- HTTP http://host:9556/mcp ----> Embedded MCP server
|-- handlers (direct, in-process)
|-- bridge.execute_js() -> JS result
'-- state.get_dom() -> cached DOM
CLI (Rust) -------- WebSocket ws://host:9555 -----> Plugin WS server
Claude Code Skill (Recommended)
Install the skill to give Claude Code (and 30+ other AI agents) full debug and code review capabilities for Tauri apps.
Install via skills.sh (easiest)
This installs from skills.sh -- the agent skills directory. Works with Claude Code, Cursor, Windsurf, Codex, Gemini CLI, and more.
The CLI also embeds the same skill bundle for version-matched agent docs:
Install manually
What's Included
The skill provides a debug & code review suite with progressive disclosure:
| File | Purpose |
|---|---|
SKILL.md |
Main skill -- core workflow, debugging, code review, interaction reference |
SETUP.md |
Step-by-step setup guide for new Tauri projects |
scripts/ |
14 Bun TypeScript scripts for fallback WebSocket automation |
references/mcp-tools.md |
MCP tool parameter tables |
references/cli-commands.md |
Every CLI subcommand with flags and examples |
references/debug-playbook.md |
10 step-by-step debug recipes (blank screen, silent clicks, form failures, slow IPC, drag issues, memory leaks, multi-window) |
references/code-review-playbook.md |
9 code review workflows (visual regression, accessibility audit, component tree, IPC contract validation, DOM structure, event flow) |
What It Enables
Once installed, Claude will automatically:
- Debug Tauri apps -- console errors, IPC monitoring, event capture, runtime state inspection
- Review code changes -- visual regression, accessibility audit, component tree review, IPC contract validation
- Interact with the UI -- click, fill, drag, type, scroll using ref-based addressing
- Set up the plugin in any Tauri v2 project when asked
- Automate testing with snapshot -> act -> verify workflows
For contributors: The release workflow skill is at
.claude/skills/tauri-connector-release/SKILL.md— it triggers automatically when you say "release" or "bump version" inside this repo.
Components
| Component | Description |
|---|---|
plugin/ |
Rust Tauri v2 plugin with embedded MCP server (tauri-plugin-connector on crates.io) |
crates/cli/ |
Rust CLI binary with ref-based element addressing |
crates/mcp-server/ |
Standalone Rust MCP server (alternative to embedded, connects via WebSocket) |
crates/client/ |
Shared Rust WebSocket client library |
Features
MCP + CLI Tools with Drag and Drop, Artifacts, and Runtime Capture
Every tool is available via both the embedded MCP server (for Claude Code) and the Rust CLI (for terminal use). The CLI uses ref-based element addressing inspired by vercel-labs/agent-browser.
| Category | MCP Tool | CLI Command |
|---|---|---|
| JavaScript | webview_execute_js |
eval <script> |
| DOM | webview_dom_snapshot |
snapshot [-i] [-c] [--mode ai|accessibility|structure] |
| DOM (cached) | get_cached_dom |
dom |
| Elements | webview_find_element |
find <selector> [-s css|xpath|text] |
| Styles | webview_get_styles |
get styles <@ref|selector> |
| Picker | webview_get_pointed_element |
pointed |
| Select | webview_select_element |
(visual picker, not yet implemented) |
| Interact | webview_interact |
click, dblclick, hover, drag, focus, fill, type, check, uncheck, select, scroll, scrollintoview |
| Keyboard | webview_keyboard |
press <key> |
| Wait | webview_wait_for |
wait <selector> [--text] [--url] [--load-state] [--fn] [--state] [--timeout] |
| Locator | webview_locator |
locator --role button --name Save --action click |
| Screenshot | webview_screenshot |
screenshot [path] [--selector @eN] [--annotate] [--name-hint debug] |
| Artifacts | artifact_list / artifact_read / artifact_compare / artifact_prune |
artifacts list|show|compare|prune |
| Debug | debug_mark / debug_snapshot / webview_act_and_verify |
debug mark|snapshot, act |
| Windows | manage_window |
windows, resize <w> <h> |
| State | ipc_get_backend_state |
state |
| IPC | ipc_execute_command |
ipc exec <cmd> [-a '{...}'] |
| Monitor | ipc_monitor |
ipc monitor / ipc unmonitor |
| Captured | ipc_get_captured |
ipc captured [-f filter] |
| Events | ipc_emit_event |
emit <event> [-p '{...}'] |
| Logs | read_logs |
logs [-n 20] [-f filter] |
| Runtime | runtime_get_captured / runtime_clear |
runtime, clear runtime |
| Logs | clear_logs |
clear logs|ipc|events|runtime|all |
| Logs | read_log_file |
(via MCP only) |
| Events | ipc_listen |
events listen|captured|stop |
| Events | event_get_captured |
events captured [-p regex] |
| DOM | webview_search_snapshot |
(via MCP only) |
| Setup | get_setup_instructions |
examples |
| Skills | (CLI only) | `skills list |
| Diagnostics | (CLI only) | doctor [--json] [--no-runtime] |
| Devices | list_devices |
(info only) |
CLI Ref-Based Addressing
Take a DOM snapshot with stable ref IDs, then interact with elements using those refs:
# Take snapshot -- assigns ref IDs, enriches with React component names, stitches portals
# Interact using refs (persist across CLI invocations)
Unified Snapshot Engine (v0.5)
The DOM snapshot engine uses a single window.__CONNECTOR_SNAPSHOT__() function with three modes:
| Mode | Output | Use case |
|---|---|---|
ai (default) |
Role, name, ARIA states, ref=eN IDs, component=Name, portal stitching |
Claude interaction |
accessibility |
Role, accessible name, ARIA states | Semantic understanding |
structure |
Tag, id, classes, data-testid | Layout debugging |
Key capabilities:
- TreeWalker API for ~78x faster traversal (no stack overflow on deep React trees)
- Portal stitching -- Ant Design modals/drawers/dropdowns are logically re-parented under their trigger via
aria-controls/aria-owns - React fiber enrichment -- Component names (
component=AppointmentModal) via__reactFiber$on DOM nodes - Visibility pruning --
aria-hidden,display:none,visibility:hidden,role=presentation/noneautomatically excluded - Virtual scroll detection --
rc-virtual-list-holdercontainers annotated with visible item count - Token budgeting --
maxDepth,maxElements, and token-aware splitting for graceful truncation on large DOMs
The plugin also auto-pushes DOM snapshots via Tauri IPC. The get_cached_dom tool returns this pre-cached snapshot instantly.
Snapshot Budget Engine (v0.8+)
Large DOMs routinely blow past LLM context windows. The snapshot engine now budgets output by estimated tokens and spills overflow to on-disk subtree files so the inline response stays compact while the full tree remains reachable on demand.
- Token estimation + section-atomic rendering -- the walker estimates tokens per section and stops inlining once the budget is hit, emitting
{overflow: N subtrees, file=subtree-K.txt}markers in place of the omitted content. - Repeating sibling collapse -- runs of 5+ structurally identical siblings (same tag + role + ARIA state hash) are collapsed to 2 examples + a marker; the collapsed rows are written to a subtree file.
- Subtree files -- overflow content is written atomically under the connector log directory (
<log_dir>/snapshots/<snapshotId>/subtree-N.txt). The activelog_diris exposed in.connector.jsonand backend/debug state; if log initialization fails, the plugin falls back to a temp.tauri-connectordirectory. Directories use0700permissions; writes are counter-based with.tmp+ rename. - Auto-prune -- old sessions are pruned by mtime with a per-window
Mutexto avoid concurrent cleanup races; prune failures fall back to a bounded policy. - Search keeps full fidelity --
search_snapshot/webview_search_snapshotmatch against the merged full-text (inline output plus all subtree contents), so filtered output never hides matches. - Defaults -- MCP callers default to
max_tokens: 4000; WebSocket / internal callers default to0(unlimited) for backward compatibility. Setno_split: true(or--no-spliton the CLI) to disable file splitting entirely.
Quick Start
Using Claude Code? Install the skill for automated setup -- see Claude Code Skill above.
1. Add the plugin (feature-gated, recommended)
The recommended pattern keeps tauri-plugin-connector and its transitive deps (xcap → libspa/pipewire on Linux, aws-sdk-s3, etc.) out of release builds entirely — they're never even compiled when the feature is off.
# src-tauri/Cargo.toml
[]
# ...
# Optional dep — only pulled when --features dev-connector is set.
= { = "0.12", = true }
[]
= []
= ["dep:tauri-plugin-connector"]
Mobile gotcha: if you also build for Android/iOS, scope the dep to desktop:
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
2. Register it behind the cargo feature
// src-tauri/src/lib.rs -- place BEFORE .invoke_handler()
const DEV_CONNECTOR_CAPABILITY: &str =
include_str!;
3. Drop the dev capability JSON outside capabilities/
tauri-build's default glob is ./capabilities/**/*, so anything under that directory is auto-loaded. Keeping the dev capability one level over (capabilities-dev/) means a release tauri build will not see it — no need to delete the file before shipping.
// src-tauri/capabilities-dev/dev-connector.json (NEW directory)
4. Wire up the dev script + withGlobalTauri
// package.json — flip the feature on for `tauri:dev` only
// src-tauri/tauri.conf.json — required for the eval+event JS fallback path
bun run tauri:dev (or cargo tauri dev --features dev-connector) compiles the plugin in. Plain tauri build skips the feature, leaves the dep uncompiled, and never sees the dev capability.
Alternative: legacy cfg(debug_assertions) pattern
If you don't want a separate dev script and don't mind the plugin (and its transitive crates) being compiled for release — they're stripped at link time by dead-code elimination, but still pulled into the dependency graph — the original cfg(debug_assertions) form still works:
# src-tauri/Cargo.toml
[]
= "0.12"
// src-tauri/src/lib.rs
// src-tauri/capabilities/default.json — add to permissions array
"connector:default"
tauri-connector doctor accepts both patterns; on the legacy form it emits a non-blocking warn nudging you toward the feature-gated layout.
5. Install snapdom (screenshot fallback)
# In your frontend project
If your project uses Vite/webpack, no extra setup needed. Otherwise expose on window:
import { snapdom } from '@zumer/snapdom';
window.snapdom = snapdom;
6. Configure Claude Code
// .mcp.json -- the MCP server starts automatically with the app
7. Run
Look for:
[connector][mcp] MCP ready for 'MyApp' -- url: http://127.0.0.1:9556/mcp (/sse legacy)
[connector] Plugin ready for 'MyApp' (com.example.app) -- WS on 0.0.0.0:9555
The MCP server is now live. Claude Code connects automatically via the URL in .mcp.json.
8. Verify with doctor (v0.11+)
tauri-connector doctor auto-detects whether the project is using the feature-gated or legacy registration pattern, then walks the current project and confirms every setup step above. It inspects src-tauri/Cargo.toml (including the [features] block), the plugin registration in lib.rs/main.rs, both src-tauri/capabilities/*.json and src-tauri/capabilities-dev/*.json, src-tauri/tauri.conf.json, the frontend package.json scripts/deps, the root .mcp.json, and the live .connector.json PID file. Runtime checks verify PID liveness, log_dir/log initialization, WebSocket reachability, bridge status, runtime/artifact/debug command availability, and the Streamable HTTP /mcp initialize -> notification -> ping -> GET 405 -> DELETE lifecycle. Each missing piece is reported with a copy-pasteable Fix: snippet. It exits non-zero when any required check fails, so it drops straight into CI.
Doctor also warns when an installed local tauri-connector skill doc differs from the CLI-bundled copy, so agents do not keep following stale command syntax.
The --json payload includes a top-level setup_pattern field with one of "feature-gated" | "legacy" | "mixed" | "none" plus a flattened fixes array so CI can surface every suggested remediation without re-parsing sections:
|
{ }
Sections reported:
| Section | Checks |
|---|---|
| Environment | CLI version, working directory, Tauri v2 project detection |
| Plugin Setup | tauri-plugin-connector Cargo dep (with (optional, feature-gated) tag when applicable), plugin registered (cites the matched cfg gate), connector:default permission in capabilities/ or capabilities-dev/, app.withGlobalTauri: true, @zumer/snapdom in package.json, .mcp.json Streamable HTTP /mcp entry; under feature-gated/mixed: also a Cargo feature that activates tauri-plugin-connector, runtime app.add_capability(include_str!(...)) when needed, and either a dev script passing the matching --features <name> or tauri.conf.json build.features enabling that feature. Legacy setups receive a non-blocking warn nudging migration. |
| Runtime | .connector.json PID file, PID alive, runtime metadata (ws_port, mcp_port, bridge_port, log_dir), JSONL logs initialized, WS ping on ws_port, bridge status, runtime/artifact/debug commands, MCP Streamable HTTP lifecycle on mcp_port |
| Integration | .claude/ auto-detect hook install status (optional) |
Example output for the feature-gated pattern (all green):
tauri-connector doctor v0.12.1
Plugin Setup
✓ Cargo dependency: tauri-plugin-connector = "0.12" (optional, feature-gated)
✓ Plugin registered in src-tauri/src/lib.rs (cfg(feature = "dev-connector"))
✓ Permission "connector:default" in src-tauri/capabilities-dev/dev-connector.json
✓ app.withGlobalTauri: true
✓ Frontend dependency: @zumer/snapdom
✓ .mcp.json registers tauri-connector (http://127.0.0.1:9556/mcp)
✓ [features] dev-connector activates tauri-plugin-connector
✓ Capability loaded at runtime via app.add_capability(include_str!("../capabilities-dev/..."))
✓ package.json dev script enables connector feature (dev-connector)
Example output for a legacy setup (passes, with the migration nudge):
Plugin Setup
✓ Cargo dependency: tauri-plugin-connector = "0.12"
✓ Plugin registered in src-tauri/src/lib.rs (cfg(debug_assertions))
✓ Permission "connector:default" in src-tauri/capabilities/default.json
✓ app.withGlobalTauri: true
✓ Frontend dependency: @zumer/snapdom
✓ .mcp.json registers tauri-connector (http://127.0.0.1:9556/mcp)
! Using legacy debug_assertions gate — consider migrating to --features dev-connector
Fix: 1. tauri-plugin-connector = { version = "0.12", optional = true }
2. [features] dev-connector = ["dep:tauri-plugin-connector"]
3. replace cfg(debug_assertions) with cfg(feature = "dev-connector")
4. move connector:default to capabilities-dev/dev-connector.json
5. register at runtime: app.add_capability(include_str!(...))
6. "tauri:dev": "tauri dev --features dev-connector"
WebSocket API via Bun
Connect directly to the plugin WebSocket on port 9555 using bun -e. No build step or extra dependencies -- bun has native WebSocket support.
Execute JavaScript
Take Screenshot
DOM Snapshot / Click / Type
# AI snapshot (default mode -- includes refs, component names, portal stitching)
# Click an element
# Type text into focused element
App State / Logs / Windows
# App metadata (no bridge needed)
# Console logs
WS Command Reference
All commands use { id, type, ...params } with snake_case types:
| Type | Key Params |
|---|---|
ping |
-- |
execute_js |
script, window_id |
screenshot |
format, quality, max_width, window_id, save, output_dir, name_hint, overwrite, selector, annotate |
dom_snapshot |
mode (ai/accessibility/structure), selector, max_depth, max_elements, max_tokens, no_split, react_enrich, follow_portals, shadow_dom, window_id |
find_element |
selector, strategy, window_id |
get_styles |
selector, properties, window_id |
interact |
action, selector, strategy, x, y, target_selector, target_x, target_y, steps, duration_ms, drag_strategy, window_id |
keyboard |
action, text, key, modifiers, window_id |
wait_for |
selector, strategy, text, url, load_state, function, state, timeout, window_id |
locator |
role, text, label, placeholder, alt, title, test_id, name, exact, first, last, nth, action, value, window_id |
window_list / window_info / window_resize |
window_id, width, height |
backend_state |
-- |
ipc_execute_command |
command, args |
ipc_monitor |
action |
ipc_get_captured |
filter, limit, pattern, since |
ipc_emit_event |
event_name, payload |
console_logs |
lines, filter, level, pattern, since, window_id |
clear_logs |
source |
read_log_file |
source, lines, level, pattern, since, window_id |
ipc_listen |
action, events |
event_get_captured |
event, pattern, limit, since |
runtime_get_captured |
kind, level, pattern, since, since_mark, limit, window_id |
artifact_list / artifact_read / artifact_compare / artifact_prune |
artifact registry operations |
debug_mark / debug_snapshot / webview_act_and_verify |
bundled debug context and action verification |
search_snapshot |
pattern, context, mode, window_id |
Rust CLI (Alternative)
A Rust CLI with ref-based element addressing is also available:
# Homebrew (macOS/Linux)
# Or build from source
# Binary at target/release/tauri-connector
Environment: TAURI_CONNECTOR_HOST (default 127.0.0.1), TAURI_CONNECTOR_PORT (default 9555).
MCP Server
Embedded (Default)
The MCP server starts automatically inside the Tauri plugin when the app runs. Configure Claude Code with:
No separate process, no Node.js, no install step. Just run your Tauri app.
Standalone (Alternative)
A standalone Rust MCP binary is also available for cases where you can't modify the Tauri app:
# Binary at target/release/tauri-connector-mcp
Plugin Configuration
Wrap the builder in whichever cfg gate matches your setup pattern (cfg(feature = "dev-connector") for the recommended feature-gated layout, or cfg(debug_assertions) for the legacy alternative):
use ConnectorBuilder;
// Or disable the embedded MCP server:
new
.disable_mcp
.build
Frontend Integration (Optional)
Push DOM snapshots from your frontend for instant LLM access:
import { invoke } from '@tauri-apps/api/core';
// The bridge auto-pushes DOM snapshots on page load and significant mutations.
// For manual push (e.g. after a custom state change):
const result = window.__CONNECTOR_SNAPSHOT__({ mode: 'ai', maxElements: 5000 });
await invoke('plugin:connector|push_dom', {
payload: {
windowId: 'main',
html: document.body.innerHTML.substring(0, 500000),
textContent: document.body.innerText.substring(0, 200000),
snapshot: result.snapshot,
snapshotMode: 'ai',
refs: JSON.stringify(result.refs),
meta: JSON.stringify(result.meta),
}
});
The bridge JS auto-pushes DOM on page load and significant mutations (5s debounce) when window.__TAURI_INTERNALS__ is available.
Alt+Shift+Click Element Picker
Alt+Shift+Click any element in the app to capture its metadata. Retrieve via webview_get_pointed_element MCP tool.
Project Structure
tauri-connector/
|-- Cargo.toml # Workspace root
|-- plugin/ # Rust Tauri v2 plugin (crates.io)
| |-- Cargo.toml
| '-- src/
| |-- lib.rs # Plugin entry + Tauri IPC commands
| |-- bridge.rs # Internal WebSocket bridge (the fix)
| |-- server.rs # External WebSocket server (for CLI)
| |-- mcp.rs # Embedded MCP HTTP server (/mcp Streamable HTTP, /sse legacy)
| |-- mcp_tools.rs # MCP tool definitions + dispatch
| |-- handlers.rs # All command handlers
| |-- protocol.rs # Message types
| '-- state.rs # Shared state (DOM cache, logs, IPC)
|-- crates/
| |-- client/ # Shared Rust WebSocket client
| | '-- src/lib.rs
| |-- mcp-server/ # Standalone MCP server (alternative)
| | '-- src/
| | |-- main.rs # Stdio JSON-RPC loop
| | |-- protocol.rs # JSON-RPC types
| | '-- tools.rs # Tool definitions + dispatch
| '-- cli/ # Rust CLI binary
| '-- src/
| |-- main.rs # Clap CLI entry point
| |-- commands.rs # Command implementations
| '-- snapshot.rs # Ref system + DOM snapshot builder
|-- skill/ # Claude Code skill -- debug & code review suite
| |-- SKILL.md # Main skill (debug + code review + interaction)
| |-- SETUP.md # Setup instructions for new projects
| |-- scripts/ # Bun scripts for WS interaction (fallback)
| | |-- connector.ts # Shared helper (auto-discovers ports via PID file)
| | |-- state.ts, eval.ts, screenshot.ts, snapshot.ts
| | |-- click.ts, drag.ts, fill.ts, find.ts, hover.ts, wait.ts
| | '-- logs.ts, events.ts, windows.ts
| '-- references/ # Progressive disclosure reference files
| |-- mcp-tools.md # MCP tool parameter tables
| |-- cli-commands.md # Full CLI command reference
| |-- debug-playbook.md # 10 debug recipes
| '-- code-review-playbook.md # 9 code review workflows
|-- LICENSE
'-- README.md
How It Works
JS Execution (Dual Path)
The bridge uses two execution paths for maximum reliability:
-
WS Bridge (primary, 2s timeout): Internal WebSocket on
127.0.0.1:9300-9400. Bridge JS injected into the webview connects back, executes scripts viaAsyncFunction, and returns results through the WebSocket. Usestokio::select!for multiplexed read/write on a single stream. -
Eval+Event fallback: If the WS bridge times out, the plugin injects JS via Tauri's
window.eval()and receives results through Tauri's event system (plugin:event|emit). RequireswithGlobalTauri: true. Handles double-serialized event payloads automatically.
The fallback is transparent -- bridge.execute_js() returns the same result regardless of which path succeeded.
Screenshot
The webview_screenshot tool uses a tiered approach:
-
xcap native capture (cross-platform): Uses the xcap crate for pixel-accurate window capture on Windows, macOS, and Linux. Matches the window by title, captures via native OS APIs, then resizes (
maxWidth) and encodes to PNG/JPEG/WebP via theimagecrate. Runs on a blocking thread to avoid stalling the Tokio runtime. -
snapdom fallback: When xcap is unavailable (e.g. Wayland without permissions, CI environments), falls back to snapdom (
@zumer/snapdom) — a fast DOM-to-image library that captures exactly what the web engine renders. Loaded via dynamicimport()orwindow.snapdomglobal. No CDN dependency, works fully offline.
Set annotate: true or tauri-connector screenshot --annotate --name-hint <slug> after an ai DOM snapshot to overlay numbered labels on visible @eN refs. The saved artifact manifest records path, sha256, refsPath, snapshotId, windowId, selector, width, and height so agents can map visual labels back to snapshot refs.
PID File Auto-Discovery
When the plugin starts, it writes target/.connector.json with all port info:
The bun scripts in skill/scripts/ auto-discover this file, verify the PID is alive, and connect without any env vars. If the Tauri app is already running in another terminal, the scripts connect directly -- no need to start a new instance.
Embedded MCP Server
- Plugin starts a streamable HTTP MCP server on port 9556 (configurable)
- New clients use
POST /mcpfor JSON-RPC Streamable HTTP.GET /mcpcurrently returns405 Method Not Alloweduntil server-initiated streaming is implemented. - Legacy clients can still use
GET /sseplusPOST /message - Handlers call the bridge and plugin state directly -- zero WebSocket overhead
Console Log Capture
The bridge intercepts console.log/warn/error/info/debug, storing entries in file-backed JSONL storage at {app_data_dir}/.tauri-connector/console.log. It also captures runtime failures and route/network signals in runtime.log: window.onerror, unhandledrejection, failed/non-OK fetch and XHR calls, history/hash navigation, and resource load failures. Logs persist across app restarts and are accessible via read_logs, read_log_file, runtime_get_captured, debug_snapshot, or the CLI logs / runtime commands.
Ref System
The unified snapshot engine assigns sequential ref IDs (e0, e1, ...) to interactive elements (buttons, links, inputs, checkboxes, etc.) and elements with onclick, tabindex, or cursor:pointer. Three ref formats are accepted: @e1, ref=e1, or e1. Refs are persisted to disk and used across subsequent CLI invocations until the next snapshot refreshes them. The ref resolution uses a three-strategy fallback: CSS selector, then role+name text matching, then [role="..."] attribute matching.
Requirements
- Tauri v2.x
- Rust 2024 edition
- Bun (for skill scripts / WebSocket API examples)
@zumer/snapdomin frontend (optional, for screenshot fallback when xcap is unavailable)
License
MIT