""" Custom themes and styling for the Gradio interface. """ import gradio as gr from typing import Dict, Any def get_custom_css() -> str: """Get custom CSS styling for the interface.""" return """ /* Global styles */ .gradio-container { font-family: 'Inter', sans-serif; max-width: 1400px !important; margin: 0 auto; } /* Header styling */ .header-container { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 2rem; border-radius: 12px; margin-bottom: 2rem; text-align: center; } .header-title { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5rem; } .header-description { font-size: 1.1rem; opacity: 0.9; max-width: 600px; margin: 0 auto; } /* Tab styling */ .tab-nav button { font-weight: 500; font-size: 1rem; padding: 12px 24px; border-radius: 8px; transition: all 0.2s ease; } .tab-nav button[aria-selected="true"] { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; } /* Upload area styling */ .upload-area { border: 2px dashed #e0e7ff; border-radius: 12px; padding: 2rem; text-align: center; background: #f8faff; transition: all 0.3s ease; } .upload-area:hover { border-color: #667eea; background: #f0f4ff; } /* Search interface styling */ .search-container { background: white; border-radius: 12px; padding: 1.5rem; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 1.5rem; } .search-input { font-size: 1.1rem; padding: 1rem; border-radius: 8px; border: 2px solid #e5e7eb; transition: border-color 0.2s ease; } .search-input:focus { border-color: #667eea; outline: none; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); } .search-button { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 1rem 2rem; border-radius: 8px; font-weight: 600; font-size: 1rem; cursor: pointer; transition: all 0.2s ease; } .search-button:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); } /* Process Documents button styling */ .process-button { background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important; color: white !important; border: none !important; padding: 1rem 2rem !important; border-radius: 12px !important; font-weight: 700 !important; font-size: 1.1rem !important; cursor: pointer !important; transition: all 0.3s ease !important; box-shadow: 0 4px 14px rgba(16, 185, 129, 0.3) !important; min-height: 60px !important; width: 100% !important; } .process-button:hover { transform: translateY(-2px) !important; box-shadow: 0 6px 20px rgba(16, 185, 129, 0.4) !important; background: linear-gradient(135deg, #059669 0%, #047857 100%) !important; } .process-button:disabled { background: #d1d5db !important; color: #9ca3af !important; cursor: not-allowed !important; transform: none !important; box-shadow: none !important; } /* Results styling */ .result-card { background: white; border: 1px solid #e5e7eb; border-radius: 12px; padding: 1.5rem; margin-bottom: 1rem; transition: all 0.2s ease; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); } .result-card:hover { border-color: #667eea; box-shadow: 0 4px 12px rgba(102, 126, 234, 0.1); } .result-title { font-size: 1.1rem; font-weight: 600; color: #374151; margin-bottom: 0.5rem; } .result-content { color: #6b7280; line-height: 1.6; margin-bottom: 1rem; } .result-metadata { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.5rem; } .metadata-tag { background: #f3f4f6; color: #374151; padding: 0.25rem 0.5rem; border-radius: 6px; font-size: 0.875rem; font-weight: 500; } .score-badge { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 0.25rem 0.75rem; border-radius: 20px; font-size: 0.875rem; font-weight: 600; } /* Progress bar styling */ .progress-container { background: #f3f4f6; border-radius: 8px; overflow: hidden; height: 12px; margin: 1rem 0; } .progress-bar { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); height: 100%; transition: width 0.3s ease; } /* Statistics cards */ .stat-card { background: white; border-radius: 12px; padding: 1.5rem; text-align: center; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); border: 1px solid #e5e7eb; } .stat-number { font-size: 2rem; font-weight: 700; color: #667eea; margin-bottom: 0.5rem; } .stat-label { color: #6b7280; font-weight: 500; } /* Analytics charts */ .chart-container { background: white; border-radius: 12px; padding: 1.5rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); border: 1px solid #e5e7eb; margin-bottom: 1rem; } /* Settings panel */ .settings-panel { background: #f8faff; border-radius: 12px; padding: 1.5rem; border: 1px solid #e0e7ff; } .settings-group { margin-bottom: 1.5rem; } .settings-label { font-weight: 600; color: #374151; margin-bottom: 0.5rem; display: block; } /* Status indicators */ .status-indicator { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.75rem 1.25rem; border-radius: 25px; font-size: 0.9rem; font-weight: 600; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); transition: all 0.2s ease; } .status-ready { background: #10b981; color: #ffffff; font-weight: 600; border: 1px solid #059669; } .status-processing { background: #f59e0b; color: #ffffff; font-weight: 600; border: 1px solid #d97706; } .status-error { background: #ef4444; color: #ffffff; font-weight: 600; border: 1px solid #dc2626; } /* Responsive design */ @media (max-width: 768px) { .gradio-container { padding: 1rem; } .header-title { font-size: 2rem; } .header-description { font-size: 1rem; } .search-container, .result-card, .stat-card, .chart-container { padding: 1rem; } .result-metadata { flex-direction: column; } } /* Animation classes */ .fade-in { animation: fadeIn 0.3s ease-in; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .pulse { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .5; } } /* Loading spinner */ .loading-spinner { display: inline-block; width: 20px; height: 20px; border: 3px solid #f3f4f6; border-radius: 50%; border-top-color: #667eea; animation: spin 1s ease-in-out infinite; } @keyframes spin { to { transform: rotate(360deg); } } /* Scrollbar styling */ ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-track { background: #f1f5f9; border-radius: 4px; } ::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: #94a3b8; } """ def get_theme() -> gr.Theme: """Get the custom Gradio theme.""" theme = gr.themes.Soft( primary_hue=gr.themes.Color( c50="#f0f4ff", c100="#e0e7ff", c200="#c7d2fe", c300="#a5b4fc", c400="#8b5cf6", c500="#667eea", c600="#5b21b6", c700="#4c1d95", c800="#3730a3", c900="#312e81", c950="#1e1b4b" ), secondary_hue=gr.themes.Color( c50="#f8fafc", c100="#f1f5f9", c200="#e2e8f0", c300="#cbd5e1", c400="#94a3b8", c500="#64748b", c600="#475569", c700="#334155", c800="#1e293b", c900="#0f172a", c950="#020617" ), neutral_hue=gr.themes.Color( c50="#f9fafb", c100="#f3f4f6", c200="#e5e7eb", c300="#d1d5db", c400="#9ca3af", c500="#6b7280", c600="#4b5563", c700="#374151", c800="#1f2937", c900="#111827", c950="#030712" ), font=[ gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif" ], font_mono=[ gr.themes.GoogleFont("JetBrains Mono"), "ui-monospace", "Consolas", "monospace" ] ) # Customize component styles (using only supported parameters) try: theme.set( button_primary_background_fill="#667eea", button_primary_background_fill_hover="#5a67d8", button_primary_text_color="white", button_secondary_background_fill="#f8fafc", button_secondary_text_color="#374151", input_background_fill="#ffffff", input_border_color="#e5e7eb", block_background_fill="#ffffff", block_border_color="#e5e7eb", panel_background_fill="#ffffff" ) except Exception as e: # Fallback if theme customization fails print(f"Theme customization failed: {e}") pass return theme def create_info_card(title: str, value: str, description: str = "") -> str: """Create an info card HTML.""" return f"""