# Guia de Implementação: System Tray, Background Mode e Task Scheduler ## Status da Implementação ### ✅ Concluído 1. **Estrutura de Tasks** (`src-tauri/src/scheduler.rs`) - `SentinelTask` com suporte a múltiplos tipos de ações - Persistência em `tasks.json` - Gerenciamento de estado thread-safe 2. **Cliente Ollama Headless** (`src-tauri/src/ollama_client.rs`) - Comunicação direta com Ollama via `reqwest` - Suporte a streaming - Verificação de conexão 3. **Executor de Tasks** (`src-tauri/src/task_executor.rs`) - Execução de `SearchAndSummarize` - Execução de `JustPing` - Execução de `CustomPrompt` - Integração com file locks para escrita segura 4. **Scheduler Loop** (`src-tauri/src/scheduler_loop.rs`) - Integração com `tokio-cron-scheduler` - Recarregamento dinâmico de tasks - Execução assíncrona em background ### 🔨 Pendente (Próximos Passos) #### 1. Comandos Tauri para CRUD de Tasks Adicione ao `src-tauri/src/lib.rs`: ```rust use scheduler::{SchedulerService, SchedulerState, SentinelTask, TaskAction}; use chrono::Utc; use uuid::Uuid; #[command] async fn create_task( scheduler: State<'_, SchedulerState>, label: String, cron_schedule: String, action: TaskAction, ) -> Result { let task = SentinelTask { id: Uuid::new_v4().to_string(), label, cron_schedule, action, enabled: true, last_run: None, created_at: Utc::now(), updated_at: Utc::now(), }; let mut sched = scheduler.lock().await; sched.upsert_task(task.clone())?; Ok(task.id) } #[command] async fn list_tasks( scheduler: State<'_, SchedulerState>, ) -> Result, String> { let sched = scheduler.lock().await; Ok(sched.list_tasks()) } #[command] async fn update_task( scheduler: State<'_, SchedulerState>, task: SentinelTask, ) -> Result<(), String> { let mut sched = scheduler.lock().await; let mut updated = task; updated.updated_at = Utc::now(); sched.upsert_task(updated) } #[command] async fn delete_task( scheduler: State<'_, SchedulerState>, id: String, ) -> Result<(), String> { let mut sched = scheduler.lock().await; sched.remove_task(&id) } #[command] async fn toggle_task( scheduler: State<'_, SchedulerState>, id: String, enabled: bool, ) -> Result<(), String> { let mut sched = scheduler.lock().await; if let Some(mut task) = sched.get_task(&id).cloned() { task.enabled = enabled; task.updated_at = Utc::now(); sched.upsert_task(task) } else { Err("Task not found".to_string()) } } ``` #### 2. System Tray Setup No `src-tauri/src/lib.rs`, modifique o `.setup()`: ```rust use tauri::{Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem}; .setup(|app| { // ... código existente ... // Criar System Tray let tray_menu = SystemTrayMenu::new() .add_item(SystemTrayMenuItem::with_id("show", "Mostrar OllaHub", true, None)) .add_item(SystemTrayMenuItem::with_id("pause", "Pausar Tasks", true, None)) .add_native_item(SystemTrayMenuItem::Separator) .add_item(SystemTrayMenuItem::with_id("quit", "Sair", true, None)); let system_tray = SystemTray::new().with_menu(tray_menu); app.handle().system_tray(system_tray)?; // Handler de eventos do Tray app.handle().on_system_tray_event(|app, event| { if let SystemTrayEvent::MenuItemClick { id, .. } = event { match id.as_str() { "show" => { if let Some(window) = app.get_window("main") { window.show().unwrap(); window.set_focus().unwrap(); } } "pause" => { // Implementar lógica de pausar tasks log::info!("Pausar tasks - implementar"); } "quit" => { app.exit(0); } _ => {} } } }); // Modificar comportamento de fechar janela if let Some(window) = app.get_window("main") { window.on_window_event(|event| { if let tauri::WindowEvent::CloseRequested { api, .. } = event { // Ocultar ao invés de fechar if let Some(window) = event.window() { window.hide().unwrap(); } api.prevent_close(); } }); } Ok(()) }) ``` #### 3. Inicializar Scheduler no Setup No mesmo `.setup()`, após criar o scheduler: ```rust // Inicializar scheduler let scheduler_service = SchedulerService::new(app.handle().clone())?; let scheduler_state: SchedulerState = Arc::new(Mutex::new(scheduler_service)); let browser_state: BrowserState = app.state(); // Iniciar loop do scheduler em background let app_handle = app.handle().clone(); let scheduler_clone = scheduler_state.clone(); let browser_clone = browser_state.clone(); tokio::spawn(async move { if let Err(e) = scheduler_loop::start_scheduler_loop( app_handle, scheduler_clone, browser_clone, None, // Ollama URL - pode vir do settings ).await { log::error!("Erro ao iniciar scheduler: {}", e); } }); // Adicionar ao manage .manage(scheduler_state) ``` #### 4. Adicionar Comandos ao Invoke Handler ```rust .invoke_handler(tauri::generate_handler![ // ... comandos existentes ... create_task, list_tasks, update_task, delete_task, toggle_task, ]) ``` #### 5. Interface React (app/tasks/page.tsx) Crie uma página básica para gerenciar tasks: ```tsx 'use client'; import { useState, useEffect } from 'react'; import { invoke } from '@tauri-apps/api/core'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; interface Task { id: string; label: string; cron_schedule: string; action: any; enabled: boolean; last_run: string | null; } export default function TasksPage() { const [tasks, setTasks] = useState([]); useEffect(() => { loadTasks(); }, []); const loadTasks = async () => { try { const loaded = await invoke('list_tasks'); setTasks(loaded); } catch (error) { console.error('Erro ao carregar tasks:', error); } }; const toggleTask = async (id: string, enabled: boolean) => { try { await invoke('toggle_task', { id, enabled: !enabled }); await loadTasks(); } catch (error) { console.error('Erro ao alternar task:', error); } }; return (

Tarefas Agendadas

{tasks.map(task => ( {task.label}

Cron: {task.cron_schedule}

Status: {task.enabled ? 'Ativa' : 'Pausada'}

))}
); } ``` ## Dependências Adicionais Necessárias No `Cargo.toml`, já foram adicionadas: - `tokio-cron-scheduler = "0.9"` - `futures-util = "0.3"` - `tauri-plugin-notification = "2"` **Nota para Linux:** Para System Tray funcionar, pode ser necessário: ```bash # Ubuntu/Debian sudo apt-get install libappindicator3-1 # Arch/Manjaro sudo pacman -S libappindicator-gtk3 ``` ## Próximos Passos 1. Compilar e testar: `cd src-tauri && cargo build` 2. Corrigir erros de compilação 3. Testar criação de task via frontend 4. Verificar se scheduler executa corretamente 5. Testar System Tray em diferentes plataformas ## Notas Importantes - **File Locking**: Já implementado - tasks usam o mesmo sistema de locks que salvar sessões - **Concorrência**: Scheduler roda em thread separada, não bloqueia UI - **Persistência**: Tasks são salvas em `app_data_dir/tasks.json` - **Notificações**: Usa `tauri-plugin-notification` para notificações nativas