"""NeuroBait UI โ€” hybrid: a standard AI-chat experience (``gr.ChatInterface``, DeepSeek/Perplexity-ish) carrying NeuroBait's identity. gradio-only (no torch/spaces) so the UI can be built and smoke-tested without the model stack. Theme + CSS are applied at ``launch()`` because Gradio 6 moved ``theme``/``css``/``js`` off the Blocks/ChatInterface constructor โ€” see the deprecation warning we hit in the Space logs. Dark-only, earthy, calm sage-teal accent (gentler / more sensory-safe for ADHD than vivid green). The palette is set straight on ``:root`` and mapped onto Gradio's core CSS vars unconditionally, so the app is dark with no dependence on JS or HF's ``?__theme``. """ from __future__ import annotations import gradio as gr MOODS = ["Calm", "Tired", "Anxious", "Focused"] THEME = gr.themes.Soft(primary_hue="teal", neutral_hue="slate") TITLE = "๐Ÿง  NeuroBait" DESCRIPTION = ( "A warm space and a gentle boost for your everyday โ€” NeuroBait works *with* " "your dopamine, not against your willpower. Your brain isn't broken; it's " "wired for a different kind of focus." ) PLACEHOLDER = ( "### ๐Ÿง  NeuroBait\n" "Tell me what's on your mind โ€” no need to be tidy, just honest.\n\n" "*No red pen ยท no urgency ยท no streaks.*" ) EXAMPLES = [ ["There's something I've been meaning to get to, and it keeps slipping by.", "Calm"], ["My space feels a bit cluttered today.", "Tired"], ["My mind feels a little foggy right now.", "Anxious"], ] CSS = """ /* Earthy, always-dark palette on :root (no .dark dependency), with a calm muted sage-teal accent instead of vivid green. */ :root { --forest: #6fb6a2; --sage: #5da894; --mint: #20362f; --cream: #1d2128; --linen: #15181d; --sand: #2e333b; --stone: #aeb6bf; --charcoal: #eef1f4; --radius: 10px; --brand-grad: linear-gradient(135deg, #5da894, #4f9582); } /* map Gradio core theme vars onto our dark palette so built-in components render dark even when Gradio's own .dark class is off */ body, .gradio-container, gradio-app, .dark { --body-background-fill: var(--linen); --background-fill-primary: var(--cream); --background-fill-secondary: var(--linen); --block-background-fill: var(--cream); --block-label-background-fill: var(--cream); --block-border-color: var(--sand); --border-color-primary: var(--sand); --body-text-color: var(--charcoal); --body-text-color-subdued: var(--stone); --input-background-fill: var(--cream); --input-border-color: var(--sand); --button-secondary-background-fill: var(--cream); --button-secondary-text-color: var(--charcoal); } body, .gradio-container { background: var(--linen) !important; color: var(--charcoal) !important; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji" !important; } /* keep the conversation in a comfortable centered column like a modern chat app */ .gradio-container { max-width: 960px !important; margin: 0 auto !important; } h1 { letter-spacing: -0.5px; font-weight: 800; } /* user bubble gets the brand accent, assistant stays flat/quiet */ .message.user, [data-testid="user"] { background: var(--brand-grad) !important; color: #fff !important; border: none !important; } .message.bot, [data-testid="bot"] { background: var(--cream) !important; border: 1px solid var(--sand) !important; color: var(--charcoal) !important; } /* rounded, calm input + primary button */ textarea, input[type="text"] { border-radius: var(--radius) !important; background: var(--cream) !important; border: 1px solid var(--sand) !important; color: var(--charcoal) !important; } button.primary, .submit-button, [variant="primary"] { background: var(--brand-grad) !important; border: none !important; color: #fff !important; } """ def message_text(content) -> str: if isinstance(content, str): return content.strip() if isinstance(content, list): parts = [] for item in content: if isinstance(item, dict): text = item.get("text") if isinstance(text, str): parts.append(text) return " ".join(part.strip() for part in parts if part.strip()).strip() return "" def build_demo(respond_fn): """Build the NeuroBait chat. ``respond_fn`` signature (streaming generator ok): respond_fn(message: str, history: list[dict], mood: str) -> str | generator[str] """ mood = gr.Radio(MOODS, value="Calm", label="๐ŸŒณ How are you feeling?") chatbot = gr.Chatbot( height=560, show_label=False, placeholder=PLACEHOLDER, avatar_images=(None, None), ) return gr.ChatInterface( fn=respond_fn, chatbot=chatbot, additional_inputs=[mood], additional_inputs_accordion=gr.Accordion("๐ŸŒณ How are you feeling?", open=True), title=TITLE, description=DESCRIPTION, examples=EXAMPLES, cache_examples=False, save_history=True, fill_height=True, autoscroll=True, )