Spaces:
Running
Running
| """ | |
| LifeFlow AI - Theme System | |
| 提供完整的主題和樣式管理,支援動態主題切換 | |
| """ | |
| def get_enhanced_css() -> str: | |
| """ | |
| 改進的 CSS 系統: | |
| 1. ✅ 修復色調功能 - 確保主題顏色正確應用 | |
| 2. ✅ 完整支援深淺模式切換 | |
| 3. ✅ 修復 Exit 按鈕樣式(Step 2) | |
| 4. ✅ 調整 Setting 和 Doc 按鈕位置和大小 | |
| """ | |
| return """ | |
| <style> | |
| :root { | |
| /* 品牌色 - 固定不變,保持品牌識別度 */ | |
| --primary-color: #4A90E2; | |
| --secondary-color: #50C878; | |
| --accent-color: #F5A623; | |
| --primary-glow: rgba(74, 144, 226, 0.3); | |
| /* 功能色 */ | |
| --success-color: #7ED321; | |
| --warning-color: #F5A623; | |
| --danger-color: #FF6B6B; | |
| } | |
| /* ============= 動畫效果 ============= */ | |
| @keyframes pulse { | |
| 0%, 100% { transform: scale(1); } | |
| 50% { transform: scale(1.05); } | |
| } | |
| @keyframes breathing { | |
| 0%, 100% { box-shadow: 0 0 10px var(--agent-glow, rgba(74, 144, 226, 0.3)); } | |
| 50% { box-shadow: 0 0 25px var(--agent-glow, rgba(74, 144, 226, 0.6)), 0 0 40px var(--agent-glow, rgba(74, 144, 226, 0.3)); } | |
| } | |
| @keyframes slide-in-left { | |
| from { transform: translateX(-100%); opacity: 0; } | |
| to { transform: translateX(0); opacity: 1; } | |
| } | |
| @keyframes fade-in { | |
| from { opacity: 0; } | |
| to { opacity: 1; } | |
| } | |
| @keyframes blink { | |
| 0%, 49% { opacity: 1; } | |
| 50%, 100% { opacity: 0; } | |
| } | |
| /* ============= 全域設定 ============= */ | |
| .gradio-container { | |
| background: var(--background-fill-primary) !important; | |
| } | |
| /* ============= 🔧 修復:Header 標題 ============= */ | |
| .app-header { | |
| text-align: center; | |
| padding: 40px 20px 30px 20px; | |
| position: relative; | |
| } | |
| .app-header h1 { | |
| font-size: 64px !important; | |
| margin: 0 !important; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| font-weight: 700 !important; | |
| } | |
| .app-header p { | |
| font-size: 20px !important; | |
| color: var(--body-text-color); | |
| opacity: 0.8; | |
| margin-top: 10px !important; | |
| } | |
| /* ============= 🔧 修復:主題切換按鈕 ============= */ | |
| #theme-toggle { | |
| position: fixed !important; | |
| top: 20px !important; | |
| right: 110px !important; | |
| z-index: 1000 !important; | |
| width: 40px !important; | |
| height: 40px !important; | |
| min-width: 40px !important; | |
| max-width: 40px !important; | |
| padding: 0 !important; | |
| font-size: 18px !important; | |
| border-radius: 10px !important; | |
| background: var(--background-fill-secondary) !important; | |
| border: 1.5px solid var(--border-color-primary) !important; | |
| box-shadow: 0 2px 6px rgba(0,0,0,0.1) !important; | |
| transition: all 0.3s ease !important; | |
| cursor: pointer !important; | |
| } | |
| #theme-toggle:hover { | |
| transform: translateY(-2px) !important; | |
| box-shadow: 0 4px 10px rgba(245, 166, 35, 0.3) !important; | |
| background: var(--accent-color) !important; | |
| color: white !important; | |
| } | |
| /* ============= 🔧 修復:Settings 按鈕 ============= */ | |
| #settings-btn { | |
| position: fixed !important; | |
| top: 20px !important; | |
| right: 60px !important; | |
| z-index: 1000 !important; | |
| width: 40px !important; | |
| height: 40px !important; | |
| min-width: 40px !important; | |
| max-width: 40px !important; | |
| padding: 0 !important; | |
| font-size: 18px !important; | |
| border-radius: 10px !important; | |
| background: var(--background-fill-secondary) !important; | |
| border: 1.5px solid var(--border-color-primary) !important; | |
| box-shadow: 0 2px 6px rgba(0,0,0,0.1) !important; | |
| transition: all 0.3s ease !important; | |
| cursor: pointer !important; | |
| } | |
| #settings-btn:hover { | |
| transform: translateY(-2px) !important; | |
| box-shadow: 0 4px 10px rgba(74, 144, 226, 0.3) !important; | |
| background: var(--primary-color) !important; | |
| color: white !important; | |
| } | |
| /* ============= 🔧 修復:Doc 按鈕 ============= */ | |
| #doc-btn { | |
| position: fixed !important; | |
| top: 20px !important; | |
| right: 10px !important; | |
| z-index: 1000 !important; | |
| width: 40px !important; | |
| height: 40px !important; | |
| min-width: 40px !important; | |
| max-width: 40px !important; | |
| padding: 0 !important; | |
| font-size: 18px !important; | |
| border-radius: 10px !important; | |
| background: var(--background-fill-secondary) !important; | |
| border: 1.5px solid var(--border-color-primary) !important; | |
| box-shadow: 0 2px 6px rgba(0,0,0,0.1) !important; | |
| transition: all 0.3s ease !important; | |
| cursor: pointer !important; | |
| } | |
| #doc-btn:hover { | |
| transform: translateY(-2px) !important; | |
| box-shadow: 0 4px 10px rgba(74, 144, 226, 0.3) !important; | |
| background: var(--primary-color) !important; | |
| color: white !important; | |
| } | |
| /* ============= 🔧 修復:Exit 按鈕樣式 (Step 2 - 頂部獨立) ============= */ | |
| #exit-button { | |
| width: 100% !important; | |
| height: 50px !important; | |
| font-size: 16px !important; | |
| font-weight: 600 !important; | |
| border-radius: 10px !important; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| color: white !important; | |
| border: none !important; | |
| box-shadow: 0 3px 10px rgba(102, 126, 234, 0.3) !important; | |
| transition: all 0.3s ease !important; | |
| margin-bottom: 15px !important; | |
| cursor: pointer; | |
| } | |
| #exit-button:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 5px 15px rgba(102, 126, 234, 0.5) !important; | |
| } | |
| /* ============= 🔧 新增:Exit 按鈕樣式 (內聯版本 - 與 Ready to plan 並排) ============= */ | |
| #exit-button-inline { | |
| height: 50px !important; | |
| font-size: 15px !important; | |
| font-weight: 600 !important; | |
| border-radius: 10px !important; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| color: white !important; | |
| border: none !important; | |
| box-shadow: 0 3px 10px rgba(102, 126, 234, 0.3) !important; | |
| transition: all 0.3s ease !important; | |
| cursor: pointer; | |
| } | |
| #exit-button-inline:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 5px 15px rgba(102, 126, 234, 0.5) !important; | |
| } | |
| /* ============= 串流輸出框 ============= */ | |
| .stream-container { | |
| background: var(--background-fill-secondary); | |
| border-radius: 10px; | |
| padding: 15px; | |
| min-height: 150px; | |
| max-height: 300px; | |
| overflow-y: auto; | |
| margin: 15px 0; | |
| border: 1px solid var(--border-color-primary); | |
| font-family: 'Monaco', 'Courier New', monospace; | |
| font-size: 13px; | |
| line-height: 1.6; | |
| } | |
| .stream-text { | |
| color: var(--body-text-color); | |
| white-space: pre-wrap; | |
| word-wrap: break-word; | |
| } | |
| .stream-cursor { | |
| display: inline-block; | |
| width: 8px; | |
| height: 16px; | |
| background: var(--primary-color); | |
| animation: blink 1s infinite; | |
| margin-left: 2px; | |
| } | |
| /* ============= Agent 卡片 ============= */ | |
| .agent-card { | |
| background: var(--background-fill-secondary); | |
| border-radius: 12px; | |
| padding: 15px; | |
| margin: 10px 0; | |
| border-left: 4px solid var(--agent-color, var(--primary-color)); | |
| transition: all 0.3s ease; | |
| box-shadow: 0 2px 8px rgba(0,0,0,0.1); | |
| } | |
| .agent-card:hover { | |
| transform: translateX(5px); | |
| box-shadow: 0 4px 12px var(--agent-glow, rgba(74, 144, 226, 0.2)); | |
| } | |
| .agent-card.active { | |
| animation: breathing 2s infinite; | |
| border-left-width: 6px; | |
| } | |
| .agent-header { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| margin-bottom: 8px; | |
| } | |
| .agent-avatar { | |
| font-size: 28px; | |
| filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2)); | |
| } | |
| .agent-info h3 { | |
| margin: 0; | |
| font-size: 16px; | |
| color: var(--body-text-color); | |
| } | |
| .agent-role { | |
| font-size: 12px; | |
| color: var(--body-text-color); | |
| opacity: 0.7; | |
| } | |
| .agent-status { | |
| display: inline-block; | |
| padding: 4px 12px; | |
| border-radius: 12px; | |
| font-size: 11px; | |
| font-weight: 600; | |
| margin-top: 8px; | |
| } | |
| .status-idle { | |
| background: rgba(128, 128, 128, 0.2); | |
| color: var(--body-text-color); | |
| } | |
| .status-working { | |
| background: var(--primary-glow); | |
| color: var(--primary-color); | |
| animation: pulse 1.5s infinite; | |
| } | |
| .status-complete { | |
| background: rgba(126, 211, 33, 0.2); | |
| color: var(--success-color); | |
| } | |
| .agent-message { | |
| margin-top: 8px; | |
| padding: 8px; | |
| background: var(--background-fill-primary); | |
| border-radius: 6px; | |
| font-size: 13px; | |
| color: var(--body-text-color); | |
| opacity: 0.9; | |
| } | |
| /* ============= Task 卡片 ============= */ | |
| .task-card { | |
| background: var(--background-fill-secondary); | |
| border-radius: 10px; | |
| padding: 16px; | |
| margin: 12px 0; | |
| border-left: 4px solid var(--task-priority-color, #999); | |
| box-shadow: 0 2px 6px rgba(0,0,0,0.08); | |
| transition: all 0.3s ease; | |
| } | |
| .task-card:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.12); | |
| } | |
| .task-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 10px; | |
| } | |
| .task-title { | |
| font-size: 16px; | |
| font-weight: 600; | |
| color: var(--body-text-color); | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .task-icon { | |
| font-size: 24px; | |
| } | |
| .priority-badge { | |
| padding: 4px 12px; | |
| border-radius: 12px; | |
| font-size: 11px; | |
| font-weight: 700; | |
| text-transform: uppercase; | |
| } | |
| .priority-high { | |
| background: rgba(255, 107, 107, 0.2); | |
| color: #FF6B6B; | |
| } | |
| .priority-medium { | |
| background: rgba(245, 166, 35, 0.2); | |
| color: #F5A623; | |
| } | |
| .priority-low { | |
| background: rgba(126, 211, 33, 0.2); | |
| color: #7ED321; | |
| } | |
| .task-details { | |
| display: flex; | |
| gap: 20px; | |
| font-size: 13px; | |
| color: var(--body-text-color); | |
| opacity: 0.8; | |
| margin-top: 10px; | |
| } | |
| .task-detail-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| /* ============= Summary Card ============= */ | |
| .summary-card { | |
| background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%); | |
| border-radius: 12px; | |
| padding: 20px; | |
| color: white; | |
| margin: 15px 0; | |
| box-shadow: 0 4px 15px rgba(74, 144, 226, 0.3); | |
| } | |
| .summary-stats { | |
| display: flex; | |
| justify-content: space-around; | |
| margin-top: 15px; | |
| } | |
| .stat-item { | |
| text-align: center; | |
| } | |
| .stat-value { | |
| font-size: 28px; | |
| font-weight: bold; | |
| display: block; | |
| } | |
| .stat-label { | |
| font-size: 12px; | |
| opacity: 0.9; | |
| margin-top: 5px; | |
| } | |
| /* ============= 🔧 優化:Ready to Plan 按鈕樣式(內聯佈局) ============= */ | |
| .ready-plan-button { | |
| height: 50px !important; | |
| font-size: 16px !important; | |
| font-weight: 700 !important; | |
| border-radius: 10px !important; | |
| background: linear-gradient(135deg, #50C878 0%, #7ED321 100%) !important; | |
| color: white !important; | |
| border: none !important; | |
| box-shadow: 0 3px 12px rgba(80, 200, 120, 0.4) !important; | |
| transition: all 0.3s ease !important; | |
| cursor: pointer; | |
| } | |
| .ready-plan-button:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 5px 20px rgba(80, 200, 120, 0.6) !important; | |
| background: linear-gradient(135deg, #5DD68D 0%, #8FE63F 100%) !important; | |
| } | |
| .ready-plan-button:active { | |
| transform: translateY(0); | |
| } | |
| /* ============= Reasoning Timeline ============= */ | |
| .reasoning-timeline { | |
| padding: 20px 0; | |
| } | |
| .reasoning-item { | |
| padding: 15px; | |
| margin: 10px 0; | |
| border-radius: 8px; | |
| background: var(--background-fill-secondary); | |
| border-left: 3px solid var(--agent-color, var(--primary-color)); | |
| animation: fade-in 0.5s ease; | |
| } | |
| .reasoning-header { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| margin-bottom: 8px; | |
| } | |
| .reasoning-agent { | |
| font-weight: 600; | |
| color: var(--agent-color, var(--primary-color)); | |
| } | |
| .reasoning-time { | |
| font-size: 11px; | |
| opacity: 0.6; | |
| margin-left: auto; | |
| } | |
| .reasoning-content { | |
| color: var(--body-text-color); | |
| opacity: 0.9; | |
| line-height: 1.6; | |
| } | |
| /* ============= Modal 樣式 ============= */ | |
| .modal-overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: rgba(0, 0, 0, 0.5); | |
| z-index: 1000; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .modal-content { | |
| background: var(--background-fill-primary); | |
| border-radius: 16px; | |
| padding: 30px; | |
| max-width: 600px; | |
| width: 90%; | |
| max-height: 80vh; | |
| overflow-y: auto; | |
| box-shadow: 0 10px 40px rgba(0,0,0,0.3); | |
| } | |
| /* ============= Responsive ============= */ | |
| @media (max-width: 768px) { | |
| #top-controls { | |
| top: 10px; | |
| right: 10px; | |
| } | |
| #top-controls button { | |
| width: 40px !important; | |
| height: 40px !important; | |
| font-size: 18px !important; | |
| } | |
| .agent-card { | |
| padding: 12px; | |
| } | |
| .task-card { | |
| padding: 12px; | |
| } | |
| } | |
| /* ============= 滾動條美化 ============= */ | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| height: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: var(--background-fill-secondary); | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: var(--primary-color); | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: var(--secondary-color); | |
| } | |
| /* ============= 🔧 主題切換 - 深色模式樣式 ============= */ | |
| .theme-dark { | |
| background: #1a1a1a !important; | |
| } | |
| .theme-dark .gradio-container { | |
| background: #1a1a1a !important; | |
| } | |
| .theme-dark [data-testid="block"] { | |
| background: #2d2d2d !important; | |
| } | |
| .theme-dark .gr-box, | |
| .theme-dark .gr-form, | |
| .theme-dark .gr-input, | |
| .theme-dark .gr-group { | |
| background: #2d2d2d !important; | |
| border-color: #444444 !important; | |
| } | |
| .theme-dark p, | |
| .theme-dark label, | |
| .theme-dark span, | |
| .theme-dark h1, | |
| .theme-dark h2, | |
| .theme-dark h3 { | |
| color: #e0e0e0 !important; | |
| } | |
| .theme-dark input, | |
| .theme-dark textarea { | |
| background: #3a3a3a !important; | |
| color: #e0e0e0 !important; | |
| border-color: #555555 !important; | |
| } | |
| .theme-dark button { | |
| background: #3a3a3a !important; | |
| color: #e0e0e0 !important; | |
| border-color: #555555 !important; | |
| } | |
| /* 保持品牌色按鈕的顏色 */ | |
| .theme-dark #exit-button, | |
| .theme-dark #exit-button-inline, | |
| .theme-dark .ready-plan-button, | |
| .theme-dark #theme-toggle, | |
| .theme-dark #settings-btn, | |
| .theme-dark #doc-btn { | |
| /* 保持原有的漸變色 */ | |
| filter: brightness(0.9); | |
| } | |
| </style> | |
| <script> | |
| // 主題切換功能 - 頁面載入時執行 | |
| (function() { | |
| // 等待 Gradio 完全載入 | |
| function initTheme() { | |
| const savedTheme = localStorage.getItem('lifeflow-theme'); | |
| console.log('Initializing theme, saved:', savedTheme); | |
| if (savedTheme === 'dark') { | |
| const container = document.querySelector('.gradio-container'); | |
| if (container) { | |
| container.classList.add('theme-dark'); | |
| document.body.classList.add('theme-dark'); | |
| console.log('Dark theme applied on load'); | |
| } else { | |
| // 如果還沒找到容器,稍後再試 | |
| setTimeout(initTheme, 100); | |
| } | |
| } | |
| } | |
| // 頁面載入完成後執行 | |
| if (document.readyState === 'loading') { | |
| document.addEventListener('DOMContentLoaded', initTheme); | |
| } else { | |
| initTheme(); | |
| } | |
| // 也在 window load 時再次檢查 | |
| window.addEventListener('load', initTheme); | |
| })(); | |
| </script> | |
| """ |