Origem: D:/Documents/Github/OllaHub Arquivos omitidos (segredos, bancos de dados, logs, >50 MB): 0
This commit is contained in:
1 parent
a7cbd1a3b4
commit
861af0bb9f
217 files changed
+39421
-1340
No files matched your search
@@ -49,14 +49,12 @@ export function useAppUpdater(): AppUpdaterState {
|
||||
|
||||
// Verificar atualizações
|
||||
const checkUpdate = useCallback(async () => {
|
||||
<<<<<<< HEAD
|
||||
// Ignorar verificação de updates em ambiente de desenvolvimento
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return;
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> 593efd42e091a845dea82ee6646e027bce1e18c5
|
||||
|
||||
setIsChecking(true);
|
||||
setError(null);
|
||||
|
||||
@@ -98,7 +96,7 @@ export function useAppUpdater(): AppUpdaterState {
|
||||
// Baixar e instalar atualização
|
||||
// O progresso pode ser monitorado via eventos do Tauri se necessário
|
||||
await update.downloadAndInstall();
|
||||
|
||||
|
||||
// Após downloadAndInstall(), o app será reiniciado automaticamente
|
||||
// Não é necessário chamar installAndRestart() que não existe na API
|
||||
}
|
||||
@@ -112,14 +110,12 @@ export function useAppUpdater(): AppUpdaterState {
|
||||
|
||||
// Verificação automática periódica (a cada 6 horas)
|
||||
useEffect(() => {
|
||||
<<<<<<< HEAD
|
||||
// Ignorar verificação automática em desenvolvimento
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return;
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> 593efd42e091a845dea82ee6646e027bce1e18c5
|
||||
|
||||
if (!autoCheckEnabled) return;
|
||||
|
||||
// Verificar imediatamente ao montar (com delay para não interferir no carregamento)
|
||||
|
||||
+49
-49
@@ -4,7 +4,7 @@ import { listen } from '@tauri-apps/api/event';
|
||||
import { removeMetadataNoise } from '@/lib/metadata';
|
||||
import { useSettingsStore } from '@/store/settings-store';
|
||||
|
||||
export type ThinkingStepType =
|
||||
export type ThinkingStepType =
|
||||
| 'preprocessing'
|
||||
| 'web-research'
|
||||
| 'sources-found'
|
||||
@@ -27,7 +27,7 @@ export interface ThinkingMessageMetadata {
|
||||
timestamp: number;
|
||||
duration?: number;
|
||||
activeQueries?: Array<{ query: string; source: string; round?: number; startedAt: number }>;
|
||||
activeUrls?: Array<{ url: string; title?: string; status: string; source?: string; startedAt: number; duration?: number }>;
|
||||
activeUrls?: Array<{ url: string; title?: string; status: 'started' | 'completed' | 'failed' | 'cached'; source?: string; startedAt: number; duration?: number }>;
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
@@ -46,15 +46,15 @@ function parseMetadata(fullContent: string): { content: string, metadata?: any }
|
||||
if (!fullContent || fullContent.trim() === '') {
|
||||
return { content: fullContent || '' };
|
||||
}
|
||||
|
||||
|
||||
// More flexible regex that handles whitespace and line breaks (case-insensitive)
|
||||
const metadataRegex = /<metadata>\s*([\s\S]*?)\s*<\/metadata>/gi;
|
||||
|
||||
|
||||
// First, extract all metadata blocks
|
||||
const metadataMatches: Array<{ match: string, json: any }> = [];
|
||||
let match;
|
||||
let lastIndex = 0;
|
||||
|
||||
|
||||
while ((match = metadataRegex.exec(fullContent)) !== null) {
|
||||
const metadataStr = match[1].trim();
|
||||
try {
|
||||
@@ -66,11 +66,11 @@ function parseMetadata(fullContent: string): { content: string, metadata?: any }
|
||||
}
|
||||
lastIndex = metadataRegex.lastIndex;
|
||||
}
|
||||
|
||||
|
||||
// Remove ALL metadata blocks from content
|
||||
let content = fullContent.replace(/<metadata>\s*[\s\S]*?\s*<\/metadata>/gi, '').trim();
|
||||
content = removeMetadataNoise(content);
|
||||
|
||||
|
||||
// Also remove common prefixes if they appear at the end of the content
|
||||
const prefixes = [
|
||||
'Bloco oculto de metadados:',
|
||||
@@ -80,30 +80,30 @@ function parseMetadata(fullContent: string): { content: string, metadata?: any }
|
||||
'JSON metadata:',
|
||||
'---'
|
||||
];
|
||||
|
||||
|
||||
for (const prefix of prefixes) {
|
||||
const regex = new RegExp(`${prefix}\\s*$`, 'i');
|
||||
if (regex.test(content)) {
|
||||
content = content.replace(regex, '').trim();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Ensure we don't return empty content if there was content before metadata
|
||||
if (!content && fullContent.length > 0) {
|
||||
// If all content was metadata, keep nothing (shouldn't happen but safe fallback)
|
||||
content = '';
|
||||
}
|
||||
|
||||
|
||||
// Return first valid metadata (if any)
|
||||
const metadata = metadataMatches.length > 0 ? metadataMatches[0].json : undefined;
|
||||
|
||||
|
||||
return { content, metadata };
|
||||
}
|
||||
|
||||
// Helper to strip metadata in real-time during streaming (for display)
|
||||
function stripMetadataTag(content: string): string {
|
||||
if (!content) return '';
|
||||
|
||||
|
||||
// Common prefixes that models might output before the metadata block
|
||||
const prefixes = [
|
||||
'Bloco oculto de metadados:',
|
||||
@@ -113,13 +113,13 @@ function stripMetadataTag(content: string): string {
|
||||
'JSON metadata:',
|
||||
'---'
|
||||
];
|
||||
|
||||
|
||||
let cleaned = content;
|
||||
|
||||
|
||||
// Remove any metadata tags (complete or incomplete) for real-time display
|
||||
// Case-insensitive regex to catch variations
|
||||
cleaned = cleaned.replace(/<metadata>[\s\S]*?$/gi, '').replace(/<metadata>[\s\S]*?<\/metadata>/gi, '').trim();
|
||||
|
||||
|
||||
// Aggressively strip known prefixes if they appear at the very end of the content
|
||||
// This helps hide "Bloco oculto de metadados:" while the tag is being generated
|
||||
for (const prefix of prefixes) {
|
||||
@@ -128,18 +128,18 @@ function stripMetadataTag(content: string): string {
|
||||
cleaned = cleaned.replace(regex, '').trim();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return removeMetadataNoise(cleaned);
|
||||
}
|
||||
|
||||
// Helper to detect tool calls in LLM response
|
||||
export function detectToolCalls(content: string, availableTools: any[]): ToolCall[] {
|
||||
const toolCalls: ToolCall[] = [];
|
||||
|
||||
|
||||
// Simple pattern matching for tool calls
|
||||
// Pattern: "use tool X with args Y" or "execute X(args)"
|
||||
// This is a basic implementation - can be enhanced with better NLP
|
||||
|
||||
|
||||
// Look for common patterns
|
||||
const patterns = [
|
||||
// Pattern 1: "use [tool_name] with [args]" or "use [tool_name] (args)"
|
||||
@@ -147,13 +147,13 @@ export function detectToolCalls(content: string, availableTools: any[]): ToolCal
|
||||
// Pattern 2: "[tool_name](args)"
|
||||
/([a-z_]+)\s*\(([^)]+)\)/gi,
|
||||
];
|
||||
|
||||
|
||||
for (const pattern of patterns) {
|
||||
let match;
|
||||
while ((match = pattern.exec(content)) !== null) {
|
||||
const potentialToolName = match[1].toLowerCase().trim();
|
||||
const potentialArgs = match[2]?.trim() || '{}';
|
||||
|
||||
|
||||
// Find matching tool in available tools
|
||||
for (const toolInfo of availableTools) {
|
||||
if (toolInfo.tool.name.toLowerCase() === potentialToolName) {
|
||||
@@ -179,7 +179,7 @@ export function detectToolCalls(content: string, availableTools: any[]): ToolCal
|
||||
args = {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
toolCalls.push({
|
||||
server_name: toolInfo.server_name,
|
||||
tool_name: toolInfo.tool.name,
|
||||
@@ -190,7 +190,7 @@ export function detectToolCalls(content: string, availableTools: any[]): ToolCal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return toolCalls;
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ export async function executeToolCall(toolCall: ToolCall): Promise<string> {
|
||||
toolName: toolCall.tool_name,
|
||||
arguments: toolCall.arguments,
|
||||
});
|
||||
|
||||
|
||||
// Format result for display
|
||||
if (typeof result === 'string') {
|
||||
return result;
|
||||
@@ -244,7 +244,7 @@ export function useChat() {
|
||||
const lastTokenRef = useRef<string>(''); // Track last token to detect duplicates
|
||||
const renderScheduledRef = useRef<boolean>(false);
|
||||
const rafIdRef = useRef<number | null>(null);
|
||||
|
||||
|
||||
// Função para atualizar o estado visual com o conteúdo acumulado
|
||||
const flushToState = () => {
|
||||
renderScheduledRef.current = false;
|
||||
@@ -265,13 +265,13 @@ export function useChat() {
|
||||
rafIdRef.current = requestAnimationFrame(flushToState);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Listener para eventos do Rust
|
||||
useEffect(() => {
|
||||
let unlistenCreated: (() => void) | null = null;
|
||||
let unlistenToken: (() => void) | null = null;
|
||||
let unlistenError: (() => void) | null = null;
|
||||
|
||||
|
||||
// Listener para chat-created (título gerado)
|
||||
listen<ChatCreatedEvent>('chat-created', (event) => {
|
||||
const { session_id, title, emoji } = event.payload;
|
||||
@@ -281,11 +281,11 @@ export function useChat() {
|
||||
}).then(unlisten => {
|
||||
unlistenCreated = unlisten;
|
||||
});
|
||||
|
||||
|
||||
// Listener para chat-token (streaming de tokens)
|
||||
listen<ChatTokenEvent>('chat-token', (event) => {
|
||||
const { content, done } = event.payload;
|
||||
|
||||
|
||||
if (content && content.length > 0) {
|
||||
// Detecção de duplicatas (MANTER LÓGICA ORIGINAL)
|
||||
if (content === lastTokenRef.current && rawContentRef.current.endsWith(content)) {
|
||||
@@ -294,14 +294,14 @@ export function useChat() {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
lastTokenRef.current = content;
|
||||
rawContentRef.current += content;
|
||||
|
||||
|
||||
// MUDANÇA: Agendar flush em vez de chamar setMessages diretamente
|
||||
scheduleFlush();
|
||||
}
|
||||
|
||||
|
||||
if (done) {
|
||||
// Cancelar qualquer flush pendente
|
||||
if (rafIdRef.current !== null) {
|
||||
@@ -309,7 +309,7 @@ export function useChat() {
|
||||
rafIdRef.current = null;
|
||||
renderScheduledRef.current = false;
|
||||
}
|
||||
|
||||
|
||||
// Flush IMEDIATO com processamento de metadata
|
||||
setMessages(prev => {
|
||||
const last = prev[prev.length - 1];
|
||||
@@ -327,7 +327,7 @@ export function useChat() {
|
||||
}).then(unlisten => {
|
||||
unlistenToken = unlisten;
|
||||
});
|
||||
|
||||
|
||||
// Listener para chat-error
|
||||
listen<ChatErrorEvent>('chat-error', (event) => {
|
||||
const { error } = event.payload;
|
||||
@@ -337,7 +337,7 @@ export function useChat() {
|
||||
}).then(unlisten => {
|
||||
unlistenError = unlisten;
|
||||
});
|
||||
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
unlistenCreated?.();
|
||||
@@ -362,14 +362,14 @@ export function useChat() {
|
||||
}
|
||||
) => {
|
||||
setIsLoading(true);
|
||||
|
||||
|
||||
// Verificar se a última mensagem já é do usuário (para evitar duplicação)
|
||||
// Se for, usar as mensagens existentes; caso contrário, adicionar nova mensagem do usuário
|
||||
const lastMessage = messages[messages.length - 1];
|
||||
const newMessages: Message[] = lastMessage?.role === 'user' && lastMessage?.content === content
|
||||
? [...messages] // Já existe, não adicionar novamente
|
||||
: [...messages, { role: 'user', content }]; // Adicionar nova mensagem do usuário
|
||||
|
||||
|
||||
setMessages(newMessages);
|
||||
|
||||
// Prepare messages for API (include system prompt if present)
|
||||
@@ -385,40 +385,40 @@ export function useChat() {
|
||||
const settings = useSettingsStore.getState();
|
||||
const maxTokens = settings.contextWindow || 4096;
|
||||
const maxMessages = 10; // Máximo de mensagens a manter
|
||||
|
||||
|
||||
// Estimar tokens (aproximação: 1 token ≈ 4 caracteres)
|
||||
function estimateTokens(text: string): number {
|
||||
return Math.ceil(text.length / 4);
|
||||
}
|
||||
|
||||
|
||||
// Aplicar janela deslizante: manter últimas N mensagens que cabem no limite de tokens
|
||||
let truncatedMessages = [...newMessages];
|
||||
let totalTokens = systemPrompt ? estimateTokens(systemPrompt) : 0;
|
||||
|
||||
|
||||
// Sempre manter a última mensagem do usuário
|
||||
const lastUserMessage = truncatedMessages[truncatedMessages.length - 1];
|
||||
truncatedMessages = truncatedMessages.slice(0, -1);
|
||||
|
||||
|
||||
// Adicionar mensagens de trás para frente até atingir o limite
|
||||
const selectedMessages: Message[] = [];
|
||||
for (let i = truncatedMessages.length - 1; i >= 0; i--) {
|
||||
const msg = truncatedMessages[i];
|
||||
const msgTokens = estimateTokens(msg.content);
|
||||
|
||||
|
||||
// Limitar por número de mensagens E por tokens
|
||||
if (selectedMessages.length >= maxMessages - 1) break; // -1 porque vamos adicionar a última depois
|
||||
if (totalTokens + msgTokens > maxTokens * 0.9) break; // 90% do limite para margem de segurança
|
||||
|
||||
|
||||
selectedMessages.unshift(msg);
|
||||
totalTokens += msgTokens;
|
||||
}
|
||||
|
||||
|
||||
// Adicionar a última mensagem do usuário
|
||||
selectedMessages.push(lastUserMessage);
|
||||
totalTokens += estimateTokens(lastUserMessage.content);
|
||||
|
||||
// Constrói mensagens para payload, aplicando override apenas à última mensagem do usuário
|
||||
const baseMessages = systemPrompt
|
||||
const baseMessages = systemPrompt
|
||||
? [{ role: 'system', content: systemPrompt }, ...selectedMessages]
|
||||
: selectedMessages;
|
||||
|
||||
@@ -458,7 +458,7 @@ export function useChat() {
|
||||
// Reset raw content buffer for this message
|
||||
rawContentRef.current = '';
|
||||
lastTokenRef.current = ''; // Reset last token tracker
|
||||
|
||||
|
||||
// Add empty assistant message to start streaming into
|
||||
setMessages(prev => {
|
||||
const last = prev[prev.length - 1];
|
||||
@@ -493,14 +493,14 @@ export function useChat() {
|
||||
systemPrompt: systemPrompt || null,
|
||||
enableRag: false, // TODO: Implementar RAG depois
|
||||
});
|
||||
|
||||
|
||||
// O streaming será processado pelos listeners de eventos
|
||||
// Não precisamos fazer mais nada aqui, os eventos cuidam do resto
|
||||
} catch (error: any) {
|
||||
console.error('Chat error:', error);
|
||||
|
||||
|
||||
let errorMessage = 'Erro ao obter resposta do Ollama.';
|
||||
|
||||
|
||||
if (error.name === 'AbortError' || error.message === 'RequestTimeout') {
|
||||
errorMessage = '**Timeout:** A requisição demorou muito para responder. Tente novamente ou use um modelo menor.';
|
||||
} else if (error.message === 'OllamaNotRunning') {
|
||||
@@ -522,7 +522,7 @@ export function useChat() {
|
||||
const errorDetails = error.message || error.toString();
|
||||
errorMessage = `**Erro:** Falha ao obter resposta do Ollama.\n\nDetalhes: ${errorDetails}`;
|
||||
}
|
||||
|
||||
|
||||
setMessages(prev => [...prev, { role: 'assistant', content: errorMessage }]);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
||||
@@ -50,7 +50,7 @@ export function useOllamaDownload() {
|
||||
const statusText = data.status || '';
|
||||
const isVerifying = statusText.includes('Verificando');
|
||||
const isCompleted = data.progress === 100 && !isVerifying;
|
||||
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
downloadProgress: data.progress || 0,
|
||||
@@ -69,7 +69,7 @@ export function useOllamaDownload() {
|
||||
const data = event.payload;
|
||||
const status = data.status || 'unknown';
|
||||
const message = data.message || 'Processando...';
|
||||
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
isInstalling: status !== 'success' && status !== 'error' && status !== 'partial',
|
||||
@@ -123,7 +123,6 @@ export function useOllamaDownload() {
|
||||
isInstalled: false,
|
||||
isChecking: false,
|
||||
checkStatus: '',
|
||||
<<<<<<< HEAD
|
||||
installStatus: null,
|
||||
isInstalling: false,
|
||||
speed: '0 MB/s',
|
||||
@@ -131,8 +130,6 @@ export function useOllamaDownload() {
|
||||
totalBytes: 0,
|
||||
eta: 0,
|
||||
status: 'downloading',
|
||||
=======
|
||||
>>>>>>> 593efd42e091a845dea82ee6646e027bce1e18c5
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -148,7 +145,6 @@ export function useOllamaDownload() {
|
||||
isInstalled: false,
|
||||
isChecking: false,
|
||||
checkStatus: '',
|
||||
<<<<<<< HEAD
|
||||
installStatus: null,
|
||||
isInstalling: false,
|
||||
speed: '0 MB/s',
|
||||
@@ -156,8 +152,6 @@ export function useOllamaDownload() {
|
||||
totalBytes: 0,
|
||||
eta: 0,
|
||||
status: 'completed',
|
||||
=======
|
||||
>>>>>>> 593efd42e091a845dea82ee6646e027bce1e18c5
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -173,7 +167,6 @@ export function useOllamaDownload() {
|
||||
isInstalled: false,
|
||||
isChecking: false,
|
||||
checkStatus: '',
|
||||
<<<<<<< HEAD
|
||||
installStatus: null,
|
||||
isInstalling: false,
|
||||
speed: '0 MB/s',
|
||||
@@ -181,8 +174,6 @@ export function useOllamaDownload() {
|
||||
totalBytes: 0,
|
||||
eta: 0,
|
||||
status: 'completed',
|
||||
=======
|
||||
>>>>>>> 593efd42e091a845dea82ee6646e027bce1e18c5
|
||||
});
|
||||
} catch (error) {
|
||||
setState({
|
||||
@@ -194,7 +185,6 @@ export function useOllamaDownload() {
|
||||
isInstalled: false,
|
||||
isChecking: false,
|
||||
checkStatus: '',
|
||||
<<<<<<< HEAD
|
||||
installStatus: null,
|
||||
isInstalling: false,
|
||||
speed: '0 MB/s',
|
||||
@@ -202,8 +192,6 @@ export function useOllamaDownload() {
|
||||
totalBytes: 0,
|
||||
eta: 0,
|
||||
status: 'downloading',
|
||||
=======
|
||||
>>>>>>> 593efd42e091a845dea82ee6646e027bce1e18c5
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -217,7 +205,6 @@ export function useOllamaDownload() {
|
||||
return;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Atualizar estado para mostrar que a instalação está começando
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
@@ -234,29 +221,12 @@ export function useOllamaDownload() {
|
||||
startAutoCheck();
|
||||
}, 2000);
|
||||
})
|
||||
=======
|
||||
// Optimistic UI: atualizar estado imediatamente antes da chamada Tauri
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
isInstalled: true, // Marcar como instalado imediatamente
|
||||
}));
|
||||
|
||||
// Iniciar verificação automática em segundo plano imediatamente
|
||||
startAutoCheck();
|
||||
|
||||
// Chamar Tauri em background (não bloquear)
|
||||
runInstaller(state.filePath)
|
||||
>>>>>>> 593efd42e091a845dea82ee6646e027bce1e18c5
|
||||
.catch((error) => {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
downloadError: error instanceof Error ? error.message : 'Erro ao executar instalador',
|
||||
<<<<<<< HEAD
|
||||
isInstalling: false,
|
||||
installStatus: null,
|
||||
=======
|
||||
isInstalled: false, // Reverter se falhar
|
||||
>>>>>>> 593efd42e091a845dea82ee6646e027bce1e18c5
|
||||
}));
|
||||
});
|
||||
};
|
||||
@@ -305,9 +275,9 @@ export function useOllamaDownload() {
|
||||
|
||||
checkingIntervalRef.current = setInterval(async () => {
|
||||
attempts++;
|
||||
|
||||
|
||||
const isRunning = await checkOllama();
|
||||
|
||||
|
||||
if (isRunning) {
|
||||
// Ollama encontrado e rodando!
|
||||
if (checkingIntervalRef.current) {
|
||||
@@ -315,7 +285,7 @@ export function useOllamaDownload() {
|
||||
checkingIntervalRef.current = null;
|
||||
}
|
||||
isCheckingRef.current = false;
|
||||
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
isChecking: false,
|
||||
@@ -331,7 +301,7 @@ export function useOllamaDownload() {
|
||||
checkingIntervalRef.current = null;
|
||||
}
|
||||
isCheckingRef.current = false;
|
||||
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
isChecking: false,
|
||||
|
||||
@@ -42,8 +42,7 @@ export function useSetupCheck() {
|
||||
await invoke('auto_start_ollama');
|
||||
// Aguardar um pouco para o servidor iniciar
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
<<<<<<< HEAD
|
||||
|
||||
// Verificar integridade completa (instalação + serviço rodando)
|
||||
try {
|
||||
const isInstalledAndRunning = await invoke<boolean>('verify_ollama_integrity_command');
|
||||
@@ -59,15 +58,8 @@ export function useSetupCheck() {
|
||||
return 'needs_setup';
|
||||
}
|
||||
}
|
||||
|
||||
=======
|
||||
// Verificar novamente se está rodando
|
||||
const running = await invoke<boolean>('check_ollama_running');
|
||||
if (!running) {
|
||||
// Ainda não está rodando, precisa de intervenção do usuário
|
||||
return 'needs_setup';
|
||||
}
|
||||
>>>>>>> 593efd42e091a845dea82ee6646e027bce1e18c5
|
||||
|
||||
|
||||
// Agora está rodando, verificar modelos
|
||||
const hasAnyModels = await checkModels();
|
||||
return hasAnyModels ? 'ready' : 'needs_setup';
|
||||
@@ -97,7 +89,7 @@ export function useSetupCheck() {
|
||||
|
||||
const status = await determineSetupStatus(ollamaStatus);
|
||||
setSetupStatus(status);
|
||||
|
||||
|
||||
if (status === 'ready') {
|
||||
setCheckingStep('Tudo pronto!');
|
||||
} else if (status === 'needs_setup') {
|
||||
|
||||
@@ -34,10 +34,6 @@ export function useSystemHealth() {
|
||||
title: 'Modo de Economia Ativado',
|
||||
description: 'Recursos limitados para manter estabilidade. Algumas funcionalidades podem estar temporariamente indisponíveis.',
|
||||
variant: 'destructive',
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
duration: 5000,
|
||||
>>>>>>> 593efd42e091a845dea82ee6646e027bce1e18c5
|
||||
});
|
||||
setHasShownCriticalToast(true);
|
||||
} else if (newHealth.status !== 'critical') {
|
||||
|
||||
Reference in new issue
Block a user