'use client'; import { useState, useEffect, useRef } from 'react'; import { invoke } from '@tauri-apps/api/core'; import { Button } from '@/components/ui/button'; import { Progress } from '@/components/ui/progress'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, CardFooter } from '@/components/ui/card'; import { useSetupWizard } from '@/hooks/use-setup-wizard'; import { useOllamaDownload } from '@/hooks/use-ollama-download'; import { useOllamaCheck } from '@/hooks/use-ollama-check'; import { useOperatingSystem } from '@/hooks/use-operating-system'; import { InstallationTerminal } from './installation-terminal'; import { Loader2, CheckCircle2, AlertCircle, Download, RefreshCw, Shield, Clock, WifiOff, ArrowRight } from 'lucide-react'; function formatBytes(bytes: number): string { 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(2))} ${sizes[i]}`; } function formatTime(seconds: number): string { if (seconds < 60) return `${Math.round(seconds)}s`; const minutes = Math.floor(seconds / 60); const secs = Math.round(seconds % 60); return `${minutes}m ${secs}s`; } type InstallErrorType = 'ERR_NO_PRIVILEGE' | 'ERR_DOWNLOAD_FAIL' | 'ERR_TIMEOUT' | 'ERR_INSTALL_FAILED'; function parseInstallError(error: string): InstallErrorType { const errorLower = error.toLowerCase(); // Verificar privilégios/permissões if ( errorLower.includes('privilege') || errorLower.includes('permission') || errorLower.includes('elevated') || errorLower.includes('uac') || errorLower.includes('administrator') || errorLower.includes('access denied') || errorLower.includes('acesso negado') ) { return 'ERR_NO_PRIVILEGE'; } // Verificar erros de download/rede if ( errorLower.includes('download') || errorLower.includes('network') || errorLower.includes('connection') || errorLower.includes('failed to download') || errorLower.includes('falha ao baixar') || errorLower.includes('conexão') || errorLower.includes('timeout') && (errorLower.includes('download') || errorLower.includes('conexão')) ) { return 'ERR_DOWNLOAD_FAIL'; } // Verificar timeout if ( errorLower.includes('timeout') || errorLower.includes('90 segundos') || errorLower.includes('timed out') || errorLower.includes('tempo esgotado') ) { return 'ERR_TIMEOUT'; } return 'ERR_INSTALL_FAILED'; } interface ErrorDisplayProps { errorType: InstallErrorType; errorMessage: string; onRetry: () => void; isRetrying: boolean; } function ErrorDisplay({ errorType, errorMessage, onRetry, isRetrying }: ErrorDisplayProps) { switch (errorType) { case 'ERR_NO_PRIVILEGE': return (
Permissões Administrativas Necessárias O Windows solicitou permissões de administrador para instalar o Ollama.

O que fazer:

  1. Verifique se apareceu um prompt do Windows pedindo permissões
  2. Clique em "Sim" ou "Allow" no prompt de UAC (User Account Control)
  3. Aguarde alguns segundos para a instalação continuar
  4. Se o prompt não apareceu, tente executar o OllaHub como administrador

{errorMessage}

); case 'ERR_DOWNLOAD_FAIL': return (
Erro ao Baixar Instalador Não foi possível baixar o instalador do Ollama. Verifique sua conexão com a internet.

{errorMessage}

); case 'ERR_TIMEOUT': return (
Instalação Demorando Mais do Que o Esperado A instalação pode ainda estar em andamento em segundo plano.

O processo de instalação pode levar alguns minutos. Se o Ollama estiver sendo instalado, você pode verificar o status manualmente.

{errorMessage}

); default: return (

Erro na instalação

{errorMessage}

); } } type InstallStepState = 'IDLE' | 'INSTALLING' | 'VERIFYING' | 'SUCCESS'; interface OllamaInstallStepProps { onNext: () => void; } export function OllamaInstallStep({ onNext }: OllamaInstallStepProps) { const { state, installOllama, checkChocolatey, installChocolatey, installOllamaViaChoco, checkSetupState } = useSetupWizard(); const { os: detectedOS } = useOperatingSystem(); const downloadState = useOllamaDownload(); const ollamaCheck = useOllamaCheck(); const [stepState, setStepState] = useState('IDLE'); const [useChocolatey, setUseChocolatey] = useState(false); const [showTerminal, setShowTerminal] = useState(false); const [isRetrying, setIsRetrying] = useState(false); const installationInProgressRef = useRef(false); const hasCheckedInitialRef = useRef(false); // Função de polling ativo para aguardar serviço ficar disponível const waitForService = async (maxAttempts = 15): Promise => { for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { const isIntact = await invoke('verify_ollama_integrity_command'); if (isIntact) { return true; } } catch (error) { console.warn(`Tentativa ${attempt}/${maxAttempts} falhou:`, error); } if (attempt < maxAttempts) { await new Promise(resolve => setTimeout(resolve, 2000)); } } throw new Error('Ollama não ficou disponível após 30 segundos'); }; // Verificação inicial no mount: se Ollama já estiver instalado, ir direto para SUCCESS useEffect(() => { if (hasCheckedInitialRef.current) return; const checkInitial = async () => { if (stepState === 'IDLE') { try { const isIntact = await invoke('verify_ollama_integrity_command'); if (isIntact) { // Se a integridade está OK, atualizar estado do wizard e ir para SUCCESS await checkSetupState(); setStepState('SUCCESS'); } } catch { // Silenciosamente ignorar - usuário ainda não instalou } } hasCheckedInitialRef.current = true; }; checkInitial(); }, [stepState, checkSetupState]); // Sincronizar estado do polling com wizard state e atualizar FSM useEffect(() => { if (ollamaCheck.status === 'running' && !state.ollamaRunning) { // Polling detectou que Ollama está rodando, atualizar wizard state checkSetupState().catch(console.error); } // Atualizar FSM baseado no status if (stepState === 'VERIFYING' && ollamaCheck.status === 'running' && state.ollamaInstalled && state.ollamaRunning) { setStepState('SUCCESS'); ollamaCheck.stopPolling(); } }, [ollamaCheck.status, state.ollamaRunning, state.ollamaInstalled, checkSetupState, stepState, ollamaCheck]); const handleManualRetry = async () => { setIsRetrying(true); try { await checkSetupState(); // Se já estiver instalado, ir para SUCCESS if (state.ollamaInstalled && state.ollamaRunning) { setStepState('SUCCESS'); } else { // Iniciar verificação manual setStepState('VERIFYING'); ollamaCheck.startPolling(); } } catch (error) { console.error('Erro ao verificar novamente:', error); } finally { setIsRetrying(false); } }; const handleCheckExisting = async () => { setStepState('VERIFYING'); ollamaCheck.startPolling(); await checkSetupState(); }; const handleInstall = async () => { if (installationInProgressRef.current) { return; // Evitar múltiplas instalações simultâneas } installationInProgressRef.current = true; setStepState('INSTALLING'); setShowTerminal(true); try { // No Windows, verificar se Chocolatey está disponível if (detectedOS === 'windows') { const hasChoco = await checkChocolatey(); if (hasChoco) { setUseChocolatey(true); // Instalar Ollama via Chocolatey await installOllamaViaChoco(); // Após instalação, fazer polling ativo setStepState('VERIFYING'); await waitForService(15); // 15 tentativas = 30 segundos // Se chegou aqui, Ollama está funcional setStepState('SUCCESS'); await checkSetupState(); // Atualizar estado do wizard installationInProgressRef.current = false; return; } else { // Tentar instalar Chocolatey primeiro try { await installChocolatey(); setUseChocolatey(true); await installOllamaViaChoco(); // Após instalação, fazer polling ativo setStepState('VERIFYING'); await waitForService(15); // 15 tentativas = 30 segundos // Se chegou aqui, Ollama está funcional setStepState('SUCCESS'); await checkSetupState(); // Atualizar estado do wizard installationInProgressRef.current = false; return; } catch (error) { console.warn('Falha ao usar Chocolatey, usando instalação direta:', error); setUseChocolatey(false); setShowTerminal(false); } } } // Fallback: instalação direta if (!downloadState.filePath && detectedOS) { await downloadState.handleDownload(detectedOS); } if (downloadState.filePath) { await installOllama(downloadState.filePath); } else { await installOllama(); } // Após instalação, fazer polling ativo setStepState('VERIFYING'); await waitForService(15); // 15 tentativas = 30 segundos // Se chegou aqui, Ollama está funcional setStepState('SUCCESS'); await checkSetupState(); // Atualizar estado do wizard } catch (error) { console.error('Erro ao aguardar serviço:', error); setStepState('IDLE'); } finally { installationInProgressRef.current = false; } }; // Estado SUCCESS: mostrar mensagem de sucesso e botão Continuar if (stepState === 'SUCCESS' || (state.ollamaInstalled && state.ollamaRunning)) { return ( Instalação Concluída Ollama está instalado e rodando com sucesso.

Ollama Service is Running

Pronto para continuar com o download de modelos.

); } // Estado IDLE: mostrar card informativo if (stepState === 'IDLE') { return ( Ollama Engine O OllaHub requer o motor Ollama para executar IAs localmente.

O Ollama é uma ferramenta open-source que permite executar modelos de linguagem grandes (LLMs) diretamente no seu computador, sem necessidade de conexão com a internet.

); } const errorType = state.lastError ? parseInstallError(state.lastError) : null; const showRetryButton = state.lastError || state.progressStatus === 'error'; const showManualRetry = showRetryButton && state.lastError; const hasTerminal = showTerminal && (state.progressStatus === 'installing_chocolatey' || state.progressStatus === 'installing_ollama' || useChocolatey); return ( {stepState === 'INSTALLING' && } {stepState === 'VERIFYING' && } {(state.lastError || state.progressStatus === 'error') && } {stepState === 'INSTALLING' ? 'Instalando Ollama...' : stepState === 'VERIFYING' ? 'Verificando Instalação...' : 'Instalação em Progresso'} Acompanhe o progresso da configuração do ambiente. {/* Terminal integrado para instalação via Chocolatey */} {(showTerminal && (state.progressStatus === 'installing_chocolatey' || state.progressStatus === 'installing_ollama' || useChocolatey)) && ( { setShowTerminal(false); }} onError={(error) => { console.error('Erro na instalação:', error); }} /> )} {/* Progresso para instalação direta (fallback) */} {state.progressMessage && !showTerminal && (
{state.progressMessage} {ollamaCheck.isChecking && ( Verificando... {formatTime(ollamaCheck.elapsedTime)} )}
{state.progressStatus === 'installing_ollama' && ( )}
)} {/* Feedback do polling em background (estado VERIFYING) */} {stepState === 'VERIFYING' && (
Instalação concluída. Aguardando inicialização do serviço...
)} {/* Display de erro granular */} {state.lastError && errorType && ( )} {/* Botão de retry manual quando não há erro específico */} {showRetryButton && !showManualRetry && ( )} {downloadState.isDownloading && (
{downloadState.status === 'verifying' ? ( ) : ( )} {downloadState.status === 'verifying' ? 'Verificando integridade...' : 'Baixando...'} {downloadState.speed || '0 MB/s'}
{formatBytes(downloadState.downloadedBytes)} / {formatBytes(downloadState.totalBytes)} {downloadState.downloadProgress.toFixed(1)}%
{downloadState.eta > 0 && downloadState.status === 'downloading' && (
Tempo restante: {formatTime(downloadState.eta)}
)}
)} {/* Estado INSTALLING: mostrar progresso */} {stepState === 'INSTALLING' && (
Instalando Ollama... Isso pode levar alguns minutos.
)}
{/* Footer opcional se necessário */}
); }