Spaces:
Running
Running
| """Dark theme, custom CSS, and logo SVGs for the NotebookLM Gradio app.""" | |
| import base64 | |
| import gradio as gr | |
| # ββ Logo SVGs ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| LOGO_SVG = """<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 280 60"> | |
| <defs> | |
| <linearGradient id="lg1" x1="0%" y1="0%" x2="100%" y2="100%"> | |
| <stop offset="0%" style="stop-color:#667eea"/> | |
| <stop offset="100%" style="stop-color:#764ba2"/> | |
| </linearGradient> | |
| <linearGradient id="lg2" x1="0%" y1="0%" x2="100%" y2="100%"> | |
| <stop offset="0%" style="stop-color:#a78bfa"/> | |
| <stop offset="100%" style="stop-color:#667eea"/> | |
| </linearGradient> | |
| <linearGradient id="sp" x1="0%" y1="0%" x2="100%" y2="100%"> | |
| <stop offset="0%" style="stop-color:#fbbf24"/> | |
| <stop offset="100%" style="stop-color:#f59e0b"/> | |
| </linearGradient> | |
| </defs> | |
| <g transform="translate(4,6)"> | |
| <rect x="2" y="4" width="36" height="44" rx="4" fill="url(#lg1)"/> | |
| <rect x="2" y="4" width="8" height="44" rx="3" fill="url(#lg2)" opacity="0.7"/> | |
| <line x1="16" y1="16" x2="32" y2="16" stroke="rgba(255,255,255,0.5)" stroke-width="1.8" stroke-linecap="round"/> | |
| <line x1="16" y1="23" x2="30" y2="23" stroke="rgba(255,255,255,0.4)" stroke-width="1.8" stroke-linecap="round"/> | |
| <line x1="16" y1="30" x2="32" y2="30" stroke="rgba(255,255,255,0.5)" stroke-width="1.8" stroke-linecap="round"/> | |
| <line x1="16" y1="37" x2="28" y2="37" stroke="rgba(255,255,255,0.4)" stroke-width="1.8" stroke-linecap="round"/> | |
| <g transform="translate(32,2)"> | |
| <path d="M6 0 L7.5 4.5 L12 6 L7.5 7.5 L6 12 L4.5 7.5 L0 6 L4.5 4.5 Z" fill="url(#sp)"/> | |
| <path d="M14 8 L14.8 10.2 L17 11 L14.8 11.8 L14 14 L13.2 11.8 L11 11 L13.2 10.2 Z" fill="#fbbf24" opacity="0.7"/> | |
| </g> | |
| </g> | |
| <text x="56" y="28" font-family="Inter,-apple-system,sans-serif" font-size="22" font-weight="700"> | |
| <tspan fill="url(#lg1)">Notebook</tspan><tspan fill="#a78bfa" font-weight="800">LM</tspan> | |
| </text> | |
| <text x="57" y="46" font-family="Inter,-apple-system,sans-serif" font-size="10.5" fill="#8888aa" font-weight="400" letter-spacing="0.8"> | |
| AI-Powered Study Companion | |
| </text> | |
| </svg>""" | |
| LOGO_ICON_SVG = """<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 60"> | |
| <defs> | |
| <linearGradient id="ig1" x1="0%" y1="0%" x2="100%" y2="100%"> | |
| <stop offset="0%" style="stop-color:#667eea"/> | |
| <stop offset="100%" style="stop-color:#764ba2"/> | |
| </linearGradient> | |
| <linearGradient id="ig2" x1="0%" y1="0%" x2="100%" y2="100%"> | |
| <stop offset="0%" style="stop-color:#a78bfa"/> | |
| <stop offset="100%" style="stop-color:#667eea"/> | |
| </linearGradient> | |
| <linearGradient id="isp" x1="0%" y1="0%" x2="100%" y2="100%"> | |
| <stop offset="0%" style="stop-color:#fbbf24"/> | |
| <stop offset="100%" style="stop-color:#f59e0b"/> | |
| </linearGradient> | |
| </defs> | |
| <g transform="translate(2,4)"> | |
| <rect x="2" y="4" width="36" height="44" rx="5" fill="url(#ig1)"/> | |
| <rect x="2" y="4" width="9" height="44" rx="4" fill="url(#ig2)" opacity="0.7"/> | |
| <line x1="16" y1="16" x2="32" y2="16" stroke="rgba(255,255,255,0.55)" stroke-width="2" stroke-linecap="round"/> | |
| <line x1="16" y1="23" x2="30" y2="23" stroke="rgba(255,255,255,0.4)" stroke-width="2" stroke-linecap="round"/> | |
| <line x1="16" y1="30" x2="32" y2="30" stroke="rgba(255,255,255,0.55)" stroke-width="2" stroke-linecap="round"/> | |
| <line x1="16" y1="37" x2="28" y2="37" stroke="rgba(255,255,255,0.4)" stroke-width="2" stroke-linecap="round"/> | |
| <g transform="translate(30,0)"> | |
| <path d="M7 0 L8.8 5.3 L14 7 L8.8 8.8 L7 14 L5.2 8.8 L0 7 L5.2 5.3 Z" fill="url(#isp)"/> | |
| <path d="M16 9 L17 11.5 L19.5 12.5 L17 13.5 L16 16 L15 13.5 L12.5 12.5 L15 11.5 Z" fill="#fbbf24" opacity="0.7"/> | |
| </g> | |
| </g> | |
| </svg>""" | |
| def get_logo_b64(svg_str: str) -> str: | |
| return base64.b64encode(svg_str.encode()).decode() | |
| LOGO_B64 = get_logo_b64(LOGO_SVG) | |
| ICON_B64 = get_logo_b64(LOGO_ICON_SVG) | |
| # ββ Gradio Dark Theme ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| dark_theme = gr.themes.Base( | |
| primary_hue=gr.themes.colors.indigo, | |
| secondary_hue=gr.themes.colors.purple, | |
| neutral_hue=gr.themes.colors.slate, | |
| font=gr.themes.GoogleFont("Inter"), | |
| ).set( | |
| body_background_fill="#0e1117", | |
| body_background_fill_dark="#0e1117", | |
| block_background_fill="rgba(255,255,255,0.02)", | |
| block_background_fill_dark="rgba(255,255,255,0.02)", | |
| block_border_color="rgba(255,255,255,0.08)", | |
| block_border_color_dark="rgba(255,255,255,0.08)", | |
| block_label_text_color="#a0a0b8", | |
| block_label_text_color_dark="#a0a0b8", | |
| block_title_text_color="#e0e0f0", | |
| block_title_text_color_dark="#e0e0f0", | |
| body_text_color="#c8c8d8", | |
| body_text_color_dark="#c8c8d8", | |
| button_primary_background_fill="linear-gradient(135deg, #667eea 0%, #764ba2 100%)", | |
| button_primary_background_fill_dark="linear-gradient(135deg, #667eea 0%, #764ba2 100%)", | |
| button_primary_text_color="white", | |
| button_primary_text_color_dark="white", | |
| button_secondary_background_fill="rgba(255,255,255,0.04)", | |
| button_secondary_background_fill_dark="rgba(255,255,255,0.04)", | |
| button_secondary_border_color="rgba(255,255,255,0.12)", | |
| button_secondary_border_color_dark="rgba(255,255,255,0.12)", | |
| button_secondary_text_color="#d0d0e0", | |
| button_secondary_text_color_dark="#d0d0e0", | |
| border_color_primary="rgba(255,255,255,0.08)", | |
| border_color_primary_dark="rgba(255,255,255,0.08)", | |
| input_background_fill="rgba(255,255,255,0.03)", | |
| input_background_fill_dark="rgba(255,255,255,0.03)", | |
| input_border_color="rgba(255,255,255,0.1)", | |
| input_border_color_dark="rgba(255,255,255,0.1)", | |
| shadow_drop="none", | |
| shadow_drop_lg="none", | |
| shadow_spread="none", | |
| ) | |
| # ββ Custom CSS βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| CUSTOM_CSS = """ | |
| /* ββ Import Google Font ββ */ | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); | |
| /* ββ Sidebar ββ */ | |
| #sidebar { | |
| background: linear-gradient(180deg, #1a1a2e 0%, #16213e 100%) !important; | |
| border-right: 1px solid rgba(255,255,255,0.06); | |
| padding: 16px !important; | |
| min-height: 100vh; | |
| position: sticky; | |
| top: 0; | |
| align-self: flex-start; | |
| max-height: 100vh; | |
| overflow-y: auto; | |
| border-radius: 0 !important; | |
| } | |
| #sidebar .gr-button-primary { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| border: none !important; | |
| border-radius: 10px !important; | |
| } | |
| #sidebar .gr-button-secondary { | |
| background: rgba(255,255,255,0.05) !important; | |
| border: 1px solid rgba(255,255,255,0.1) !important; | |
| border-radius: 10px !important; | |
| color: #d0d0e0 !important; | |
| } | |
| /* ββ Notebook selector radio ββ */ | |
| #notebook-selector label { | |
| border-radius: 10px !important; | |
| padding: 8px 14px !important; | |
| transition: all 0.2s ease !important; | |
| font-size: 0.85rem !important; | |
| } | |
| #notebook-selector label.selected { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| color: white !important; | |
| } | |
| /* ββ Tab styling ββ */ | |
| .tabs > .tab-nav > button { | |
| border-radius: 10px !important; | |
| padding: 10px 24px !important; | |
| font-weight: 500 !important; | |
| font-size: 0.9rem !important; | |
| } | |
| .tabs > .tab-nav > button.selected { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| color: white !important; | |
| } | |
| /* ββ Chat ββ */ | |
| #chatbot { | |
| border-radius: 14px !important; | |
| border: 1px solid rgba(255,255,255,0.08) !important; | |
| } | |
| #chatbot .message { | |
| border-radius: 14px !important; | |
| padding: 14px 18px !important; | |
| } | |
| /* User bubble β shrink to fit content */ | |
| #chatbot .bot-row { justify-content: flex-start !important; } | |
| #chatbot .user-row { justify-content: flex-end !important; } | |
| #chatbot .user-row .message-bubble-border { | |
| max-width: fit-content !important; | |
| } | |
| #chatbot .message.user { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| color: white !important; | |
| border: none !important; | |
| box-shadow: 0 4px 14px rgba(102,126,234,0.25); | |
| max-width: fit-content !important; | |
| } | |
| /* Assistant bubble */ | |
| #chatbot .message.bot { | |
| background: rgba(40, 44, 66, 0.85) !important; | |
| border: 1px solid rgba(102, 126, 234, 0.3) !important; | |
| color: #c8c8d8 !important; | |
| box-shadow: 0 4px 12px rgba(102, 126, 234, 0.25); | |
| border-radius: 14px !important; | |
| padding: 14px 18px !important; | |
| backdrop-filter: blur(6px); | |
| } | |
| /* ββ Cards ββ */ | |
| .source-card { | |
| display: flex; | |
| align-items: center; | |
| gap: 16px; | |
| padding: 16px 20px; | |
| background: rgba(255,255,255,0.02); | |
| border: 1px solid rgba(255,255,255,0.08); | |
| border-radius: 14px; | |
| margin-bottom: 10px; | |
| transition: all 0.2s ease; | |
| } | |
| .source-card:hover { | |
| border-color: rgba(102,126,234,0.3); | |
| background: rgba(255,255,255,0.04); | |
| } | |
| .source-icon { | |
| width: 48px; height: 48px; border-radius: 12px; | |
| display: flex; align-items: center; justify-content: center; | |
| font-size: 1.5rem; flex-shrink: 0; | |
| } | |
| .source-icon.pdf { background: rgba(239,68,68,0.15); } | |
| .source-icon.pptx { background: rgba(249,115,22,0.15); } | |
| .source-icon.txt { background: rgba(59,130,246,0.15); } | |
| .source-icon.url { background: rgba(34,197,94,0.15); } | |
| .source-icon.youtube { background: rgba(239,68,68,0.15); } | |
| .source-info { flex: 1; min-width: 0; } | |
| .source-info .name { | |
| font-weight: 600; font-size: 0.95rem; color: #e0e0f0; | |
| white-space: nowrap; overflow: hidden; text-overflow: ellipsis; | |
| } | |
| .source-info .meta { font-size: 0.8rem; color: #707088; margin-top: 2px; } | |
| .source-badge { | |
| padding: 4px 12px; border-radius: 20px; font-size: 0.75rem; | |
| font-weight: 600; letter-spacing: 0.3px; | |
| } | |
| .source-badge.ready { background: rgba(34,197,94,0.15); color: #22c55e; } | |
| .source-badge.processing { | |
| background: rgba(234,179,8,0.15); color: #eab308; | |
| animation: pulse-badge 1.5s ease-in-out infinite; | |
| } | |
| .source-badge.failed { background: rgba(239,68,68,0.15); color: #ef4444; cursor: help; } | |
| @keyframes pulse-badge { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| } | |
| /* ββ Welcome hero ββ */ | |
| .welcome-hero { | |
| text-align: center; padding: 80px 40px; | |
| background: linear-gradient(135deg, rgba(102,126,234,0.08) 0%, rgba(118,75,162,0.08) 100%); | |
| border-radius: 20px; border: 1px solid rgba(102,126,234,0.15); margin: 20px 0; | |
| } | |
| .welcome-hero h1 { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| -webkit-background-clip: text; -webkit-text-fill-color: transparent; | |
| font-size: 2.5rem; font-weight: 700; margin-bottom: 12px; | |
| } | |
| .welcome-hero p { color: #9090a8; font-size: 1.1rem; line-height: 1.6; } | |
| /* ββ Empty state ββ */ | |
| .empty-state { | |
| text-align: center; padding: 60px 30px; color: #707088; | |
| } | |
| .empty-state h3 { color: #a0a0b8; margin-bottom: 8px; font-weight: 600; } | |
| .empty-state p { font-size: 0.95rem; line-height: 1.5; } | |
| /* ββ Notebook header ββ */ | |
| .notebook-header { | |
| padding: 0 0 16px 0; margin-bottom: 16px; | |
| border-bottom: 1px solid rgba(255,255,255,0.06); | |
| } | |
| .notebook-header h2 { | |
| font-weight: 700; font-size: 1.5rem; margin: 0; color: #e8e8f8; | |
| } | |
| .notebook-header .meta { font-size: 0.85rem; color: #707088; margin-top: 4px; } | |
| /* ββ Citation chip ββ */ | |
| .citation-chip { | |
| display: inline-flex; align-items: center; gap: 6px; | |
| padding: 6px 14px; background: rgba(102,126,234,0.1); | |
| border: 1px solid rgba(102,126,234,0.2); border-radius: 20px; | |
| font-size: 0.8rem; color: #a0b0f0; margin: 3px 4px; | |
| } | |
| /* ββ Artifact section header ββ */ | |
| .artifact-section-header { | |
| display: flex; align-items: center; gap: 10px; margin-bottom: 12px; | |
| } | |
| .artifact-section-icon { | |
| width: 36px; height: 36px; border-radius: 10px; | |
| display: flex; align-items: center; justify-content: center; font-size: 1.1rem; | |
| } | |
| /* ββ Locked state ββ */ | |
| .locked-state { | |
| text-align: center; padding: 50px 30px; | |
| background: rgba(255,255,255,0.02); | |
| border: 1px solid rgba(255,255,255,0.06); | |
| border-radius: 16px; | |
| } | |
| /* ββ File uploader ββ */ | |
| .gr-file-upload { | |
| border-radius: 14px !important; | |
| border: 2px dashed rgba(102,126,234,0.3) !important; | |
| background: rgba(102,126,234,0.03) !important; | |
| } | |
| /* ββ Primary button ββ */ | |
| .gr-button-primary { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| border: none !important; border-radius: 10px !important; | |
| font-weight: 600 !important; | |
| } | |
| .gr-button-primary:hover { | |
| opacity: 0.9 !important; | |
| transform: translateY(-1px) !important; | |
| box-shadow: 0 4px 15px rgba(102,126,234,0.3) !important; | |
| } | |
| /* ββ Source delete dropdown ββ */ | |
| #source-delete-dropdown { | |
| border: 1px solid rgba(102,126,234,0.3); | |
| border-radius: 12px; | |
| background: rgba(102,126,234,0.06); | |
| } | |
| #source-delete-dropdown label { | |
| color: #a0a0f0 !important; | |
| font-weight: 600; | |
| } | |
| #source-delete-dropdown input, #source-delete-dropdown .wrap { | |
| background: rgba(14,17,23,0.8) !important; | |
| border-color: rgba(102,126,234,0.25) !important; | |
| color: #e0e0f0 !important; | |
| border-radius: 10px !important; | |
| } | |
| #source-delete-dropdown ul { | |
| background: #1a1d2e !important; | |
| border: 1px solid rgba(102,126,234,0.3) !important; | |
| border-radius: 10px !important; | |
| } | |
| #source-delete-dropdown li { | |
| color: #c0c0e0 !important; | |
| } | |
| #source-delete-dropdown li:hover, #source-delete-dropdown li.selected { | |
| background: rgba(102,126,234,0.15) !important; | |
| color: #e0e0f0 !important; | |
| } | |
| /* ββ Scrollbar ββ */ | |
| ::-webkit-scrollbar { width: 6px; } | |
| ::-webkit-scrollbar-track { background: transparent; } | |
| ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 3px; } | |
| ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.25); } | |
| /* ββ Hide Gradio footer ββ */ | |
| footer { display: none !important; } | |
| /* ββ Auth gate ββ */ | |
| #auth-gate { max-width: 500px; margin: 100px auto; } | |
| """ | |
| # ββ Reusable HTML Templates ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| WELCOME_HTML = f""" | |
| <div class="welcome-hero"> | |
| <img src="data:image/svg+xml;base64,{ICON_B64}" style="width:64px; margin-bottom:16px;" /> | |
| <h1>NotebookLM</h1> | |
| <p>Your AI-powered study companion.<br> | |
| Sign in with your Hugging Face account to get started.</p> | |
| </div> | |
| """ | |
| NO_NOTEBOOKS_HTML = """ | |
| <div class="welcome-hero"> | |
| <h1>NotebookLM</h1> | |
| <p>Create a notebook from the sidebar to get started.</p> | |
| </div> | |
| """ | |
| SIDEBAR_LOGO_HTML = f""" | |
| <div style="padding: 8px 0 4px 0;"> | |
| <img src="data:image/svg+xml;base64,{LOGO_B64}" style="width:100%; max-width:240px;" /> | |
| </div> | |
| """ | |