""" Thème visuel de PolyglotRAG : thème Gradio + feuille de style. Design tokens (couleurs, rayons, ombres) définis en variables CSS, avec variantes clair / sombre (classe `.dark` posée par Gradio) et prise en charge complète du sens d'écriture droite-à-gauche pour l'arabe. """ from __future__ import annotations import gradio as gr FONT_STACK = [ gr.themes.GoogleFont("Inter"), "Noto Sans Arabic", "ui-sans-serif", "system-ui", "-apple-system", "Segoe UI", "sans-serif", ] MONO_STACK = [gr.themes.GoogleFont("JetBrains Mono"), "ui-monospace", "SFMono-Regular", "monospace"] HEAD_HTML = """ """ def build_theme() -> gr.themes.Base: return gr.themes.Base( primary_hue=gr.themes.colors.indigo, secondary_hue=gr.themes.colors.cyan, neutral_hue=gr.themes.colors.slate, radius_size=gr.themes.sizes.radius_lg, spacing_size=gr.themes.sizes.spacing_md, text_size=gr.themes.sizes.text_md, font=FONT_STACK, font_mono=MONO_STACK, ).set( body_background_fill="#F5F7FB", body_background_fill_dark="#0B1020", block_background_fill="#FFFFFF", block_background_fill_dark="#121829", block_border_color="#E4E8F0", block_border_color_dark="#232B42", block_shadow="0 1px 2px rgba(15,23,42,.04)", block_shadow_dark="none", input_background_fill="#FFFFFF", input_background_fill_dark="#0F1526", button_primary_background_fill="linear-gradient(135deg,#5B6CFF 0%,#4F46E5 100%)", button_primary_background_fill_dark="linear-gradient(135deg,#6D7CFF 0%,#5B54F0 100%)", button_primary_background_fill_hover="linear-gradient(135deg,#6D7CFF 0%,#5B54F0 100%)", button_primary_background_fill_hover_dark="linear-gradient(135deg,#7C89FF 0%,#6A63F5 100%)", button_primary_text_color="#FFFFFF", button_primary_text_color_dark="#FFFFFF", ) CUSTOM_CSS = r""" /* ============================ Design tokens ============================ */ :root { --pg-bg: #F5F7FB; --pg-surface: #FFFFFF; --pg-surface-2: #F8FAFC; --pg-border: #E4E8F0; --pg-border-strong: #CBD3E1; --pg-text: #0F172A; --pg-text-2: #334155; --pg-muted: #64748B; --pg-primary: #4F46E5; --pg-primary-2: #5B6CFF; --pg-primary-soft: #EEF0FF; --pg-accent: #0891B2; --pg-ok: #047857; --pg-ok-soft: #ECFDF5; --pg-warn: #B45309; --pg-warn-soft: #FFF7E6; --pg-err: #B91C1C; --pg-err-soft: #FEF2F2; --pg-info: #1D4ED8; --pg-info-soft: #EFF6FF; --pg-track: #EDF0F6; --pg-radius: 14px; --pg-radius-sm: 10px; --pg-shadow: 0 1px 2px rgba(15,23,42,.04), 0 4px 16px -8px rgba(15,23,42,.10); --pg-shadow-lg: 0 12px 40px -16px rgba(30,41,90,.25); } .dark { --pg-bg: #0B1020; --pg-surface: #121829; --pg-surface-2: #161D31; --pg-border: #232B42; --pg-border-strong: #33405F; --pg-text: #E6E9F2; --pg-text-2: #C3CADB; --pg-muted: #8F9AB3; --pg-primary: #8B95FF; --pg-primary-2: #A5ADFF; --pg-primary-soft: rgba(109,124,255,.14); --pg-accent: #22D3EE; --pg-ok: #34D399; --pg-ok-soft: rgba(52,211,153,.12); --pg-warn: #FBBF24; --pg-warn-soft: rgba(251,191,36,.12); --pg-err: #F87171; --pg-err-soft: rgba(248,113,113,.12); --pg-info: #60A5FA; --pg-info-soft: rgba(96,165,250,.12); --pg-track: #1E2640; --pg-shadow: 0 1px 2px rgba(0,0,0,.3); --pg-shadow-lg: 0 12px 40px -16px rgba(0,0,0,.6); } /* ================================ Base ================================= */ .gradio-container { max-width: 1360px !important; margin: 0 auto !important; padding: 20px 24px 8px !important; color: var(--pg-text); } .gradio-container[dir="rtl"], .gradio-container[dir="rtl"] * { font-family: "Noto Sans Arabic", "Inter", system-ui, sans-serif; } footer { display: none !important; } .pg-icon { flex: none; vertical-align: middle; } .pg-num { font-variant-numeric: tabular-nums; } .pg-muted { color: var(--pg-muted); font-size: .9rem; line-height: 1.55; margin: 0; } .pg-html-flush, .pg-html-flush > div { padding: 0 !important; border: none !important; background: transparent !important; box-shadow: none !important; } /* =============================== Header ================================ */ #pg-header { position: relative; background: var(--pg-surface); border: 1px solid var(--pg-border); border-radius: 18px; padding: 16px 20px !important; margin-bottom: 14px; box-shadow: var(--pg-shadow); align-items: center !important; gap: 16px !important; overflow: hidden; } #pg-header::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 3px; background: linear-gradient(90deg, #6D7CFF, #22D3EE 55%, #A78BFA); } .pg-brand { display: flex; align-items: center; gap: 14px; min-width: 0; } .pg-logo { flex: none; border-radius: 11px; box-shadow: 0 6px 18px -6px rgba(91,108,255,.55); } .pg-brand-text { min-width: 0; } .pg-brand-title { font-size: 1.28rem; font-weight: 700; letter-spacing: -.02em; color: var(--pg-text); display: flex; align-items: center; flex-wrap: wrap; gap: 10px; } .pg-brand-tag { font-size: .72rem; font-weight: 600; letter-spacing: .02em; text-transform: uppercase; color: var(--pg-primary); background: var(--pg-primary-soft); padding: 3px 9px; border-radius: 999px; } .pg-brand-sub { margin-top: 3px; color: var(--pg-muted); font-size: .88rem; line-height: 1.45; } #pg-header-right { align-items: flex-end !important; gap: 8px !important; } .gradio-container[dir="rtl"] #pg-header-right { align-items: flex-start !important; } #pg-ui-lang { max-width: 220px; min-width: 180px; } #pg-ui-lang input { font-weight: 600 !important; } .pg-status-wrap { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; } .gradio-container[dir="rtl"] .pg-status-wrap { align-items: flex-start; } .pg-status { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; font-size: .78rem; font-weight: 600; padding: 4px 10px; border-radius: 999px; } .pg-status-ok { color: var(--pg-ok); background: var(--pg-ok-soft); } .pg-status-warn { color: var(--pg-warn); background: var(--pg-warn-soft); } .pg-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 22%, transparent); } .pg-status-langs { display: flex; gap: 4px; } .pg-chip-lang { font-size: .64rem; font-weight: 700; letter-spacing: .04em; color: var(--pg-muted); border: 1px solid var(--pg-border); border-radius: 6px; padding: 1px 5px; } /* =============================== Banners =============================== */ .pg-banner { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-radius: var(--pg-radius-sm); font-size: .9rem; margin-bottom: 12px; } .pg-banner-warn { color: var(--pg-warn); background: var(--pg-warn-soft); border: 1px solid color-mix(in srgb, var(--pg-warn) 25%, transparent); } .pg-banner-error { color: var(--pg-err); background: var(--pg-err-soft); border: 1px solid color-mix(in srgb, var(--pg-err) 25%, transparent); } /* ================================ Tabs ================================= */ #pg-tabs { border: none !important; background: transparent !important; } #pg-tabs .tab-wrapper, #pg-tabs > .tab-nav, #pg-tabs [role="tablist"] { border-bottom: 1px solid var(--pg-border) !important; gap: 4px; margin-bottom: 16px; } #pg-tabs button[role="tab"] { font-weight: 600 !important; font-size: .92rem !important; color: var(--pg-muted) !important; padding: 10px 16px !important; border: none !important; background: transparent !important; border-bottom: 2px solid transparent !important; border-radius: 0 !important; transition: color .15s ease, border-color .15s ease; } #pg-tabs button[role="tab"]:hover { color: var(--pg-text) !important; } #pg-tabs button[role="tab"][aria-selected="true"], #pg-tabs button[role="tab"].selected { color: var(--pg-primary) !important; border-bottom-color: var(--pg-primary) !important; } #pg-tabs .tabitem { border: none !important; padding: 0 !important; background: transparent !important; } /* ============================== Cards ================================== */ .pg-card { background: var(--pg-surface); border: 1px solid var(--pg-border); border-radius: var(--pg-radius); padding: 16px 18px; box-shadow: var(--pg-shadow); } .pg-card + .pg-card { margin-top: 12px; } .pg-chart-grid > .pg-card { margin-top: 0; } .pg-section-title { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; font-size: .78rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--pg-muted); } .pg-section-title .pg-icon { color: var(--pg-primary); } .gradio-container[dir="rtl"] .pg-section-title { letter-spacing: 0; } /* ============================== Chat area ============================== */ #pg-chat-col { gap: 12px !important; } #pg-chatbot { border: 1px solid var(--pg-border) !important; border-radius: 18px !important; background: var(--pg-surface) !important; box-shadow: var(--pg-shadow) !important; } #pg-chatbot .message, #pg-chatbot .message-content { font-size: .95rem; line-height: 1.65; } #pg-chatbot .message p, #pg-chatbot .message li, #pg-chatbot .message h1, #pg-chatbot .message h2, #pg-chatbot .message h3, #pg-chatbot .message td { unicode-bidi: plaintext; text-align: start; } #pg-chatbot .placeholder, #pg-chatbot .placeholder-content { height: 100%; } .pg-welcome { text-align: center; max-width: 460px; margin: 0 auto; padding: 24px 12px; } .pg-welcome .pg-logo { width: 52px; height: 52px; margin-bottom: 12px; } .pg-welcome h3 { font-size: 1.35rem; font-weight: 700; letter-spacing: -.02em; margin: 0 0 6px; color: var(--pg-text); } .pg-welcome p { color: var(--pg-muted); margin: 0; line-height: 1.55; } #pg-input { border: 1px solid var(--pg-border) !important; border-radius: 16px !important; background: var(--pg-surface) !important; box-shadow: var(--pg-shadow-lg) !important; padding: 6px 8px !important; } #pg-input:focus-within { border-color: var(--pg-primary) !important; box-shadow: 0 0 0 4px var(--pg-primary-soft), var(--pg-shadow-lg) !important; } #pg-input textarea { font-size: .97rem !important; border: none !important; box-shadow: none !important; background: transparent !important; unicode-bidi: plaintext; text-align: start; } #pg-input button { background: linear-gradient(135deg,#5B6CFF,#4F46E5) !important; color: #fff !important; border-radius: 12px !important; border: none !important; } #pg-examples-label { margin: 2px 2px -4px; } #pg-examples { gap: 8px !important; flex-wrap: wrap !important; } #pg-examples button { flex: 0 1 auto !important; min-width: 0 !important; width: auto !important; font-size: .84rem !important; font-weight: 500 !important; color: var(--pg-text-2) !important; background: var(--pg-surface) !important; border: 1px solid var(--pg-border) !important; border-radius: 999px !important; padding: 6px 14px !important; box-shadow: none !important; transition: all .15s ease; unicode-bidi: plaintext; } #pg-examples button:hover { border-color: var(--pg-primary) !important; color: var(--pg-primary) !important; background: var(--pg-primary-soft) !important; } /* ============================== Side panel ============================= */ #pg-side { gap: 12px !important; } #pg-settings { background: var(--pg-surface) !important; border: 1px solid var(--pg-border) !important; border-radius: var(--pg-radius) !important; padding: 16px 16px 8px !important; box-shadow: var(--pg-shadow); gap: 10px !important; } #pg-settings > .form, #pg-settings .block { border: none !important; box-shadow: none !important; background: transparent !important; padding: 0 !important; } #pg-clear { border-radius: 12px !important; font-weight: 600 !important; } .pg-pills { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; } .pg-pill { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; font-size: .74rem; font-weight: 600; padding: 3px 9px; border-radius: 999px; } .pg-pill-ok { color: var(--pg-ok); background: var(--pg-ok-soft); } .pg-pill-warn { color: var(--pg-warn); background: var(--pg-warn-soft); } .pg-pill-info { color: var(--pg-info); background: var(--pg-info-soft); } .pg-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 14px 16px; margin: 0; } .pg-stats > div { min-width: 0; } .pg-stats dt { font-size: .72rem; color: var(--pg-muted); margin-bottom: 4px; font-weight: 500; } .pg-stats dd { margin: 0; font-size: .92rem; font-weight: 600; color: var(--pg-text); display: flex; align-items: center; gap: 6px; flex-wrap: wrap; } .pg-stats dd small { color: var(--pg-muted); font-weight: 500; font-size: .74rem; } .pg-stats dd.pg-num { font-size: 1.15rem; } .pg-lang-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 26px; font-size: .66rem; font-weight: 700; letter-spacing: .04em; line-height: 1; padding: 4px 6px; border-radius: 6px; color: var(--pg-primary); background: var(--pg-primary-soft); } .pg-lang-badge[data-lang="fr"] { color: #2563EB; background: rgba(37,99,235,.10); } .pg-lang-badge[data-lang="en"] { color: #7C3AED; background: rgba(124,58,237,.10); } .pg-lang-badge[data-lang="es"] { color: #C2410C; background: rgba(194,65,12,.10); } .pg-lang-badge[data-lang="pt"] { color: #047857; background: rgba(4,120,87,.10); } .pg-lang-badge[data-lang="ru"] { color: #BE123C; background: rgba(190,18,60,.10); } .pg-lang-badge[data-lang="ar"] { color: #0E7490; background: rgba(14,116,144,.10); } .dark .pg-lang-badge { filter: brightness(1.45) saturate(.9); } .pg-meter { height: 6px; border-radius: 999px; background: var(--pg-track); overflow: hidden; margin-top: 6px; } .pg-meter span { display: block; height: 100%; border-radius: inherit; } .pg-meter-ok span { background: linear-gradient(90deg, #5B6CFF, #22D3EE); } .pg-meter-warn span { background: linear-gradient(90deg, #F59E0B, #FBBF24); } .pg-sources { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; } .pg-source { display: flex; gap: 12px; padding: 12px; border-radius: var(--pg-radius-sm); background: var(--pg-surface-2); border: 1px solid var(--pg-border); } .pg-source-idx { flex: none; width: 22px; height: 22px; border-radius: 7px; display: grid; place-items: center; font-size: .72rem; font-weight: 700; color: #fff; background: linear-gradient(135deg,#5B6CFF,#4F46E5); } .pg-source-body { flex: 1; min-width: 0; } .pg-source-file { display: flex; align-items: center; gap: 6px; font-weight: 600; font-size: .86rem; color: var(--pg-text); } .pg-source-file span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .pg-source-title { font-size: .8rem; color: var(--pg-muted); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .pg-source-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 6px; font-size: .76rem; color: var(--pg-muted); } .pg-sep { opacity: .5; } /* =============================== Admin ================================= */ #pg-admin-bar { background: var(--pg-surface) !important; border: 1px solid var(--pg-border) !important; border-radius: var(--pg-radius) !important; padding: 14px 16px !important; box-shadow: var(--pg-shadow); align-items: flex-end !important; margin-bottom: 14px; } #pg-admin-bar .block { border: none !important; box-shadow: none !important; background: transparent !important; padding: 0 !important; } .pg-page-head { margin: 4px 2px 14px; } .pg-page-head h2 { font-size: 1.35rem; font-weight: 700; letter-spacing: -.02em; margin: 0 0 4px; color: var(--pg-text); } .pg-page-head p { margin: 0; color: var(--pg-muted); } .pg-admin .pg-section-title { margin-top: 18px; } .pg-admin .pg-section-title:first-child { margin-top: 0; } .pg-kpi-grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; } .pg-kpi { background: var(--pg-surface); border: 1px solid var(--pg-border); border-radius: var(--pg-radius); padding: 14px 16px; box-shadow: var(--pg-shadow); min-width: 0; } .pg-kpi-head { display: flex; align-items: center; gap: 7px; color: var(--pg-muted); font-size: .78rem; font-weight: 500; } .pg-kpi-head span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .pg-kpi-head .pg-icon { color: var(--pg-primary); } .pg-kpi-value { margin-top: 8px; font-size: 1.6rem; font-weight: 700; letter-spacing: -.02em; color: var(--pg-text); line-height: 1.15; } .pg-kpi-hint { font-size: .74rem; color: var(--pg-muted); margin-top: 2px; } .pg-chart-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 18px 0 12px; } .pg-chart .pg-chart-title { font-weight: 600; font-size: .92rem; margin-bottom: 14px; color: var(--pg-text); } .pg-bar-row { display: grid; grid-template-columns: 130px 1fr 74px; align-items: center; gap: 12px; padding: 6px 0; } .pg-bar-label { display: flex; align-items: center; gap: 8px; font-size: .85rem; color: var(--pg-text-2); min-width: 0; } .pg-bar-label span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .pg-bar-track { height: 10px; border-radius: 999px; background: var(--pg-track); overflow: hidden; } .pg-bar-track span { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, #5B6CFF, #22D3EE); } .gradio-container[dir="rtl"] .pg-bar-track span { background: linear-gradient(270deg, #5B6CFF, #22D3EE); margin-inline-start: 0; } .pg-bar-value { text-align: end; font-weight: 600; font-size: .86rem; color: var(--pg-text); } .pg-bar-value small { color: var(--pg-muted); font-weight: 500; margin-inline-start: 4px; } .pg-table-wrap { overflow-x: auto; margin: 0 -4px; } .pg-table { width: 100%; border-collapse: collapse; font-size: .86rem; } .pg-table th { text-align: start; font-size: .72rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--pg-muted); padding: 8px 10px; border-bottom: 1px solid var(--pg-border); white-space: nowrap; } .pg-table td { padding: 10px; border-bottom: 1px solid var(--pg-border); color: var(--pg-text-2); white-space: nowrap; } .pg-table tr:last-child td { border-bottom: none; } .pg-table tbody tr:hover td { background: var(--pg-surface-2); } .pg-table .pg-td-q { white-space: normal; min-width: 260px; color: var(--pg-text); font-weight: 500; unicode-bidi: plaintext; text-align: start; } .pg-empty-state { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 10px; padding: 40px 20px; color: var(--pg-muted); } .pg-empty-state .pg-icon { color: var(--pg-primary); } .pg-empty-state p { margin: 0; max-width: 520px; line-height: 1.55; } /* ============================ Architecture ============================= */ .pg-arch-hero { border-radius: 18px; padding: 26px 28px; margin-bottom: 16px; color: #fff; background: radial-gradient(120% 140% at 0% 0%, #5B6CFF 0%, #3B32B8 45%, #101736 100%); box-shadow: var(--pg-shadow-lg); } .pg-arch-hero h2 { margin: 0 0 6px; font-size: 1.45rem; font-weight: 700; letter-spacing: -.02em; color: #fff; } .pg-arch-hero p { margin: 0; max-width: 760px; color: rgba(255,255,255,.82); line-height: 1.55; } .pg-lane { margin-bottom: 16px; } .pg-lane-head { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: .9rem; color: var(--pg-text); margin: 0 2px 10px; } .pg-lane-dot { width: 10px; height: 10px; border-radius: 3px; } .pg-lane-a { background: #22D3EE; } .pg-lane-b { background: #6D7CFF; } .pg-steps { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; counter-reset: step; } .pg-step { position: relative; background: var(--pg-surface); border: 1px solid var(--pg-border); border-radius: var(--pg-radius); padding: 14px 14px 16px; box-shadow: var(--pg-shadow); } .pg-step:not(:last-child)::after { content: ""; position: absolute; top: 30px; inset-inline-end: -10px; width: 8px; height: 8px; border-top: 2px solid var(--pg-border-strong); border-inline-end: 2px solid var(--pg-border-strong); transform: rotate(45deg); z-index: 1; } .gradio-container[dir="rtl"] .pg-step:not(:last-child)::after { transform: rotate(-45deg); } .pg-step-icon { width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center; color: var(--pg-primary); background: var(--pg-primary-soft); } .pg-step-n { position: absolute; top: 14px; inset-inline-end: 14px; font-size: .72rem; font-weight: 700; color: var(--pg-muted); font-variant-numeric: tabular-nums; } .pg-step-t { margin-top: 12px; font-weight: 600; font-size: .9rem; color: var(--pg-text); } .pg-step-d { margin-top: 4px; font-size: .8rem; color: var(--pg-muted); line-height: 1.5; } .pg-why-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; } .pg-why { display: flex; gap: 12px; align-items: flex-start; background: var(--pg-surface); border: 1px solid var(--pg-border); border-radius: var(--pg-radius); padding: 16px; box-shadow: var(--pg-shadow); } .pg-why .pg-icon { color: var(--pg-primary); margin-top: 2px; } .pg-why b { color: var(--pg-text); font-size: .92rem; } .pg-why p { margin: 4px 0 0; color: var(--pg-muted); font-size: .85rem; line-height: 1.55; } /* =============================== Footer ================================ */ .pg-footer { display: flex; align-items: center; justify-content: center; gap: 8px; color: var(--pg-muted); font-size: .78rem; padding: 18px 0 6px; text-align: center; } /* ============================= Responsive ============================== */ @media (max-width: 1100px) { .pg-kpi-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } .pg-steps { grid-template-columns: repeat(3, minmax(0, 1fr)); } .pg-step::after { display: none; } } @media (max-width: 760px) { .gradio-container { padding: 12px 12px 4px !important; } #pg-header { flex-direction: column !important; align-items: stretch !important; } #pg-header-right, .pg-status-wrap { align-items: flex-start !important; } #pg-ui-lang { max-width: none; } .pg-brand-sub { display: none; } .pg-kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .pg-chart-grid, .pg-why-grid { grid-template-columns: 1fr; } .pg-steps { grid-template-columns: 1fr 1fr; } .pg-bar-row { grid-template-columns: 96px 1fr 64px; } } """