DeepBoner / docs /bugs /p3-progress-bar-positioning.md
VibecoderMcSwaggins's picture
style(docs): Rename all docs to kebab-case for consistency
1838162

A newer version of the Gradio SDK is available: 6.1.0

Upgrade

P3 Bug: Progress Bar Positioning/Overlap in ChatInterface

Severity: P3 (Low - UX polish) Status: Open Discovered: 2025-12-01 Reporter: Internal QA

Symptom

The gr.Progress() bar renders in a strange position when used inside ChatInterface:

  • Progress bar appears to "float" in the middle of the chat output
  • Text overlaps with progress bar elements
  • Bar appears static/stuck at certain percentages
  • Visual artifact: Round 2/5 (~2m 15s remaining) - 48.0% renders both above and inside bar

Screenshot Evidence

Progress bar appears inline between chat messages rather than in a fixed position (top/bottom of component).

Root Cause Analysis

The Conflict

We're mixing two different progress mechanisms:

  1. gr.Progress() - Gradio's general-purpose progress bar API

    • Designed for gr.Interface and gr.Blocks functions
    • Renders as an overlay on the output component
  2. ChatInterface.show_progress - Built-in chat progress

    • Options: "full", "minimal", "hidden"
    • "full" = spinner + runtime display
    • "minimal" = runtime display only (default)

When both are used together in ChatInterface, the gr.Progress() bar fights for position with the chat's streaming output, causing visual glitches.

Current Implementation (src/app.py)

async def research_agent(
    message: str,
    history: list[dict[str, Any]],
    ...
    progress: gr.Progress = gr.Progress(),  # <- ISSUE: Manual progress bar
) -> AsyncGenerator[str, None]:
    ...
    if event.type == "started":
        progress(0, desc="Starting research...")  # <- Updates overlay
    elif event.type == "progress":
        progress(p, desc=event.message)  # <- Conflicts with chat streaming

Gradio Documentation

From Gradio ChatInterface Docs:

show_progress: how to show the progress animation while event is running: 'full' shows a spinner which covers the output component area as well as a runtime display in the upper right corner, 'minimal' only shows the runtime display, 'hidden' shows no progress animation at all

From GitHub Issue #5967:

gr.Progress is "not integrated with ChatInterface or Chatbots" - known limitation.

Impact

Aspect Impact
Functionality None - app works correctly
UX Visual confusion, looks unprofessional
Accessibility May cause screen reader confusion

Proposed Solutions

Option 1: Remove gr.Progress() - Use Chat Text Only (RECOMMENDED)

Remove the gr.Progress() parameter entirely and rely on our existing emoji status messages:

async def research_agent(
    message: str,
    history: list[dict[str, Any]],
    domain: str = "sexual_health",
    api_key: str = "",
    api_key_state: str = "",
    # REMOVED: progress: gr.Progress = gr.Progress(),
) -> AsyncGenerator[str, None]:
    ...
    # Keep emoji status updates in chat output
    # ⏱️ **PROGRESS**: Round 1/5 (~3m 0s remaining)
    # These are already being yielded to the chat

Pros:

  • Simplest solution
  • No CSS hacks
  • Status is visible in chat history
  • Works with ChatInterface's built-in progress

Cons:

  • No visual progress bar (just text status)

Option 2: Use show_progress="full" Parameter

Add explicit show_progress to ChatInterface and remove gr.Progress():

demo = gr.ChatInterface(
    fn=research_agent,
    title="🍆 DeepBoner",
    show_progress="full",  # Built-in spinner + runtime
    ...
)

Pros:

  • Uses Gradio's intended mechanism
  • Consistent with Gradio UX patterns

Cons:

  • Less granular (no percentage, no custom desc)

Option 3: Custom Progress Component with CSS (COMPLEX)

Wrap ChatInterface in gr.Blocks and add a separate gr.HTML progress bar outside the chat:

with gr.Blocks() as demo:
    progress_html = gr.HTML("<div id='custom-progress'></div>", visible=False)
    chat = gr.ChatInterface(...)

    # Update progress_html separately from chat

Pros:

  • Full control over positioning
  • Can match exact design requirements

Cons:

  • Significant refactor required
  • May break MCP server integration
  • More moving parts = more bugs

Option 4: Hybrid - Progress in additional_inputs_accordion

Place a separate gr.Progress component in a fixed position (e.g., accordion header):

with gr.Blocks() as demo:
    with gr.Row():
        progress_bar = gr.Slider(0, 100, value=0, label="Progress", interactive=False)
    chat = gr.ChatInterface(...)

Pros:

  • Fixed position, no overlap

Cons:

  • Requires gr.Blocks wrapper (breaks MCP?)
  • Clunky UX

Recommended Fix

Option 1: Remove gr.Progress(), keep emoji status text

Rationale:

  1. Our emoji status updates (⏱️ **PROGRESS**: Round 2/5) already provide the information
  2. gr.Progress() was never designed for ChatInterface
  3. Removing it eliminates the positioning conflict entirely
  4. ChatInterface's built-in show_progress="minimal" handles the spinner

Implementation Plan

  1. Remove progress: gr.Progress = gr.Progress() parameter from research_agent()
  2. Remove all progress(...) calls in the function
  3. Keep emoji status yields (⏱️ **PROGRESS**: ...)
  4. Optionally add show_progress="minimal" to ChatInterface (already default)
  5. Test on HuggingFace Spaces

Testing

# Local test
uv run python -c "from src.app import create_demo; demo, _ = create_demo(); demo.launch()"

# Verify:
# 1. No floating progress bar
# 2. Emoji status updates visible in chat
# 3. ChatInterface spinner works as expected

References

Version Info

  • Gradio: >=6.0.0 (pyproject.toml constraint)
  • ChatInterface: Streaming async generator mode
  • MCP Server: Enabled