#!/usr/bin/env python3 """ DCI-Agent Pi Search – Prestige Purple edition Gradio 6.x · dark-first · follows prestige-purple-DESIGN.md """ from __future__ import annotations import html as _html import json import os import re import shutil import tempfile import time import zipfile from datetime import datetime from pathlib import Path from typing import Any, Dict, Generator, List, Optional import gradio as gr try: from charset_normalizer import from_bytes except ImportError: from_bytes = None try: from pypdf import PdfReader except ImportError: PdfReader = None from pi_wrapper import run_pi_stream # ── Paths ───────────────────────────────────────────────────────────────── APP_DIR = Path(__file__).resolve().parent DEFAULT_CORPUS_DIR = APP_DIR / "default_corpus" DEFAULT_CORPUS_LABEL = "Default (bright_corpus)" UPLOAD_FILE_LABEL = "Upload your own corpus(single file)" UPLOAD_FOLDER_LABEL = "Upload your own corpus(folder)" HERO_LOGO_PATH = APP_DIR / "img" / "logo.png" TEXT_FILE_SUFFIXES = { ".txt", ".md", ".csv", ".tsv", ".json", ".jsonl", ".xml", ".html", ".htm", ".yaml", ".yml", ".log", ".srt", ".py", ".js", ".ts", ".tsx", ".jsx", ".java", ".c", ".cc", ".cpp", ".h", ".hpp", ".sql", ".rst", } # ────────────────────────────────────────────────────────────────────────── # Minimal Slate Gradio theme # Sets Gradio's own CSS variables so every component inherits dark colors. # ────────────────────────────────────────────────────────────────────────── PP_THEME = gr.themes.Base( primary_hue=gr.themes.colors.slate, secondary_hue=gr.themes.colors.slate, neutral_hue=gr.themes.colors.slate, font=[gr.themes.GoogleFont("Plus Jakarta Sans"), "ui-sans-serif", "sans-serif"], font_mono=[gr.themes.GoogleFont("JetBrains Mono"), "ui-monospace", "monospace"], ).set( # ── Light mode backgrounds ───────────────────────────────────────── body_background_fill="#F5F7FA", background_fill_primary="#FFFFFF", background_fill_secondary="#F0F4F8", block_background_fill="#FFFFFF", block_border_color="#E5EBF0", block_border_width="1px", block_label_background_fill="#FFFFFF", block_label_text_color="#64748B", block_label_text_size="*text_xs", block_title_text_color="#64748B", block_title_background_fill="transparent", block_shadow="0 2px 8px rgba(15,23,42,0.06)", input_background_fill="#FFFFFF", input_border_color="#CBD5E1", input_border_color_focus="#475569", input_placeholder_color="#94A3B8", input_shadow="none", input_shadow_focus="0 0 0 3px rgba(71,84,105,0.1)", body_text_color="#0F172A", body_text_color_subdued="#64748B", button_primary_background_fill="linear-gradient(135deg,#334155 0%,#475569 100%)", button_primary_background_fill_hover="linear-gradient(135deg,#1E293B 0%,#334155 100%)", button_primary_text_color="#FFFFFF", button_primary_border_color="transparent", button_secondary_background_fill="transparent", button_secondary_background_fill_hover="rgba(71,84,105,0.06)", button_secondary_text_color="#334155", button_secondary_border_color="#475569", checkbox_background_color="#FFFFFF", checkbox_background_color_selected="#334155", checkbox_border_color="#CBD5E1", checkbox_border_color_focus="#475569", checkbox_label_background_fill="#F0F4F8", checkbox_label_background_fill_selected="#334155", checkbox_label_text_color="#1E293B", checkbox_label_text_color_selected="#FFFFFF", slider_color="#475569", border_color_primary="#E5EBF0", border_color_accent="#475569", color_accent="#334155", color_accent_soft="rgba(51,65,85,0.08)", panel_background_fill="#F5F3FF", panel_border_color="#DDD6FE", error_background_fill="#FFF1F2", error_border_color="#F87171", error_text_color="#DC2626", button_large_padding="10px 24px", button_large_radius="10px", form_gap_width="8px", layout_gap="12px", # ── Dark mode overrides ──────────────────────────────────────────── body_background_fill_dark="#0F172A", background_fill_primary_dark="#1E293B", background_fill_secondary_dark="#334155", block_background_fill_dark="#1E293B", block_border_color_dark="#475569", block_label_background_fill_dark="#1E293B", block_label_text_color_dark="#CBD5E1", block_title_text_color_dark="#CBD5E1", block_title_background_fill_dark="transparent", block_shadow_dark="0 2px 12px rgba(0,0,0,0.45)", input_background_fill_dark="#1E293B", input_border_color_dark="#475569", input_border_color_focus_dark="#E2E8F0", input_placeholder_color_dark="#94A3B8", input_shadow_focus_dark="0 0 0 3px rgba(226,232,240,0.1)", body_text_color_dark="#E2E8F0", body_text_color_subdued_dark="#CBD5E1", button_primary_background_fill_dark="linear-gradient(135deg,#334155 0%,#475569 100%)", button_primary_background_fill_hover_dark="linear-gradient(135deg,#1E293B 0%,#334155 100%)", button_primary_text_color_dark="#FFFFFF", button_primary_border_color_dark="transparent", button_secondary_background_fill_dark="transparent", button_secondary_background_fill_hover_dark="rgba(226,232,240,0.08)", button_secondary_text_color_dark="#E2E8F0", button_secondary_border_color_dark="#E2E8F0", checkbox_background_color_dark="#1E293B", checkbox_background_color_selected_dark="#334155", checkbox_border_color_dark="#475569", checkbox_border_color_focus_dark="#E2E8F0", checkbox_label_background_fill_dark="#334155", checkbox_label_background_fill_selected_dark="#334155", checkbox_label_text_color_dark="#CBD5E1", checkbox_label_text_color_selected_dark="#FFFFFF", slider_color_dark="#E2E8F0", border_color_primary_dark="#475569", border_color_accent_dark="#E2E8F0", color_accent_soft_dark="rgba(226,232,240,0.12)", panel_background_fill_dark="#0F172A", panel_border_color_dark="#475569", error_background_fill_dark="#160808", error_border_color_dark="#EF4444", error_text_color_dark="#FC8181", ) # ────────────────────────────────────────────────────────────────────────── # Supplementary CSS (only for things the theme system can't set) # ────────────────────────────────────────────────────────────────────────── CUSTOM_CSS = """ /* ── Hero ─────────────────────────────────────────────────────────── */ .hero-wrap { padding: 8px 0 12px; margin-bottom: 8px; } html.dark .hero-wrap { border-bottom: 1px solid #475569; } html:not(.dark) .hero-wrap { border-bottom: 1px solid #E5EBF0; } .hero-title-row { display: flex; align-items: center; gap: 14px; margin: 0 0 12px !important; } .hero-logo { width: 72.5px; height: 72.5px; object-fit: contain; flex: 0 0 72.5px; } .hero-title { font-size: 35px !important; font-weight: 800 !important; letter-spacing: -0.03em !important; line-height: 1.1 !important; background: linear-gradient(128deg, #334155 0%, #1E293B 100%) !important; -webkit-background-clip: text !important; -webkit-text-fill-color: transparent !important; background-clip: text !important; margin: 0 !important; padding-bottom: 4px !important; display: block; } .hero-subtitle { font-size: 11px !important; font-weight: 600 !important; letter-spacing: 0.1em !important; text-transform: uppercase !important; color: #64748B !important; margin: 2px 0 12px !important; display: block; } .hero-links { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; } .hero-links a { display: inline-flex; align-items: center; gap: 5px; padding: 3px 11px 4px; border-radius: 20px; font-size: 11px !important; font-weight: 600 !important; letter-spacing: 0.04em !important; text-decoration: none !important; transition: background 0.15s, border-color 0.15s, color 0.15s; } html.dark .hero-links a { background: rgba(180,175,242,0.07); border: 1px solid #2A2A3C; color: #8B8BA8 !important; } html:not(.dark) .hero-links a { background: #F0EEFF; border: 1px solid #DDD6FE; color: #7C6FA8 !important; } html.dark .hero-links a:hover { background: rgba(124,92,252,0.18); border-color: #7C5CFC; color: #C4BFFF !important; } html:not(.dark) .hero-links a:hover { background: #EDE9FE; border-color: #7C5CFC; color: #5822B4 !important; } .hero-links .sep { color: #2A2A3C; font-size: 11px; user-select: none; } /* ── Card groups (sidebar sections) ──────────────────────────────────── */ .pp-card { border-radius: 12px !important; padding: 10px 12px !important; } html.dark .pp-card { background: #1E293B !important; border: 1px solid #475569 !important; box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important; } html:not(.dark) .pp-card { background: #FFFFFF !important; border: 1px solid #E5EBF0 !important; box-shadow: 0 2px 8px rgba(51,65,85,0.07) !important; } /* Tighten Gradio's own block padding inside cards */ .pp-card .block, .pp-card .wrap { padding-top: 2px !important; padding-bottom: 2px !important; } .pp-card .gap { gap: 6px !important; } .main-panel { margin-top: 60px; } .sidebar-panel .hero-subtitle { font-size: 13px !important; } .sidebar-panel .hero-links a, .sidebar-panel .hero-links .sep { font-size: 13px !important; } .sidebar-panel label, .sidebar-panel .block > label, .sidebar-panel .wrap > label, .sidebar-panel span.svelte-1gfkn6j, .sidebar-panel .gr-label { font-size: 12.5px !important; font-weight: 800 !important; } .sidebar-panel input, .sidebar-panel textarea, .sidebar-panel select, .sidebar-panel [data-testid="radio"] span, .sidebar-panel [data-testid="checkbox-group"] span, .sidebar-panel .status-box textarea, .sidebar-panel [data-testid="file"] { font-size: 13.5px !important; } .sidebar-panel [data-testid="radio"] label, .sidebar-panel [data-testid="radio"] span { font-weight: 700 !important; } html.dark .sidebar-panel [data-testid="radio"] span { color: #111111 !important; } html.dark .sidebar-panel [data-testid="radio"] input:checked + span { color: #FFFFFF !important; } html:not(.dark) .sidebar-panel [data-testid="radio"] span { color: #111111 !important; } html:not(.dark) .sidebar-panel [data-testid="radio"] input:checked + span { color: #FFFFFF !important; } .card-hdr { display: flex; align-items: center; gap: 7px; font-size: 10px !important; font-weight: 800 !important; letter-spacing: 0.1em !important; text-transform: uppercase !important; margin-bottom: 8px !important; padding-bottom: 7px !important; } html.dark .card-hdr { color: #94A3B8 !important; border-bottom: 1px solid #475569 !important; } html:not(.dark) .card-hdr { color: #64748B !important; border-bottom: 1px solid #E5EBF0 !important; } html.dark .card-hdr svg { width:13px; height:13px; stroke:#94A3B8; fill:none; } html:not(.dark) .card-hdr svg { width:13px; height:13px; stroke:#64748B; fill:none; } /* ── Labels (override Gradio uppercase labels to match design) ────────── */ label, .block > label, .wrap > label, span.svelte-1gfkn6j, .gr-label { font-size: 10.5px !important; font-weight: 700 !important; text-transform: uppercase !important; letter-spacing: 0.07em !important; color: #8B8BA8 !important; } /* ── Slider accent ───────────────────────────────────────────────────── */ input[type=range] { accent-color: #475569 !important; } input[type=range]::-webkit-slider-thumb { background: #475569 !important; } input[type=range]::-moz-range-thumb { background: #475569 !important; } /* ── File upload (Gradio 6 targets) ─────────────────────────────────── */ .upload-container, [data-testid="file"], [data-testid="upload"], div[class*="fileupload"], div[class*="file-upload"], .file-preview, .upload-btn-wrapper { background: #141420 !important; border: 2px dashed #2A2A3C !important; border-radius: 12px !important; color: #6B6B8A !important; transition: border-color 0.2s, background 0.2s !important; } [data-testid="file"]:hover, [data-testid="upload"]:hover, div[class*="fileupload"]:hover, div[class*="file-upload"]:hover { border-color: #7C5CFC !important; background: rgba(124,92,252,0.05) !important; } /* upload icon color */ [data-testid="file"] svg, [data-testid="upload"] svg { stroke: #6B6B8A !important; } /* ── Terminal composer ───────────────────────────────────────────────── */ #terminal-shell { display: flex !important; flex-direction: column !important; gap: 8px !important; } #terminal-input-wrap { order: 1; } #terminal-actions-wrap { order: 2; } #terminal-examples-wrap { order: 3; } #terminal-log-wrap { order: 4; } #terminal-examples-wrap { width: 100% !important; min-width: 0 !important; overflow: visible !important; } #terminal-shell.has-run #terminal-log-wrap { order: 1; } #terminal-shell.has-run #terminal-input-wrap { order: 2; } #terminal-shell.has-run #terminal-actions-wrap { order: 3; } #terminal-shell.has-run #terminal-examples-wrap { order: 4; } #terminal-shell.log-hidden #terminal-log-wrap { display: none !important; } #terminal-shell.log-collapsed .terminal-wrapper { height: 74px !important; overflow: hidden !important; } #terminal-shell.log-collapsed .terminal-body { padding-top: 8px; padding-bottom: 8px; } #terminal-shell.log-floating #terminal-log-wrap { position: fixed !important; top: 18px; right: 24px; width: min(980px, calc(100vw - 48px)); height: min(560px, calc(100vh - 36px)); min-width: 520px; min-height: 280px; max-width: calc(100vw - 32px); max-height: calc(100vh - 24px); overflow: auto !important; resize: both; z-index: 9999; } #terminal-shell.log-floating .terminal-wrapper { height: 100%; box-shadow: 0 18px 60px rgba(0,0,0,0.38); } #terminal-question-input textarea { min-height: 90px !important; max-height: 180px !important; font-size: 16px !important; line-height: 1.75 !important; padding: 14px 16px !important; border-radius: 10px !important; resize: vertical !important; font-family: 'Plus Jakarta Sans', sans-serif !important; } html.dark #terminal-question-input textarea { color: #E6E6F0 !important; background: #0D1117 !important; border: 1px solid #1E2030 !important; } html:not(.dark) #terminal-question-input textarea { color: #2D1A5E !important; background: #FFFFFF !important; border: 1px solid #DDD6FE !important; } html.dark #terminal-question-input textarea:focus { border-color: #B4AFF2 !important; box-shadow: 0 0 0 3px rgba(180,175,242,0.14) !important; } html:not(.dark) #terminal-question-input textarea:focus { border-color: #7C5CFC !important; box-shadow: 0 0 0 3px rgba(124,92,252,0.15) !important; } /* ── Run / Clear / Stop buttons ──────────────────────────────────────── */ .btn-run { height: 46px !important; font-size: 14px !important; font-weight: 700 !important; letter-spacing: 0.02em !important; } .btn-clear { height: 46px !important; font-weight: 600 !important; background: rgba(124,92,252,0.12) !important; color: #6C56D9 !important; border: 1px solid rgba(180,175,242,0.28) !important; border-radius: 10px !important; } .btn-clear:hover { background: rgba(124,92,252,0.22) !important; border-color: rgba(180,175,242,0.5) !important; } .btn-stop { height: 46px !important; font-weight: 700 !important; background: rgba(239,68,68,0.13) !important; color: #F87171 !important; border: 1px solid rgba(239,68,68,0.35) !important; border-radius: 10px !important; } .btn-stop:hover { background: rgba(239,68,68,0.22) !important; border-color: rgba(239,68,68,0.55) !important; } /* ── Section labels above answer / log ──────────────────────────────── */ .sec-label { font-size: 14px !important; font-weight: 700 !important; letter-spacing: 0.04em !important; text-transform: uppercase !important; margin: 18px 0 8px !important; display: block; } html.dark .sec-label { color: #B4AFF2 !important; } html:not(.dark) .sec-label { color: #5822B4 !important; } /* ── Execution log terminal ──────────────────────────────────────────── */ .terminal-wrapper { border-radius: 12px; padding: 0; height: 525px; overflow-y: auto; font-family: 'Plus Jakarta Sans', sans-serif; font-size: 13.5px; line-height: 1.75; scroll-behavior: smooth; } .terminal-wrapper { background:#050505; border:1px solid #1A1A1A; } .terminal-chrome { position: sticky; top: 0; z-index: 2; display: flex; align-items: center; gap: 7px; height: 38px; padding: 0 14px; border-bottom: 1px solid transparent; backdrop-filter: blur(10px); cursor: default; } #terminal-shell.log-floating .terminal-chrome { cursor: move; } .terminal-controls { display: inline-flex; align-items: center; gap: 7px; line-height: 0; } .terminal-chrome { background: rgba(12,12,12,0.96); border-bottom-color: #1F1F1F; } .term-dot { width: 11px; height: 11px; border-radius: 999px; display: block; flex: 0 0 11px; border: 0; padding: 0; margin: 0; cursor: pointer; appearance: none; vertical-align: middle; } .term-dot.red { background: #FF5F57; } .term-dot.yellow { background: #FEBC2E; } .term-dot.green { background: #28C840; } .term-dot:hover { transform: scale(1.08); } .terminal-body { padding: 18px 18px 22px; } .term-stack { display: flex; flex-direction: column; gap: 12px; } .term-block { padding: 12px 14px; border-radius: 12px; border: 1px solid transparent; } .term-block { background: rgba(16,16,16,0.92); border-color: #202020; } .term-block.term-question, .term-block.term-answer { background: transparent !important; border: 0 !important; border-top: 2px solid #303030 !important; border-radius: 0 !important; padding-left: 12px !important; padding-right: 0 !important; } .term-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; } .term-icon { width: 22px; height: 22px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 800; flex: 0 0 22px; } .term-block.is-active .term-icon { animation: termPulse 0.9s ease-in-out infinite; } @keyframes termPulse { 0%, 100% { opacity: 0.5; transform: scale(0.96); } 50% { opacity: 1; transform: scale(1.08); box-shadow: 0 0 0 6px rgba(124,92,252,0.10); } } .term-label { font-size: 11px; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase; } .term-title { font-size: 14px; font-weight: 700; } .term-meta { margin-left: auto; font-size: 11px; } .term-body { word-break: break-word; overflow-wrap: anywhere; font-size: 14px; line-height: 1.75; } .term-footer { display: flex; justify-content: flex-end; margin-top: 8px; font-size: 10px; } .term-question .term-body, .term-answer .term-body { font-size: 15px; } .term-think .term-body, .term-result .term-body { font-size: 14px; line-height: 1.68; } .term-think .term-title, .term-tool .term-title, .term-result .term-title { font-size: 15px; } .term-tool .term-body { font-size: 15px; line-height: 1.7; } .term-body > *:first-child { margin-top: 0; } .term-body > *:last-child { margin-bottom: 0; } .term-body p, .term-body li, .term-body blockquote { margin: 0 0 10px; } .term-body ul, .term-body ol { margin: 0 0 10px 20px; padding: 0; } .term-body pre { margin: 8px 0 10px; padding: 12px 14px; border-radius: 10px; overflow-x: auto; } .term-body code { padding: 2px 6px; border-radius: 6px; font-size: 0.93em; } .term-body pre code { padding: 0; background: transparent !important; } .term-body blockquote { padding-left: 12px; border-left: 2px solid #7C5CFC; } .term-body a { color: #7C5CFC; text-decoration: none; } .term-body a:hover { text-decoration: underline; } .term-body pre { background: #0A0A0A; border: 1px solid #232323; } .term-body code { background: rgba(255,255,255,0.08); color: #F5F5F5; } .term-divider { height: 1px; background: linear-gradient(90deg, rgba(255,255,255,0.32), rgba(255,255,255,0.06)); margin: 2px 0 6px; } .term-muted { color: #9A9A9A; } .term-text { color: #F5F5F5; } .term-think .term-body, .term-think .term-body.term-text, .term-think .term-body *, .term-think .term-body code { color: #CFCFCF !important; } .term-tool .term-body, .term-tool .term-body.term-text, .term-tool .term-body *, .term-tool .term-body code { color: #8EC5FF !important; } .term-result .term-body, .term-result .term-body.term-text, .term-result .term-body *, .term-result .term-body code { color: #F5E6A8 !important; } .term-answer .term-body, .term-answer .term-body.term-text, .term-answer .term-body *, .term-answer .term-body code, .term-assistant .term-body, .term-assistant .term-body.term-text, .term-assistant .term-body *, .term-assistant .term-body code { color: #FFFFFF !important; } .term-info .term-icon, .term-status .term-icon { background: rgba(255,255,255,.08); color: #F5F5F5; } .term-think .term-icon { background: rgba(255,255,255,.08); color: #EAEAEA; } .term-tool .term-icon { background: rgba(255,255,255,.08); color: #EAEAEA; } .term-result .term-icon { background: rgba(255,255,255,.08); color: #EAEAEA; } .term-question .term-icon { background: rgba(124,92,252,.18); color: #F5F3FF; } .term-answer .term-icon { background: rgba(255,255,255,.08); color: #FFFFFF; } .term-error .term-icon { background: rgba(255,255,255,.08); color: #FFFFFF; } /* ── Status box ──────────────────────────────────────────────────────── */ .status-box textarea { font-family: 'JetBrains Mono', monospace !important; font-size: 11.5px !important; color: #6B6B8A !important; border-radius: 8px !important; min-height: unset !important; } /* ── Examples 2-column grid ──────────────────────────────────────────── */ .examples-section { margin-top: 28px; padding-top: 20px; border-top: 1px solid #2A2A3C; } .ex-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-top: 10px; padding-top: 4px; width: 100%; box-sizing: border-box; overflow: visible; } @media (max-width: 680px) { .ex-grid { grid-template-columns: 1fr; } } .ex-card { border-radius: 12px; padding: 14px 16px; width: 100%; min-width: 0; box-sizing: border-box; overflow: hidden; cursor: pointer; transition: border-color .18s, background .18s, transform .12s, box-shadow .18s; } html.dark .ex-card { background:#141420; border:1px solid #2A2A3C; } html:not(.dark) .ex-card { background:#FFFFFF; border:1px solid #DDD6FE; } html.dark .ex-card:hover { border-color: #7C5CFC; background: rgba(124,92,252,.06); transform: translateY(-2px); box-shadow: 0 8px 32px rgba(0,0,0,.5); } html:not(.dark) .ex-card:hover { border-color: #7C5CFC; background: #F5F3FF; transform: translateY(-2px); box-shadow: 0 8px 24px rgba(88,34,180,.12); } .ex-card:active { transform: translateY(0); } .ex-ds { display: inline-block; font-size: 9px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; padding: 2px 9px; border-radius: 4px; margin-bottom: 10px; } html.dark .ex-ds { background:rgba(88,34,180,.28); color:#B4AFF2; } html:not(.dark) .ex-ds { background:#EDE9FE; color:#5822B4; } .ex-q { font-size: 15.5px; line-height: 1.55; margin: 0 0 10px; font-family: 'Plus Jakarta Sans', sans-serif; } html.dark .ex-q { color:#DEE1F0; } html:not(.dark) .ex-q { color:#1E1B4B; } .ex-a { font-size: 11.5px; line-height: 1.5; margin: 0; padding-top: 10px; font-family: 'Plus Jakarta Sans', sans-serif; } html.dark .ex-a { color:#6B6B8A; border-top:1px solid #2A2A3C; } html:not(.dark) .ex-a { color:#7C6FA8; border-top:1px solid #EDE9FE; } .ex-a::before { content: "A · "; font-weight: 700; } html.dark .ex-a::before { color:#4E4E72; } html:not(.dark) .ex-a::before { color:#A89CC8; } .ex-g { font-size: 10.5px; line-height: 1.45; margin: 8px 0 0; word-break: break-word; overflow-wrap: anywhere; white-space: normal; font-family: 'JetBrains Mono', monospace; } html.dark .ex-g { color:#8B8BA8; } html:not(.dark) .ex-g { color:#64748B; } /* ── Scrollbar ───────────────────────────────────────────────────────── */ ::-webkit-scrollbar { width: 5px; height: 5px; } ::-webkit-scrollbar-thumb { border-radius: 3px; } html.dark ::-webkit-scrollbar-track { background: #0A0A12; } html.dark ::-webkit-scrollbar-thumb { background: #1E2030; } html.dark ::-webkit-scrollbar-thumb:hover { background: #2A2A3C; } html:not(.dark) ::-webkit-scrollbar-track { background: #F5F3FF; } html:not(.dark) ::-webkit-scrollbar-thumb { background: #DDD6FE; } html:not(.dark) ::-webkit-scrollbar-thumb:hover { background: #C4B5FD; } /* ── Dropdown popup list ─────────────────────────────────────────────── */ html.dark ul[role="listbox"], html.dark [role="option"] { background: #141420 !important; border-color: #2A2A3C !important; color: #E6E6F0 !important; } html.dark [role="option"]:hover { background: #1A1A28 !important; } html:not(.dark) ul[role="listbox"], html:not(.dark) [role="option"] { background: #FFFFFF !important; border-color: #DDD6FE !important; color: #1E1B4B !important; } html:not(.dark) [role="option"]:hover { background: #F5F3FF !important; } /* ── Nuke residual backgrounds ───────────────────────────────────────── */ .tabs, .tabitem, .tab-nav, footer { background: transparent !important; } /* ════════════════════════════════════════════════════════════════════════ DARK MODE Key insight: Gradio wraps every component in a .block div with background #141420 that shows as a "lighter box" against the #0A0A12 page background. Fix = make ALL .block transparent by default; only pp-card siblings keep an explicit card background. ════════════════════════════════════════════════════════════════════════ */ /* CSS variable layer — components that read vars get the right colour */ html.dark { --block-background-fill: transparent; --input-background-fill: #141420; --background-fill-primary: #141420; --background-fill-secondary: #1A1A28; --body-background-fill: #0A0A12; --border-color-primary: #2A2A3C; --block-border-color: transparent; --input-border-color: #2A2A3C; --body-text-color: #E6E6F0; --body-text-color-subdued: #8B8BA8; --input-placeholder-color: #6B6B8A; --shadow-drop: none; --shadow-drop-lg: none; --block-shadow: none; } /* All generic wrappers → transparent (no extra box) */ html.dark .block, html.dark .wrap, html.dark .form, html.dark .gap, html.dark .contain, html.dark .padded, html.dark .compact, html.dark fieldset, html.dark .panel, html.dark .gr-box, html.dark .gr-form, html.dark .gr-group, html.dark .gradio-group, html.dark [data-testid="html"], html.dark [data-testid="html"] > *, html.dark [data-testid="markdown"], html.dark [data-testid="markdown"] > * { background: transparent !important; border-color: transparent !important; box-shadow: none !important; } /* pp-card gets its own visible card surface */ html.dark .pp-card { background: #141420 !important; border: 1px solid #2A2A3C !important; box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important; } /* inner wrappers inside a pp-card inherit the card bg */ html.dark .pp-card .block, html.dark .pp-card .wrap, html.dark .pp-card fieldset, html.dark .pp-card .form { background: #141420 !important; border-color: #2A2A3C !important; box-shadow: none !important; } /* Actual input / textarea elements */ html.dark input, html.dark textarea, html.dark select { background: #141420 !important; color: #E6E6F0 !important; border-color: #2A2A3C !important; } html.dark input:focus, html.dark textarea:focus { border-color: #B4AFF2 !important; box-shadow: 0 0 0 3px rgba(180,175,242,0.14) !important; outline: none !important; } /* Radio / checkbox spans */ html.dark .wrap span, html.dark [data-testid="radio"] span, html.dark [data-testid="checkbox-group"] span { color: #E6E6F0 !important; } html.dark [data-testid="radio"] input + span, html.dark [data-testid="checkbox-group"] input + span { background: #141420 !important; border-color: #2A2A3C !important; } /* Labels */ html.dark .block > label > span, html.dark .wrap > label > span, html.dark label > span.text-sm { color: #8B8BA8 !important; } """ # ────────────────────────────────────────────────────────────────────────── # Global JavaScript # ────────────────────────────────────────────────────────────────────────── GLOBAL_JS = """ /* Gradio 6: js param is executed as a plain code string on page load. Do NOT wrap in an arrow function — it won't be called. */ /* fillQuestion: populate the terminal composer from an example card click. Tries multiple selectors for robustness across Gradio 6 DOM layouts. */ window.fillQuestion = function(text) { var el = ( document.querySelector('#terminal-question-input textarea') || document.querySelector('textarea[placeholder*="search question"]') || document.querySelector('textarea[placeholder*="Enter your"]') || document.querySelector('textarea[placeholder*="question here"]') ); if (!el) { console.warn('fillQuestion: textarea not found'); return; } var setter = Object.getOwnPropertyDescriptor( window.HTMLTextAreaElement.prototype, 'value' ).set; setter.call(el, text); el.dispatchEvent(new Event('input', { bubbles: true })); el.focus(); el.scrollIntoView({ behavior: 'smooth', block: 'center' }); }; window.syncTerminalShellState = function() { var shell = document.querySelector('#terminal-shell'); var terminal = document.querySelector('.terminal-wrapper'); var wrap = document.querySelector('#terminal-log-wrap'); if (!shell || !terminal) return; var text = (terminal.textContent || '').trim(); var isIdle = !text || text.indexOf('Waiting for run') !== -1; shell.classList.toggle('has-run', !isIdle); var lastState = shell.dataset.runState || 'idle'; if (!isIdle && lastState !== 'active') { shell.classList.remove('log-hidden', 'log-collapsed', 'log-floating'); if (wrap) { wrap.style.left = ''; wrap.style.top = ''; wrap.style.right = ''; wrap.style.width = ''; wrap.style.height = ''; delete wrap.dataset.floatingInit; } } shell.dataset.runState = isIdle ? 'idle' : 'active'; }; window.resetTerminalLogState = function() { var shell = document.querySelector('#terminal-shell'); var wrap = document.querySelector('#terminal-log-wrap'); if (!shell) return; shell.classList.remove('log-hidden', 'log-collapsed', 'log-floating'); if (wrap) { wrap.style.left = ''; wrap.style.top = ''; wrap.style.right = ''; wrap.style.width = ''; wrap.style.height = ''; delete wrap.dataset.floatingInit; } }; window.terminalLogControl = function(action) { var shell = document.querySelector('#terminal-shell'); var wrap = document.querySelector('#terminal-log-wrap'); if (!shell) return; if (action === 'hide') { var stopBtn = document.querySelector('.btn-stop button') || document.querySelector('.btn-stop'); if (stopBtn && typeof stopBtn.click === 'function') { stopBtn.click(); } shell.classList.remove('log-collapsed', 'log-floating'); shell.classList.add('log-hidden'); if (wrap) { wrap.style.left = ''; wrap.style.top = ''; wrap.style.right = ''; wrap.style.width = ''; wrap.style.height = ''; } return; } shell.classList.remove('log-hidden'); if (action === 'collapse') { if (shell.classList.contains('log-collapsed')) { shell.classList.remove('log-collapsed'); return; } shell.classList.remove('log-floating'); shell.classList.add('log-collapsed'); if (wrap) { wrap.style.left = ''; wrap.style.top = ''; wrap.style.right = ''; wrap.style.width = ''; wrap.style.height = ''; delete wrap.dataset.floatingInit; } return; } if (action === 'float') { if (shell.classList.contains('log-floating')) { shell.classList.remove('log-floating'); if (wrap) { wrap.style.left = ''; wrap.style.top = ''; wrap.style.right = ''; wrap.style.width = ''; wrap.style.height = ''; delete wrap.dataset.floatingInit; } return; } shell.classList.remove('log-collapsed'); shell.classList.add('log-floating'); if (wrap && !wrap.dataset.floatingInit) { wrap.style.right = '24px'; wrap.style.top = '18px'; wrap.dataset.floatingInit = '1'; } } }; window.__terminalDrag = { active: false, offsetX: 0, offsetY: 0, target: null }; document.addEventListener('mousedown', function(e) { var shell = document.querySelector('#terminal-shell'); if (!shell || !shell.classList.contains('log-floating')) return; var chrome = e.target && e.target.closest ? e.target.closest('.terminal-chrome') : null; var wrap = document.querySelector('#terminal-log-wrap'); if (!chrome || !wrap) return; if (e.target && e.target.closest && e.target.closest('.term-dot')) return; var rect = wrap.getBoundingClientRect(); window.__terminalDrag = { active: true, offsetX: e.clientX - rect.left, offsetY: e.clientY - rect.top, target: wrap }; wrap.style.right = 'auto'; wrap.style.left = rect.left + 'px'; wrap.style.top = rect.top + 'px'; wrap.style.width = rect.width + 'px'; wrap.style.height = rect.height + 'px'; document.body.style.userSelect = 'none'; }); document.addEventListener('mousemove', function(e) { var drag = window.__terminalDrag; if (!drag || !drag.active || !drag.target) return; var maxLeft = Math.max(0, window.innerWidth - drag.target.offsetWidth); var maxTop = Math.max(0, window.innerHeight - drag.target.offsetHeight); var left = Math.min(Math.max(0, e.clientX - drag.offsetX), maxLeft); var top = Math.min(Math.max(0, e.clientY - drag.offsetY), maxTop); drag.target.style.left = left + 'px'; drag.target.style.top = top + 'px'; }); document.addEventListener('mouseup', function() { if (window.__terminalDrag) { window.__terminalDrag.active = false; window.__terminalDrag.target = null; } document.body.style.userSelect = ''; }); document.addEventListener('click', function(e) { var runBtn = e.target && e.target.closest ? e.target.closest('.btn-run') : null; if (runBtn) { window.resetTerminalLogState(); } }); document.addEventListener('keydown', function(e) { var target = e.target; if (!target || !target.matches || !target.matches('#terminal-question-input textarea')) return; if (e.key === 'Enter' && !e.shiftKey) { window.resetTerminalLogState(); } }); /* Auto-scroll terminal whenever new log lines arrive */ new MutationObserver(function() { var t = document.querySelector('.terminal-wrapper'); if (t) t.scrollTop = t.scrollHeight; window.syncTerminalShellState(); }).observe(document.body, { childList: true, subtree: true }); window.syncTerminalShellState(); """ # ────────────────────────────────────────────────────────────────────────── # Example data # ────────────────────────────────────────────────────────────────────────── EXAMPLES_DATA: List[Dict[str, str]] = [ { "dataset": "Animal Science", "corpus": "biology", "question": "According to the documents about animal handedness, what types of animals are mentioned as being kept as pets? Cite the file path where you found this.", "answer": "Parrots, dogs, cats, and rabbits are mentioned as commonly kept pets.", "gold_doc": "biology/animals_handedness_Animal_8_3.txt.txt", }, { "dataset": "Biology", "corpus": "biology", "question": "What bacterium is mentioned as being used as a pesticide in the biology documents? Provide the specific name.", "answer": "Bacillus thuringiensis (Bt) — a Gram-positive, soil-dwelling bacterium.", "gold_doc": "biology/bacterium_infect_another_Bacteria_13_2.txt.txt", }, { "dataset": "Earth Science", "corpus": "earth_science", "question": "According to the earth science documents, what annual rainfall range is given for a tropical savanna?", "answer": "Between 750 millimetres and 1,270 millimetres per year.", "gold_doc": "earth_science/arid_area_Earth_rainfall_climatology4_10.txt.txt", }, { "dataset": "Earth Science", "corpus": "earth_science", "question": "According to the drifting guide in the earth science corpus, which drivetrain is particularly good for drifting?", "answer": "Rear-wheel drive.", "gold_doc": "earth_science/continental_drift_what_is_drifting_guide1_7.txt.txt", }, { "dataset": "Economics", "corpus": "economics", "question": "According to the economics documents, what major event is described as a turning point after which fertility mostly continued to fall?", "answer": "The Great Recession.", "gold_doc": "economics/uspopulationgrowth_thelongtermdeclineinfertilityandwhatitmeansforstatebudgets_76.txt.txt", }, { "dataset": "Robotics", "corpus": "robotics", "question": "In the robotics documents, which message type is converted to Ackermann inputs?", "answer": "geometry_msgs/Twist.", "gold_doc": "robotics/ackermann_interfacecontrolchec_53.txt.txt", }, { "dataset": "Robotics", "corpus": "robotics", "question": "According to the robotics documents, which launch file combines tf-broadcaster and pcl2-spammer data into an octomap?", "answer": "octomap_mapping.launch.", "gold_doc": "robotics/octomap_publish_4NI0GL435o_171.txt.txt", }, ] def _build_examples_html() -> str: cards: List[str] = [] for ex in EXAMPLES_DATA: q_attr = _html.escape(ex["question"], quote=True) q_disp = _html.escape(ex["question"]) a_disp = _html.escape(ex.get("answer", "")) d_disp = _html.escape(ex["dataset"]) gold_doc = ex.get("gold_doc", "").replace(".txt.txt", ".txt") g_disp = _html.escape(gold_doc) cards.append( f'
{q_disp}
' f'{a_disp}
' f'Gold doc: {g_disp}
' f"\1", escaped)
escaped = re.sub(r"\*\*([^*]+)\*\*", r"\1", escaped)
escaped = re.sub(r"\*([^*]+)\*", r"\1", escaped)
return escaped
def _simple_markdown_to_html(text: str) -> str:
lines = text.splitlines()
html_parts: List[str] = []
in_list = False
in_code = False
code_lines: List[str] = []
paragraph: List[str] = []
def flush_paragraph() -> None:
nonlocal paragraph
if paragraph:
html_parts.append(f"{'
'.join(_inline_markdown(line) for line in paragraph)}
{_esc(chr(10).join(code_lines))}")
code_lines = []
in_code = False
for line in lines:
stripped = line.rstrip()
if stripped.startswith("```"):
flush_paragraph()
flush_list()
if in_code:
flush_code()
else:
in_code = True
continue
if in_code:
code_lines.append(line)
continue
if not stripped:
flush_paragraph()
flush_list()
continue
heading_match = re.match(r"^(#{1,6})\s+(.*)$", stripped)
if heading_match:
flush_paragraph()
flush_list()
level = len(heading_match.group(1))
html_parts.append(f"