""" 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")