rag_template / ui /theme.py
Guilherme Favaron
Sync: Complete project update (Phase 6) - API, Metadata, Eval, Docs
a686b1b
"""
Sistema de temas e personalizacao da UI.
Suporta:
- Dark mode
- Custom CSS
- Keyboard shortcuts
"""
import gradio as gr
# CSS Customizado
LIGHT_MODE_CSS = """
/* Light Mode */
:root {
--primary-color: #1f77b4;
--secondary-color: #ff7f0e;
--background-color: #ffffff;
--surface-color: #f5f5f5;
--text-color: #333333;
--border-color: #e0e0e0;
}
.gradio-container {
background-color: var(--background-color);
color: var(--text-color);
}
.tab-nav {
background-color: var(--surface-color);
border-bottom: 2px solid var(--primary-color);
}
.primary-btn {
background-color: var(--primary-color) !important;
color: white !important;
border: none !important;
}
.secondary-btn {
background-color: var(--secondary-color) !important;
color: white !important;
border: none !important;
}
.code-block {
background-color: #f8f8f8;
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 10px;
font-family: monospace;
}
"""
DARK_MODE_CSS = """
/* Dark Mode */
:root {
--primary-color: #4a9eff;
--secondary-color: #ffa94d;
--background-color: #1a1a1a;
--surface-color: #2d2d2d;
--text-color: #e0e0e0;
--border-color: #404040;
}
body {
background-color: var(--background-color) !important;
color: var(--text-color) !important;
}
.gradio-container {
background-color: var(--background-color) !important;
color: var(--text-color) !important;
}
.tab-nav {
background-color: var(--surface-color) !important;
border-bottom: 2px solid var(--primary-color) !important;
}
.tab-nav button {
color: var(--text-color) !important;
}
.tab-nav button.selected {
background-color: var(--primary-color) !important;
color: white !important;
}
input, textarea, select {
background-color: var(--surface-color) !important;
color: var(--text-color) !important;
border: 1px solid var(--border-color) !important;
}
.markdown-text {
color: var(--text-color) !important;
}
.primary-btn {
background-color: var(--primary-color) !important;
color: white !important;
border: none !important;
}
.secondary-btn {
background-color: var(--secondary-color) !important;
color: #1a1a1a !important;
border: none !important;
}
.code-block {
background-color: #1e1e1e !important;
border: 1px solid var(--border-color) !important;
border-radius: 4px;
padding: 10px;
font-family: monospace;
color: #d4d4d4 !important;
}
.warning {
background-color: #332200 !important;
border-left: 4px solid var(--secondary-color) !important;
padding: 10px;
margin: 10px 0;
}
.info {
background-color: #002233 !important;
border-left: 4px solid var(--primary-color) !important;
padding: 10px;
margin: 10px 0;
}
/* Scrollbars dark mode */
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-track {
background: var(--surface-color);
}
::-webkit-scrollbar-thumb {
background: var(--border-color);
border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--primary-color);
}
"""
# JavaScript para keyboard shortcuts
KEYBOARD_SHORTCUTS_JS = """
function setupKeyboardShortcuts() {
document.addEventListener('keydown', function(e) {
// Ctrl/Cmd + K - Focus search
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
const searchInput = document.querySelector('input[placeholder*="query"], input[placeholder*="pergunta"]');
if (searchInput) {
searchInput.focus();
}
}
// Ctrl/Cmd + Enter - Submit form
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
const submitBtn = document.querySelector('button.primary-btn');
if (submitBtn) {
submitBtn.click();
}
}
// Ctrl/Cmd + / - Show shortcuts help
if ((e.ctrlKey || e.metaKey) && e.key === '/') {
e.preventDefault();
alert(
'Atalhos de Teclado:\\n\\n' +
'Ctrl/Cmd + K - Focar na busca\\n' +
'Ctrl/Cmd + Enter - Enviar formulario\\n' +
'Ctrl/Cmd + D - Alternar dark mode\\n' +
'Ctrl/Cmd + / - Mostrar ajuda de atalhos\\n' +
'Esc - Limpar entrada'
);
}
// Ctrl/Cmd + D - Toggle dark mode
if ((e.ctrlKey || e.metaKey) && e.key === 'd') {
e.preventDefault();
const darkModeToggle = document.querySelector('#dark-mode-toggle');
if (darkModeToggle) {
darkModeToggle.click();
}
}
// Esc - Clear input
if (e.key === 'Escape') {
const activeElement = document.activeElement;
if (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA') {
activeElement.value = '';
}
}
});
}
// Run on load
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', setupKeyboardShortcuts);
} else {
setupKeyboardShortcuts();
}
"""
def get_theme(dark_mode: bool = False) -> str:
"""
Retorna CSS do tema.
Args:
dark_mode: Se True, retorna dark mode
Returns:
String CSS
"""
base_css = DARK_MODE_CSS if dark_mode else LIGHT_MODE_CSS
return base_css + "\n" + KEYBOARD_SHORTCUTS_JS
def create_theme_toggle() -> tuple:
"""
Cria toggle de dark mode.
Returns:
Tuple (checkbox, css_component)
"""
dark_mode_toggle = gr.Checkbox(
label="Dark Mode",
value=False,
elem_id="dark-mode-toggle"
)
return dark_mode_toggle
def apply_theme(dark_mode: bool) -> str:
"""
Aplica tema baseado no dark_mode.
Args:
dark_mode: Se True, aplica dark mode
Returns:
CSS string
"""
return get_theme(dark_mode=dark_mode)
# Funcoes de exportacao
def create_export_buttons() -> tuple:
"""
Cria botoes de exportacao.
Returns:
Tuple de botoes (json, csv, md, pdf)
"""
with gr.Row():
export_json_btn = gr.Button("Exportar JSON", size="sm")
export_csv_btn = gr.Button("Exportar CSV", size="sm")
export_md_btn = gr.Button("Exportar Markdown", size="sm")
export_pdf_btn = gr.Button("Exportar PDF", size="sm")
return export_json_btn, export_csv_btn, export_md_btn, export_pdf_btn
def create_keyboard_shortcuts_help() -> gr.Markdown:
"""
Cria componente de ajuda de atalhos.
Returns:
Componente Markdown
"""
shortcuts_text = """
### Atalhos de Teclado
| Atalho | Acao |
|--------|------|
| `Ctrl/Cmd + K` | Focar na busca |
| `Ctrl/Cmd + Enter` | Enviar formulario |
| `Ctrl/Cmd + D` | Alternar dark mode |
| `Ctrl/Cmd + /` | Mostrar ajuda |
| `Esc` | Limpar entrada |
"""
return gr.Markdown(shortcuts_text, visible=False, elem_id="shortcuts-help")