/* ───────────────────────────────────────────────────────────────────────── * Custom chat surface * * Layout * ────── * #hy-chat-host → the gr.HTML wrapper component. Owns the * viewport-relative height; pure flex container. * #hy-chat → our scrollable chat surface. Append-only DOM * owned by static/chat.js; one .message-row per * bubble. * * The rest of the file is component styling for the renderer's emitted * DOM. The class names (``.message-row``, ``.message``, ``.bot``, * ``.user``, ``.markdown``) are deliberately shared with the rules in * _chatbot.css / _math.css / _thinking.css / _misc.css so those * rulesets apply transparently to our bubbles. * ───────────────────────────────────────────────────────────────────────── */ /* ── Outer wrapper: takes up the available vertical space. ───────────── * We use absolute positioning on #hy-chat so it pins to all four edges * of the host regardless of how many intermediate wrapper divs Gradio * inserts (varies between versions). The host establishes the * positioning context with ``position: relative``. */ #hy-chat-host { position: relative !important; height: calc(100vh - 200px) !important; padding: 0 !important; margin: 0 !important; background: var(--hy-bg) !important; border: none !important; box-shadow: none !important; min-height: 0 !important; overflow: hidden !important; } /* Strip styling and positioning from any wrappers Gradio inserts * between #hy-chat-host and #hy-chat — they would otherwise create new * containing blocks and break the absolute-positioned scroll surface. */ #hy-chat-host > div, #hy-chat-host > div > div { position: static !important; padding: 0 !important; margin: 0 !important; background: transparent !important; border: none !important; box-shadow: none !important; height: auto !important; } /* ── Inner scrollable surface ──────────────────────────────────────────── */ #hy-chat { position: absolute; inset: 0; overflow-y: auto; overflow-x: hidden; padding: 16px 24px 32px; background: var(--hy-bg); scroll-behavior: auto; /* programmatic scroll-to-bottom should be instant */ } /* ── Message rows ──────────────────────────────────────────────────────── */ #hy-chat .message-row { display: block; width: 100%; margin: 0 0 4px; padding: 0; } #hy-chat .message-row.user-row { /* Right-align user messages similar to Gradio's bubble layout. */ display: flex; justify-content: flex-end; margin: 12px 0 8px; } #hy-chat .message-row.bot-row { margin: 0 0 18px; } /* ── Inner message containers ──────────────────────────────────────────── */ #hy-chat .message { max-width: 95%; background: transparent; border: none; padding: 0; } #hy-chat .message.user { background: var(--hy-bg-muted, #f5f5f7); border-radius: 18px; padding: 10px 18px !important; max-width: min(720px, 75%); } .dark #hy-chat .message.user { background: #2a2a2a; } #hy-chat .message.bot { background: transparent; padding: 4px 0 !important; } /* ── User-text appearance (no markdown rendering — pre-wrap text). ────── */ #hy-chat .hy-user-text { font-size: 15px; line-height: 1.7; color: var(--hy-text); } /* ── Frozen vs streaming segments inside the assistant bubble ────────── * Both are direct children of .markdown so the existing #hy-chat .bot * .markdown styling cascades into them transparently. The only thing * we add here is to mark the streaming segment so future debugging or * styling can target it (e.g. a subtle pulse to indicate "still * generating" without flickering already-rendered formulas). */ #hy-chat .hy-frozen, #hy-chat .hy-streaming { display: block; } /* ── Tool-call display block (server-rendered Markdown HTML). ─────────── */ #hy-chat .hy-tool-calls { margin-top: 10px; } #hy-chat .hy-tool-call + .hy-tool-call { margin-top: 8px; } /* ── Hide the delta channel completely ───────────────────────────────── * The bridge component for delta JSON. Must never occupy layout, must * never trap pointer / focus / scroll events — but its content has to * remain readable to the JS MutationObserver that drives the renderer * (so we cannot use display:none). */ #hy-chat-delta, #hy-chat-delta * { position: absolute !important; width: 0 !important; height: 0 !important; margin: 0 !important; padding: 0 !important; border: 0 !important; overflow: hidden !important; clip: rect(0 0 0 0) !important; clip-path: inset(50%) !important; visibility: hidden !important; opacity: 0 !important; pointer-events: none !important; user-select: none !important; }