Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -31,6 +31,7 @@ from syllabus_utils import extract_course_topics_from_file
|
|
| 31 |
HANBRIDGE_LOGO_PATH = "hanbridge_logo.png"
|
| 32 |
CLARE_LOGO_PATH = "clare_mascot.png"
|
| 33 |
CLARE_RUN_PATH = "Clare_Run.png"
|
|
|
|
| 34 |
|
| 35 |
# ================== Base64 Helper ==================
|
| 36 |
def image_to_base64(image_path):
|
|
@@ -187,13 +188,32 @@ CUSTOM_CSS = """
|
|
| 187 |
/* --- Main Header --- */
|
| 188 |
.header-container { padding: 10px 20px; background-color: #ffffff; border-bottom: 2px solid #f3f4f6; margin-bottom: 15px; display: flex; align-items: center; }
|
| 189 |
|
| 190 |
-
/*
|
| 191 |
-
.
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
|
| 198 |
/* User Guide */
|
| 199 |
.main-user-guide { border: none !important; background: transparent !important; box-shadow: none !important; }
|
|
@@ -236,7 +256,7 @@ with gr.Blocks(title="Clare – Hanbridge AI Teaching Assistant", css=CUSTOM_CSS
|
|
| 236 |
user_name_state = gr.State("")
|
| 237 |
user_id_state = gr.State("")
|
| 238 |
|
| 239 |
-
# --- Header
|
| 240 |
with gr.Row(elem_classes="header-container"):
|
| 241 |
# Left: Logo & Title
|
| 242 |
with gr.Column(scale=2):
|
|
@@ -257,37 +277,15 @@ with gr.Blocks(title="Clare – Hanbridge AI Teaching Assistant", css=CUSTOM_CSS
|
|
| 257 |
"""
|
| 258 |
)
|
| 259 |
|
| 260 |
-
# Right: Hanbridge Logo
|
| 261 |
with gr.Column(scale=1):
|
| 262 |
-
# Top: Logo
|
| 263 |
gr.HTML(
|
| 264 |
f"""
|
| 265 |
-
<div style="display:flex; justify-content:flex-end; margin-bottom:
|
| 266 |
<img src="{image_to_base64(HANBRIDGE_LOGO_PATH)}" style="height: 55px; object-fit: contain;">
|
| 267 |
</div>
|
| 268 |
"""
|
| 269 |
)
|
| 270 |
-
# Bottom: Login UI Logic
|
| 271 |
-
with gr.Row():
|
| 272 |
-
with gr.Column(scale=1): pass # Spacer to push content right
|
| 273 |
-
|
| 274 |
-
with gr.Column(scale=0, min_width=300):
|
| 275 |
-
|
| 276 |
-
# 1. Default State: Login Button (Visible initially)
|
| 277 |
-
login_start_btn = gr.Button("👤 Student Login", size="sm", elem_classes="header-login-btn")
|
| 278 |
-
|
| 279 |
-
# 2. Input State: Inputs + Confirm (Hidden initially)
|
| 280 |
-
with gr.Group(visible=False) as login_form_group:
|
| 281 |
-
with gr.Row(elem_classes="no-gap"):
|
| 282 |
-
name_input = gr.Textbox(placeholder="Name", show_label=False, container=False, scale=2, min_width=80)
|
| 283 |
-
id_input = gr.Textbox(placeholder="ID", show_label=False, container=False, scale=2, min_width=80)
|
| 284 |
-
login_confirm_btn = gr.Button("✅", size="sm", scale=1, min_width=40)
|
| 285 |
-
|
| 286 |
-
# 3. Logged In State: Info + Logout (Hidden initially)
|
| 287 |
-
with gr.Group(visible=False) as logged_in_group:
|
| 288 |
-
with gr.Row(elem_classes="no-gap"):
|
| 289 |
-
student_info_disp = gr.Markdown(value="", elem_id="student-info")
|
| 290 |
-
logout_btn = gr.Button("Log Out", elem_classes="link-btn")
|
| 291 |
|
| 292 |
# --- Main Layout ---
|
| 293 |
with gr.Row():
|
|
@@ -385,39 +383,70 @@ with gr.Blocks(title="Clare – Hanbridge AI Teaching Assistant", css=CUSTOM_CSS
|
|
| 385 |
|
| 386 |
# === Right Sidebar ===
|
| 387 |
with gr.Column(scale=1, min_width=180):
|
| 388 |
-
gr.HTML("<div style='height: 210px; width: 100%;'></div>") # Spacer
|
| 389 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
gr.Markdown("### Actions")
|
| 391 |
export_btn = gr.Button("Export Conversation", size="sm", elem_classes="action-btn")
|
| 392 |
quiz_btn = gr.Button("Let's Try (Micro-Quiz)", size="sm", elem_classes="action-btn")
|
| 393 |
summary_btn = gr.Button("Summarization", size="sm", elem_classes="action-btn")
|
| 394 |
|
|
|
|
| 395 |
gr.Markdown("### Results")
|
| 396 |
with gr.Group(elem_classes="result-box"):
|
| 397 |
result_display = gr.Markdown(value="Results will appear here...", label="Generated Content")
|
| 398 |
|
| 399 |
-
# ================== Logic: Login Flow ==================
|
| 400 |
|
| 401 |
-
# 1.
|
| 402 |
-
def
|
| 403 |
return {
|
| 404 |
-
|
| 405 |
-
|
|
|
|
| 406 |
}
|
| 407 |
|
| 408 |
-
login_start_btn.click(
|
| 409 |
|
| 410 |
-
# 2.
|
| 411 |
def confirm_login(name, id_val):
|
| 412 |
if not name or not id_val:
|
| 413 |
-
#
|
| 414 |
-
return gr.update(), gr.update(), gr.update(), gr.update()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 415 |
|
| 416 |
-
display_text = f"🎓 **{name}** ({id_val})"
|
| 417 |
return {
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
|
|
|
| 421 |
user_name_state: name,
|
| 422 |
user_id_state: id_val
|
| 423 |
}
|
|
@@ -425,20 +454,22 @@ with gr.Blocks(title="Clare – Hanbridge AI Teaching Assistant", css=CUSTOM_CSS
|
|
| 425 |
login_confirm_btn.click(
|
| 426 |
confirm_login,
|
| 427 |
inputs=[name_input, id_input],
|
| 428 |
-
outputs=[
|
| 429 |
)
|
| 430 |
|
| 431 |
-
# 3.
|
| 432 |
def logout():
|
| 433 |
return {
|
| 434 |
-
|
| 435 |
-
|
|
|
|
| 436 |
name_input: gr.update(value=""),
|
| 437 |
id_input: gr.update(value=""),
|
| 438 |
-
|
|
|
|
| 439 |
}
|
| 440 |
|
| 441 |
-
logout_btn.click(logout, outputs=[
|
| 442 |
|
| 443 |
# ================== Main App Logic ==================
|
| 444 |
|
|
|
|
| 31 |
HANBRIDGE_LOGO_PATH = "hanbridge_logo.png"
|
| 32 |
CLARE_LOGO_PATH = "clare_mascot.png"
|
| 33 |
CLARE_RUN_PATH = "Clare_Run.png"
|
| 34 |
+
CLARE_READING_PATH = "clare_reading.png" # 请确保上传了这张图片
|
| 35 |
|
| 36 |
# ================== Base64 Helper ==================
|
| 37 |
def image_to_base64(image_path):
|
|
|
|
| 188 |
/* --- Main Header --- */
|
| 189 |
.header-container { padding: 10px 20px; background-color: #ffffff; border-bottom: 2px solid #f3f4f6; margin-bottom: 15px; display: flex; align-items: center; }
|
| 190 |
|
| 191 |
+
/* --- Sidebar Login Panel --- */
|
| 192 |
+
.login-panel {
|
| 193 |
+
background-color: #e5e7eb; /* 浅灰色背景 */
|
| 194 |
+
padding: 15px;
|
| 195 |
+
border-radius: 8px;
|
| 196 |
+
text-align: center;
|
| 197 |
+
margin-bottom: 20px;
|
| 198 |
+
}
|
| 199 |
+
.login-panel img {
|
| 200 |
+
display: block;
|
| 201 |
+
margin: 0 auto 10px auto;
|
| 202 |
+
height: 80px; /* 调整图片高度 */
|
| 203 |
+
object-fit: contain;
|
| 204 |
+
}
|
| 205 |
+
.login-main-btn {
|
| 206 |
+
background-color: #ffffff !important;
|
| 207 |
+
color: #000 !important;
|
| 208 |
+
border: 1px solid #000 !important;
|
| 209 |
+
font-weight: bold !important;
|
| 210 |
+
}
|
| 211 |
+
.logout-btn {
|
| 212 |
+
background-color: #6b2828 !important; /* 深红色背景 */
|
| 213 |
+
color: #fff !important;
|
| 214 |
+
border: none !important;
|
| 215 |
+
font-weight: bold !important;
|
| 216 |
+
}
|
| 217 |
|
| 218 |
/* User Guide */
|
| 219 |
.main-user-guide { border: none !important; background: transparent !important; box-shadow: none !important; }
|
|
|
|
| 256 |
user_name_state = gr.State("")
|
| 257 |
user_id_state = gr.State("")
|
| 258 |
|
| 259 |
+
# --- Header ---
|
| 260 |
with gr.Row(elem_classes="header-container"):
|
| 261 |
# Left: Logo & Title
|
| 262 |
with gr.Column(scale=2):
|
|
|
|
| 277 |
"""
|
| 278 |
)
|
| 279 |
|
| 280 |
+
# Right: Hanbridge Logo (Static)
|
| 281 |
with gr.Column(scale=1):
|
|
|
|
| 282 |
gr.HTML(
|
| 283 |
f"""
|
| 284 |
+
<div style="display:flex; justify-content:flex-end; margin-bottom: 5px;">
|
| 285 |
<img src="{image_to_base64(HANBRIDGE_LOGO_PATH)}" style="height: 55px; object-fit: contain;">
|
| 286 |
</div>
|
| 287 |
"""
|
| 288 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 289 |
|
| 290 |
# --- Main Layout ---
|
| 291 |
with gr.Row():
|
|
|
|
| 383 |
|
| 384 |
# === Right Sidebar ===
|
| 385 |
with gr.Column(scale=1, min_width=180):
|
|
|
|
| 386 |
|
| 387 |
+
# --- New Login Panel (占用原来的 Spacer 空间) ---
|
| 388 |
+
with gr.Group(elem_classes="login-panel"):
|
| 389 |
+
# 图片始终显示
|
| 390 |
+
gr.HTML(f"<img src='{image_to_base64(CLARE_READING_PATH)}'>")
|
| 391 |
+
|
| 392 |
+
# 状态 1: 未登录 (显示 Login 按钮)
|
| 393 |
+
with gr.Group(visible=True) as login_state_1:
|
| 394 |
+
login_start_btn = gr.Button("Student Login", elem_classes="login-main-btn")
|
| 395 |
+
|
| 396 |
+
# 状态 2: 输入信息
|
| 397 |
+
with gr.Group(visible=False) as login_state_2:
|
| 398 |
+
name_input = gr.Textbox(label="Student Name", placeholder="Name", container=True)
|
| 399 |
+
id_input = gr.Textbox(label="Email/ID", placeholder="ID", container=True)
|
| 400 |
+
login_confirm_btn = gr.Button("Enter", variant="primary", size="sm")
|
| 401 |
+
|
| 402 |
+
# 状态 3: 已登录 (显示信息 + Logout)
|
| 403 |
+
with gr.Group(visible=False) as login_state_3:
|
| 404 |
+
# 使用 HTML 显示居中的加粗名字
|
| 405 |
+
student_info_html = gr.HTML()
|
| 406 |
+
logout_btn = gr.Button("Log out", elem_classes="logout-btn", size="sm")
|
| 407 |
+
|
| 408 |
+
# Actions
|
| 409 |
gr.Markdown("### Actions")
|
| 410 |
export_btn = gr.Button("Export Conversation", size="sm", elem_classes="action-btn")
|
| 411 |
quiz_btn = gr.Button("Let's Try (Micro-Quiz)", size="sm", elem_classes="action-btn")
|
| 412 |
summary_btn = gr.Button("Summarization", size="sm", elem_classes="action-btn")
|
| 413 |
|
| 414 |
+
# Results
|
| 415 |
gr.Markdown("### Results")
|
| 416 |
with gr.Group(elem_classes="result-box"):
|
| 417 |
result_display = gr.Markdown(value="Results will appear here...", label="Generated Content")
|
| 418 |
|
| 419 |
+
# ================== Logic: Sidebar Login Flow ==================
|
| 420 |
|
| 421 |
+
# 1. Start Login -> Show Inputs
|
| 422 |
+
def show_inputs():
|
| 423 |
return {
|
| 424 |
+
login_state_1: gr.update(visible=False),
|
| 425 |
+
login_state_2: gr.update(visible=True),
|
| 426 |
+
login_state_3: gr.update(visible=False)
|
| 427 |
}
|
| 428 |
|
| 429 |
+
login_start_btn.click(show_inputs, outputs=[login_state_1, login_state_2, login_state_3])
|
| 430 |
|
| 431 |
+
# 2. Confirm Login -> Show Profile
|
| 432 |
def confirm_login(name, id_val):
|
| 433 |
if not name or not id_val:
|
| 434 |
+
# 简单验证,若为空则不跳转
|
| 435 |
+
return gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
| 436 |
+
|
| 437 |
+
# 构建居中的 HTML 信息
|
| 438 |
+
info_html = f"""
|
| 439 |
+
<div style="margin-bottom:10px;">
|
| 440 |
+
<div style="font-weight:bold; font-size:16px;">{name}</div>
|
| 441 |
+
<div style="color:#666; font-size:12px;">{id_val}</div>
|
| 442 |
+
</div>
|
| 443 |
+
"""
|
| 444 |
|
|
|
|
| 445 |
return {
|
| 446 |
+
login_state_1: gr.update(visible=False),
|
| 447 |
+
login_state_2: gr.update(visible=False),
|
| 448 |
+
login_state_3: gr.update(visible=True),
|
| 449 |
+
student_info_html: gr.update(value=info_html),
|
| 450 |
user_name_state: name,
|
| 451 |
user_id_state: id_val
|
| 452 |
}
|
|
|
|
| 454 |
login_confirm_btn.click(
|
| 455 |
confirm_login,
|
| 456 |
inputs=[name_input, id_input],
|
| 457 |
+
outputs=[login_state_1, login_state_2, login_state_3, student_info_html, user_name_state, user_id_state]
|
| 458 |
)
|
| 459 |
|
| 460 |
+
# 3. Logout -> Reset
|
| 461 |
def logout():
|
| 462 |
return {
|
| 463 |
+
login_state_1: gr.update(visible=True),
|
| 464 |
+
login_state_2: gr.update(visible=False),
|
| 465 |
+
login_state_3: gr.update(visible=False),
|
| 466 |
name_input: gr.update(value=""),
|
| 467 |
id_input: gr.update(value=""),
|
| 468 |
+
user_name_state: "",
|
| 469 |
+
user_id_state: ""
|
| 470 |
}
|
| 471 |
|
| 472 |
+
logout_btn.click(logout, outputs=[login_state_1, login_state_2, login_state_3, name_input, id_input, user_name_state, user_id_state])
|
| 473 |
|
| 474 |
# ================== Main App Logic ==================
|
| 475 |
|