teste / src /ui /theme_manager.py
torxyton's picture
feat: Implementar botão de alternância de tema e tema escuro padrão
349675c
"""Gerenciador de temas para a interface do usuário."""
from typing import Dict, Any
from enum import Enum
class ThemeType(Enum):
"""Tipos de tema disponíveis."""
LIGHT = "light"
DARK = "dark"
class ThemeManager:
"""Gerenciador de temas da aplicação."""
def __init__(self, default_theme: ThemeType = ThemeType.DARK):
"""Inicializa o gerenciador de temas.
Args:
default_theme: Tema padrão a ser usado
"""
self.current_theme = default_theme
self._themes = {
ThemeType.LIGHT: self._get_light_theme(),
ThemeType.DARK: self._get_dark_theme()
}
def get_current_theme(self) -> Dict[str, Any]:
"""Retorna o tema atual.
Returns:
Dicionário com as configurações do tema atual
"""
return self._themes[self.current_theme]
def set_theme(self, theme: ThemeType) -> None:
"""Define o tema atual.
Args:
theme: Tipo de tema a ser definido
"""
self.current_theme = theme
def toggle_theme(self) -> ThemeType:
"""Alterna entre os temas disponíveis.
Returns:
O novo tema ativo
"""
if self.current_theme == ThemeType.LIGHT:
self.current_theme = ThemeType.DARK
else:
self.current_theme = ThemeType.LIGHT
return self.current_theme
def _get_light_theme(self) -> Dict[str, Any]:
"""Configurações do tema claro.
Returns:
Dicionário com as configurações do tema claro
"""
return {
'name': 'light',
'colors': {
'primary': '#667eea',
'secondary': '#764ba2',
'background': '#ffffff',
'surface': '#f8f9fa',
'card_background': '#ffffff',
'text_primary': '#495057',
'text_secondary': '#6c757d',
'text_muted': '#adb5bd',
'border': '#dee2e6',
'success': '#28a745',
'success_background': '#d4edda',
'success_border': '#c3e6cb',
'success_text': '#155724',
'warning': '#ffc107',
'warning_background': '#fff3cd',
'warning_border': '#ffeaa7',
'warning_text': '#856404',
'error': '#dc3545',
'info': '#007bff',
'gradient_start': '#667eea',
'gradient_end': '#764ba2',
'shadow': 'rgba(0,0,0,0.1)',
'shadow_hover': 'rgba(0,0,0,0.2)'
},
'css_variables': {
'--bg-primary': '#ffffff',
'--bg-secondary': '#f8f9fa',
'--bg': '#ffffff',
'--surface': '#f8f9fa',
'--text-primary': '#495057',
'--text-secondary': '#6c757d',
'--border-color': '#dee2e6',
'--shadow': '0 2px 4px rgba(0,0,0,0.1)',
'--gradient': 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
}
}
def _get_dark_theme(self) -> Dict[str, Any]:
"""Configurações do tema escuro.
Returns:
Dicionário com as configurações do tema escuro
"""
return {
'name': 'dark',
'colors': {
'primary': '#4f46e5',
'secondary': '#7c3aed',
'background': '#0f172a',
'surface': '#1e293b',
'card_background': '#334155',
'text_primary': '#f1f5f9',
'text_secondary': '#cbd5e1',
'text_muted': '#94a3b8',
'border': '#475569',
'success': '#10b981',
'success_background': '#064e3b',
'success_border': '#065f46',
'success_text': '#34d399',
'warning': '#f59e0b',
'warning_background': '#451a03',
'warning_border': '#92400e',
'warning_text': '#fbbf24',
'error': '#ef4444',
'info': '#3b82f6',
'gradient_start': '#4f46e5',
'gradient_end': '#7c3aed',
'shadow': 'rgba(0,0,0,0.3)',
'shadow_hover': 'rgba(0,0,0,0.5)'
},
'css_variables': {
'--bg-primary': '#0f172a',
'--bg-secondary': '#1e293b',
'--bg': '#0f172a',
'--surface': '#1e293b',
'--text-primary': '#f1f5f9',
'--text-secondary': '#cbd5e1',
'--border-color': '#475569',
'--shadow': '0 4px 8px rgba(0,0,0,0.3)',
'--gradient': 'linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%)'
}
}
def get_css_variables(self) -> str:
"""Retorna as variáveis CSS do tema atual.
Returns:
String com as variáveis CSS formatadas
"""
theme = self.get_current_theme()
variables = []
for var_name, var_value in theme['css_variables'].items():
variables.append(f" {var_name}: {var_value};")
return ":root {\n" + "\n".join(variables) + "\n}"
def get_custom_css(self) -> str:
"""Retorna o CSS customizado para o tema atual.
Returns:
String com o CSS customizado completo
"""
theme = self.get_current_theme()
colors = theme['colors']
return f"""
{self.get_css_variables()}
/* Tema {theme['name'].title()} */
body {{
background-color: {colors['background']} !important;
color: {colors['text_primary']} !important;
}}
body[data-theme="{theme['name']}"] {{
background-color: {colors['background']} !important;
color: {colors['text_primary']} !important;
}}
.gradio-container {{
max-width: 1200px !important;
margin: auto !important;
background-color: {colors['background']} !important;
color: {colors['text_primary']} !important;
}}
/* Botões */
.gr-button {{
transition: all 0.3s ease !important;
background: {colors['gradient_start']} !important;
border: none !important;
color: white !important;
border-radius: 8px !important;
font-weight: 600 !important;
}}
.gr-button:hover {{
transform: translateY(-2px) !important;
box-shadow: 0 8px 16px {colors['shadow_hover']} !important;
background: {colors['gradient_end']} !important;
}}
/* Inputs e TextAreas */
.gr-textbox, .gr-number, .gr-dropdown {{
background-color: {colors['surface']} !important;
border: 1px solid {colors['border']} !important;
color: {colors['text_primary']} !important;
border-radius: 8px !important;
}}
.gr-textbox textarea, .gr-textbox input {{
font-family: 'Courier New', monospace !important;
background-color: {colors['surface']} !important;
color: {colors['text_primary']} !important;
border: none !important;
}}
/* Tabs */
.gr-tab-nav {{
background: {colors['surface']} !important;
border-bottom: 1px solid {colors['border']} !important;
}}
.gr-tab-nav button {{
border-radius: 8px 8px 0 0 !important;
background: {colors['card_background']} !important;
color: {colors['text_primary']} !important;
border: 1px solid {colors['border']} !important;
border-bottom: none !important;
}}
.gr-tab-nav button.selected {{
background: {colors['primary']} !important;
color: white !important;
}}
/* Cards e Containers */
.gr-panel, .gr-box {{
background-color: {colors['card_background']} !important;
border: 1px solid {colors['border']} !important;
border-radius: 12px !important;
box-shadow: {colors['shadow']} !important;
}}
/* HTML Components */
.gr-html {{
background-color: transparent !important;
}}
/* JSON Viewer */
.gr-json {{
background-color: {colors['surface']} !important;
border: 1px solid {colors['border']} !important;
border-radius: 8px !important;
color: {colors['text_primary']} !important;
}}
/* Scrollbars */
::-webkit-scrollbar {{
width: 8px;
height: 8px;
}}
::-webkit-scrollbar-track {{
background: {colors['surface']};
border-radius: 4px;
}}
::-webkit-scrollbar-thumb {{
background: {colors['border']};
border-radius: 4px;
}}
::-webkit-scrollbar-thumb:hover {{
background: {colors['text_muted']};
}}
/* Animações */
* {{
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease !important;
}}
/* Loading Spinner */
.loading {{
border: 3px solid {colors['border']};
border-top: 3px solid {colors['primary']};
border-radius: 50%;
animation: spin 1s linear infinite;
}}
@keyframes spin {{
0% {{ transform: rotate(0deg); }}
100% {{ transform: rotate(360deg); }}
}}
"""
# Instância global do gerenciador de temas
theme_manager = ThemeManager(ThemeType.DARK)