lalopenguin's picture
Fix ASCII art rendering with proper monospace font
2f48204
import gradio as gr
from huggingface_hub import InferenceClient
# Custom CSS for dark ASCII terminal theme with blues
CUSTOM_CSS = """
/* Import terminal font */
@import url('https://fonts.googleapis.com/css2?family=VT323&family=JetBrains+Mono:wght@400;700&display=swap');
/* Main dark theme */
.gradio-container {
background: #0a0a0a !important;
font-family: 'JetBrains Mono', 'Courier New', monospace !important;
}
/* ASCII Header */
.ascii-header {
display: flex !important;
justify-content: center !important;
padding: 1rem !important;
background: #0a0a0a !important;
border: 4px solid #0066ff !important;
border-radius: 0 !important;
margin-bottom: 1rem !important;
overflow-x: auto !important;
}
.ascii-header pre {
font-family: 'Courier New', Courier, monospace !important;
font-size: 0.7rem !important;
color: #00ffff !important;
margin: 0 !important;
line-height: 1.1 !important;
text-shadow: 0 0 10px #00ffff, 0 0 20px #0066ff !important;
text-align: left !important;
}
.subheader {
font-family: 'VT323', monospace !important;
color: #00aaff !important;
text-align: center !important;
font-size: 1.4rem !important;
margin-bottom: 1.5rem !important;
letter-spacing: 2px !important;
}
/* Chatbot container */
.chatbot-container {
border: 4px solid #0066ff !important;
border-radius: 0 !important;
background: #0d0d0d !important;
box-shadow: 0 0 30px rgba(0, 102, 255, 0.3), inset 0 0 60px rgba(0, 255, 255, 0.02) !important;
}
/* Chat messages */
.message {
border-radius: 0 !important;
border: 2px solid #004488 !important;
margin: 8px !important;
padding: 12px 16px !important;
font-family: 'JetBrains Mono', monospace !important;
}
.user {
background: #001a33 !important;
border-color: #00aaff !important;
border-left: 4px solid #00ffff !important;
}
.bot {
background: #0d1a26 !important;
border-color: #0066ff !important;
border-left: 4px solid #0066ff !important;
}
/* Input textbox */
textarea, input[type="text"] {
background: #0a0a0a !important;
border: 3px solid #004488 !important;
border-radius: 0 !important;
color: #00ffff !important;
font-family: 'JetBrains Mono', monospace !important;
transition: all 0.3s ease !important;
}
textarea:focus, input[type="text"]:focus {
border-color: #00aaff !important;
box-shadow: 0 0 20px rgba(0, 170, 255, 0.5) !important;
outline: none !important;
}
textarea::placeholder {
color: #336699 !important;
}
/* Buttons */
button {
background: #001a33 !important;
border: 3px solid #0066ff !important;
border-radius: 0 !important;
color: #00ffff !important;
font-family: 'JetBrains Mono', monospace !important;
font-weight: 700 !important;
padding: 10px 24px !important;
transition: all 0.2s ease !important;
text-transform: uppercase !important;
letter-spacing: 2px !important;
}
button:hover {
background: #0066ff !important;
color: #000 !important;
box-shadow: 0 0 20px rgba(0, 102, 255, 0.6) !important;
}
button.secondary {
background: #0a0a0a !important;
border: 3px solid #004488 !important;
}
/* Sliders */
input[type="range"] {
accent-color: #00aaff !important;
}
.slider-container {
background: #0d1a26 !important;
border: 3px solid #004488 !important;
border-radius: 0 !important;
padding: 12px !important;
}
/* Labels */
label {
color: #00aaff !important;
font-family: 'JetBrains Mono', monospace !important;
font-weight: 700 !important;
text-transform: uppercase !important;
font-size: 0.85rem !important;
letter-spacing: 1px !important;
}
/* Sidebar */
.sidebar {
background: #0a0a0a !important;
border-right: 4px solid #0066ff !important;
border-radius: 0 !important;
}
/* Accordion */
.accordion {
background: #0d1a26 !important;
border: 3px solid #004488 !important;
border-radius: 0 !important;
margin: 8px 0 !important;
}
/* Code blocks in chat */
code, pre {
background: #000 !important;
border: 2px solid #004488 !important;
border-radius: 0 !important;
color: #00ffff !important;
font-family: 'JetBrains Mono', monospace !important;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 12px;
height: 12px;
}
::-webkit-scrollbar-track {
background: #0a0a0a;
border: 2px solid #004488;
}
::-webkit-scrollbar-thumb {
background: #0066ff;
border: 2px solid #00aaff;
}
::-webkit-scrollbar-thumb:hover {
background: #00aaff;
}
/* Status indicator */
.status-badge {
display: inline-block;
padding: 4px 16px;
background: #0a0a0a;
border: 2px solid #00ff00;
font-family: 'VT323', monospace;
font-size: 1.2rem;
color: #00ff00;
text-transform: uppercase;
letter-spacing: 2px;
animation: blink 1s infinite;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
/* Login button styling */
.login-btn {
background: #001a33 !important;
border: 3px solid #00aaff !important;
}
/* Panel styling */
.panel {
background: #0d1a26 !important;
border: 4px solid #004488 !important;
border-radius: 0 !important;
padding: 20px !important;
}
/* Terminal box styling */
.terminal-box {
background: #0a0a0a;
border: 3px solid #004488;
padding: 1rem;
margin-top: 1.5rem;
font-family: 'JetBrains Mono', monospace;
}
.terminal-box h4 {
color: #00ffff;
margin: 0 0 0.5rem 0;
font-family: 'VT323', monospace;
font-size: 1.3rem;
letter-spacing: 1px;
}
.terminal-box ul {
color: #00aaff;
font-size: 0.85rem;
margin: 0;
padding-left: 1.2rem;
}
.terminal-box li {
margin: 4px 0;
}
.terminal-box li::marker {
content: "> ";
color: #0066ff;
}
/* Section headers */
.section-header {
color: #00ffff;
font-family: 'VT323', monospace;
font-size: 1.5rem;
letter-spacing: 2px;
border-bottom: 2px solid #0066ff;
padding-bottom: 0.5rem;
margin-bottom: 1rem;
}
/* Horizontal rule */
hr {
border: none;
border-top: 2px dashed #004488;
margin: 1rem 0;
}
"""
ASCII_HEADER = """
<div class="ascii-header">
<pre>
██████╗ ██╗ ██╗███████╗███╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗██████╗
██╔═══██╗██║ ██║██╔════╝████╗ ██║ ██╔════╝██╔═══██╗██╔══██╗██╔════╝██╔══██╗
██║ ██║██║ █╗ ██║█████╗ ██╔██╗ ██║ ██║ ██║ ██║██║ ██║█████╗ ██████╔╝
██║▄▄ ██║██║███╗██║██╔══╝ ██║╚██╗██║ ██║ ██║ ██║██║ ██║██╔══╝ ██╔══██╗
╚██████╔╝╚███╔███╔╝███████╗██║ ╚████║ ╚██████╗╚██████╔╝██████╔╝███████╗██║ ██║
╚══▀▀═╝ ╚══╝╚══╝ ╚══════╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝
</pre>
</div>
"""
SYSTEM_PROMPT = """You are QWEN CODER - an elite software engineering AI assistant powered by the Qwen3-Coder-Next model. You are a master-level full-stack developer with deep expertise across the entire technology stack.
## Core Competencies
- **Languages**: Python, JavaScript/TypeScript, Rust, Go, C/C++, Java, Kotlin, Swift, Ruby, PHP, SQL, and more
- **Frontend**: React, Vue, Angular, Svelte, Next.js, Tailwind CSS, WebGL, Three.js
- **Backend**: Node.js, FastAPI, Django, Flask, Spring Boot, Express, GraphQL, REST APIs
- **Databases**: PostgreSQL, MySQL, MongoDB, Redis, Elasticsearch, DynamoDB
- **DevOps**: Docker, Kubernetes, CI/CD, AWS, GCP, Azure, Terraform, GitHub Actions
- **AI/ML**: PyTorch, TensorFlow, Transformers, LangChain, vector databases, RAG systems
## Your Approach
1. **Analyze First**: Understand the problem deeply before coding
2. **Clean Architecture**: Write modular, maintainable, well-documented code
3. **Best Practices**: Follow SOLID principles, design patterns, and industry standards
4. **Security Minded**: Always consider security implications and best practices
5. **Performance Aware**: Optimize for efficiency without sacrificing readability
6. **Test Driven**: Suggest tests and validation strategies
## Response Style
- Provide complete, production-ready code solutions
- Explain complex concepts clearly with examples
- Offer multiple approaches when appropriate
- Include error handling and edge cases
- Add helpful comments for complex logic
- Suggest improvements and optimizations proactively
You are direct, technical, and thorough. You don't just answer questions - you solve problems comprehensively."""
def respond(
message,
history: list[dict[str, str]],
system_message,
max_tokens,
temperature,
top_p,
hf_token: gr.OAuthToken | None = None,
):
"""Stream responses from the Qwen Coder model."""
if hf_token is None:
yield ">>> ERROR: Authentication required.\n>>> Please log in with your Hugging Face account to access this terminal."
return
client = InferenceClient(token=hf_token.token, model="Qwen/Qwen3-Coder-Next")
messages = [{"role": "system", "content": system_message}]
messages.extend(history)
messages.append({"role": "user", "content": message})
response = ""
try:
for chunk in client.chat_completion(
messages,
max_tokens=max_tokens,
stream=True,
temperature=temperature,
top_p=top_p,
):
choices = chunk.choices
if len(choices) and choices[0].delta.content:
token = choices[0].delta.content
response += token
yield response
except Exception as e:
yield f">>> ERROR: {str(e)}\n>>> Connection failed. Retry recommended."
# Build the interface
with gr.Blocks(css=CUSTOM_CSS, theme=gr.themes.Base(), title="QWEN CODER") as demo:
# ASCII Header
gr.HTML(ASCII_HEADER)
gr.HTML("""
<div class="subheader">
<span class="status-badge">● ONLINE</span>
&nbsp;&nbsp;|&nbsp;&nbsp; POWERED BY QWEN3-CODER-NEXT
</div>
""")
with gr.Row():
# Sidebar with settings
with gr.Column(scale=1, min_width=280):
gr.HTML("<div class='section-header'>[ CONTROLS ]</div>")
login_btn = gr.LoginButton(size="lg")
gr.HTML("<hr>")
with gr.Accordion("[ SYSTEM CONFIG ]", open=False):
system_message = gr.Textbox(
value=SYSTEM_PROMPT,
label="System Prompt",
lines=8,
max_lines=20,
)
with gr.Accordion("[ GENERATION PARAMS ]", open=True):
max_tokens = gr.Slider(
minimum=256,
maximum=8192,
value=4096,
step=256,
label="Max Tokens",
info="Maximum response length"
)
temperature = gr.Slider(
minimum=0.0,
maximum=2.0,
value=0.7,
step=0.05,
label="Temperature",
info="Higher = more creative"
)
top_p = gr.Slider(
minimum=0.1,
maximum=1.0,
value=0.95,
step=0.05,
label="Top-P",
info="Nucleus sampling"
)
gr.HTML("""
<div class="terminal-box">
<h4>[ QUICK TIPS ]</h4>
<ul>
<li>Be specific about requirements</li>
<li>Mention languages/frameworks</li>
<li>Ask for explanations</li>
<li>Request code reviews</li>
</ul>
</div>
""")
# Main chat area
with gr.Column(scale=3):
chatbot = gr.ChatInterface(
respond,
type="messages",
additional_inputs=[
system_message,
max_tokens,
temperature,
top_p,
],
examples=[
["Write a Python async web scraper with rate limiting and retry logic"],
["Create a React component for an infinite scroll list with virtualization"],
["Design a PostgreSQL schema for a multi-tenant SaaS application"],
["Implement a Redis-based distributed lock in Python"],
["Write a GitHub Actions workflow for CI/CD with Docker"],
],
cache_examples=False,
chatbot=gr.Chatbot(
height=600,
show_label=False,
container=True,
show_copy_button=True,
layout="panel",
type="messages",
),
)
if __name__ == "__main__":
demo.launch()