Elysia Tools

Explore ferramentas, exemplos, explicações visuais e guias temáticos em um espaço claro.

Categorias populares

Temas

Idioma
ZHESPTRUFRDEEN

Elysia Tools

Navegação

Desenvolvimento desktop

Exemplos Tauri

Exemplos de desenvolvimento de aplicativos de desktop leves usando o framework Tauri com backend Rust e frontend web

Exemplos

Entradas desta coleção

CódigorustSimples

Aplicação Tauri Hello World

Configuração básica de aplicação Tauri com configuração mínima e comunicação simples Rust-JavaScript

Dificuldade

3/10

Tempo estimado

15 min

Etiquetas

tauri, desktop, rust, web

Pré-requisitos

Rust basics, HTML/CSS/JavaScript, Node.js

rust
// Tauri Hello World Application Examples

// 1. Cargo.toml Dependencies
[package]
name = "tauri-app"
version = "0.1.0"
edition = "2021"

[build-dependencies]
tauri-build = { version = "1.0", features = [] }

[dependencies]
tauri = { version = "1.0", features = ["api-all"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["time"] }

[features]
default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"]

// 2. tauri.conf.json - Application Configuration
{
  "build": {
    "beforeBuildCommand": "npm run build",
    "beforeDevCommand": "npm run dev",
    "devPath": "http://localhost:3000",
    "distDir": "../dist"
  },
  "package": {
    "productName": "Tauri App",
    "version": "0.1.0"
  },
  "tauri": {
    "allowlist": {
      "all": false,
      "shell": {
        "all": false,
        "open": true
      },
      "dialog": {
        "all": false,
        "open": true,
        "save": true
      }
    },
    "bundle": {
      "active": true,
      "category": "DeveloperTool",
      "copyright": "",
      "deb": {
        "depends": []
      },
      "externalBin": [],
      "icon": [
        "icons/32x32.png",
        "icons/128x128.png",
        "icons/[email protected]",
        "icons/icon.icns",
        "icons/icon.ico"
      ],
      "identifier": "com.tauri.app",
      "longDescription": "",
      "macOS": {
        "entitlements": null,
        "exceptionDomain": "",
        "frameworks": [],
        "providerShortName": null,
        "signingIdentity": null
      },
      "resources": [],
      "shortDescription": "",
      "targets": "all",
      "windows": {
        "certificateThumbprint": null,
        "digestAlgorithm": "sha256",
        "timestampUrl": ""
      }
    },
    "security": {
      "csp": null
    },
    "updater": {
      "active": false
    },
    "windows": [
      {
        "fullscreen": false,
        "height": 600,
        "resizable": true,
        "title": "Tauri App",
        "width": 800
      }
    ]
  }
}

// 3. main.rs - Rust Backend
#![cfg_attr(
    all(not(debug_assertions), target_os = "windows"),
    windows_subsystem = "windows"
)]

use tauri::Manager;

// Simple greeting command
#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}! You've been greeted from Rust!", name)
}

// Command to get system info
#[tauri::command]
fn get_system_info() -> serde_json::Value {
    serde_json::json!({
        "platform": std::env::consts::OS,
        "arch": std::env::consts::ARCH,
        "version": env!("CARGO_PKG_VERSION"),
        "name": env!("CARGO_PKG_NAME")
    })
}

// Command with async operation
#[tauri::command]
async fn delayed_message(delay_ms: u64, message: String) -> String {
    tokio::time::sleep(tokio::time::Duration::from_millis(delay_ms)).await;
    format!("Delayed message: {}", message)
}

// Command that returns Result
#[tauri::command]
fn validate_input(input: String) -> Result<String, String> {
    if input.trim().is_empty() {
        Err("Input cannot be empty".to_string())
    } else if input.len() > 100 {
        Err("Input too long (max 100 characters)".to_string())
    } else {
        Ok(format!("Valid input: {}", input))
    }
}

// File operations
#[tauri::command]
async fn read_file_content(path: String) -> Result<String, String> {
    use std::fs;

    match fs::read_to_string(&path) {
        Ok(content) => Ok(content),
        Err(e) => Err(format!("Failed to read file: {}", e))
    }
}

#[tauri::command]
async fn write_file_content(path: String, content: String) -> Result<(), String> {
    use std::fs;

    match fs::write(&path, content) {
        Ok(_) => Ok(()),
        Err(e) => Err(format!("Failed to write file: {}", e))
    }
}

fn main() {
    tauri::Builder::default()
        .setup(|app| {
            // Get the main window
            let window = app.get_window("main").unwrap();

            // Set window title
            window.set_title("Tauri Hello World").unwrap();

            Ok(())
        })
        .invoke_handler(tauri::generate_handler![
            greet,
            get_system_info,
            delayed_message,
            validate_input,
            read_file_content,
            write_file_content
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

// 4. build.rs - Build Script
fn main() {
    tauri_build::build()
}

// 5. HTML Frontend (index.html)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tauri App</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .container {
            background: white;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .input-group {
            margin: 20px 0;
        }
        input, button {
            padding: 10px;
            margin: 5px;
            border: 1px solid #ddd;
            border-radius: 5px;
        }
        button {
            background-color: #007bff;
            color: white;
            cursor: pointer;
        }
        button:hover {
            background-color: #0056b3;
        }
        .result {
            margin-top: 10px;
            padding: 10px;
            background-color: #f8f9fa;
            border-radius: 5px;
            border-left: 4px solid #007bff;
        }
        .error {
            border-left-color: #dc3545;
            background-color: #f8d7da;
            color: #721c24;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Tauri Hello World Application</h1>

        <div class="input-group">
            <input type="text" id="nameInput" placeholder="Enter your name" value="Tauri">
            <button onclick="greetUser()">Greet</button>
        </div>
        <div id="greetResult" class="result" style="display: none;"></div>

        <div class="input-group">
            <button onclick="getSystemInfo()">Get System Info</button>
        </div>
        <div id="systemInfoResult" class="result" style="display: none;"></div>

        <div class="input-group">
            <input type="text" id="delayMessage" placeholder="Message for delay" value="Hello from the future!">
            <input type="number" id="delayMs" placeholder="Delay (ms)" value="2000">
            <button onclick="sendDelayedMessage()">Send Delayed Message</button>
        </div>
        <div id="delayResult" class="result" style="display: none;"></div>

        <div class="input-group">
            <input type="text" id="validationInput" placeholder="Text to validate (max 100 chars)">
            <button onclick="validateInput()">Validate</button>
        </div>
        <div id="validationResult" class="result" style="display: none;"></div>

        <div class="input-group">
            <input type="text" id="filePath" placeholder="File path">
            <button onclick="readFile()">Read File</button>
            <button onclick="writeFile()">Write File</button>
        </div>
        <div id="fileResult" class="result" style="display: none;"></div>
    </div>

    <script>
        const { invoke } = window.__TAURI__.core;

        async function greetUser() {
            const name = document.getElementById('nameInput').value;
            const resultDiv = document.getElementById('greetResult');

            try {
                const response = await invoke('greet', { name });
                resultDiv.textContent = response;
                resultDiv.className = 'result';
                resultDiv.style.display = 'block';
            } catch (error) {
                resultDiv.textContent = 'Error: ' + error;
                resultDiv.className = 'result error';
                resultDiv.style.display = 'block';
            }
        }

        async function getSystemInfo() {
            const resultDiv = document.getElementById('systemInfoResult');

            try {
                const info = await invoke('get_system_info');
                const formatted = 'Platform: ' + info.platform +
                               ', Arch: ' + info.arch +
                               ', Version: ' + info.version;
                resultDiv.textContent = formatted;
                resultDiv.className = 'result';
                resultDiv.style.display = 'block';
            } catch (error) {
                resultDiv.textContent = 'Error: ' + error;
                resultDiv.className = 'result error';
                resultDiv.style.display = 'block';
            }
        }

        async function sendDelayedMessage() {
            const message = document.getElementById('delayMessage').value;
            const delayMs = parseInt(document.getElementById('delayMs').value);
            const resultDiv = document.getElementById('delayResult');

            resultDiv.textContent = 'Sending delayed message...';
            resultDiv.className = 'result';
            resultDiv.style.display = 'block';

            try {
                const response = await invoke('delayed_message', { delayMs, message });
                resultDiv.textContent = response;
            } catch (error) {
                resultDiv.textContent = 'Error: ' + error;
                resultDiv.className = 'result error';
            }
        }

        async function validateInput() {
            const input = document.getElementById('validationInput').value;
            const resultDiv = document.getElementById('validationResult');

            try {
                const response = await invoke('validate_input', { input });
                resultDiv.textContent = response;
                resultDiv.className = 'result';
                resultDiv.style.display = 'block';
            } catch (error) {
                resultDiv.textContent = 'Validation Error: ' + error;
                resultDiv.className = 'result error';
                resultDiv.style.display = 'block';
            }
        }

        async function readFile() {
            const path = document.getElementById('filePath').value;
            const resultDiv = document.getElementById('fileResult');

            if (!path) {
                resultDiv.textContent = 'Please enter a file path';
                resultDiv.className = 'result error';
                resultDiv.style.display = 'block';
                return;
            }

            try {
                const content = await invoke('read_file_content', { path });
                resultDiv.textContent = 'File content:\n' + content;
                resultDiv.className = 'result';
                resultDiv.style.display = 'block';
            } catch (error) {
                resultDiv.textContent = 'Error reading file: ' + error;
                resultDiv.className = 'result error';
                resultDiv.style.display = 'block';
            }
        }

        async function writeFile() {
            const path = document.getElementById('filePath').value;
            const resultDiv = document.getElementById('fileResult');

            if (!path) {
                resultDiv.textContent = 'Please enter a file path';
                resultDiv.className = 'result error';
                resultDiv.style.display = 'block';
                return;
            }

            const content = 'Hello from Tauri! Written at ' + new Date().toISOString();

            try {
                await invoke('write_file_content', { path, content });
                resultDiv.textContent = 'File written successfully!';
                resultDiv.className = 'result';
                resultDiv.style.display = 'block';
            } catch (error) {
                resultDiv.textContent = 'Error writing file: ' + error;
                resultDiv.className = 'result error';
                resultDiv.style.display = 'block';
            }
        }
    </script>
</body>
</html>

// 6. package.json for Frontend Dependencies
{
  "name": "tauri-app",
  "private": true,
  "version": "0.1.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "tauri": "tauri"
  },
  "dependencies": {
    "@tauri-apps/api": "^1.0.0"
  },
  "devDependencies": {
    "@tauri-apps/cli": "^1.0.0",
    "vite": "^4.0.0"
  }
}

// 7. Development Commands
/*
# Install dependencies
npm install

# Start development server
npm run tauri dev

# Build application
npm run tauri build

# Development only (frontend)
npm run dev
*/
CódigorustIntermediário

Aplicação Gerenciador de Arquivos Tauri

Aplicação completa de gerenciador de arquivos com arrastar e soltar, operações de arquivo e integração do sistema

Dificuldade

8/10

Tempo estimado

45 min

Etiquetas

tauri, file manager, desktop, rust

Pré-requisitos

Tauri basics, Rust async programming, Advanced HTML/CSS/JavaScript

rust
// Tauri File Manager Application

// 1. main.rs - Enhanced Rust Backend
#![cfg_attr(
    all(not(debug_assertions), target_os = "windows"),
    windows_subsystem = "windows"
)]

use tauri::{Manager, Menu, MenuItem, Submenu};
use std::path::PathBuf;
use std::fs;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct FileInfo {
    name: String,
    path: String,
    is_dir: bool,
    size: u64,
    modified: String,
    extension: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
struct DirectoryContents {
    path: String,
    files: Vec<FileInfo>,
    parent: Option<String>,
}

// List directory contents
#[tauri::command]
async fn list_directory(path: String) -> Result<DirectoryContents, String> {
    let dir_path = PathBuf::from(&path);

    if !dir_path.exists() || !dir_path.is_dir() {
        return Err("Invalid directory path".to_string());
    }

    let mut files = Vec::new();

    match fs::read_dir(&dir_path) {
        Ok(entries) => {
            for entry in entries {
                if let Ok(entry) = entry {
                    let metadata = match entry.metadata() {
                        Ok(meta) => meta,
                        Err(_) => continue,
                    };

                    let file_name = entry.file_name().to_string_lossy().to_string();
                    let file_path = entry.path().to_string_lossy().to_string();
                    let is_dir = metadata.is_dir();
                    let size = metadata.len() as u64;

                    let modified = metadata.modified()
                        .ok()
                        .and_then(|time| {
                            time.duration_since(std::time::SystemTime::UNIX_EPOCH)
                                .ok()
                                .map(|d| d.as_secs())
                        })
                        .map(|timestamp| {
                            chrono::DateTime::from_timestamp(timestamp as i64, 0)
                                .unwrap_or_default()
                                .format("%Y-%m-%d %H:%M:%S")
                                .to_string()
                        })
                        .unwrap_or_else(|| "Unknown".to_string());

                    let extension = if !is_dir {
                        PathBuf::from(&file_name)
                            .extension()
                            .and_then(|ext| ext.to_str())
                            .map(|s| s.to_lowercase())
                    } else {
                        None
                    };

                    files.push(FileInfo {
                        name: file_name,
                        path: file_path,
                        is_dir,
                        size,
                        modified,
                        extension,
                    });
                }
            }
        }
        Err(e) => return Err(format!("Failed to read directory: {}", e)),
    }

    // Sort files: directories first, then files, both alphabetically
    files.sort_by(|a, b| {
        match (a.is_dir, b.is_dir) {
            (true, false) => std::cmp::Ordering::Less,
            (false, true) => std::cmp::Ordering::Greater,
            _ => a.name.cmp(&b.name),
        }
    });

    let parent = dir_path.parent()
        .map(|p| p.to_string_lossy().to_string());

    Ok(DirectoryContents {
        path,
        files,
        parent,
    })
}

// Create directory
#[tauri::command]
async fn create_directory(path: String) -> Result<(), String> {
    match fs::create_dir(&path) {
        Ok(_) => Ok(()),
        Err(e) => Err(format!("Failed to create directory: {}", e))
    }
}

// Delete file or directory
#[tauri::command]
async fn delete_item(path: String, is_dir: bool) -> Result<(), String> {
    let path_buf = PathBuf::from(&path);

    let result = if is_dir {
        fs::remove_dir_all(&path_buf)
    } else {
        fs::remove_file(&path_buf)
    };

    match result {
        Ok(_) => Ok(()),
        Err(e) => Err(format!("Failed to delete item: {}", e))
    }
}

// Copy file
#[tauri::command]
async fn copy_file(source: String, destination: String) -> Result<(), String> {
    match fs::copy(&source, &destination) {
        Ok(_) => Ok(()),
        Err(e) => Err(format!("Failed to copy file: {}", e))
    }
}

// Move/rename file
#[tauri::command]
async fn move_file(source: String, destination: String) -> Result<(), String> {
    match fs::rename(&source, &destination) {
        Ok(_) => Ok(()),
        Err(e) => Err(format!("Failed to move file: {}", e))
    }
}

// Get file content (for text files)
#[tauri::command]
async fn get_file_content(path: String) -> Result<String, String> {
    match fs::read_to_string(&path) {
        Ok(content) => Ok(content),
        Err(e) => Err(format!("Failed to read file: {}", e))
    }
}

// Save file content
#[tauri::command]
async fn save_file_content(path: String, content: String) -> Result<(), String> {
    match fs::write(&path, content) {
        Ok(_) => Ok(()),
        Err(e) => Err(format!("Failed to write file: {}", e))
    }
}

// Get system drives/volumes
#[tauri::command]
async fn get_system_drives() -> Result<Vec<String>, String> {
    #[cfg(windows)]
    {
        use std::ffi::OsStr;
        use std::os::windows::ffi::OsStrExt;

        let mut drives = Vec::new();
        let mut buffer = [0u16; 256];

        unsafe {
            let result = windows_sys::Win32::Storage::FileSystem::GetLogicalDriveStringsW(
                buffer.len() as u32,
                buffer.as_mut_ptr()
            );

            if result > 0 {
                let mut pos = 0;
                while pos < result as usize {
                    let mut end = pos;
                    while buffer[end] != 0 && end < buffer.len() {
                        end += 1;
                    }

                    if end > pos {
                        let drive_str = OsStr::new_wide(&buffer[pos..end])
                            .to_string_lossy()
                            .to_string();
                        drives.push(drive_str);
                    }

                    pos = end + 1;
                }
            }
        }

        Ok(drives)
    }

    #[cfg(unix)]
    {
        Ok(vec!["/".to_string()])
    }

    #[cfg(not(any(windows, unix)))]
    {
        Ok(vec![])
    }
}

// Open file with default application
#[tauri::command]
async fn open_file_with_default(path: String) -> Result<(), String> {
    #[cfg(windows)]
    {
        use std::process::Command;

        match Command::new("cmd")
            .args(["/C", "start", "", &path])
            .spawn()
        {
            Ok(_) => Ok(()),
            Err(e) => Err(format!("Failed to open file: {}", e))
        }
    }

    #[cfg(unix)]
    {
        use std::process::Command;

        match Command::new("open")
            .arg(&path)
            .spawn()
        {
            Ok(_) => Ok(()),
            Err(e) => Err(format!("Failed to open file: {}", e))
        }
    }

    #[cfg(not(any(windows, unix)))]
    {
        Err("Platform not supported".to_string())
    }
}

fn main() {
    // Create application menu
    let menu = Menu::new()
        .add_submenu(Submenu::new(
            "File",
            Menu::new()
                .add_native_item(MenuItem::Separator)
                .add_item(MenuItem::new("Exit", true, None, None, None, None, None, None))
        ))
        .add_submenu(Submenu::new(
            "Help",
            Menu::new()
                .add_item(MenuItem::new("About", true, None, None, None, None, None, None))
        ));

    tauri::Builder::default()
        .menu(menu)
        .setup(|app| {
            let window = app.get_window("main").unwrap();

            // Set window properties
            window.set_title("Tauri File Manager").unwrap();
            window.set_min_size(Some((800.0, 600.0)).into()).unwrap();

            Ok(())
        })
        .invoke_handler(tauri::generate_handler![
            list_directory,
            create_directory,
            delete_item,
            copy_file,
            move_file,
            get_file_content,
            save_file_content,
            get_system_drives,
            open_file_with_default
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

// 2. HTML Frontend with Enhanced UI
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tauri File Manager</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            height: 100vh;
            display: flex;
            flex-direction: column;
            background-color: #f5f5f5;
        }

        .header {
            background: #2c3e50;
            color: white;
            padding: 10px 20px;
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .path-bar {
            flex: 1;
            background: #34495e;
            border: none;
            color: white;
            padding: 8px 12px;
            border-radius: 4px;
            font-family: inherit;
        }

        .main-container {
            flex: 1;
            display: flex;
            overflow: hidden;
        }

        .sidebar {
            width: 200px;
            background: white;
            border-right: 1px solid #ddd;
            overflow-y: auto;
        }

        .drive-item {
            padding: 10px 20px;
            cursor: pointer;
            border-bottom: 1px solid #f0f0f0;
        }

        .drive-item:hover {
            background: #e3f2fd;
        }

        .drive-item.active {
            background: #2196f3;
            color: white;
        }

        .file-list {
            flex: 1;
            background: white;
            overflow: auto;
        }

        .file-item {
            display: flex;
            align-items: center;
            padding: 12px 20px;
            border-bottom: 1px solid #f0f0f0;
            cursor: pointer;
            user-select: none;
        }

        .file-item:hover {
            background: #f5f5f5;
        }

        .file-item.selected {
            background: #e3f2fd;
        }

        .file-item.dragging {
            opacity: 0.5;
        }

        .file-icon {
            width: 20px;
            height: 20px;
            margin-right: 12px;
            font-size: 16px;
        }

        .file-info {
            flex: 1;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .file-name {
            font-weight: 500;
        }

        .file-meta {
            font-size: 12px;
            color: #666;
            display: flex;
            gap: 15px;
        }

        .context-menu {
            position: fixed;
            background: white;
            border: 1px solid #ddd;
            border-radius: 4px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            z-index: 1000;
            display: none;
        }

        .context-menu-item {
            padding: 10px 20px;
            cursor: pointer;
            border-bottom: 1px solid #f0f0f0;
        }

        .context-menu-item:hover {
            background: #f0f0f0;
        }

        .context-menu-item:last-child {
            border-bottom: none;
        }

        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.5);
            z-index: 2000;
            align-items: center;
            justify-content: center;
        }

        .modal-content {
            background: white;
            padding: 30px;
            border-radius: 8px;
            min-width: 400px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.2);
        }

        .modal-header {
            margin-bottom: 20px;
        }

        .modal-body {
            margin-bottom: 20px;
        }

        .modal-footer {
            display: flex;
            justify-content: flex-end;
            gap: 10px;
        }

        input, textarea {
            width: 100%;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-family: inherit;
        }

        button {
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-family: inherit;
        }

        .btn-primary {
            background: #2196f3;
            color: white;
        }

        .btn-primary:hover {
            background: #1976d2;
        }

        .btn-secondary {
            background: #6c757d;
            color: white;
        }

        .btn-secondary:hover {
            background: #5a6268;
        }

        .btn-danger {
            background: #dc3545;
            color: white;
        }

        .btn-danger:hover {
            background: #c82333;
        }

        .toolbar {
            background: white;
            padding: 10px 20px;
            border-bottom: 1px solid #ddd;
            display: flex;
            gap: 10px;
        }

        .toolbar button {
            padding: 8px 16px;
        }

        .loading {
            display: flex;
            align-items: center;
            justify-content: center;
            height: 100%;
            font-size: 18px;
            color: #666;
        }
    </style>
</head>
<body>
    <div class="header">
        <input type="text" class="path-bar" id="pathBar" readonly>
        <button onclick="navigateUp()">↑</button>
        <button onclick="refreshDirectory()">↻</button>
    </div>

    <div class="toolbar">
        <button onclick="createFolder()">📁 New Folder</button>
        <button onclick="createFile()">📄 New File</button>
        <button onclick="deleteSelected()">🗑️ Delete</button>
        <button onclick="copySelected()">📋 Copy</button>
        <button onclick="pasteItems()">📄 Paste</button>
    </div>

    <div class="main-container">
        <div class="sidebar" id="sidebar">
            <!-- Drives will be populated here -->
        </div>

        <div class="file-list" id="fileList">
            <div class="loading">Loading...</div>
        </div>
    </div>

    <div class="context-menu" id="contextMenu">
        <div class="context-menu-item" onclick="openItem()">Open</div>
        <div class="context-menu-item" onclick="renameItem()">Rename</div>
        <div class="context-menu-item" onclick="deleteItem()">Delete</div>
        <div class="context-menu-item" onclick="copyItem()">Copy</div>
    </div>

    <!-- Create/Name Modal -->
    <div class="modal" id="nameModal">
        <div class="modal-content">
            <div class="modal-header">
                <h2 id="modalTitle">Create New</h2>
            </div>
            <div class="modal-body">
                <input type="text" id="nameInput" placeholder="Enter name">
            </div>
            <div class="modal-footer">
                <button class="btn-secondary" onclick="closeModal()">Cancel</button>
                <button class="btn-primary" onclick="confirmNameAction()">Create</button>
            </div>
        </div>
    </div>

    <!-- File Editor Modal -->
    <div class="modal" id="editorModal">
        <div class="modal-content" style="width: 80%; height: 80%;">
            <div class="modal-header">
                <h2 id="editorTitle">Edit File</h2>
            </div>
            <div class="modal-body" style="height: calc(100% - 120px);">
                <textarea id="editorContent" style="height: 100%; resize: none;"></textarea>
            </div>
            <div class="modal-footer">
                <button class="btn-secondary" onclick="closeEditor()">Cancel</button>
                <button class="btn-primary" onclick="saveFile()">Save</button>
            </div>
        </div>
    </div>

    <script>
        const { invoke } = window.__TAURI__.core;

        let currentPath = '';
        let selectedFile = null;
        let clipboard = null;

        // Initialize application
        async function init() {
            await loadSystemDrives();
            setupEventListeners();
        }

        // Load system drives
        async function loadSystemDrives() {
            try {
                const drives = await invoke('get_system_drives');
                const sidebar = document.getElementById('sidebar');
                sidebar.innerHTML = '';

                drives.forEach(drive => {
                    const item = document.createElement('div');
                    item.className = 'drive-item';
                    item.textContent = drive;
                    item.onclick = () => navigateToPath(drive);
                    sidebar.appendChild(item);
                });

                // Navigate to first drive
                if (drives.length > 0) {
                    navigateToPath(drives[0]);
                }
            } catch (error) {
                console.error('Failed to load drives:', error);
            }
        }

        // Navigate to specific path
        async function navigateToPath(path) {
            currentPath = path;
            document.getElementById('pathBar').value = path;

            // Update sidebar active state
            document.querySelectorAll('.drive-item').forEach(item => {
                item.classList.toggle('active', item.textContent.trim() === path.trim());
            });

            await loadDirectory(path);
        }

        // Load directory contents
        async function loadDirectory(path) {
            const fileList = document.getElementById('fileList');
            fileList.innerHTML = '<div class="loading">Loading...</div>';

            try {
                const contents = await invoke('list_directory', { path });
                renderFileList(contents);
            } catch (error) {
                fileList.innerHTML = '<div class="loading">Error: ' + error + '</div>';
            }
        }

        // Render file list
        function renderFileList(contents) {
            const fileList = document.getElementById('fileList');
            fileList.innerHTML = '';

            if (contents.parent) {
                const parentItem = createFileItem({
                    name: '..',
                    path: contents.parent,
                    is_dir: true,
                    size: 0,
                    modified: '',
                    extension: null
                });
                fileList.appendChild(parentItem);
            }

            contents.files.forEach(file => {
                const fileItem = createFileItem(file);
                fileList.appendChild(fileItem);
            });
        }

        // Create file item element
        function createFileItem(file) {
            const item = document.createElement('div');
            item.className = 'file-item';
            item.dataset.path = file.path;
            item.dataset.isDirectory = file.is_dir;

            const icon = file.is_dir ? '📁' : getFileIcon(file.extension);
            const size = file.is_dir ? '—' : formatFileSize(file.size);

            item.innerHTML = `
                <div class="file-icon">${icon}</div>
                <div class="file-info">
                    <span class="file-name">${file.name}</span>
                    <div class="file-meta">
                        <span>${size}</span>
                        <span>${file.modified}</span>
                    </div>
                </div>
            `;

            item.onclick = () => handleFileClick(file);
            item.ondblclick = () => handleFileDoubleClick(file);
            item.oncontextmenu = (e) => showContextMenu(e, file);

            // Drag and drop
            item.draggable = true;
            item.ondragstart = (e) => handleDragStart(e, file);
            item.ondragover = (e) => handleDragOver(e);
            item.ondrop = (e) => handleDrop(e, file);
            item.ondragend = handleDragEnd;

            return item;
        }

        // Get file icon based on extension
        function getFileIcon(extension) {
            const iconMap = {
                'txt': '📄',
                'pdf': '📑',
                'doc': '📝',
                'docx': '📝',
                'xls': '📊',
                'xlsx': '📊',
                'jpg': '🖼️',
                'jpeg': '🖼️',
                'png': '🖼️',
                'gif': '🖼️',
                'mp4': '🎬',
                'mp3': '🎵',
                'zip': '🗜️',
                'exe': '⚙️',
                'js': '📜',
                'html': '🌐',
                'css': '🎨',
                'json': '📋',
                'md': '📖'
            };

            return iconMap[extension] || '📄';
        }

        // Format file size
        function formatFileSize(bytes) {
            if (bytes === 0) return '0 B';

            const k = 1024;
            const sizes = ['B', 'KB', 'MB', 'GB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));

            return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
        }

        // Handle file click (selection)
        function handleFileClick(file) {
            document.querySelectorAll('.file-item').forEach(item => {
                item.classList.remove('selected');
            });

            const item = document.querySelector(`[data-path="${file.path}"]`);
            if (item) {
                item.classList.add('selected');
                selectedFile = file;
            }
        }

        // Handle file double click (open)
        async function handleFileDoubleClick(file) {
            if (file.is_dir) {
                await navigateToPath(file.path);
            } else {
                await openFile(file);
            }
        }

        // Open file
        async function openFile(file) {
            const textExtensions = ['txt', 'js', 'html', 'css', 'json', 'md', 'xml', 'csv', 'log'];

            if (textExtensions.includes(file.extension || '')) {
                try {
                    const content = await invoke('get_file_content', { path: file.path });
                    showFileEditor(file, content);
                } catch (error) {
                    console.error('Failed to open file:', error);
                }
            } else {
                try {
                    await invoke('open_file_with_default', { path: file.path });
                } catch (error) {
                    console.error('Failed to open file with default app:', error);
                }
            }
        }

        // Show file editor
        function showFileEditor(file, content) {
            document.getElementById('editorTitle').textContent = `Edit: ${file.name}`;
            document.getElementById('editorContent').value = content;
            document.getElementById('editorModal').style.display = 'flex';

            // Store current file path for saving
            window.currentEditingFile = file.path;
        }

        // Save file
        async function saveFile() {
            if (!window.currentEditingFile) return;

            const content = document.getElementById('editorContent').value;

            try {
                await invoke('save_file_content', {
                    path: window.currentEditingFile,
                    content
                });
                closeEditor();
            } catch (error) {
                alert('Failed to save file: ' + error);
            }
        }

        // Close editor
        function closeEditor() {
            document.getElementById('editorModal').style.display = 'none';
            delete window.currentEditingFile;
        }

        // Navigate up
        function navigateUp() {
            const parts = currentPath.split(/[/\\]/);
            if (parts.length > 1) {
                parts.pop();
                const parentPath = parts.join('/') || (currentPath.startsWith('/') ? '/' : '');
                navigateToPath(parentPath);
            }
        }

        // Refresh directory
        function refreshDirectory() {
            loadDirectory(currentPath);
        }

        // Context menu
        function showContextMenu(event, file) {
            event.preventDefault();
            selectedFile = file;

            const menu = document.getElementById('contextMenu');
            menu.style.display = 'block';
            menu.style.left = event.pageX + 'px';
            menu.style.top = event.pageY + 'px';
        }

        // Close context menu
        function hideContextMenu() {
            document.getElementById('contextMenu').style.display = 'none';
        }

        // Context menu actions
        async function openItem() {
            if (selectedFile) {
                await handleFileDoubleClick(selectedFile);
            }
            hideContextMenu();
        }

        async function deleteItem() {
            if (selectedFile && confirm(`Delete '${selectedFile.name}'?`)) {
                try {
                    await invoke('delete_item', {
                        path: selectedFile.path,
                        is_dir: selectedFile.is_dir
                    });
                    await loadDirectory(currentPath);
                } catch (error) {
                    alert('Failed to delete: ' + error);
                }
            }
            hideContextMenu();
        }

        function renameItem() {
            if (selectedFile) {
                showNameModal(`Rename '${selectedFile.name}'`, selectedFile.name, (newName) => {
                    const newPath = currentPath + '/' + newName;
                    moveSelectedFile(newPath);
                });
            }
            hideContextMenu();
        }

        function copyItem() {
            if (selectedFile) {
                clipboard = { ...selectedFile };
            }
            hideContextMenu();
        }

        // Drag and drop handlers
        function handleDragStart(event, file) {
            event.dataTransfer.effectAllowed = 'move';
            event.dataTransfer.setData('text/plain', file.path);
            event.target.classList.add('dragging');
        }

        function handleDragOver(event) {
            event.preventDefault();
            event.dataTransfer.dropEffect = 'move';
        }

        async function handleDrop(event, targetFile) {
            event.preventDefault();

            if (!targetFile.is_dir) return;

            const sourcePath = event.dataTransfer.getData('text/plain');

            try {
                const fileName = sourcePath.split(/[/\\]/).pop();
                const destinationPath = targetFile.path + '/' + fileName;

                await invoke('move_file', { source: sourcePath, destination: destinationPath });
                await loadDirectory(currentPath);
            } catch (error) {
                alert('Failed to move file: ' + error);
            }
        }

        function handleDragEnd(event) {
            event.target.classList.remove('dragging');
        }

        // Modal functions
        function showNameModal(title, defaultValue, callback) {
            document.getElementById('modalTitle').textContent = title;
            document.getElementById('nameInput').value = defaultValue || '';
            document.getElementById('nameModal').style.display = 'flex';

            window.nameModalCallback = callback;
        }

        function closeModal() {
            document.getElementById('nameModal').style.display = 'none';
            delete window.nameModalCallback;
        }

        function confirmNameAction() {
            const name = document.getElementById('nameInput').value.trim();

            if (name && window.nameModalCallback) {
                window.nameModalCallback(name);
            }

            closeModal();
        }

        // Toolbar actions
        function createFolder() {
            showNameModal('Create New Folder', '', (name) => {
                const path = currentPath + '/' + name;
                invoke('create_directory', { path })
                    .then(() => loadDirectory(currentPath))
                    .catch(error => alert('Failed to create folder: ' + error));
            });
        }

        function createFile() {
            showNameModal('Create New File', 'untitled.txt', (name) => {
                const path = currentPath + '/' + name;
                invoke('save_file_content', { path, content: '' })
                    .then(() => loadDirectory(currentPath))
                    .catch(error => alert('Failed to create file: ' + error));
            });
        }

        function deleteSelected() {
            if (selectedFile) {
                deleteItem();
            }
        }

        function copySelected() {
            if (selectedFile) {
                clipboard = { ...selectedFile };
            }
        }

        async function pasteItems() {
            if (!clipboard) return;

            const destinationPath = currentPath + '/' + clipboard.name;

            try {
                await invoke('copy_file', {
                    source: clipboard.path,
                    destination: destinationPath
                });
                await loadDirectory(currentPath);
            } catch (error) {
                alert('Failed to paste: ' + error);
            }
        }

        // Setup event listeners
        function setupEventListeners() {
            // Hide context menu on click outside
            document.addEventListener('click', hideContextMenu);

            // Keyboard shortcuts
            document.addEventListener('keydown', (event) => {
                if (event.key === 'Delete' && selectedFile) {
                    deleteSelected();
                } else if (event.ctrlKey && event.key === 'c' && selectedFile) {
                    copySelected();
                } else if (event.ctrlKey && event.key === 'v' && clipboard) {
                    pasteItems();
                }
            });

            // Modal close on escape
            document.addEventListener('keydown', (event) => {
                if (event.key === 'Escape') {
                    closeModal();
                    closeEditor();
                }
            });
        }

        // Initialize when DOM is loaded
        document.addEventListener('DOMContentLoaded', init);
    </script>
</body>
</html>
CódigorustComplexo

Aplicação Monitor de Sistema Tauri

Dashboard de monitoramento de sistema em tempo real com CPU, uso de memória, disco e gerenciamento de processos

Dificuldade

9/10

Tempo estimado

60 min

Etiquetas

tauri, system monitor, rust, performance

Pré-requisitos

Advanced Tauri, Rust async programming, Chart.js, System monitoring concepts

rust
// Tauri System Monitor Application

// 1. Cargo.toml with additional dependencies
[package]
name = "tauri-system-monitor"
version = "0.1.0"
edition = "2021"

[dependencies]
tauri = { version = "1.0", features = ["api-all"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sysinfo = "0.29"
chrono = { version = "0.4", features = ["serde"] }
tokio = { version = "1", features = ["time"] }

[build-dependencies]
tauri-build = { version = "1.0", features = [] }

[features]
default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"]

// 2. main.rs - System Monitor Backend
#![cfg_attr(
    all(not(debug_assertions), target_os = "windows"),
    windows_subsystem = "windows"
)]

use tauri::{Manager, State, Window};
use sysinfo::{System, SystemExt, ProcessExt, CpuExt, DiskExt, NetworkExt};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use serde::{Deserialize, Serialize};

// Shared system state
pub struct AppState {
    system: Arc<Mutex<System>>,
}

#[derive(Debug, Serialize, Deserialize)]
struct SystemInfo {
    cpu_usage: f32,
    memory_total: u64,
    memory_used: u64,
    memory_usage: f32,
    disk_total: u64,
    disk_used: u64,
    disk_usage: f32,
    uptime: u64,
    process_count: usize,
    load_average: [f64; 3],
}

#[derive(Debug, Serialize, Deserialize)]
struct ProcessInfo {
    pid: u32,
    name: String,
    cpu_usage: f32,
    memory_usage: u64,
    memory_percent: f32,
    status: String,
    start_time: u64,
}

#[derive(Debug, Serialize, Deserialize)]
struct DiskInfo {
    name: String,
    mount_point: String,
    file_system: String,
    total_space: u64,
    available_space: u64,
    used_space: u64,
    usage_percent: f32,
}

#[derive(Debug, Serialize, Deserialize)]
struct NetworkInfo {
    interface: String,
    data_received: u64,
    data_transmitted: u64,
    packets_received: u64,
    packets_transmitted: u64,
    errors_on_received: u64,
    errors_on_transmitted: u64,
}

// Get overall system information
#[tauri::command]
async fn get_system_info(state: State<'_, AppState>) -> Result<SystemInfo, String> {
    let system = state.system.lock().map_err(|e| e.to_string())?;
    system.refresh_all();

    let cpu_usage = system.global_cpu_info().cpu_usage();
    let memory_total = system.total_memory();
    let memory_used = system.used_memory();
    let memory_usage = (memory_used as f32 / memory_total as f32) * 100.0;

    // Calculate disk usage
    let mut disk_total = 0u64;
    let mut disk_used = 0u64;

    for disk in system.disks() {
        disk_total += disk.total_space();
        disk_used += disk.total_space() - disk.available_space();
    }

    let disk_usage = if disk_total > 0 {
        (disk_used as f32 / disk_total as f32) * 100.0
    } else {
        0.0
    };

    let uptime = system.uptime();
    let process_count = system.processes().len();

    // Get load average (Unix systems only)
    let load_average = sysinfo::get_current_pid()
        .ok()
        .and_then(|pid| system.process(pid))
        .map(|process| {
            // This is a simplified approach - in production you'd use platform-specific APIs
            [0.0, 0.0, 0.0] // Placeholder
        })
        .unwrap_or([0.0, 0.0, 0.0]);

    Ok(SystemInfo {
        cpu_usage,
        memory_total,
        memory_used,
        memory_usage,
        disk_total,
        disk_used,
        disk_usage,
        uptime,
        process_count,
        load_average,
    })
}

// Get process list
#[tauri::command]
async fn get_processes(state: State<'_, AppState>) -> Result<Vec<ProcessInfo>, String> {
    let system = state.system.lock().map_err(|e| e.to_string())?;
    system.refresh_processes();

    let total_memory = system.total_memory();
    let mut processes = Vec::new();

    for (pid, process) in system.processes() {
        let memory_percent = (process.memory() as f32 / total_memory as f32) * 100.0;

        processes.push(ProcessInfo {
            pid: pid.as_u32(),
            name: process.name().to_string(),
            cpu_usage: process.cpu_usage(),
            memory_usage: process.memory(),
            memory_percent,
            status: format!("{:?}", process.status()),
            start_time: process.start_time(),
        });
    }

    // Sort by CPU usage (descending)
    processes.sort_by(|a, b| b.cpu_usage.partial_cmp(&a.cpu_usage).unwrap_or(std::cmp::Ordering::Equal));

    // Limit to top 50 processes
    processes.truncate(50);

    Ok(processes)
}

// Get disk information
#[tauri::command]
async fn get_disks(state: State<'_, AppState>) -> Result<Vec<DiskInfo>, String> {
    let system = state.system.lock().map_err(|e| e.to_string())?;
    system.refresh_disks();

    let mut disks = Vec::new();

    for disk in system.disks() {
        let total_space = disk.total_space();
        let available_space = disk.available_space();
        let used_space = total_space - available_space;
        let usage_percent = if total_space > 0 {
            (used_space as f32 / total_space as f32) * 100.0
        } else {
            0.0
        };

        disks.push(DiskInfo {
            name: disk.name().to_string_lossy().to_string(),
            mount_point: disk.mount_point().to_string_lossy().to_string(),
            file_system: std::str::from_utf8(disk.file_system()).unwrap_or("Unknown").to_string(),
            total_space,
            available_space,
            used_space,
            usage_percent,
        });
    }

    Ok(disks)
}

// Get network information
#[tauri::command]
async fn get_network_info(state: State<'_, AppState>) -> Result<Vec<NetworkInfo>, String> {
    let system = state.system.lock().map_err(|e| e.to_string())?;
    system.refresh_networks();

    let mut networks = Vec::new();

    for (interface_name, data) in system.networks() {
        networks.push(NetworkInfo {
            interface: interface_name.clone(),
            data_received: data.total_received(),
            data_transmitted: data.total_transmitted(),
            packets_received: data.total_packets_received(),
            packets_transmitted: data.total_packets_transmitted(),
            errors_on_received: data.errors_on_received(),
            errors_on_transmitted: data.errors_on_transmitted(),
        });
    }

    Ok(networks)
}

// Kill process
#[tauri::command]
async fn kill_process(pid: u32, state: State<'_, AppState>) -> Result<bool, String> {
    let system = state.system.lock().map_err(|e| e.to_string())?;

    // Convert u32 to sysinfo's Pid type
    if let Some(sys_pid) = sysinfo::Pid::from_u32(pid) {
        if system.process(sys_pid).is_some() {
            // In a real implementation, you would use platform-specific APIs to kill the process
            // For now, we'll just return true
            Ok(true)
        } else {
            Err("Process not found".to_string())
        }
    } else {
        Err("Invalid PID".to_string())
    }
}

// Start background monitoring
#[tauri::command]
async fn start_monitoring(window: Window, state: State<'_, AppState>) -> Result<(), String> {
    let window_clone = window.clone();
    let system_state = state.inner().clone();

    thread::spawn(move || {
        loop {
            // Update system info every 2 seconds
            if let Err(_) = window_clone.emit("system-update", "refresh") {
                // Window closed, exit thread
                break;
            }

            thread::sleep(Duration::from_secs(2));
        }
    });

    Ok(())
}

fn main() {
    let mut system = System::new();
    system.refresh_all();

    let app_state = AppState {
        system: Arc::new(Mutex::new(system)),
    };

    tauri::Builder::default()
        .manage(app_state)
        .setup(|app| {
            let window = app.get_window("main").unwrap();

            // Set window properties
            window.set_title("System Monitor").unwrap();
            window.set_min_size(Some((1000.0, 700.0)).into()).unwrap();

            // Start monitoring
            let state = app.state::<AppState>();
            let window_clone = window.clone();
            tauri::async_runtime::spawn(async move {
                if let Err(e) = start_monitoring(window_clone, state).await {
                    eprintln!("Failed to start monitoring: {}", e);
                }
            });

            Ok(())
        })
        .invoke_handler(tauri::generate_handler![
            get_system_info,
            get_processes,
            get_disks,
            get_network_info,
            kill_process,
            start_monitoring
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

// 3. System Monitor Frontend
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>System Monitor</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: #1e1e1e;
            color: #ffffff;
            overflow: hidden;
        }

        .container {
            height: 100vh;
            display: flex;
            flex-direction: column;
        }

        .header {
            background: #2d2d30;
            padding: 15px 20px;
            border-bottom: 1px solid #3e3e42;
        }

        .header h1 {
            font-size: 18px;
            font-weight: 400;
        }

        .main-content {
            flex: 1;
            display: flex;
            overflow: hidden;
        }

        .sidebar {
            width: 250px;
            background: #252526;
            border-right: 1px solid #3e3e42;
            overflow-y: auto;
        }

        .nav-item {
            padding: 12px 20px;
            cursor: pointer;
            border-left: 3px solid transparent;
            transition: all 0.2s;
        }

        .nav-item:hover {
            background: #2d2d30;
        }

        .nav-item.active {
            background: #2d2d30;
            border-left-color: #007acc;
        }

        .content {
            flex: 1;
            overflow-y: auto;
            padding: 20px;
        }

        .dashboard {
            display: none;
        }

        .dashboard.active {
            display: block;
        }

        .metrics-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 20px;
            margin-bottom: 30px;
        }

        .metric-card {
            background: #2d2d30;
            border: 1px solid #3e3e42;
            border-radius: 8px;
            padding: 20px;
        }

        .metric-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
        }

        .metric-title {
            font-size: 14px;
            color: #cccccc;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }

        .metric-value {
            font-size: 24px;
            font-weight: bold;
            color: #ffffff;
        }

        .metric-unit {
            font-size: 14px;
            color: #cccccc;
            margin-left: 5px;
        }

        .chart-container {
            height: 200px;
            margin-top: 15px;
        }

        .process-table {
            width: 100%;
            background: #2d2d30;
            border: 1px solid #3e3e42;
            border-radius: 8px;
            overflow: hidden;
        }

        .process-table table {
            width: 100%;
            border-collapse: collapse;
        }

        .process-table th,
        .process-table td {
            padding: 12px 15px;
            text-align: left;
            border-bottom: 1px solid #3e3e42;
        }

        .process-table th {
            background: #3e3e42;
            font-weight: 600;
            color: #cccccc;
            text-transform: uppercase;
            font-size: 12px;
            letter-spacing: 0.5px;
        }

        .process-table tr:hover {
            background: #1e1e1e;
        }

        .progress-bar {
            width: 100%;
            height: 8px;
            background: #3e3e42;
            border-radius: 4px;
            overflow: hidden;
        }

        .progress-fill {
            height: 100%;
            background: #007acc;
            transition: width 0.3s ease;
        }

        .progress-fill.high {
            background: #d9534f;
        }

        .progress-fill.medium {
            background: #f0ad4e;
        }

        .status-indicator {
            display: inline-block;
            width: 8px;
            height: 8px;
            border-radius: 50%;
            margin-right: 8px;
        }

        .status-running {
            background: #5cb85c;
        }

        .status-sleeping {
            background: #f0ad4e;
        }

        .status-zombie {
            background: #d9534f;
        }

        .refresh-btn {
            background: #007acc;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }

        .refresh-btn:hover {
            background: #005a9e;
        }

        .tab-container {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
        }

        .tab {
            background: #2d2d30;
            border: 1px solid #3e3e42;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            transition: all 0.2s;
        }

        .tab.active {
            background: #007acc;
            border-color: #007acc;
        }

        .tab:hover:not(.active) {
            background: #3e3e42;
        }

        @keyframes pulse {
            0% { opacity: 1; }
            50% { opacity: 0.5; }
            100% { opacity: 1; }
        }

        .updating {
            animation: pulse 1s infinite;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🖥️ System Monitor</h1>
        </div>

        <div class="main-content">
            <div class="sidebar">
                <div class="nav-item active" data-panel="overview">📊 Overview</div>
                <div class="nav-item" data-panel="processes">⚙️ Processes</div>
                <div class="nav-item" data-panel="disks">💾 Disks</div>
                <div class="nav-item" data-panel="network">🌐 Network</div>
            </div>

            <div class="content">
                <!-- Overview Panel -->
                <div class="dashboard active" id="overview">
                    <div class="metrics-grid">
                        <div class="metric-card">
                            <div class="metric-header">
                                <span class="metric-title">CPU Usage</span>
                                <span class="metric-value" id="cpuUsage">0%</span>
                            </div>
                            <div class="progress-bar">
                                <div class="progress-fill" id="cpuProgress"></div>
                            </div>
                            <div class="chart-container">
                                <canvas id="cpuChart"></canvas>
                            </div>
                        </div>

                        <div class="metric-card">
                            <div class="metric-header">
                                <span class="metric-title">Memory Usage</span>
                                <span class="metric-value">
                                    <span id="memoryUsed">0</span>
                                    <span class="metric-unit">GB</span>
                                </span>
                            </div>
                            <div class="progress-bar">
                                <div class="progress-fill" id="memoryProgress"></div>
                            </div>
                            <div class="chart-container">
                                <canvas id="memoryChart"></canvas>
                            </div>
                        </div>

                        <div class="metric-card">
                            <div class="metric-header">
                                <span class="metric-title">Disk Usage</span>
                                <span class="metric-value" id="diskUsage">0%</span>
                            </div>
                            <div class="progress-bar">
                                <div class="progress-fill" id="diskProgress"></div>
                            </div>
                            <div class="chart-container">
                                <canvas id="diskChart"></canvas>
                            </div>
                        </div>

                        <div class="metric-card">
                            <div class="metric-header">
                                <span class="metric-title">System Info</span>
                                <button class="refresh-btn" onclick="refreshSystemInfo()">Refresh</button>
                            </div>
                            <div style="margin-top: 15px;">
                                <div style="margin-bottom: 10px;">
                                    <strong>Uptime:</strong> <span id="uptime">0h 0m</span>
                                </div>
                                <div style="margin-bottom: 10px;">
                                    <strong>Processes:</strong> <span id="processCount">0</span>
                                </div>
                                <div style="margin-bottom: 10px;">
                                    <strong>Total Memory:</strong> <span id="totalMemory">0 GB</span>
                                </div>
                                <div>
                                    <strong>Total Disk:</strong> <span id="totalDisk">0 GB</span>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- Processes Panel -->
                <div class="dashboard" id="processes">
                    <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
                        <h2>Running Processes</h2>
                        <button class="refresh-btn" onclick="refreshProcesses()">Refresh</button>
                    </div>

                    <div class="tab-container">
                        <div class="tab active" onclick="setProcessSort('cpu')">Sort by CPU</div>
                        <div class="tab" onclick="setProcessSort('memory')">Sort by Memory</div>
                        <div class="tab" onclick="setProcessSort('name')">Sort by Name</div>
                    </div>

                    <div class="process-table">
                        <table>
                            <thead>
                                <tr>
                                    <th>PID</th>
                                    <th>Name</th>
                                    <th>CPU %</th>
                                    <th>Memory</th>
                                    <th>Memory %</th>
                                    <th>Status</th>
                                    <th>Action</th>
                                </tr>
                            </thead>
                            <tbody id="processTableBody">
                                <tr>
                                    <td colspan="7" style="text-align: center;">Loading...</td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>

                <!-- Disks Panel -->
                <div class="dashboard" id="disks">
                    <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
                        <h2>Disk Usage</h2>
                        <button class="refresh-btn" onclick="refreshDisks()">Refresh</button>
                    </div>

                    <div id="diskInfo">
                        <div class="process-table">
                            <table>
                                <thead>
                                    <tr>
                                        <th>Name</th>
                                        <th>Mount Point</th>
                                        <th>File System</th>
                                        <th>Used</th>
                                        <th>Available</th>
                                        <th>Total</th>
                                        <th>Usage</th>
                                    </tr>
                                </thead>
                                <tbody id="diskTableBody">
                                    <tr>
                                        <td colspan="7" style="text-align: center;">Loading...</td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>

                <!-- Network Panel -->
                <div class="dashboard" id="network">
                    <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
                        <h2>Network Activity</h2>
                        <button class="refresh-btn" onclick="refreshNetwork()">Refresh</button>
                    </div>

                    <div id="networkInfo">
                        <div class="process-table">
                            <table>
                                <thead>
                                    <tr>
                                        <th>Interface</th>
                                        <th>Received</th>
                                        <th>Transmitted</th>
                                        <th>Packets Rx</th>
                                        <th>Packets Tx</th>
                                        <th>Errors Rx</th>
                                        <th>Errors Tx</th>
                                    </tr>
                                </thead>
                                <tbody id="networkTableBody">
                                    <tr>
                                        <td colspan="7" style="text-align: center;">Loading...</td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
        const { invoke } = window.__TAURI__.core;

        let currentPanel = 'overview';
        let processSortBy = 'cpu';
        let charts = {};
        let updateInterval;

        // Initialize application
        async function init() {
            setupEventListeners();
            await loadInitialData();
            startAutoUpdate();
        }

        // Setup event listeners
        function setupEventListeners() {
            // Navigation
            document.querySelectorAll('.nav-item').forEach(item => {
                item.addEventListener('click', () => {
                    const panel = item.dataset.panel;
                    switchPanel(panel);
                });
            });
        }

        // Switch between panels
        function switchPanel(panel) {
            currentPanel = panel;

            // Update navigation
            document.querySelectorAll('.nav-item').forEach(item => {
                item.classList.toggle('active', item.dataset.panel === panel);
            });

            // Update content
            document.querySelectorAll('.dashboard').forEach(dashboard => {
                dashboard.classList.toggle('active', dashboard.id === panel);
            });

            // Load panel-specific data
            switch (panel) {
                case 'processes':
                    refreshProcesses();
                    break;
                case 'disks':
                    refreshDisks();
                    break;
                case 'network':
                    refreshNetwork();
                    break;
                default:
                    refreshSystemInfo();
            }
        }

        // Load initial data
        async function loadInitialData() {
            await refreshSystemInfo();
            await refreshProcesses();
            await refreshDisks();
            await refreshNetwork();
            initializeCharts();
        }

        // Refresh system information
        async function refreshSystemInfo() {
            try {
                const info = await invoke('get_system_info');
                updateSystemInfo(info);
                updateCharts(info);
            } catch (error) {
                console.error('Failed to get system info:', error);
            }
        }

        // Update system information display
        function updateSystemInfo(info) {
            // CPU
            const cpuUsage = Math.round(info.cpu_usage);
            document.getElementById('cpuUsage').textContent = cpuUsage + '%';
            updateProgressBar('cpuProgress', cpuUsage);

            // Memory
            const memoryUsedGB = (info.memory_used / (1024 * 1024 * 1024)).toFixed(1);
            const totalMemoryGB = (info.memory_total / (1024 * 1024 * 1024)).toFixed(1);
            const memoryUsage = Math.round(info.memory_usage);

            document.getElementById('memoryUsed').textContent = memoryUsedGB;
            document.getElementById('totalMemory').textContent = totalMemoryGB + ' GB';
            updateProgressBar('memoryProgress', memoryUsage);

            // Disk
            const diskUsage = Math.round(info.disk_usage);
            const totalDiskGB = (info.disk_total / (1024 * 1024 * 1024)).toFixed(1);

            document.getElementById('diskUsage').textContent = diskUsage + '%';
            document.getElementById('totalDisk').textContent = totalDiskGB + ' GB';
            updateProgressBar('diskProgress', diskUsage);

            // Other info
            const uptime = formatUptime(info.uptime);
            document.getElementById('uptime').textContent = uptime;
            document.getElementById('processCount').textContent = info.process_count;
        }

        // Update progress bar
        function updateProgressBar(id, percentage) {
            const bar = document.getElementById(id);
            bar.style.width = percentage + '%';

            // Change color based on usage
            bar.className = 'progress-fill';
            if (percentage >= 80) {
                bar.classList.add('high');
            } else if (percentage >= 60) {
                bar.classList.add('medium');
            }
        }

        // Format uptime
        function formatUptime(seconds) {
            const hours = Math.floor(seconds / 3600);
            const minutes = Math.floor((seconds % 3600) / 60);
            return `${hours}h ${minutes}m`;
        }

        // Initialize charts
        function initializeCharts() {
            const chartOptions = {
                responsive: true,
                maintainAspectRatio: false,
                scales: {
                    y: {
                        beginAtZero: true,
                        max: 100,
                        ticks: {
                            color: '#cccccc'
                        },
                        grid: {
                            color: '#3e3e42'
                        }
                    },
                    x: {
                        ticks: {
                            color: '#cccccc'
                        },
                        grid: {
                            color: '#3e3e42'
                        }
                    }
                },
                plugins: {
                    legend: {
                        display: false
                    }
                }
            };

            // CPU Chart
            const cpuCtx = document.getElementById('cpuChart').getContext('2d');
            charts.cpu = new Chart(cpuCtx, {
                type: 'line',
                data: {
                    labels: Array(20).fill(''),
                    datasets: [{
                        data: Array(20).fill(0),
                        borderColor: '#007acc',
                        backgroundColor: 'rgba(0, 122, 204, 0.1)',
                        borderWidth: 2,
                        tension: 0.4,
                        fill: true
                    }]
                },
                options: chartOptions
            });

            // Memory Chart
            const memoryCtx = document.getElementById('memoryChart').getContext('2d');
            charts.memory = new Chart(memoryCtx, {
                type: 'line',
                data: {
                    labels: Array(20).fill(''),
                    datasets: [{
                        data: Array(20).fill(0),
                        borderColor: '#5cb85c',
                        backgroundColor: 'rgba(92, 184, 92, 0.1)',
                        borderWidth: 2,
                        tension: 0.4,
                        fill: true
                    }]
                },
                options: chartOptions
            });

            // Disk Chart
            const diskCtx = document.getElementById('diskChart').getContext('2d');
            charts.disk = new Chart(diskCtx, {
                type: 'line',
                data: {
                    labels: Array(20).fill(''),
                    datasets: [{
                        data: Array(20).fill(0),
                        borderColor: '#f0ad4e',
                        backgroundColor: 'rgba(240, 173, 78, 0.1)',
                        borderWidth: 2,
                        tension: 0.4,
                        fill: true
                    }]
                },
                options: chartOptions
            });
        }

        // Update charts with new data
        function updateCharts(info) {
            if (charts.cpu) {
                const cpuData = charts.cpu.data.datasets[0].data;
                cpuData.shift();
                cpuData.push(Math.round(info.cpu_usage));
                charts.cpu.update('none');
            }

            if (charts.memory) {
                const memoryData = charts.memory.data.datasets[0].data;
                memoryData.shift();
                memoryData.push(Math.round(info.memory_usage));
                charts.memory.update('none');
            }

            if (charts.disk) {
                const diskData = charts.disk.data.datasets[0].data;
                diskData.shift();
                diskData.push(Math.round(info.disk_usage));
                charts.disk.update('none');
            }
        }

        // Refresh processes
        async function refreshProcesses() {
            try {
                const processes = await invoke('get_processes');
                displayProcesses(processes);
            } catch (error) {
                console.error('Failed to get processes:', error);
            }
        }

        // Display processes in table
        function displayProcesses(processes) {
            const tbody = document.getElementById('processTableBody');
            tbody.innerHTML = '';

            processes.forEach(process => {
                const row = document.createElement('tr');
                row.innerHTML = `
                    <td>${process.pid}</td>
                    <td>${process.name}</td>
                    <td>${process.cpu_usage.toFixed(1)}%</td>
                    <td>${formatBytes(process.memory_usage)}</td>
                    <td>${process.memory_percent.toFixed(1)}%</td>
                    <td>
                        <span class="status-indicator ${getStatusClass(process.status)}"></span>
                        ${process.status}
                    </td>
                    <td>
                        <button class="refresh-btn" onclick="killProcess(${process.pid})">Kill</button>
                    </td>
                `;
                tbody.appendChild(row);
            });
        }

        // Get status class for styling
        function getStatusClass(status) {
            if (status.includes('Run')) return 'status-running';
            if (status.includes('Sleep')) return 'status-sleeping';
            if (status.includes('Zombie')) return 'status-zombie';
            return '';
        }

        // Format bytes to human readable
        function formatBytes(bytes) {
            if (bytes === 0) return '0 B';

            const k = 1024;
            const sizes = ['B', 'KB', 'MB', 'GB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));

            return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
        }

        // Kill process
        async function killProcess(pid) {
            if (confirm(`Are you sure you want to kill process ${pid}?`)) {
                try {
                    await invoke('kill_process', { pid });
                    await refreshProcesses();
                } catch (error) {
                    alert('Failed to kill process: ' + error);
                }
            }
        }

        // Set process sort
        function setProcessSort(sortBy) {
            processSortBy = sortBy;

            // Update tab UI
            document.querySelectorAll('.tab').forEach(tab => {
                tab.classList.remove('active');
            });
            event.target.classList.add('active');

            refreshProcesses();
        }

        // Refresh disks
        async function refreshDisks() {
            try {
                const disks = await invoke('get_disks');
                displayDisks(disks);
            } catch (error) {
                console.error('Failed to get disk info:', error);
            }
        }

        // Display disk information
        function displayDisks(disks) {
            const tbody = document.getElementById('diskTableBody');
            tbody.innerHTML = '';

            disks.forEach(disk => {
                const row = document.createElement('tr');
                row.innerHTML = `
                    <td>${disk.name}</td>
                    <td>${disk.mount_point}</td>
                    <td>${disk.file_system}</td>
                    <td>${formatBytes(disk.used_space)}</td>
                    <td>${formatBytes(disk.available_space)}</td>
                    <td>${formatBytes(disk.total_space)}</td>
                    <td>
                        <div class="progress-bar" style="width: 100px;">
                            <div class="progress-fill ${disk.usage_percent > 80 ? 'high' : disk.usage_percent > 60 ? 'medium' : ''}"
                                 style="width: ${disk.usage_percent}%"></div>
                        </div>
                        ${disk.usage_percent.toFixed(1)}%
                    </td>
                `;
                tbody.appendChild(row);
            });
        }

        // Refresh network info
        async function refreshNetwork() {
            try {
                const networks = await invoke('get_network_info');
                displayNetworks(networks);
            } catch (error) {
                console.error('Failed to get network info:', error);
            }
        }

        // Display network information
        function displayNetworks(networks) {
            const tbody = document.getElementById('networkTableBody');
            tbody.innerHTML = '';

            networks.forEach(network => {
                const row = document.createElement('tr');
                row.innerHTML = `
                    <td>${network.interface}</td>
                    <td>${formatBytes(network.data_received)}</td>
                    <td>${formatBytes(network.data_transmitted)}</td>
                    <td>${network.packets_received.toLocaleString()}</td>
                    <td>${network.packets_transmitted.toLocaleString()}</td>
                    <td>${network.errors_on_received}</td>
                    <td>${network.errors_on_transmitted}</td>
                `;
                tbody.appendChild(row);
            });
        }

        // Start auto-update
        function startAutoUpdate() {
            updateInterval = setInterval(async () => {
                if (currentPanel === 'overview') {
                    await refreshSystemInfo();
                }
            }, 2000);
        }

        // Listen for system updates from backend
        if (window.__TAURI__) {
            window.__TAURI__.event.listen('system-update', async () => {
                if (currentPanel === 'overview') {
                    await refreshSystemInfo();
                }
            });
        }

        // Initialize when DOM is loaded
        document.addEventListener('DOMContentLoaded', init);
    </script>
</body>
</html>

Ferramentas

Ferramentas frequentemente usadas com este exemplo

Relacionado

Coleções de exemplos relacionadas

Guia de uso da ferramenta

Contexto para entender esta coleção de exemplos

Título

Exemplos Tauri

Descrição

Exemplos de desenvolvimento de aplicativos de desktop leves usando o framework Tauri com backend Rust e frontend web

Visão geral

Exemplos de desenvolvimento de aplicativos de desktop leves usando o framework Tauri com backend Rust e frontend web

Quando usar

  • Use this sample set when you want reference inputs for Exemplos Tauri.
  • Use it to preview output formats and compare related sample variants.

Como funciona

  1. 1Open a sample entry to inspect the prepared example payload or asset.
  2. 2Compare it with related tools to understand how the workflow connects.

Casos de uso

  • Aplicação Tauri Hello World
  • Aplicação Gerenciador de Arquivos Tauri
  • Aplicação Monitor de Sistema Tauri

Perguntas frequentes

What is included in Exemplos Tauri?

This page includes curated sample entries, related tools, and neighboring sample collections for Exemplos Tauri.

Início
Ferramentas3197
Exemplos593
Visualizações461
Visualizadores de arquivos103
Temas229
Etiquetas333
Imagens, áudio e vídeo655

Ferramentas de processamento, conversão e otimização de imagens, áudio e vídeo

Matemática, datas e finanças644

Calculadoras, ferramentas numéricas, lógica de datas, estatística e finanças

Design e cor296

Ferramentas de cor, layout, gráficos, estilo visual e apoio ao design

Texto e escrita209

Ferramentas de escrita, limpeza, formatação, extração e análise de texto

Desenvolvimento e Web193

Utilitários para desenvolvedores, rede, depuração web e automação

Conversão e codificação175

Ferramentas de conversão de formatos, arquivos, unidades, arquivos compactados e codificação

Estruturar uma captura de interface, revisar e entregar ao desenvolvimento
Esboço de piano, revisão MIDI e entrega de uma demonstração de áudio
Revisão de texturas retrô e entrega de recursos
Revisão e entrega de guias de procedimento com capturas
Revisão visual de alterações responsivas e entrega de evidências

© 2026 Elysia Tools. Todos os direitos reservados.

DocumentaçãoPreçosSobreTermos de serviçoPolítica de privacidadeCookiesContato

Development

Referência de Tags HTML

Guia de referência completo para todas as tags HTML5 padrão com categorias e descrições

Development

JSON para Rust Struct

Converte dados JSON em structs Rust com derives serde Serialize/Deserialize, campos snake_case e atributos serde rename

Rust

Exemplos de Recursos de Desktop Web Rust

Exemplos de recursos de desktop Web Rust incluindo diálogos de arquivo, caixas de mensagem e integração do sistema