Spaces:
Sleeping
Sleeping
vighnesh-shetty-vs commited on
Commit ·
526fab1
1
Parent(s): 2bf4a82
Add updated files
Browse files
app.py
CHANGED
|
@@ -131,40 +131,33 @@ def get_impact_cards(impact, category):
|
|
| 131 |
"""
|
| 132 |
|
| 133 |
def format_alt_button(text, impact):
|
| 134 |
-
"""Formats the button text with spacing and potential savings metrics."""
|
| 135 |
icon = "💡"
|
| 136 |
if "Search" in text or "Google" in text: icon = "🔍"
|
| 137 |
elif "Wikipedia" in text or "Encyclopedia" in text: icon = "📚"
|
| 138 |
elif "Expert" in text or "Quora" in text: icon = "👤"
|
| 139 |
elif "Wolfram" in text or "Geogebra" in text or "Desmos" in text: icon = "🧮"
|
| 140 |
elif "Reddit" in text or "Gutenberg" in text or "Thesaurus" in text: icon = "📝"
|
| 141 |
-
|
| 142 |
return f"{icon} {text}\n\n🏆 Score: +10 Pts\n💧 Save: {impact['water_ml']} mL\n⚡ Save: {impact['energy_wh']} Wh\n☁️ Save: {impact['co2_g']} g"
|
| 143 |
|
| 144 |
def generate_victory_dashboard(state):
|
| 145 |
-
"""Generates a
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
total_energy_saved = 0
|
| 150 |
-
total_co2_saved = 0
|
| 151 |
|
| 152 |
rows = ""
|
| 153 |
for item in state["history"]:
|
| 154 |
-
# If it was a justified complex query, there are no savings
|
| 155 |
if item['alt_name'] == "None (Justified Use)":
|
| 156 |
saved_w, saved_e, saved_c = 0.0, 0.0, 0.0
|
| 157 |
alt_display = "<span style='color: #94a3b8;'>Justified Use (No Alternative)</span>"
|
| 158 |
else:
|
| 159 |
-
|
| 160 |
-
saved_w = max(0, item['water_ml'] - 0.0)
|
| 161 |
saved_e = max(0, item['energy_wh'] - 0.001)
|
| 162 |
saved_c = max(0, item['co2_g'] - 0.01)
|
| 163 |
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
alt_display = f"<span style='color: #34d399;'>{item['alt_name']}</span>"
|
| 169 |
|
| 170 |
rows += f"""
|
|
@@ -177,55 +170,40 @@ def generate_victory_dashboard(state):
|
|
| 177 |
"""
|
| 178 |
|
| 179 |
return f"""
|
| 180 |
-
<style>
|
| 181 |
-
@keyframes pulseGlow {{
|
| 182 |
-
0% {{ box-shadow: 0 0 20px rgba(52, 211, 153, 0.4); }}
|
| 183 |
-
50% {{ box-shadow: 0 0 60px rgba(52, 211, 153, 0.8); }}
|
| 184 |
-
100% {{ box-shadow: 0 0 20px rgba(52, 211, 153, 0.4); }}
|
| 185 |
-
}}
|
| 186 |
-
.victory-dashboard {{ background: linear-gradient(135deg, #0f172a, #1e293b); padding: 40px; border-radius: 24px; border: 2px solid #34d399; animation: pulseGlow 2s infinite; color: white; }}
|
| 187 |
-
.victory-title {{ text-align: center; color: #34d399; font-size: 3.5em !important; text-shadow: 0 0 20px rgba(52, 211, 153, 0.6); margin-bottom: 5px; }}
|
| 188 |
-
.victory-subtitle {{ text-align: center; font-size: 1.3em; color: #a7f3d0; margin-bottom: 30px; }}
|
| 189 |
-
.victory-stats-row {{ display: flex; gap: 20px; margin-top: 30px; margin-bottom: 40px; }}
|
| 190 |
-
.v-stat-box {{ flex: 1; background: rgba(0,0,0,0.4); padding: 25px; border-radius: 16px; text-align: center; border: 1px solid rgba(52, 211, 153, 0.3); }}
|
| 191 |
-
.v-stat-box h4 {{ color: #94a3b8; font-size: 0.9em; text-transform: uppercase; letter-spacing: 1.5px; margin: 0 0 10px 0; }}
|
| 192 |
-
.v-stat-box h2 {{ color: #38bdf8; font-size: 2.8em; margin: 0; }}
|
| 193 |
-
.v-stat-highlight h2 {{ color: #34d399; }}
|
| 194 |
-
</style>
|
| 195 |
-
|
| 196 |
<div class="victory-dashboard">
|
| 197 |
-
<
|
| 198 |
-
<
|
|
|
|
| 199 |
|
| 200 |
<div class="victory-stats-row">
|
| 201 |
<div class="v-stat-box">
|
| 202 |
-
<h4>
|
| 203 |
<h2>{state['water']:.2f} L</h2>
|
| 204 |
</div>
|
| 205 |
-
<div class="v-stat-box
|
| 206 |
-
<h4
|
| 207 |
-
<p style="
|
| 208 |
-
<p style="
|
| 209 |
-
<p style="
|
| 210 |
</div>
|
| 211 |
<div class="v-stat-box" style="border-color: #fca5a5;">
|
| 212 |
-
<h4 style="color: #fca5a5;">
|
| 213 |
-
<p style="
|
| 214 |
-
<p style="
|
| 215 |
-
<p style="
|
| 216 |
</div>
|
| 217 |
</div>
|
| 218 |
|
| 219 |
-
<h3 style="margin-top:
|
| 220 |
-
<p style="opacity: 0.9; margin-bottom: 20px;
|
| 221 |
|
| 222 |
<div style="max-height: 400px; overflow-y: auto; background: rgba(15, 23, 42, 0.9); border-radius: 12px; border: 1px solid rgba(255,255,255,0.1);">
|
| 223 |
-
<table style="width: 100%; text-align: left; border-collapse: collapse; font-size:
|
| 224 |
<tr style="background: rgba(56, 189, 248, 0.1); border-bottom: 2px solid #38bdf8;">
|
| 225 |
-
<th style="padding: 15px;
|
| 226 |
-
<th style="padding: 15px;
|
| 227 |
-
<th style="padding: 15px;
|
| 228 |
-
<th style="padding: 15px;
|
| 229 |
</tr>
|
| 230 |
{rows}
|
| 231 |
</table>
|
|
@@ -254,19 +232,17 @@ def process_query(user_input, selected_model, state):
|
|
| 254 |
|
| 255 |
if category == "complex research query":
|
| 256 |
state["points"] += 50
|
| 257 |
-
dialogue = f"Finally, a
|
| 258 |
else:
|
| 259 |
show_alts = True
|
| 260 |
raw_alts = ALTERNATIVES.get(category, [{"text": "Google", "url": "https://google.com"}])
|
| 261 |
state["current_best_alt"] = raw_alts[0]
|
| 262 |
best_alt_name = raw_alts[0]["text"]
|
| 263 |
-
|
| 264 |
random.shuffle(raw_alts)
|
| 265 |
state["current_shuffled_alts"] = raw_alts
|
| 266 |
alt_choices = [format_alt_button(a["text"], impact) for a in raw_alts]
|
| 267 |
dialogue = get_dialogue(state["water"], category)
|
| 268 |
|
| 269 |
-
# Log the exact metrics for the victory dashboard
|
| 270 |
state["history"].append({
|
| 271 |
"query": user_input,
|
| 272 |
"water_ml": impact["water_ml"],
|
|
@@ -279,9 +255,8 @@ def process_query(user_input, selected_model, state):
|
|
| 279 |
drip_ui = get_drip_visuals(state["water"])
|
| 280 |
impact_ui = get_impact_cards(impact, category)
|
| 281 |
audio_path = generate_audio(dialogue)
|
| 282 |
-
feedback_text = f"<div class='feedback-box'>🧠 <strong>{category.upper()}</strong> Detected
|
| 283 |
|
| 284 |
-
# Check for Win/Loss immediately upon sending query
|
| 285 |
if state["points"] >= 500 or state["water"] <= 0:
|
| 286 |
state["game_over"] = True
|
| 287 |
victory_html = generate_victory_dashboard(state)
|
|
@@ -297,7 +272,7 @@ def select_alternative(choice_text, state):
|
|
| 297 |
if state["game_over"] or not state.get("current_best_alt"):
|
| 298 |
return state, gr.update(), gr.update(), gr.update(), gr.update(visible=True), gr.update(visible=False), gr.update()
|
| 299 |
|
| 300 |
-
clean_choice = choice_text.split("\n")[
|
| 301 |
selected_dict = next((item for item in state["current_shuffled_alts"] if item["text"] == clean_choice), None)
|
| 302 |
|
| 303 |
if clean_choice == state["current_best_alt"]["text"]:
|
|
@@ -308,7 +283,6 @@ def select_alternative(choice_text, state):
|
|
| 308 |
|
| 309 |
stats_ui = get_stats_html(state["water"], state["energy"], state["co2"], state["points"])
|
| 310 |
|
| 311 |
-
# Check for win after points are awarded from selecting an alternative
|
| 312 |
if state["points"] >= 500:
|
| 313 |
state["game_over"] = True
|
| 314 |
victory_html = generate_victory_dashboard(state)
|
|
@@ -353,19 +327,27 @@ h1 { text-align: center; color: #38bdf8; text-shadow: 0 0 15px rgba(56, 189, 248
|
|
| 353 |
.bad-impact { background: linear-gradient(180deg, rgba(127, 29, 29, 0.3), rgba(69, 10, 10, 0.5)); border-top: 3px solid #ef4444; }
|
| 354 |
.good-impact { background: linear-gradient(180deg, rgba(20, 83, 45, 0.3), rgba(6, 78, 59, 0.5)); border-top: 3px solid #10b981; }
|
| 355 |
|
| 356 |
-
/* Enhanced Alternative Cards */
|
| 357 |
.alt-group { padding: 25px; background: linear-gradient(145deg, rgba(16, 185, 129, 0.05), rgba(6, 95, 70, 0.1)); border-radius: 20px; border: 1px solid rgba(16, 185, 129, 0.3); margin-top: 25px; box-shadow: 0 10px 30px rgba(0,0,0,0.2);}
|
| 358 |
.alt-card { background: linear-gradient(145deg, #1e293b, #0f172a) !important; border: 1px solid #10b981 !important; color: #a7f3d0 !important; height: auto !important; min-height: 180px !important; border-radius: 16px !important; font-size: 1.05em !important; font-weight: 500 !important; white-space: pre-wrap !important; line-height: 1.5 !important; padding: 15px !important; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; cursor: pointer; box-shadow: 0 4px 15px rgba(0,0,0,0.4) !important;}
|
| 359 |
.alt-card:hover { background: linear-gradient(145deg, #064e3b, #065f46) !important; transform: translateY(-8px) !important; box-shadow: 0 12px 30px rgba(16, 185, 129, 0.3) !important; color: white !important; border-color: #34d399 !important;}
|
| 360 |
.custom-dropdown { background: rgba(30, 41, 59, 0.8) !important; border: 1px solid #38bdf8 !important; border-radius: 12px !important; }
|
| 361 |
|
| 362 |
-
/* Victory Dashboard
|
| 363 |
-
|
| 364 |
-
.victory-
|
|
|
|
| 365 |
.victory-stats-row { display: flex; gap: 20px; margin-top: 30px; }
|
| 366 |
-
.v-stat-box { flex: 1; background: rgba(0,0,0,0.
|
| 367 |
.v-stat-box h4 { color: #94a3b8; font-size: 0.9em; text-transform: uppercase; letter-spacing: 1px; margin: 0 0 10px 0; }
|
| 368 |
.v-stat-box h2 { color: #38bdf8; font-size: 2.5em; margin: 0; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 369 |
"""
|
| 370 |
|
| 371 |
with gr.Blocks(title="EcoQueryQuest") as demo:
|
|
@@ -377,10 +359,8 @@ with gr.Blocks(title="EcoQueryQuest") as demo:
|
|
| 377 |
gr.Markdown("<h1>🌍 EcoQueryQuest</h1>")
|
| 378 |
gr.Markdown("<div class='subtitle'>Select a model, type a query, and watch Drip react. Reach 500 points to win!</div>")
|
| 379 |
|
| 380 |
-
# The main stats dashboard remains visible
|
| 381 |
stats_html = gr.HTML(get_stats_html(10.0, 0.0, 0.0, 0))
|
| 382 |
|
| 383 |
-
# ---- MAIN GAME UI ----
|
| 384 |
with gr.Column(visible=True) as main_game_ui:
|
| 385 |
with gr.Row():
|
| 386 |
with gr.Column(scale=1, elem_classes=["drip-pod"]):
|
|
@@ -390,11 +370,9 @@ with gr.Blocks(title="EcoQueryQuest") as demo:
|
|
| 390 |
|
| 391 |
with gr.Column(scale=2):
|
| 392 |
model_selector = gr.Dropdown(choices=list(MODELS.keys()), value="GPT-5.4", label="Select Target LLM Backend", elem_classes=["custom-dropdown"])
|
| 393 |
-
|
| 394 |
with gr.Row(elem_classes=["input-row"]):
|
| 395 |
-
user_input = gr.Textbox(show_label=False, placeholder="Type your query here...
|
| 396 |
submit_btn = gr.Button("Send Query", elem_classes=["custom-btn"], scale=1)
|
| 397 |
-
|
| 398 |
impact_display = gr.HTML()
|
| 399 |
|
| 400 |
with gr.Group(visible=False, elem_classes=["alt-group"]) as alternatives_group:
|
|
@@ -405,18 +383,15 @@ with gr.Blocks(title="EcoQueryQuest") as demo:
|
|
| 405 |
alt_btn_3 = gr.Button("", elem_classes=["alt-card"])
|
| 406 |
alt_feedback = gr.Markdown()
|
| 407 |
|
| 408 |
-
# ---- VICTORY SCREEN UI ----
|
| 409 |
with gr.Column(visible=False) as victory_ui:
|
| 410 |
victory_display = gr.HTML()
|
| 411 |
|
| 412 |
-
# Ensure these outputs match EXACTLY with the 15 return items in process_query
|
| 413 |
submit_btn.click(
|
| 414 |
fn=process_query,
|
| 415 |
inputs=[user_input, model_selector, game_state],
|
| 416 |
outputs=[game_state, stats_html, drip_html, drip_audio, impact_display, drip_feedback, alternatives_group, alt_btn_1, alt_btn_2, alt_btn_3, alt_feedback, user_input, main_game_ui, victory_ui, victory_display]
|
| 417 |
)
|
| 418 |
|
| 419 |
-
# Ensure these outputs match EXACTLY with the 7 return items in select_alternative
|
| 420 |
for btn in [alt_btn_1, alt_btn_2, alt_btn_3]:
|
| 421 |
btn.click(
|
| 422 |
fn=select_alternative,
|
|
|
|
| 131 |
"""
|
| 132 |
|
| 133 |
def format_alt_button(text, impact):
|
|
|
|
| 134 |
icon = "💡"
|
| 135 |
if "Search" in text or "Google" in text: icon = "🔍"
|
| 136 |
elif "Wikipedia" in text or "Encyclopedia" in text: icon = "📚"
|
| 137 |
elif "Expert" in text or "Quora" in text: icon = "👤"
|
| 138 |
elif "Wolfram" in text or "Geogebra" in text or "Desmos" in text: icon = "🧮"
|
| 139 |
elif "Reddit" in text or "Gutenberg" in text or "Thesaurus" in text: icon = "📝"
|
|
|
|
| 140 |
return f"{icon} {text}\n\n🏆 Score: +10 Pts\n💧 Save: {impact['water_ml']} mL\n⚡ Save: {impact['energy_wh']} Wh\n☁️ Save: {impact['co2_g']} g"
|
| 141 |
|
| 142 |
def generate_victory_dashboard(state):
|
| 143 |
+
"""Generates a celebratory end-game report with detailed query analytics[cite: 14, 41]."""
|
| 144 |
+
total_w_saved = 0
|
| 145 |
+
total_e_saved = 0
|
| 146 |
+
total_c_saved = 0
|
|
|
|
|
|
|
| 147 |
|
| 148 |
rows = ""
|
| 149 |
for item in state["history"]:
|
|
|
|
| 150 |
if item['alt_name'] == "None (Justified Use)":
|
| 151 |
saved_w, saved_e, saved_c = 0.0, 0.0, 0.0
|
| 152 |
alt_display = "<span style='color: #94a3b8;'>Justified Use (No Alternative)</span>"
|
| 153 |
else:
|
| 154 |
+
saved_w = item['water_ml']
|
|
|
|
| 155 |
saved_e = max(0, item['energy_wh'] - 0.001)
|
| 156 |
saved_c = max(0, item['co2_g'] - 0.01)
|
| 157 |
|
| 158 |
+
total_w_saved += saved_w
|
| 159 |
+
total_e_saved += saved_e
|
| 160 |
+
total_c_saved += saved_c
|
|
|
|
| 161 |
alt_display = f"<span style='color: #34d399;'>{item['alt_name']}</span>"
|
| 162 |
|
| 163 |
rows += f"""
|
|
|
|
| 170 |
"""
|
| 171 |
|
| 172 |
return f"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
<div class="victory-dashboard">
|
| 174 |
+
<div class="confetti"></div>
|
| 175 |
+
<h1 class="victory-title">🏆 PLANET SAVED! 🏆</h1>
|
| 176 |
+
<p style="text-align: center; font-size: 1.3em; color: #a7f3d0; margin-bottom: 30px;">You reached {state['points']} points and kept Drip alive!</p>
|
| 177 |
|
| 178 |
<div class="victory-stats-row">
|
| 179 |
<div class="v-stat-box">
|
| 180 |
+
<h4>Final Water</h4>
|
| 181 |
<h2>{state['water']:.2f} L</h2>
|
| 182 |
</div>
|
| 183 |
+
<div class="v-stat-box" style="border-color: #34d399;">
|
| 184 |
+
<h4 style="color: #34d399;">Planetary Savings</h4>
|
| 185 |
+
<p style="color: #34d399; margin: 5px 0;">💧 {total_w_saved:.1f} mL Saved</p>
|
| 186 |
+
<p style="color: #facc15; margin: 5px 0;">⚡ {total_e_saved:.2f} Wh Saved</p>
|
| 187 |
+
<p style="color: #a8a29e; margin: 5px 0;">☁️ {total_c_saved:.2f} g Saved</p>
|
| 188 |
</div>
|
| 189 |
<div class="v-stat-box" style="border-color: #fca5a5;">
|
| 190 |
+
<h4 style="color: #fca5a5;">Total Usage</h4>
|
| 191 |
+
<p style="color: #fca5a5; margin: 5px 0;">💧 {10.0 - state['water']:.2f} L</p>
|
| 192 |
+
<p style="color: #fca5a5; margin: 5px 0;">⚡ {state['energy']:.4f} kWh</p>
|
| 193 |
+
<p style="color: #fca5a5; margin: 5px 0;">☁️ {state['co2']:.2f} g</p>
|
| 194 |
</div>
|
| 195 |
</div>
|
| 196 |
|
| 197 |
+
<h3 style="margin-top: 40px; color: #38bdf8; font-size: 1.8em;">📊 AI Usage Audit Log</h3>
|
| 198 |
+
<p style="opacity: 0.9; margin-bottom: 20px;">Detailed breakdown of your session's environmental impact[cite: 14].</p>
|
| 199 |
|
| 200 |
<div style="max-height: 400px; overflow-y: auto; background: rgba(15, 23, 42, 0.9); border-radius: 12px; border: 1px solid rgba(255,255,255,0.1);">
|
| 201 |
+
<table style="width: 100%; text-align: left; border-collapse: collapse; font-size: 1em;">
|
| 202 |
<tr style="background: rgba(56, 189, 248, 0.1); border-bottom: 2px solid #38bdf8;">
|
| 203 |
+
<th style="padding: 15px;">User Query</th>
|
| 204 |
+
<th style="padding: 15px;">LLM Cost (Wasted)</th>
|
| 205 |
+
<th style="padding: 15px;">Best Alternative</th>
|
| 206 |
+
<th style="padding: 15px;">Potential Savings</th>
|
| 207 |
</tr>
|
| 208 |
{rows}
|
| 209 |
</table>
|
|
|
|
| 232 |
|
| 233 |
if category == "complex research query":
|
| 234 |
state["points"] += 50
|
| 235 |
+
dialogue = f"A legitimate research query! Finally, a worthy use of my massive architecture. Drain away!"
|
| 236 |
else:
|
| 237 |
show_alts = True
|
| 238 |
raw_alts = ALTERNATIVES.get(category, [{"text": "Google", "url": "https://google.com"}])
|
| 239 |
state["current_best_alt"] = raw_alts[0]
|
| 240 |
best_alt_name = raw_alts[0]["text"]
|
|
|
|
| 241 |
random.shuffle(raw_alts)
|
| 242 |
state["current_shuffled_alts"] = raw_alts
|
| 243 |
alt_choices = [format_alt_button(a["text"], impact) for a in raw_alts]
|
| 244 |
dialogue = get_dialogue(state["water"], category)
|
| 245 |
|
|
|
|
| 246 |
state["history"].append({
|
| 247 |
"query": user_input,
|
| 248 |
"water_ml": impact["water_ml"],
|
|
|
|
| 255 |
drip_ui = get_drip_visuals(state["water"])
|
| 256 |
impact_ui = get_impact_cards(impact, category)
|
| 257 |
audio_path = generate_audio(dialogue)
|
| 258 |
+
feedback_text = f"<div class='feedback-box'>🧠 <strong>{category.upper()}</strong> Detected!<br><br><em>\"{dialogue}\"</em></div>"
|
| 259 |
|
|
|
|
| 260 |
if state["points"] >= 500 or state["water"] <= 0:
|
| 261 |
state["game_over"] = True
|
| 262 |
victory_html = generate_victory_dashboard(state)
|
|
|
|
| 272 |
if state["game_over"] or not state.get("current_best_alt"):
|
| 273 |
return state, gr.update(), gr.update(), gr.update(), gr.update(visible=True), gr.update(visible=False), gr.update()
|
| 274 |
|
| 275 |
+
clean_choice = choice_text.split("\n")[0][2:].strip()
|
| 276 |
selected_dict = next((item for item in state["current_shuffled_alts"] if item["text"] == clean_choice), None)
|
| 277 |
|
| 278 |
if clean_choice == state["current_best_alt"]["text"]:
|
|
|
|
| 283 |
|
| 284 |
stats_ui = get_stats_html(state["water"], state["energy"], state["co2"], state["points"])
|
| 285 |
|
|
|
|
| 286 |
if state["points"] >= 500:
|
| 287 |
state["game_over"] = True
|
| 288 |
victory_html = generate_victory_dashboard(state)
|
|
|
|
| 327 |
.bad-impact { background: linear-gradient(180deg, rgba(127, 29, 29, 0.3), rgba(69, 10, 10, 0.5)); border-top: 3px solid #ef4444; }
|
| 328 |
.good-impact { background: linear-gradient(180deg, rgba(20, 83, 45, 0.3), rgba(6, 78, 59, 0.5)); border-top: 3px solid #10b981; }
|
| 329 |
|
|
|
|
| 330 |
.alt-group { padding: 25px; background: linear-gradient(145deg, rgba(16, 185, 129, 0.05), rgba(6, 95, 70, 0.1)); border-radius: 20px; border: 1px solid rgba(16, 185, 129, 0.3); margin-top: 25px; box-shadow: 0 10px 30px rgba(0,0,0,0.2);}
|
| 331 |
.alt-card { background: linear-gradient(145deg, #1e293b, #0f172a) !important; border: 1px solid #10b981 !important; color: #a7f3d0 !important; height: auto !important; min-height: 180px !important; border-radius: 16px !important; font-size: 1.05em !important; font-weight: 500 !important; white-space: pre-wrap !important; line-height: 1.5 !important; padding: 15px !important; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; cursor: pointer; box-shadow: 0 4px 15px rgba(0,0,0,0.4) !important;}
|
| 332 |
.alt-card:hover { background: linear-gradient(145deg, #064e3b, #065f46) !important; transform: translateY(-8px) !important; box-shadow: 0 12px 30px rgba(16, 185, 129, 0.3) !important; color: white !important; border-color: #34d399 !important;}
|
| 333 |
.custom-dropdown { background: rgba(30, 41, 59, 0.8) !important; border: 1px solid #38bdf8 !important; border-radius: 12px !important; }
|
| 334 |
|
| 335 |
+
/* Victory Dashboard & Celebration */
|
| 336 |
+
@keyframes pulseGlow { 0% { box-shadow: 0 0 20px rgba(52, 211, 153, 0.4); } 50% { box-shadow: 0 0 50px rgba(52, 211, 153, 0.8); } 100% { box-shadow: 0 0 20px rgba(52, 211, 153, 0.4); } }
|
| 337 |
+
.victory-dashboard { background: linear-gradient(135deg, #0f172a, #1e293b); padding: 40px; border-radius: 24px; border: 2px solid #34d399; animation: pulseGlow 2s infinite; color: white; position: relative; overflow: hidden; }
|
| 338 |
+
.victory-title { text-align: center; color: #34d399; font-size: 3.5em !important; text-shadow: 0 0 20px rgba(52, 211, 153, 0.6); margin-bottom: 5px; }
|
| 339 |
.victory-stats-row { display: flex; gap: 20px; margin-top: 30px; }
|
| 340 |
+
.v-stat-box { flex: 1; background: rgba(0,0,0,0.4); padding: 25px; border-radius: 16px; text-align: center; border: 1px solid rgba(52, 211, 153, 0.3); }
|
| 341 |
.v-stat-box h4 { color: #94a3b8; font-size: 0.9em; text-transform: uppercase; letter-spacing: 1px; margin: 0 0 10px 0; }
|
| 342 |
.v-stat-box h2 { color: #38bdf8; font-size: 2.5em; margin: 0; }
|
| 343 |
+
|
| 344 |
+
/* Confetti Blast Animation */
|
| 345 |
+
.confetti { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 10; }
|
| 346 |
+
.confetti::before { content: '🎉'; position: absolute; font-size: 2rem; animation: scatter 3s infinite linear; }
|
| 347 |
+
@keyframes scatter {
|
| 348 |
+
0% { transform: translate(50vw, 50vh) rotate(0deg); opacity: 1; }
|
| 349 |
+
100% { transform: translate(calc(100vw * var(--x)), calc(100vh * var(--y))) rotate(360deg); opacity: 0; }
|
| 350 |
+
}
|
| 351 |
"""
|
| 352 |
|
| 353 |
with gr.Blocks(title="EcoQueryQuest") as demo:
|
|
|
|
| 359 |
gr.Markdown("<h1>🌍 EcoQueryQuest</h1>")
|
| 360 |
gr.Markdown("<div class='subtitle'>Select a model, type a query, and watch Drip react. Reach 500 points to win!</div>")
|
| 361 |
|
|
|
|
| 362 |
stats_html = gr.HTML(get_stats_html(10.0, 0.0, 0.0, 0))
|
| 363 |
|
|
|
|
| 364 |
with gr.Column(visible=True) as main_game_ui:
|
| 365 |
with gr.Row():
|
| 366 |
with gr.Column(scale=1, elem_classes=["drip-pod"]):
|
|
|
|
| 370 |
|
| 371 |
with gr.Column(scale=2):
|
| 372 |
model_selector = gr.Dropdown(choices=list(MODELS.keys()), value="GPT-5.4", label="Select Target LLM Backend", elem_classes=["custom-dropdown"])
|
|
|
|
| 373 |
with gr.Row(elem_classes=["input-row"]):
|
| 374 |
+
user_input = gr.Textbox(show_label=False, placeholder="Type your query here...", elem_classes=["custom-textbox"], scale=4, lines=3)
|
| 375 |
submit_btn = gr.Button("Send Query", elem_classes=["custom-btn"], scale=1)
|
|
|
|
| 376 |
impact_display = gr.HTML()
|
| 377 |
|
| 378 |
with gr.Group(visible=False, elem_classes=["alt-group"]) as alternatives_group:
|
|
|
|
| 383 |
alt_btn_3 = gr.Button("", elem_classes=["alt-card"])
|
| 384 |
alt_feedback = gr.Markdown()
|
| 385 |
|
|
|
|
| 386 |
with gr.Column(visible=False) as victory_ui:
|
| 387 |
victory_display = gr.HTML()
|
| 388 |
|
|
|
|
| 389 |
submit_btn.click(
|
| 390 |
fn=process_query,
|
| 391 |
inputs=[user_input, model_selector, game_state],
|
| 392 |
outputs=[game_state, stats_html, drip_html, drip_audio, impact_display, drip_feedback, alternatives_group, alt_btn_1, alt_btn_2, alt_btn_3, alt_feedback, user_input, main_game_ui, victory_ui, victory_display]
|
| 393 |
)
|
| 394 |
|
|
|
|
| 395 |
for btn in [alt_btn_1, alt_btn_2, alt_btn_3]:
|
| 396 |
btn.click(
|
| 397 |
fn=select_alternative,
|