LifeFlow-AI / ui /theme.py
Marco310's picture
feat: improve UI/UX
ecbe9e4
raw
history blame
47.6 kB
# ui/theme.py
# ui/theme.py
def get_enhanced_css() -> str:
return """
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700;800&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
<style>
/* ============= CSS Variables ============= */
:root {
--primary-color: #6366f1;
--primary-dark: #4f46e5;
--secondary-color: #10b981;
--accent-color: #f59e0b;
--danger-color: #ef4444;
--glass-bg: rgba(255, 255, 255, 0.7);
--glass-border: 1px solid rgba(255, 255, 255, 0.5);
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--radius-xl: 20px;
--font-main: 'Inter', sans-serif;
}
/* ============= Base Styles ============= */
body, .gradio-container {
font-family: var(--font-main) !important;
background: #f8fafc !important;
/* ⭐ 新增以下兩行:強制鎖定高度,解決 HF Space 捲動問題 ⭐ */
height: 100vh !important; /* 高度限制為視窗大小 */
overflow-y: auto !important; /* 超出範圍時,在內部產生捲軸 */
}
* { box-sizing: border-box; }
/* ============= 1. Header (保留 Backup 樣式) ============= */
.app-header-container {
align-items: center !important;
margin-bottom: 24px !important;
padding: 16px 24px !important;
background: rgba(255, 255, 255, 0.8) !important;
backdrop-filter: blur(12px) !important;
-webkit-backdrop-filter: blur(12px) !important;
border-radius: var(--radius-lg) !important;
border: 1px solid rgba(255, 255, 255, 0.5) !important;
box-shadow: var(--shadow-md) !important;
}
.app-header-left {
display: flex;
flex-direction: column;
gap: 4px;
}
.header-controls {
position: fixed !important; /* 固定定位 */
top: 30px !important; /* 距離頂部 */
right: 30px !important; /* 距離右側 */
z-index: 99999 !important; /* 最上層 */
display: flex !important;
flex-direction: column !important; /* 🔥 關鍵:改為垂直排列 */
gap: 12px !important; /* 按鈕間距 */
align-items: center !important; /* 水平置中 */
/* 視覺優化:垂直膠囊 */
background: rgba(255, 255, 255, 0.9) !important;
backdrop-filter: blur(12px) !important;
padding: 12px 8px !important; /* 上下寬鬆,左右緊湊 */
border-radius: 50px !important; /* 大圓角 */
border: 1px solid rgba(255, 255, 255, 0.6) !important;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08) !important;
width: auto !important;
min-width: 0 !important;
height: auto !important;
}
/* 按鈕微調 (確保按鈕是圓的) */
.header-btn {
min-width: 40px !important;
max-width: 40px !important;
height: 40px !important;
font-size: 1.1rem !important;
margin: 0 !important;
}
.header-btn:hover {
background: #f1f5f9 !important;
border-color: var(--primary-color) !important;
color: var(--primary-color) !important;
transform: translateY(-2px) !important;
box-shadow: var(--shadow-md) !important;
}
.header-btn:active {
transform: translateY(0) !important;
}
/* ============= 2. Animations & Agent Cards (保留 Backup 樣式) ============= */
@keyframes breathing-glow {
0% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.6), 0 0 20px rgba(99, 102, 241, 0.3); border-color: #6366f1; }
50% { box-shadow: 0 0 0 8px rgba(99, 102, 241, 0), 0 0 30px rgba(99, 102, 241, 0.5); border-color: #818cf8; }
100% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0), 0 0 20px rgba(99, 102, 241, 0.3); border-color: #6366f1; }
}
@keyframes pulse-scale {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.03); }
}
/* Agent Grid (用於 Step 3 的 Agent 卡片基礎) */
.agent-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 12px;
margin-top: 10px;
}
.agent-card-mini {
background: white;
padding: 12px;
border-radius: var(--radius-md);
border: 2px solid #e2e8f0;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.agent-card-mini.working {
animation: breathing-glow 2s ease-in-out infinite !important;
background: linear-gradient(135deg, #ffffff 0%, #eff6ff 100%) !important;
border-width: 2px !important;
z-index: 10 !important;
position: relative !important;
}
.agent-card-mini.working::before {
content: '';
position: absolute; top: -50%; left: -50%; width: 200%; height: 200%;
background: radial-gradient(circle, rgba(99, 102, 241, 0.1) 0%, transparent 70%);
animation: pulse-scale 2s ease-in-out infinite;
}
.agent-card-mini:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.agent-avatar-mini { font-size: 28px; margin-bottom: 6px; position: relative; z-index: 1; }
.agent-name-mini { font-weight: 600; font-size: 0.85rem; color: #1e293b; position: relative; z-index: 1; }
.agent-status-dot { height: 8px; width: 8px; border-radius: 50%; display: inline-block; margin-top: 6px; position: relative; z-index: 1; }
/* ============= 3. Chat & Tasks (保留 Backup 樣式) ============= */
.chat-history {
display: flex; flex-direction: column; gap: 16px; padding: 20px;
background: #fff; border-radius: var(--radius-lg); border: 1px solid #e2e8f0;
max-height: 500px; overflow-y: auto;
}
.chat-message { display: flex; align-items: flex-end; gap: 10px; max-width: 85%; animation: fade-in 0.3s ease-out; }
.chat-message.user { align-self: flex-end; flex-direction: row-reverse; }
.chat-message.assistant { align-self: flex-start; }
.chat-bubble { padding: 12px 16px; border-radius: 18px; font-size: 0.95rem; line-height: 1.5; position: relative; box-shadow: var(--shadow-sm); }
.chat-message.user .chat-bubble { background: var(--primary-color); color: white; border-bottom-right-radius: 4px; }
.chat-message.assistant .chat-bubble { background: #f1f5f9; color: #334155; border-bottom-left-radius: 4px; }
.chat-time { font-size: 0.7rem; opacity: 0.7; margin-top: 4px; text-align: right; }
/* Task Cards */
.task-card-item {
background: white;
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 16px;
margin-bottom: 12px;
transition: transform 0.2s;
}
.task-card-item:hover {
border-color: var(--primary-color);
transform: translateY(-2px);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
/* ============= 4. Timeline Redesign (Step 4 修正) ============= */
.timeline-container {
position: relative;
padding: 10px 0 10px 20px;
margin-top: 10px;
}
/* 垂直連接線 */
.timeline-container::before {
content: '';
position: absolute;
left: 29px;
top: 20px;
bottom: 20px;
width: 2px;
background: #e2e8f0;
z-index: 0;
}
/* 時間軸項目 - 強制橫排 */
.timeline-item {
position: relative;
display: flex !important;
flex-direction: row !important;
gap: 20px;
margin-bottom: 24px;
z-index: 1;
align-items: stretch;
}
/* 左側圓點 */
.timeline-left {
display: flex;
flex-direction: column;
align-items: center;
min-width: 20px;
padding-top: 4px;
}
.timeline-marker {
width: 20px;
height: 20px;
border-radius: 50%;
background: white;
border: 4px solid var(--primary-color);
box-shadow: 0 0 0 4px #fff;
z-index: 2;
}
/* Timeline 卡片:改為淺灰色背景,製造層次感 */
.timeline-card {
background: #f8fafc !important; /* 淺灰底 */
border: 1px solid #e2e8f0 !important;
border-radius: 12px !important;
padding: 16px !important;
box-shadow: 0 1px 2px rgba(0,0,0,0.03) !important;
transition: transform 0.2s, box-shadow 0.2s;
display: block !important;
}
/* Hover 效果:卡片變白並浮起 */
.timeline-card:hover {
background: #ffffff !important;
transform: translateX(4px);
box-shadow: 0 4px 12px rgba(0,0,0,0.08) !important;
border-color: var(--primary) !important;
}
.timeline-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 8px;
gap: 10px;
}
.timeline-location { font-size: 1rem; font-weight: 700; color: #1e293b; line-height: 1.3; }
.timeline-time-badge {
background: #f1f5f9; color: #64748b; font-size: 0.75rem; font-weight: 600;
padding: 4px 10px; border-radius: 20px; white-space: nowrap; display: flex; align-items: center; gap: 4px;
}
.timeline-meta {
background: #f1f5f9;
border: 1px solid #e2e8f0;
padding: 8px; border-radius: 8px; margin-top: 8px; color: #64748b;
}
.timeline-item:first-child .timeline-marker { border-color: #10b981 !important; }
.timeline-item:last-child .timeline-marker { border-color: #ef4444 !important; }
/* ============= 5. Step 3 War Room (新樣式) ============= */
.live-report-wrapper {
flex: 1;
background: white;
border: 1px solid #e2e8f0;
border-radius: 16px;
padding: 30px;
min-height: 600px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
}
.report-title {
font-size: 1.2rem; font-weight: 700; color: #1e293b;
margin-bottom: 16px; display: flex; align-items: center; gap: 8px;
border-bottom: 2px solid #f1f5f9; padding-bottom: 12px;
}
.war-room-wrapper {
width: 450px; flex-shrink: 0; display: flex; flex-direction: column; gap: 20px; position: sticky; top: 20px;
}
.agent-war-room {
background: white; border-radius: 16px; border: 1px solid #e2e8f0; padding: 24px;
display: flex; flex-direction: column; align-items: center; box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.org-chart { width: 100%; display: flex; flex-direction: column; align-items: center; gap: 20px; }
.org-level { display: flex !important; flex-direction: row !important; flex-wrap: wrap; justify-content: center; gap: 12px; width: 100%; z-index: 2; }
.connector-line { width: 2px; height: 20px; background: #cbd5e1; }
.connector-horizontal { height: 2px; width: 80%; background: #cbd5e1; margin-top: -20px; margin-bottom: 10px; }
/* Step 3 的 Agent Card (複用 mini card 但調整寬度) */
.agent-card-wrap { width: 110px; text-align: center; display: flex; flex-direction: column; align-items: center; }
.agent-card-inner {
background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 10px; padding: 10px;
display: flex; flex-direction: column; align-items: center; width: 100%; transition: transform 0.2s;
}
.agent-card-wrap.working .agent-card-inner {
background: white; border-color: #6366f1;
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.15); animation: pulse 2s infinite;
}
.agent-avatar { font-size: 24px; margin-bottom: 4px; }
.agent-name { font-weight: 700; font-size: 0.8rem; color: #1e293b; }
.agent-role { font-size: 0.65rem; color: #64748b; text-transform: uppercase; }
.status-badge { font-size: 0.65rem; padding: 2px 6px; border-radius: 8px; background: #e2e8f0; color: #64748b; margin-top: 4px; }
.agent-card-wrap.working .status-badge { background: #e0e7ff; color: #6366f1; }
/* ============= 6. Utils & Layouts ============= */
.step-container { max-width: 1400px; margin: 0 auto; padding: 0 16px; animation: fade-in 0.4s ease-out; }
.centered-input-container { max-width: 1000px !important; margin: 0 auto; padding: 40px 0; }
.panel-container { background: white; border: 1px solid #e2e8f0; border-radius: 16px; height: 650px !important; display: flex !important; flex-direction: column !important; overflow: hidden; }
.panel-header { padding: 16px 20px; background: #f8fafc; border-bottom: 1px solid #e2e8f0; font-weight: 700; color: var(--text-main); flex-shrink: 0; }
.scrollable-content { flex: 1; overflow-y: auto !important; padding: 20px; background: #fff; }
.chat-input-row { padding: 12px !important; border-top: 1px solid #e2e8f0; background: white; align-items: center !important; }
.split-view-container { display: flex; gap: 24px; height: calc(100vh - 140px); min-height: 600px; }
.split-left-panel .panel-container, .split-left-panel .gradio-group {
background: white !important;
}
.split-right-panel { flex: 2; position: sticky; top: 100px; height: 100%; }
/* Animations */
@keyframes fade-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
@keyframes slide-in-left { from { opacity: 0; transform: translateX(-20px); } to { opacity: 1; transform: translateX(0); } }
@keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.4); } 70% { box-shadow: 0 0 0 6px rgba(0,0,0,0); } 100% { box-shadow: 0 0 0 0 rgba(0,0,0,0); } }
/* Scrollbar */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: #f1f5f9; border-radius: 4px; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
/* 修正 Scrollbar 樣式,讓它明顯一點但好看 */
.drawer-content::-webkit-scrollbar { width: 6px; }
.drawer-content::-webkit-scrollbar-track { background: transparent; }
.drawer-content::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
.drawer-content::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
/* Dark Mode Support */
.theme-dark body, .theme-dark .gradio-container { background: #0f172a !important; }
.theme-dark .app-header-container, .theme-dark .chat-history, .theme-dark .timeline-card, .theme-dark .metric-card, .theme-dark .agent-card-mini, .theme-dark .agent-card-inner, .theme-dark .live-report-wrapper, .theme-dark .agent-war-room, .theme-dark .task-card-item {
background: #1e293b !important; border-color: #334155 !important; color: #e2e8f0 !important;
}
.theme-dark h1, .theme-dark h2, .theme-dark h3, .theme-dark p, .theme-dark span, .theme-dark .agent-name { color: #e2e8f0 !important; }
.theme-dark .chat-message.assistant .chat-bubble { background: #334155 !important; color: #e2e8f0 !important; }
.theme-dark .agent-name-mini, .theme-dark .metric-card h3 { color: #cbd5e1 !important; }
/* Step 1 Log */
.agent-stream-box-step1 {
background: #f8fafc; border-radius: 8px; padding: 12px; border: 1px solid #e2e8f0;
min-height: 60px; max-height: 150px; overflow-y: auto; font-family: monospace; font-size: 0.9rem;
margin-bottom: 16px; margin-top: 10px;
}
/* ============= 新版 Quick Start Buttons ============= */
/* 容器稍微給點間距 */
.example-container {
gap: 8px !important;
margin-bottom: 16px !important;
}
/* 把按鈕偽裝成卡片列表 */
.example-btn {
text-align: left !important; /* 文字靠左 */
justify-content: flex-start !important; /* Flex 內容靠左 */
height: auto !important; /* 高度自動 */
white-space: normal !important; /* ⭐ 允許換行 */
word-break: break-word !important; /* 長字換行 */
padding: 12px 16px !important;
background: #f8fafc !important;
border: 1px solid #e2e8f0 !important;
color: #475569 !important;
font-weight: 400 !important; /* 字體不要太粗 */
}
.example-btn:hover {
background: white !important;
border-color: var(--primary-color) !important;
color: var(--primary-color) !important;
box-shadow: 0 4px 6px rgba(0,0,0,0.05) !important;
transform: translateY(-1px);
}
/* ============= 設定彈窗 (Settings Modal) ============= */
/* 1. 全螢幕遮罩層 (Overlay) */
.modal-overlay {
position: fixed !important;
top: 0 !important;
left: 0 !important;
width: 100vw !important;
height: 100vh !important;
background: rgba(0, 0, 0, 0.6) !important; /* 半透明黑色背景 */
backdrop-filter: blur(4px) !important; /* 背景模糊效果 */
z-index: 100000 !important; /* 必須比 Header 的 99999 還大 */
display: flex !important;
align-items: center !important; /* 垂直置中 */
justify-content: center !important; /* 水平置中 */
padding: 20px !important;
}
/* 2. 彈窗本體 (Modal Box) */
.modal-box {
background: white !important;
border-radius: 24px !important;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25) !important;
border: 1px solid rgba(255, 255, 255, 0.6) !important;
overflow: hidden !important;
/* 🔥 寬度加大,讓橫向空間更舒服 */
max-width: 600px !important;
width: 100% !important;
display: flex !important;
flex-direction: column !important;
max-height: 90vh !important; /* 最大高度放寬到 90% 視窗高度 */
}
/* 2. 內容區域 */
.modal-content {
padding: 0 24px 10px 24px !important;
overflow-y: auto !important;
flex-grow: 1 !important;
/* 🔥🔥🔥 關鍵修正:最小高度加大 🔥🔥🔥 */
/* 原本 300px -> 改為 500px,確保所有欄位都能直接顯示不用捲動 */
min-height: 500px !important;
}
/* 3. Footer (保持不變) */
.modal-footer {
background: #f8fafc !important;
padding: 20px 24px !important;
border-top: 1px solid #f1f5f9 !important;
gap: 12px !important;
flex-shrink: 0 !important;
height: auto !important;
display: flex !important;
align-items: center !important;
}
.modal-title h3 {
font-size: 1.5rem !important;
font-weight: 800 !important;
background: linear-gradient(135deg, #4f46e5, #9333ea); /* 藍紫漸層 */
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 4px !important;
}
.tab-desc p {
font-size: 0.9rem !important;
color: #64748b !important;
margin-bottom: 16px !important;
}
/* 3. 輸入框美化 (移除預設的生硬邊框) */
.modern-input textarea, .modern-input input {
background-color: #f8fafc !important;
border: 1px solid #e2e8f0 !important;
border-radius: 12px !important;
padding: 10px 14px !important;
transition: all 0.2s ease;
font-size: 0.95rem !important;
}
.modern-input textarea:focus, .modern-input input:focus {
background-color: white !important;
border-color: #6366f1 !important;
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1) !important;
}
/* 4. Dropdown 美化 & 修復寬度問題 */
.modern-dropdown .wrap-inner {
background-color: #f8fafc !important;
border-radius: 12px !important;
border: 1px solid #e2e8f0 !important;
}
/* 修復文字被切斷的問題 */
#provider-dropdown {
min-width: 150px !important; /* 強制最小寬度 */
}
/* 5. 按鈕美化 */
.modal-footer {
background: #f8fafc !important;
padding: 20px 24px !important;
border-top: 1px solid #f1f5f9 !important;
gap: 12px !important;
}
/* Cancel 按鈕:柔和灰 */
.btn-cancel {
background: white !important;
border: 1px solid #e2e8f0 !important;
color: #64748b !important;
border-radius: 10px !important;
font-weight: 500 !important;
}
.btn-cancel:hover {
background: #f1f5f9 !important;
color: #334155 !important;
}
/* Save 按鈕:品牌漸層紫 (取代橘色) */
#btn-save-config {
background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%) !important;
border: none !important;
color: white !important;
border-radius: 10px !important;
font-weight: 600 !important;
box-shadow: 0 4px 6px -1px rgba(99, 102, 241, 0.3) !important;
transition: transform 0.1s !important;
}
#btn-save-config:hover {
opacity: 0.95 !important;
box-shadow: 0 6px 10px -1px rgba(99, 102, 241, 0.4) !important;
transform: translateY(-1px) !important;
}
#btn-save-config:active {
transform: translateY(0) !important;
}
/* 2. 內容區域:設定最小高度,並讓它吃掉剩餘空間 */
.modal-content {
padding: 0 24px 10px 24px !important;
overflow-y: auto !important;
flex-grow: 1 !important; /* 🔥 關鍵:佔據剩餘空間 */
min-height: 300px !important; /* 🔥 關鍵:設定最小高度,防止內容太少時視窗縮成一團 */
}
/* 3. Footer:固定高度,不要拉伸 */
.modal-footer {
background: #f8fafc !important;
padding: 20px 24px !important;
border-top: 1px solid #f1f5f9 !important;
gap: 12px !important;
/* 🔥 關鍵:防止 Footer 被拉高 */
flex-shrink: 0 !important;
height: auto !important;
display: flex !important;
align-items: center !important; /* 垂直置中,防止按鈕變形 */
}
/* 彈出動畫 */
@keyframes modal-pop {
0% { transform: scale(0.95) translateY(10px); opacity: 0; }
100% { transform: scale(1) translateY(0); opacity: 1; }
}
/* ============= API Key 佈局優化 ============= */
/* 1. 縮小左右欄位的間距 (原本約 20px -> 改為 6px) */
.api-row {
gap: 6px !important;
}
/* 2. 修正 Dropdown 在窄欄位時文字被切斷的問題 */
#provider-dropdown .wrap-inner {
padding-right: 25px !important; /* 預留空間給箭頭 */
}
#provider-dropdown input {
text-overflow: ellipsis !important;
min-width: 0 !important;
}
/* 讓 Dropdown 的選單箭頭不要擠到文字 */
#provider-dropdown svg {
margin-right: -5px !important;
}
.agent-war-room {
background: white !important; /* 強制純白不透明 */
border: 1px solid #cbd5e1 !important; /* 加深邊框顏色 */
border-radius: 16px !important;
padding: 24px !important;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1) !important; /* 加強陰影 */
opacity: 1 !important;
}
/* 2. Agent 小卡片:確保它是獨立的實體 */
.agent-card-inner {
background: #f8fafc !important; /* 淺灰底色,區分層次 */
border: 1px solid #cbd5e1 !important; /* 明顯的邊框 */
border-radius: 12px !important;
opacity: 1 !important; /* 拒絕透明 */
box-shadow: 0 2px 4px rgba(0,0,0,0.05) !important;
transition: all 0.2s !important;
}
/* 3. 正在工作的卡片:亮起來 */
.agent-card-wrap.working .agent-card-inner {
background: white !important;
border-color: #6366f1 !important; /* 亮紫色邊框 */
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2) !important; /* 發光效果 */
}
/* 4. ⭐ 關鍵:文字顏色強制矯正 ⭐ */
/* 不管外面主題怎麼變,這裡的字一定要是深色的 */
.agent-name {
color: #0f172a !important; /* 深黑藍色 */
font-weight: 700 !important;
font-size: 0.85rem !important;
opacity: 1 !important;
}
.agent-role {
color: #475569 !important; /* 深灰色 */
font-weight: 600 !important;
opacity: 1 !important;
}
.status-badge {
color: #334155 !important;
background: #e2e8f0 !important;
border: 1px solid #cbd5e1 !important;
font-weight: 600 !important;
opacity: 1 !important;
}
/* 工作的狀態標籤 */
.agent-card-wrap.working .status-badge {
background: #e0e7ff !important;
color: #4338ca !important;
border-color: #818cf8 !important;
}
/* 連接線也要加深,不然看不到 */
.connector-line, .connector-horizontal {
background: #94a3b8 !important; /* 加深灰色 */
opacity: 0.6 !important;
}
.tabs > .tab-nav > button {
color: #64748b !important;
font-weight: 500 !important;
}
/* 選中的 Tab:品牌紫色 + 紫色底線 */
.tabs > .tab-nav > button.selected {
color: #6366f1 !important; /* 文字變紫 */
border-bottom-color: #6366f1 !important; /* 底線變紫 */
border-bottom-width: 2px !important;
font-weight: 700 !important;
}
.api-status-msg p {
font-size: 0.8rem !important;
margin-top: 6px !important;
margin-left: 8px !important;
font-weight: 600 !important;
font-family: 'Inter', sans-serif !important;
color: #64748b; /* 預設灰色 */
}
/* 讓輸入框和狀態訊息之間更緊湊 */
.api-status-msg {
min-height: 20px !important; /* 預留高度防止跳動 */
}
/* === 1. Summary Card (儀表板) === */
.summary-card-modern {
/* 改為藍白漸層 */
background: linear-gradient(135deg, #ffffff 0%, #eff6ff 100%) !important;
border: 1px solid #dbeafe !important;
/* 🔥 修正:縮小內距與下邊距,防止撐出滾動條 */
border-radius: 16px;
padding: 14px 18px !important; /* 原本是 24px */
margin-bottom: 12px !important; /* 原本是 20px */
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
/* 消除 Gradio 預設可能帶入的額外高度 */
min-height: 0 !important;
}
/* 讓 Summary 內的地點文字更跳 */
.summary-loc {
font-size: 1.4rem;
font-weight: 800;
color: #312e81; /* 深靛藍 */
margin: 4px 0;
text-shadow: 0 1px 0 rgba(255,255,255,0.8);
}
.summary-label { font-size: 0.75rem; color: #94a3b8; font-weight: 700; letter-spacing: 0.5px; }
.summary-date { font-size: 0.9rem; color: #6366f1; }
.summary-metrics { display: flex; gap: 12px; }
.metric-box {
background: rgba(255, 255, 255, 0.7);
border: 1px solid #e2e8f0;
padding: 8px 16px;
border-radius: 12px;
min-width: 80px;
backdrop-filter: blur(4px);
box-shadow: 0 2px 4px rgba(0,0,0,0.02);
}
.m-val { display: block; font-size: 1.2rem; font-weight: 800; color: #334155; line-height: 1; }
.m-val small { font-size: 0.7rem; margin-left: 2px; }
.m-label { font-size: 0.7rem; color: #64748b; }
.metric-box.alert .m-val { color: #ef4444; }
/* === 2. Agent Status Row (橫向狀態列) === */
.agent-status-row {
display: flex;
gap: 10px;
overflow-x: auto; /* 如果螢幕太窄,允許橫向捲動 */
padding-bottom: 5px;
margin-bottom: 15px;
}
.agent-status-card {
flex: 1;
min-width: 120px;
background: white;
border: 1px solid #e2e8f0;
border-radius: 10px;
padding: 10px;
display: flex;
align-items: center;
gap: 10px;
transition: all 0.3s;
}
.agent-status-card.working {
border-color: #6366f1;
background: #eff6ff;
box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.1);
}
.agent-name { font-size: 0.8rem; font-weight: 700; color: #1e293b; }
.agent-msg { font-size: 0.7rem; color: #64748b; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.agent-status-card.working .agent-msg { color: #6366f1; font-weight: 600; }
/* === 3. Task Card Modern (任務卡片) === */
.task-card-modern {
background: white;
border: 1px solid #e2e8f0;
border-radius: 10px;
padding: 14px;
margin-bottom: 10px;
border-left-width: 4px;
}
.border-high { border-left-color: #ef4444; }
.border-medium { border-left-color: #f59e0b; }
.border-low { border-left-color: #10b981; }
.tc-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.tc-title { font-weight: 600; color: #334155; font-size: 0.95rem; }
.tc-badge { font-size: 0.7rem; padding: 2px 8px; border-radius: 12px; font-weight: 700; text-transform: uppercase; }
.badge-high { background: #fef2f2; color: #ef4444; }
.badge-medium { background: #fffbeb; color: #f59e0b; }
.badge-low { background: #ecfdf5; color: #10b981; }
.tc-body { font-size: 0.85rem; color: #64748b; display: flex; flex-direction: column; gap: 4px; }
/* 1. 報告與任務列表的通用容器 */
.live-report-wrapper, .panel-container {
background: white !important;
border: 1px solid #e2e8f0 !important;
border-radius: 16px !important;
padding: 30px !important;
/* 🔥 關鍵:給它一個最小高度,讓它看起來像一張完整的紙,不會因為內容少而縮成一團 */
min-height: 600px !important;
/* 增加立體感 */
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03) !important;
/* 讓內部的 Markdown 排版更舒適 */
line-height: 1.6 !important;
font-size: 1rem !important;
color: #334155 !important;
}
/* 2. 針對 Tab 內容的微調 */
/* 讓 Tab 下方的內容區塊與 Tab 標籤無縫銜接 (可選,視 Gradio 版本而定) */
.tabitem {
border: none !important;
background: transparent !important;
}
/* 3. 優化 "Analyzing..." 載入文字 */
/* 讓它不要只是孤單地浮在左上角,而是置中並帶點呼吸感 */
.live-report-wrapper p:only-child, .panel-container p:only-child {
text-align: center !important;
margin-top: 100px !important;
color: #94a3b8 !important;
font-weight: 500 !important;
animation: pulse-text 2s infinite !important;
}
@keyframes pulse-text {
0% { opacity: 0.6; }
50% { opacity: 1; }
100% { opacity: 0.6; }
}
/* 讓氣泡容器不要拉伸 (預設靠左) */
.message-wrap {
display: flex !important;
flex-direction: column !important;
align-items: flex-start !important;
gap: 6px !important;
}
/* User 的氣泡改為靠右 */
.message-wrap.user-row {
align-items: flex-end !important;
}
/* 氣泡本體:寬度適應內容,不要繼承 100% */
.message-bubble {
width: fit-content !important;
max-width: 80% !important;
height: auto !important;
display: block !important;
white-space: pre-wrap !important;
padding: 10px 14px !important;
}
/* ============= 3. 強化 Page 3 Agent 特效 (呼吸燈) ============= */
/* 定義強烈的脈衝動畫 */
@keyframes strong-pulse {
0% {
box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.4);
border-color: #6366f1;
transform: scale(1);
}
50% {
box-shadow: 0 0 0 6px rgba(99, 102, 241, 0); /* 擴散光圈 */
border-color: #818cf8;
transform: scale(1.02); /* 微微放大 */
}
100% {
box-shadow: 0 0 0 0 rgba(99, 102, 241, 0);
border-color: #6366f1;
transform: scale(1);
}
}
/* Agent 容器基礎樣式 */
.agent-status-row {
display: flex; gap: 10px; overflow-x: auto; padding: 10px 5px; margin-bottom: 15px;
}
.agent-status-card {
flex: 1; min-width: 140px; background: white;
border: 1px solid #e2e8f0; border-radius: 12px;
padding: 12px; display: flex; align-items: center; gap: 10px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
}
/* 🔥 關鍵:套用動畫到 Working 狀態 */
.agent-status-card.working {
animation: strong-pulse 2s infinite !important; /* 強制執行動畫 */
background: linear-gradient(135deg, #ffffff 0%, #e0e7ff 100%) !important; /* 微藍背景 */
z-index: 10;
}
/* 文字顏色也跟著變亮 */
.agent-status-card.working .agent-msg {
color: #4f46e5 !important;
font-weight: 700 !important;
}
/* 靜態樣式補充 */
.agent-icon { font-size: 1.5rem; }
.agent-info { overflow: hidden; }
.agent-name { font-size: 0.85rem; font-weight: 700; color: #1e293b; }
.agent-msg { font-size: 0.75rem; color: #64748b; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 強制修正氣泡變形與留白問題 */
.map-container-relative {
position: relative !important;
height: 650px !important; /* 🔥 強制高度 */
width: 100% !important;
border-radius: 16px;
overflow: hidden !important;
border: 1px solid #e2e8f0;
background: white;
}
/* 強制 iframe 填滿容器 */
.map-container-relative iframe {
width: 100% !important;
height: 100% !important;
border: none !important;
}
/* 2. 浮動抽屜本體 */
.map-overlay-drawer {
position: absolute !important;
top: 20px !important;
left: 20px !important;
bottom: 20px !important;
width: 380px !important; /* 固定寬度,確保不會擋住太多地圖 */
height: 85% !important;
background: rgba(255, 255, 255, 0.95) !important;
backdrop-filter: blur(12px) !important; /* 毛玻璃效果 */
border-radius: 16px !important;
border: 1px solid rgba(255, 255, 255, 0.6) !important;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.15), 0 4px 10px -2px rgba(0, 0, 0, 0.1) !important;
z-index: 500 !important; /* 確保在地圖之上 (Leaflet 通常是 z-index 400) */
display: flex !important;
flex-direction: column !important;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
/* 🔥 關鍵:讓內部元素可以用 Flex 排列 */
display: flex !important;
flex-direction: column !important;
overflow: hidden !important; /* 防止圓角被內容切掉 */
}
/* 3. 抽屜標頭 */
.drawer-header {
padding: 16px 20px !important;
border-bottom: 1px solid #e2e8f0 !important;
background: #f8fafc !important;
border-radius: 16px 16px 0 0 !important;
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
flex-shrink: 0 !important;
height: 60px !important;
}
.drawer-title p {
margin: 0 !important;
font-size: 1rem !important;
font-weight: 700 !important;
color: #334155 !important;
}
.drawer-tabs .tab-nav {
border-bottom: 1px solid #f1f5f9 !important;
margin: 0 !important;
}
/* 5. 關閉按鈕 (X) */
.drawer-close-btn {
background: transparent !important;
color: #94a3b8 !important;
border: none !important;
box-shadow: none !important;
font-size: 1.2rem !important;
padding: 4px !important;
width: 32px !important;
min-width: 32px !important;
}
.drawer-close-btn:hover {
color: #ef4444 !important;
background: #fef2f2 !important;
border-radius: 50% !important;
}
/* === 修復抽屜開關按鈕 === */
.map-overlay-btn {
position: absolute !important;
top: 10px !important;
left:60px !important;
/* 🔥 關鍵:必須比 Leaflet 地圖 (400) 和 Drawer (500) 高 */
z-index: 1000 !important;
background: white !important;
color: #6366f1 !important;
border: 1px solid #cbd5e1 !important;
box-shadow: 0 4px 6px rgba(0,0,0,0.1) !important;
font-weight: 600 !important;
padding: 8px 12px !important;
}
.map-overlay-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 10px rgba(0,0,0,0.15) !important;
}
/* 隱藏 Map 預設的 Label */
.map-container-relative .label-wrap {
display: none !important;
}
/* 顏色修正 (確保不被舊樣式覆蓋) */
/* --- 2. Page 4: Map Overlay Drawer (捲軸與排版修復) --- */
/* 抽屜容器:Flex Column 佈局 */
.map-overlay-drawer {
display: flex !important;
flex-direction: column !important;
max-height: calc(100% - 40px) !important; /* 防止超出地圖邊界 */
overflow: hidden !important; /* 外層隱藏溢出 */
background: rgba(255, 255, 255, 0.98) !important;
border: 1px solid rgba(226, 232, 240, 0.8) !important;
z-index: 5000 !important;
height: 85% !important;
max-height: 600px !important;
}
/* Header:固定高度 */
.drawer-header {
flex-shrink: 0 !important;
height: auto !important;
border-bottom: 1px solid #e2e8f0 !important;
background: #f8fafc !important;
}
/* 🔥 修正 Tab 跑版:移除負邊距,讓它自然排列 */
.drawer-tabs {
margin-top: 0 !important; /* 還原 margin */
display: flex !important;
flex-direction: column !important;
height: 100% !important;
}
/* 讓 Tab 的內容區塊也能捲動 */
.drawer-tabs .tabitem {
overflow-y: visible !important;
height: auto !important;
}
/* 美化捲軸 (Chrome/Safari) */
.drawer-content::-webkit-scrollbar {
width: 6px;
}
.drawer-content::-webkit-scrollbar-track {
background: transparent;
}
.drawer-content::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 3px;
}
/* --- 3. Summary Card Modern (漸層色補丁) --- */
.summary-card-modern {
background: linear-gradient(120deg, #ffffff 0%, #eff6ff 100%) !important;
border: 1px solid #dbeafe !important;
box-shadow: 0 4px 6px -1px rgba(99, 102, 241, 0.1) !important;
}
.header-controls {
position: fixed !important;
top: 24px !important;
right: 24px !important;
z-index: 99999 !important;
display: flex !important;
flex-direction: column !important; /* 垂直排列 */
gap: 8px !important;
/* 強制縮小寬度,防止佔滿橫條 */
width: auto !important;
min-width: 0 !important;
height: auto !important;
/* 膠囊視覺還原 */
background: rgba(255, 255, 255, 0.9) !important;
backdrop-filter: blur(8px) !important;
padding: 8px !important;
border-radius: 50px !important;
border: 1px solid rgba(255, 255, 255, 0.6) !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
}
/* 3. Page 4 Map 滾動條修復:強制高度與捲軸 */
.map-overlay-drawer {
display: flex !important;
flex-direction: column !important;
height: 85% !important; /* 🔥 必須給定高度,捲軸才會出現 */
overflow: hidden !important;
}
/* 4. Page 4 Map 按鈕位移:把 Zoom 按鈕移到右邊 */
.leaflet-left {
left: auto !important;
right: 20px !important;
}
.header-controls {
position: fixed !important; top: 24px !important; right: 24px !important; z-index: 99999 !important;
display: flex !important; flex-direction: column !important; gap: 8px !important;
width: auto !important; height: auto !important;
background: rgba(255, 255, 255, 0.9) !important; backdrop-filter: blur(8px) !important;
padding: 8px !important; border-radius: 50px !important;
border: 1px solid rgba(255, 255, 255, 0.6) !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
}
/* 2. Page 4 Map Drawer: 強制內容區塊顯示 */
/* 這裡只做最基本的顯示,不搞複雜的捲軸邏輯,讓瀏覽器自己處理 */
.map-overlay-drawer {
display: flex !important;
flex-direction: column !important;
height: 80% !important;
overflow: hidden !important;
}
.drawer-content {
flex: 1 !important;
overflow-y: auto !important;
background: white !important; /* 確保內容區是白色的 */
padding: 0 !important;
display: block !important;
}
/* 3. Page 4 Map 按鈕: 不要動它! */
/* 刪除任何 .leaflet-left 的設定,讓它自己回到左邊 */
/* 4. 修改 Show Summary 按鈕位置 (避開左邊的原生按鈕) */
.map-overlay-btn {
position: absolute !important;
top: 10px !important;
left: 60px !important; /* 強制往右移 60px */
z-index: 5000 !important;
}
這確實讓人崩潰,那個 "321 / 31" 的斷行顯示 Gradio 內部的 Flex 容器寬度被壓縮到了極限,而藍色方塊依然巨大則是因為 垂直拉伸 (Stretch) 屬性 仍然存在。
這表示即使我們拿掉了 height,Gradio 內部的 CSS 權重還是比我們的高,或者是某層 div 被我們之前的 CSS 誤傷導致寬度歸零。
請執行這個 「最終純淨版」 修正。這一次,我們不再對抗 Gradio 的 Flex 機制,而是順著它的毛摸,但強制加上「最小寬度」保護,並殺死「垂直拉伸」。
1. 確保 app.py 的 height 真的刪掉了
(如果你剛剛已經刪了,這步請再次確認就好)
Python
chatbot = gr.Chatbot(
label="AI Assistant",
type="messages",
# height=540, <-- 這行絕對不能有
elem_classes="native-chatbot",
bubble_full_width=False
)
2. 替換 ui/theme.py (純淨修復版)
請全選刪除 ui/theme.py 裡 Chatbot 相關的 CSS,換成這段。這段代碼移除了所有復雜的絕對定位,回歸最單純的排版,但加強了對 寬度 (Width) 的保護。
CSS
/* ============= 🛡️ LifeFlow Chatbot: 純淨修復版 🛡️ ============= */
/* 1. 外層容器:給定高度,讓它有捲軸 */
.chat-panel-native {
background: white !important;
border: 1px solid #e2e8f0 !important;
border-radius: 16px !important;
height: 600px !important;
overflow: hidden !important;
display: flex !important;
flex-direction: column !important;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05) !important;
}
/* 2. Chatbot 本體:自然填滿剩餘空間 */
.native-chatbot {
background: white !important;
flex-grow: 1 !important;
overflow-y: auto !important;
padding: 20px !important;
border: none !important;
}
/* 3. 訊息行 (Row) */
.native-chatbot .message-row {
display: flex !important;
flex-direction: row !important;
width: 100% !important;
margin-bottom: 16px !important;
/* 🔥 關鍵一:殺死垂直拉伸 (解決巨大方塊) */
align-items: flex-start !important;
height: auto !important;
/* 避免被壓縮 */
flex-shrink: 0 !important;
}
/* 4. 氣泡外殼 (Wrapper) - 這是解決 "321/31" 的關鍵 */
/* 之前的版本可能漏了這個,導致外殼寬度為 0 */
.native-chatbot .message-wrap {
display: flex !important;
flex-direction: column !important;
/* 🔥 關鍵二:寬度自動,但不能小於內容 */
width: auto !important;
min-width: 0 !important;
max-width: 85% !important;
flex-shrink: 1 !important; /* 允許在極端狀況下縮小 */
flex-grow: 0 !important; /* 禁止無故變寬 */
}
/* 5. 氣泡本體 (Bubble) */
.native-chatbot .message {
width: fit-content !important;
height: auto !important;
padding: 8px 12px !important;
font-size: 0.95rem !important;
line-height: 1.5 !important;
/* 🔥 關鍵三:正確的斷字設定 */
white-space: pre-wrap !important;
word-break: break-word !important;
overflow-wrap: break-word !important;
}
/* 6. Bot (靠左) */
.native-chatbot .message-row:not(.user-row) {
justify-content: flex-start !important;
}
.native-chatbot .message.bot,
.native-chatbot .message.model,
.native-chatbot .message.assistant,
.native-chatbot .message-row:not(.user-row) .message {
background-color: #f1f5f9 !important;
color: #1e293b !important;
border: 1px solid #e2e8f0 !important;
border-radius: 4px 16px 16px 16px !important;
}
.native-chatbot .message.bot *, .native-chatbot .message-row:not(.user-row) .message * {
color: #1e293b !important;
}
/* 7. User (靠右) */
.native-chatbot .message-row.user-row {
justify-content: flex-end !important;
}
.native-chatbot .message.user,
.native-chatbot .message-row.user-row .message {
background-color: #6366f1 !important;
color: white !important;
border: none !important;
border-radius: 16px 4px 16px 16px !important;
text-align: left !important;
}
.native-chatbot .message.user * {
color: white !important;
}
/* 8. 輸入框 */
.chat-input-row {
background: white !important;
border-top: 1px solid #e2e8f0 !important;
padding: 16px !important;
flex-shrink: 0 !important; /* 防止被壓扁 */
}
/* 9. 隱藏垃圾 */
.native-chatbot button,
.native-chatbot .avatar-container,
.native-chatbot .message-icon { display: none !important; }
</style>
"""