Token导航 LogoToken导航TokenDH.com
前端设计需要联网github未标认证来源可访问clear审计通过

tauri-event-systemTauri event 系统

Agent Skill

用于辅助前端页面、组件、样式和交互逻辑的开发与维护。它适合让 Agent 生成或审查 React、Next.js、Vue、Tailwind、CSS 等相关代码,整理组件结构,或定位布局和性能问题。使用时需要结合项目现有设计系统、路由和构建方式,避免只生成孤立片段;涉及页面改动时,应配合本地预览和构建检查确认视觉效果。

总安装

4,967

周安装

199

GitHub Stars

40

下载量

1,608
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:tauri-event-system(Tauri event 系统)
来源仓库:https://github.com/bobmatnyc/claude-mpm-skills
仓库路径:skills/tauri-event-system
安装命令:
npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill tauri-event-system
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill tauri-event-system

简介

tauri-event-system 用于前后端事件通信,适合在 Codex、Claude 等宿主中使用。

  • 适用于 React、Next.js 等项目的事件处理和状态管理。
  • 支持异步通信和结构化有效负载传输。
  • 可帮助代理处理高频事件的节流和批量操作。
  • 安装前建议确认是否会触发进程间通信或网络套接字操作。

SKILL.md

Tauri Advanced Event System

Event Fundamentals

Backend → Frontend Events

Basic event emission:

use tauri::Window;

#[tauri::command]
async fn start_download(
    url: String,
    window: Window,
) -> Result<(), String> {
    window.emit("download-started", url)
        .map_err(|e| e.to_string())?;

    // Perform download...

    window.emit("download-complete", "Success")
        .map_err(|e| e.to_string())
}

Frontend listener:

import { listen, UnlistenFn } from '@tauri-apps/api/event';

const unlisten = await listen<string>('download-started', (event) => {
    console.log('Download started:', event.payload);
});

// Clean up when done
unlisten();

Structured Event Payloads

Typed Events with Serde

Backend:

use serde::Serialize;

#[derive(Serialize, Clone)]
struct ProgressEvent {
    current: usize,
    total: usize,
    percentage: f64,
    message: String,
    speed_mbps: Option<f64>,
}

#[tauri::command]
async fn download_file(
    url: String,
    window: Window,
) -> Result<(), String> {
    let total_size = get_file_size(&url).await?;

    for chunk in 0..total_size {
        // Download chunk...

        let progress = ProgressEvent {
            current: chunk,
            total: total_size,
            percentage: (chunk as f64 / total_size as f64) * 100.0,
            message: format!("Downloading... {}/{}", chunk, total_size),
            speed_mbps: Some(calculate_speed()),
        };

        window.emit("download-progress", progress)
            .map_err(|e| e.to_string())?;
    }

    Ok(())
}

Frontend:

interface ProgressEvent {
    current: number;
    total: number;
    percentage: number;
    message: string;
    speed_mbps?: number;
}

const unlisten = await listen<ProgressEvent>('download-progress', (event) => {
    const { current, total, percentage, message, speed_mbps } = event.payload;

    updateProgressBar(percentage);
    updateStatus(message);

    if (speed_mbps) {
        updateSpeed(speed_mbps);
    }
});

Complex Event Payloads

#[derive(Serialize, Clone)]
#[serde(tag = "type", content = "data")]
enum AppEvent {
    UserLoggedIn { user_id: String, username: String },
    UserLoggedOut { user_id: String },
    DataSynced { items_count: usize, timestamp: String },
    ErrorOccurred { code: String, message: String, recoverable: bool },
}

#[tauri::command]
async fn perform_login(
    username: String,
    password: String,
    window: Window,
) -> Result<String, String> {
    let user = authenticate(&username, &password).await?;

    // Emit structured event
    window.emit("app-event", AppEvent::UserLoggedIn {
        user_id: user.id.clone(),
        username: user.username.clone(),
    }).map_err(|e| e.to_string())?;

    Ok(user.id)
}

Frontend:

type AppEvent =
    | { type: 'UserLoggedIn'; data: { user_id: string; username: string } }
    | { type: 'UserLoggedOut'; data: { user_id: string } }
    | { type: 'DataSynced'; data: { items_count: number; timestamp: string } }
    | { type: 'ErrorOccurred'; data: { code: string; message: string; recoverable: boolean } };

listen<AppEvent>('app-event', (event) => {
    const appEvent = event.payload;

    switch (appEvent.type) {
        case 'UserLoggedIn':
            handleLogin(appEvent.data.user_id, appEvent.data.username);
            break;
        case 'UserLoggedOut':
            handleLogout(appEvent.data.user_id);
            break;
        case 'DataSynced':
            showSyncSuccess(appEvent.data.items_count);
            break;
        case 'ErrorOccurred':
            handleError(appEvent.data);
            break;
    }
});

Streaming Data Patterns

Real-Time Data Stream

#[tauri::command]
async fn stream_sensor_data(
    sensor_id: String,
    window: Window,
) -> Result<(), String> {
    let mut interval = tokio::time::interval(Duration::from_millis(100));

    for _ in 0..100 {
        interval.tick().await;

        let reading = read_sensor(&sensor_id).await?;

        window.emit("sensor-reading", reading)
            .map_err(|e| e.to_string())?;
    }

    window.emit("sensor-stream-ended", sensor_id)
        .map_err(|e| e.to_string())
}

Frontend with React:

import { useEffect, useState } from 'react';
import { listen } from '@tauri-apps/api/event';

interface SensorReading {
    value: number;
    timestamp: number;
    unit: string;
}

function SensorMonitor() {
    const [readings, setReadings] = useState<SensorReading[]>([]);

    useEffect(() => {
        let unlisten: UnlistenFn | undefined;

        listen<SensorReading>('sensor-reading', (event) => {
            setReadings(prev => [...prev.slice(-99), event.payload]);
        }).then(fn => unlisten = fn);

        return () => unlisten?.();
    }, []);

    return (
        <div>
            {readings.map((r, i) => (
                <div key={i}>{r.value} {r.unit}</div>
            ))}
        </div>
    );
}

Buffered Streaming

#[tauri::command]
async fn stream_logs(
    log_file: String,
    window: Window,
) -> Result<(), String> {
    use tokio::io::{AsyncBufReadExt, BufReader};
    use tokio::fs::File;

    let file = File::open(log_file).await
        .map_err(|e| e.to_string())?;

    let reader = BufReader::new(file);
    let mut lines = reader.lines();

    let mut buffer = Vec::new();

    while let Some(line) = lines.next_line().await
        .map_err(|e| e.to_string())? {

        buffer.push(line);

        // Send in batches of 10 lines
        if buffer.len() >= 10 {
            window.emit("log-batch", buffer.clone())
                .map_err(|e| e.to_string())?;
            buffer.clear();
        }
    }

    // Send remaining lines
    if !buffer.is_empty() {
        window.emit("log-batch", buffer)
            .map_err(|e| e.to_string())?;
    }

    Ok(())
}

Multi-Window Communication

Broadcasting to All Windows

use tauri::{AppHandle, Manager};

#[tauri::command]
async fn broadcast_message(
    message: String,
    app: AppHandle,
) -> Result<(), String> {
    // Emit to ALL windows
    app.emit_all("broadcast", message)
        .map_err(|e| e.to_string())
}

Targeted Window Messaging

#[tauri::command]
async fn send_to_window(
    target_window: String,
    message: String,
    app: AppHandle,
) -> Result<(), String> {
    // Get specific window
    if let Some(window) = app.get_window(&target_window) {
        window.emit("private-message", message)
            .map_err(|e| e.to_string())?;
        Ok(())
    } else {
        Err(format!("Window '{}' not found", target_window))
    }
}

Window-to-Window via Backend

Window A (sender):

import { invoke } from '@tauri-apps/api/core';

async function sendToSettings(data: any) {
    await invoke('relay_to_settings', { data });
}

Backend relay:

#[tauri::command]
async fn relay_to_settings(
    data: serde_json::Value,
    app: AppHandle,
) -> Result<(), String> {
    if let Some(settings_window) = app.get_window("settings") {
        settings_window.emit("data-update", data)
            .map_err(|e| e.to_string())?;
    }
    Ok(())
}

Window B (receiver - settings):

import { listen } from '@tauri-apps/api/event';

useEffect(() => {
    let unlisten: UnlistenFn | undefined;

    listen('data-update', (event) => {
        console.log('Received from main window:', event.payload);
        updateSettings(event.payload);
    }).then(fn => unlisten = fn);

    return () => unlisten?.();
}, []);

Frontend → Backend Events

Custom Frontend Events

import { emit } from '@tauri-apps/api/event';

// Frontend emits event
await emit('user-action', {
    action: 'button-click',
    button_id: 'save-button',
    timestamp: Date.now()
});

Backend listener:

use tauri::{Manager, Listener};

fn main() {
    tauri::Builder::default()
        .setup(|app| {
            let app_handle = app.handle();

            // Listen for frontend events
            app_handle.listen_global("user-action", move |event| {
                if let Some(payload) = event.payload() {
                    println!("User action: {}", payload);
                    // Process event...
                }
            });

            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

Advanced Listener Management

React Hook for Events

import { useEffect, useState } from 'react';
import { listen, UnlistenFn } from '@tauri-apps/api/event';

function useEvent<T>(eventName: string): T | null {
    const [payload, setPayload] = useState<T | null>(null);

    useEffect(() => {
        let unlisten: UnlistenFn | undefined;

        listen<T>(eventName, (event) => {
            setPayload(event.payload);
        }).then(fn => unlisten = fn);

        return () => unlisten?.();
    }, [eventName]);

    return payload;
}

// Usage
function ProgressDisplay() {
    const progress = useEvent<ProgressEvent>('download-progress');

    if (!progress) return null;

    return (
        <div>
            Progress: {progress.percentage.toFixed(2)}%
        </div>
    );
}

Event Queue Pattern

import { listen } from '@tauri-apps/api/event';

class EventQueue<T> {
    private queue: T[] = [];
    private unlisten?: UnlistenFn;

    async start(eventName: string) {
        this.unlisten = await listen<T>(eventName, (event) => {
            this.queue.push(event.payload);
        });
    }

    dequeue(): T | undefined {
        return this.queue.shift();
    }

    clear() {
        this.queue = [];
    }

    stop() {
        this.unlisten?.();
    }

    get length() {
        return this.queue.length;
    }
}

// Usage
const progressQueue = new EventQueue<ProgressEvent>();
await progressQueue.start('download-progress');

// Process queue periodically
setInterval(() => {
    while (progressQueue.length > 0) {
        const event = progressQueue.dequeue();
        processProgress(event);
    }
}, 100);

One-Time Events

import { once } from '@tauri-apps/api/event';

// Listen for event only once
await once<string>('initialization-complete', (event) => {
    console.log('App initialized:', event.payload);
    startApp();
});

Error Handling in Events

Safe Event Emission

async fn emit_safe(window: &Window, event: &str, payload: impl Serialize) -> Result<(), String> {
    window.emit(event, payload)
        .map_err(|e| {
            eprintln!("Failed to emit event '{}': {}", event, e);
            e.to_string()
        })
}

#[tauri::command]
async fn process_with_events(
    window: Window,
) -> Result<(), String> {
    emit_safe(&window, "processing-started", "Starting...")
        .await?;

    // Process...

    emit_safe(&window, "processing-complete", "Done!")
        .await?;

    Ok(())
}

Performance Considerations

Throttling Events

use std::time::{Duration, Instant};

#[tauri::command]
async fn high_frequency_updates(
    window: Window,
) -> Result<(), String> {
    let mut last_emit = Instant::now();
    let throttle_duration = Duration::from_millis(100);

    for i in 0..10000 {
        let value = compute_value(i);

        // Only emit every 100ms
        if last_emit.elapsed() >= throttle_duration {
            window.emit("update", value)
                .map_err(|e| e.to_string())?;
            last_emit = Instant::now();
        }
    }

    Ok(())
}

Batching Events

#[tauri::command]
async fn batch_updates(
    window: Window,
) -> Result<(), String> {
    let mut batch = Vec::new();

    for item in process_items() {
        batch.push(item);

        // Emit in batches of 50
        if batch.len() >= 50 {
            window.emit("batch-update", batch.clone())
                .map_err(|e| e.to_string())?;
            batch.clear();
        }
    }

    // Emit remaining items
    if !batch.is_empty() {
        window.emit("batch-update", batch)
            .map_err(|e| e.to_string())?;
    }

    Ok(())
}

Best Practices

  1. Always clean up listeners - Use unlisten() to prevent memory leaks
  2. Type event payloads - Define interfaces for type safety
  3. Use structured events - Tagged unions for multiple event types
  4. Throttle high-frequency events - Prevent overwhelming frontend
  5. Batch when possible - Reduce serialization overhead
  6. Handle errors gracefully - Log failed emissions, don't crash
  7. Use once() for one-time events - Initialization, completion signals
  8. Namespace event names - Use prefixes like "download:", "user:", "system:"

Common Pitfalls

Forgetting to unlisten:

// WRONG - memory leak
function Component() {
    listen('my-event', handler);  // Never cleaned up!
}

// CORRECT
function Component() {
    useEffect(() => {
        let unlisten: UnlistenFn | undefined;
        listen('my-event', handler).then(fn => unlisten = fn);
        return () => unlisten?.();
    }, []);
}

Not handling serialization errors:

// WRONG - struct can't serialize
#[derive(Clone)]  // Missing Serialize!
struct Event { }

window.emit("event", Event {});  // Runtime error!

// CORRECT
#[derive(Serialize, Clone)]
struct Event { }

Emitting too frequently:

// WRONG - 10000 events in quick succession
for i in 0..10000 {
    window.emit("update", i);  // Overwhelming!
}

// CORRECT - throttle or batch

Summary

  • Events are async - Backend → Frontend communication
  • Always type payloads - Use serde::Serialize + TypeScript interfaces
  • Clean up listeners - Call unlisten() in cleanup
  • Throttle/batch - High-frequency events need rate limiting
  • Use structured payloads - Tagged unions for multiple event types
  • Window targeting - emit() for specific, emit_all() for broadcast
  • Frontend events - Use emit() from frontend, listen in backend setup

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Claude Code

29.28%
按下载量换算471

OpenCode

23.05%
按下载量换算371

Gemini CLI

19.41%
按下载量换算312

Cursor

14.72%
按下载量换算237

Codex

7.78%
按下载量换算125

Antigravity

3.87%
按下载量换算62

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

来源信息

继续浏览同类 Skills