Marco310 commited on
Commit
c72280e
·
1 Parent(s): 3b3daa9

Add outdated demo

Browse files
Files changed (1) hide show
  1. prototypes/app_demo_v2.py +532 -0
prototypes/app_demo_v2.py ADDED
@@ -0,0 +1,532 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ LifeFlow AI - Main Application v3.1
3
+ """
4
+
5
+ import sys
6
+ from pathlib import Path
7
+
8
+
9
+ import gradio as gr
10
+ from datetime import datetime
11
+ import time as time_module
12
+
13
+ # 導入配置
14
+ from config import DEFAULT_SETTINGS, APP_TITLE
15
+
16
+ # 導入 UI 組件
17
+ from ui.theme import get_enhanced_css
18
+ from ui.components.header import create_header, create_top_controls
19
+ from ui.components.input_form import create_input_form, toggle_location_inputs
20
+ from ui.components.confirmation import create_confirmation_area, create_exit_button
21
+ from ui.components.results import create_team_area, create_result_area, create_tabs
22
+ from ui.components.modals import create_settings_modal, create_doc_modal
23
+
24
+ # 導入核心工具
25
+ from core.utils import (
26
+ create_agent_stream_output, create_agent_card_enhanced,
27
+ create_task_card, create_summary_card, create_animated_map,
28
+ get_reasoning_html_reversed, create_celebration_animation,
29
+ create_result_visualization
30
+ )
31
+
32
+
33
+ class LifeFlowAI:
34
+ """LifeFlow AI - v3.1 主類"""
35
+
36
+ def __init__(self):
37
+ self.settings = DEFAULT_SETTINGS.copy()
38
+ self.task_list = []
39
+ self.reasoning_messages = []
40
+ self.planning_completed = False
41
+ self.chat_history = []
42
+
43
+ def step1_analyze_tasks(self, user_input, auto_location, lat, lon):
44
+ """
45
+ Step 1: 分析任務 - 帶串流輸出
46
+ 完成後進入 Step 2 (Confirmation)
47
+ """
48
+ if not user_input.strip():
49
+ return self._empty_step1_outputs()
50
+
51
+ # 🔧 串流輸出 1: 開始分析
52
+ stream_str = "🤔 Analyzing your request..."
53
+ stream_html = self._create_stream_html(stream_str)
54
+ yield (
55
+ stream_html, "", "", get_reasoning_html_reversed(),
56
+ gr.update(visible=False), gr.update(visible=False),
57
+ "", # chat_history_output
58
+ "Starting analysis...",
59
+ *[create_agent_card_enhanced("planner", "working", "Analyzing..."),
60
+ *[create_agent_card_enhanced(k, "idle", "On standby")
61
+ for k in ["scout", "optimizer", "validator", "weather", "traffic"]]]
62
+ )
63
+
64
+ time_module.sleep(0.5)
65
+
66
+ stream_str += "\n📋 Extracting tasks from your input..."
67
+ # 🔧 串流輸出 2: 提取任務
68
+ stream_html = self._create_stream_html(stream_str)
69
+ yield (
70
+ stream_html, "", "", get_reasoning_html_reversed(),
71
+ gr.update(visible=False), gr.update(visible=False),
72
+ "", # chat_history_output
73
+ "Extracting tasks...",
74
+ *[create_agent_card_enhanced("planner", "working", "Extracting tasks..."),
75
+ *[create_agent_card_enhanced(k, "idle", "On standby")
76
+ for k in ["scout", "optimizer", "validator", "weather", "traffic"]]]
77
+ )
78
+
79
+ time_module.sleep(0.5)
80
+
81
+ # 模擬提取的任務
82
+ self.task_list = [
83
+ {"id": 1, "title": "Visit hospital", "priority": "HIGH",
84
+ "time": "08:00-12:00", "duration": "45 minutes", "location": "Hospital nearby", "icon": "🏥"},
85
+ {"id": 2, "title": "Buy groceries", "priority": "MEDIUM",
86
+ "time": "Anytime", "duration": "30 minutes", "location": "Supermarket", "icon": "🛒"},
87
+ {"id": 3, "title": "Mail package", "priority": "HIGH",
88
+ "time": "Before 15:00", "duration": "20 minutes", "location": "Post office", "icon": "📮"}
89
+ ]
90
+
91
+ # 添加推理訊息
92
+ self._add_reasoning("planner", "Analyzing input tasks...")
93
+ self._add_reasoning("planner", f"Extracted {len(self.task_list)} tasks")
94
+
95
+ # 生成摘要和任務卡片
96
+ high_priority = sum(1 for t in self.task_list if t["priority"] == "HIGH")
97
+ total_time = sum(int(t["duration"].split()[0]) for t in self.task_list)
98
+
99
+ summary_html = create_summary_card(len(self.task_list), high_priority, total_time)
100
+ task_list_html = self._generate_task_list_html()
101
+
102
+ stream_str += "\n✅ Analysis complete! Please review your tasks."
103
+ stream_html = self._create_stream_html(stream_str)
104
+
105
+ # Step 1 完成,準備進入 Step 2
106
+ yield (
107
+ stream_html, summary_html, task_list_html,
108
+ get_reasoning_html_reversed(self.reasoning_messages),
109
+ gr.update(visible=True), # task_confirm_area
110
+ gr.update(visible=False), # chat_input_area (先隱藏)
111
+ self._generate_chat_welcome_html(), # chat_history_output
112
+ "Tasks extracted successfully ✓",
113
+ *[create_agent_card_enhanced("planner", "complete", "Tasks extracted"),
114
+ *[create_agent_card_enhanced(k, "idle", "On standby")
115
+ for k in ["scout", "optimizer", "validator", "weather", "traffic"]]]
116
+ )
117
+
118
+ def modify_task_chat(self, user_message):
119
+ """
120
+ 處理用戶在 Chat with LifeFlow Tab 中的修改請求
121
+ 這在 Step 2 時可用
122
+ """
123
+ if not user_message.strip():
124
+ return self._generate_chat_history_html(), self._generate_task_list_html()
125
+
126
+ # 添加用戶消息
127
+ self.chat_history.append({
128
+ "role": "user",
129
+ "message": user_message,
130
+ "time": datetime.now().strftime("%H:%M:%S")
131
+ })
132
+
133
+ # 模擬 AI 回應
134
+ time_module.sleep(0.3)
135
+ ai_response = f"✅ I've updated your tasks based on your request: '{user_message}'"
136
+
137
+ self.chat_history.append({
138
+ "role": "assistant",
139
+ "message": ai_response,
140
+ "time": datetime.now().strftime("%H:%M:%S")
141
+ })
142
+
143
+ # 更新 reasoning
144
+ self._add_reasoning("planner", f"User requested: {user_message}")
145
+
146
+ # 返回更新的聊天歷史和任務列表
147
+ return self._generate_chat_history_html(), self._generate_task_list_html()
148
+
149
+ def step2_search_pois(self):
150
+ """
151
+ Step 2.5: 搜索 POI - 專家團隊開始工作
152
+ Scout Agent 開始工作
153
+ """
154
+ time_module.sleep(1)
155
+ self._add_reasoning("scout", "Searching for POIs...")
156
+ self._add_reasoning("scout", "Found hospital: NTU Hospital (800m, 4.8★)")
157
+ self._add_reasoning("scout", "Found supermarket: PX Mart (1.2km)")
158
+
159
+ reasoning_html = get_reasoning_html_reversed(self.reasoning_messages)
160
+
161
+ agent_updates = [
162
+ create_agent_card_enhanced("planner", "complete", "Tasks ready"),
163
+ create_agent_card_enhanced("scout", "working", "Searching POIs..."),
164
+ *[create_agent_card_enhanced(k, "idle", "On standby")
165
+ for k in ["optimizer", "validator", "weather", "traffic"]]
166
+ ]
167
+
168
+ return reasoning_html, "🗺️ Searching for locations...", *agent_updates
169
+
170
+ def step3_optimize_route(self):
171
+ """Step 3: 優化路線 - Optimizer 開始工作"""
172
+ time_module.sleep(1)
173
+ self._add_reasoning("optimizer", "Running TSPTW solver...")
174
+ self._add_reasoning("optimizer", "Optimized route: Hospital → Supermarket → Post Office")
175
+
176
+ report = """
177
+ ## 🎯 Optimization Complete
178
+
179
+ ### Route Details:
180
+ 1. **🏥 Hospital** (09:00 - 10:00)
181
+ 2. **🛒 Supermarket** (10:15 - 10:45)
182
+ 3. **📮 Post Office** (11:05 - 11:25)
183
+
184
+ ### Metrics:
185
+ - ✅ Total distance: 2.8 km
186
+ - ✅ Total time: 95 minutes
187
+ - ✅ All deadlines met
188
+ - ✅ Minimal travel distance
189
+ - ✅ Weather conditions favorable
190
+ """
191
+
192
+ agent_updates = [
193
+ create_agent_card_enhanced("planner", "complete", "Analysis done"),
194
+ create_agent_card_enhanced("scout", "complete", "POI search done"),
195
+ create_agent_card_enhanced("optimizer", "working", "Optimizing route..."),
196
+ *[create_agent_card_enhanced(k, "idle", "On standby")
197
+ for k in ["validator", "weather", "traffic"]]
198
+ ]
199
+
200
+ reasoning_html = get_reasoning_html_reversed(self.reasoning_messages)
201
+ return reasoning_html, report, "🎯 Optimizing route...", *agent_updates
202
+
203
+ def step4_finalize(self):
204
+ """Step 4: 完成並生成報告(含慶祝動畫和增強視覺化)"""
205
+ time_module.sleep(1)
206
+ self._add_reasoning("validator", "Validating route quality...")
207
+ self._add_reasoning("weather", "Checking weather conditions...")
208
+ self._add_reasoning("traffic", "Analyzing traffic patterns...")
209
+
210
+ self.planning_completed = True
211
+
212
+ # 生成慶祝動畫
213
+ celebration_html = create_celebration_animation()
214
+
215
+ # 生成詳細的結果視覺化
216
+ result_html = create_result_visualization(self.task_list) + celebration_html
217
+
218
+ # 簡化的 timeline 和 metrics(保留兼容性,但現在主要信息在 result_html 中)
219
+ timeline_html = "<h3>🗓️ Detailed Timeline</h3><p>See the complete timeline in the result panel</p>"
220
+ metrics_html = "<h3>📈 Performance Metrics</h3><p>All optimization metrics are displayed above</p>"
221
+
222
+ map_fig = create_animated_map()
223
+
224
+ agent_updates = [
225
+ create_agent_card_enhanced(k, "complete", "Task complete")
226
+ for k in ["planner", "scout", "optimizer", "validator", "weather", "traffic"]
227
+ ]
228
+
229
+ return (
230
+ timeline_html, metrics_html, result_html, map_fig,
231
+ gr.update(visible=True), # map_tab
232
+ gr.update(visible=False), # team_area (隱藏,任務完成)
233
+ "🎉 Planning complete! Review your optimized route.",
234
+ *agent_updates
235
+ )
236
+
237
+ def save_settings(self, google_key, weather_key, anthropic_key, model):
238
+ """保存設定"""
239
+ self.settings['google_maps_api_key'] = google_key
240
+ self.settings['openweather_api_key'] = weather_key
241
+ self.settings['anthropic_api_key'] = anthropic_key
242
+ self.settings['model'] = model
243
+ return "✅ Settings saved successfully!"
244
+
245
+ def _create_stream_html(self, message):
246
+ """創建串流輸出 HTML"""
247
+ return f"""
248
+ <div class="stream-container">
249
+ <div class="stream-text">{message}<span class="stream-cursor"></span></div>
250
+ </div>
251
+ """
252
+
253
+ def _add_reasoning(self, agent, message):
254
+ """添加推理訊息"""
255
+ self.reasoning_messages.append({
256
+ 'agent': agent,
257
+ 'message': message,
258
+ 'time': datetime.now().strftime("%H:%M:%S")
259
+ })
260
+
261
+ def _generate_task_list_html(self):
262
+ """生成任務列表 HTML"""
263
+ html = ""
264
+ for task in self.task_list:
265
+ html += create_task_card(
266
+ task["id"], task["title"], task["priority"],
267
+ task["time"], task["duration"], task["location"], task["icon"]
268
+ )
269
+ return html
270
+
271
+ def _generate_chat_welcome_html(self):
272
+ """生成 Chat 歡迎訊息"""
273
+ return """
274
+ <div style="padding: 20px; text-align: center;">
275
+ <h3 style="color: #4A90E2; margin-bottom: 10px;">💬 Chat with LifeFlow</h3>
276
+ <p style="opacity: 0.8;">You can now modify your tasks by chatting with me!</p>
277
+ <p style="opacity: 0.6; font-size: 0.9em;">Try: "Change task 2 to high priority" or "Add a new task"</p>
278
+ </div>
279
+ """
280
+
281
+ def _generate_chat_history_html(self):
282
+ """生成聊天歷史 HTML"""
283
+ if not self.chat_history:
284
+ return self._generate_chat_welcome_html()
285
+
286
+ html = '<div class="chat-history-container" style="max-height: 400px; overflow-y: auto;">'
287
+
288
+ for msg in self.chat_history:
289
+ role = msg["role"]
290
+ message = msg["message"]
291
+ time = msg["time"]
292
+
293
+ if role == "user":
294
+ html += f'''
295
+ <div style="margin-bottom: 15px; text-align: right;">
296
+ <div style="display: inline-block; max-width: 70%; background: #E3F2FD; padding: 10px 15px; border-radius: 15px 15px 0 15px;">
297
+ <div style="font-size: 0.9em; font-weight: 500;">{message}</div>
298
+ <div style="font-size: 0.75em; opacity: 0.6; margin-top: 5px;">{time}</div>
299
+ </div>
300
+ </div>
301
+ '''
302
+ else:
303
+ html += f'''
304
+ <div style="margin-bottom: 15px; text-align: left;">
305
+ <div style="display: inline-block; max-width: 70%; background: #F5F5F5; padding: 10px 15px; border-radius: 15px 15px 15px 0;">
306
+ <div style="font-size: 0.85em; color: #4A90E2; font-weight: 600; margin-bottom: 5px;">🤖 LifeFlow AI</div>
307
+ <div style="font-size: 0.9em;">{message}</div>
308
+ <div style="font-size: 0.75em; opacity: 0.6; margin-top: 5px;">{time}</div>
309
+ </div>
310
+ </div>
311
+ '''
312
+
313
+ html += '</div>'
314
+ return html
315
+
316
+ def _empty_step1_outputs(self):
317
+ """返回空的 Step 1 輸出"""
318
+ return (
319
+ create_agent_stream_output(),
320
+ "", "", get_reasoning_html_reversed(),
321
+ gr.update(visible=False),
322
+ gr.update(visible=False),
323
+ self._generate_chat_welcome_html(),
324
+ "Please enter your tasks",
325
+ *[create_agent_card_enhanced(k, "idle", "On standby")
326
+ for k in ["planner", "scout", "optimizer", "validator", "weather", "traffic"]]
327
+ )
328
+
329
+ def build_interface(self):
330
+ """構建 Gradio 界面"""
331
+ with gr.Blocks(title=APP_TITLE) as demo:
332
+ # 注入 CSS 樣式
333
+ gr.HTML(get_enhanced_css())
334
+ # Header
335
+ create_header()
336
+
337
+ # Top Controls (Theme, Settings & Doc)
338
+ theme_btn, settings_btn, doc_btn = create_top_controls()
339
+
340
+ # Main Layout
341
+ with gr.Row():
342
+ # ========== Left Column (主操作區) ==========
343
+ with gr.Column(scale=2, min_width=400):
344
+ # Step 1: Input Form
345
+ (input_area, agent_stream_output, user_input, auto_location,
346
+ location_inputs, lat_input, lon_input, analyze_btn) = create_input_form(
347
+ create_agent_stream_output()
348
+ )
349
+
350
+ # Step 2: Confirmation Area (包含 Exit 和 Ready to plan 按鈕)
351
+ (task_confirm_area, task_summary_display,
352
+ task_list_display, exit_btn_inline, ready_plan_btn) = create_confirmation_area()
353
+
354
+ # Step 2.5/3: Team Area (取代 Confirmation Area)
355
+ team_area, agent_displays = create_team_area(create_agent_card_enhanced)
356
+
357
+ # Step 3: Result Area (最終結果展示)
358
+ (result_area, result_display,
359
+ timeline_display, metrics_display) = create_result_area(create_animated_map)
360
+
361
+ # ========== Right Column (狀態 + Tabs) ==========
362
+ with gr.Column(scale=3, min_width=500):
363
+ status_bar = gr.Textbox(
364
+ label="📊 Status",
365
+ value="Waiting for input...",
366
+ interactive=False,
367
+ max_lines=1
368
+ )
369
+
370
+ # Tabs (包含新的 Chat with LifeFlow Tab)
371
+ (tabs, report_tab, map_tab, report_output, map_output, reasoning_output,
372
+ chat_input_area, chat_history_output, chat_input, chat_send) = create_tabs(
373
+ create_animated_map,
374
+ get_reasoning_html_reversed()
375
+ )
376
+
377
+ # Modals
378
+ (settings_modal, google_maps_key, openweather_key, anthropic_key,
379
+ model_choice, close_settings_btn, save_settings_btn,
380
+ settings_status) = create_settings_modal()
381
+
382
+ doc_modal, close_doc_btn = create_doc_modal()
383
+
384
+ # ============= Event Handlers =============
385
+
386
+ # Auto location toggle
387
+ auto_location.change(
388
+ fn=toggle_location_inputs,
389
+ inputs=[auto_location],
390
+ outputs=[location_inputs]
391
+ )
392
+
393
+ # ====== Step 1: Analyze button ======
394
+ analyze_btn.click(
395
+ fn=self.step1_analyze_tasks,
396
+ inputs=[user_input, auto_location, lat_input, lon_input],
397
+ outputs=[
398
+ agent_stream_output, task_summary_display, task_list_display,
399
+ reasoning_output, task_confirm_area, chat_input_area,
400
+ chat_history_output, status_bar, *agent_displays
401
+ ]
402
+ ).then(
403
+ # Step 1 → Step 2: 隱藏輸入區,顯示確認區 + 開啟 Chat 輸入框
404
+ fn=lambda: (
405
+ gr.update(visible=False), # input_area
406
+ gr.update(visible=True), # task_confirm_area
407
+ gr.update(visible=True) # chat_input_area (開啟聊天功能)
408
+ ),
409
+ outputs=[input_area, task_confirm_area, chat_input_area]
410
+ )
411
+
412
+ # ====== Step 2: Chat with LifeFlow (任務修改) ======
413
+ chat_send.click(
414
+ fn=self.modify_task_chat,
415
+ inputs=[chat_input],
416
+ outputs=[chat_history_output, task_list_display]
417
+ ).then(
418
+ fn=lambda: "", # 清空輸入框
419
+ outputs=[chat_input]
420
+ )
421
+
422
+ # ====== Step 2: Exit button ======
423
+ exit_btn_inline.click(
424
+ fn=lambda: (
425
+ gr.update(visible=True), # input_area
426
+ gr.update(visible=False), # task_confirm_area
427
+ gr.update(visible=False), # chat_input_area (關閉聊天)
428
+ gr.update(visible=False), # result_area
429
+ gr.update(visible=False), # team_area
430
+ gr.update(visible=False), # report_tab
431
+ gr.update(visible=False), # map_tab
432
+ "", # user_input
433
+ create_agent_stream_output(), # agent_stream_output
434
+ self._generate_chat_welcome_html(), # chat_history_output (重置)
435
+ "Ready to start planning..." # status_bar
436
+ ),
437
+ outputs=[
438
+ input_area, task_confirm_area, chat_input_area, result_area,
439
+ team_area, report_tab, map_tab, user_input,
440
+ agent_stream_output, chat_history_output, status_bar
441
+ ]
442
+ )
443
+
444
+ # ====== Step 2 → Step 2.5: Ready to Plan button ======
445
+ ready_plan_btn.click(
446
+ # 隱藏確認區和聊天輸入,顯示專家團隊,切換到 AI Conversation Tab
447
+ fn=lambda: (
448
+ gr.update(visible=False), # task_confirm_area
449
+ gr.update(visible=False), # chat_input_area (關閉聊天輸入)
450
+ gr.update(visible=True), # team_area (顯示專家團隊)
451
+ gr.update(selected="ai_conversation_tab") # 切換到 AI Conversation Tab
452
+ ),
453
+ outputs=[task_confirm_area, chat_input_area, team_area, tabs]
454
+ ).then(
455
+ # Step 2.5: Scout 開始工作
456
+ fn=self.step2_search_pois,
457
+ outputs=[reasoning_output, status_bar, *agent_displays]
458
+ ).then(
459
+ # Step 3: Optimizer 開始工作,切換到 Full Report Tab
460
+ fn=self.step3_optimize_route,
461
+ outputs=[reasoning_output, report_output, status_bar, *agent_displays]
462
+ ).then(
463
+ # 顯示 Report 和 Map Tabs,並切換到 Full Report
464
+ fn=lambda: (
465
+ gr.update(visible=True), # report_tab
466
+ gr.update(visible=True), # map_tab
467
+ gr.update(selected="report_tab") # 切換到 Full Report Tab
468
+ ),
469
+ outputs=[report_tab, map_tab, tabs]
470
+ ).then(
471
+ # Step 4: 完成規劃
472
+ fn=self.step4_finalize,
473
+ outputs=[
474
+ timeline_display, metrics_display, result_display,
475
+ map_output, map_tab, team_area, status_bar, *agent_displays
476
+ ]
477
+ ).then(
478
+ fn=lambda: gr.update(visible=True),
479
+ outputs=[result_area]
480
+ )
481
+
482
+ # ====== Settings ======
483
+ settings_btn.click(
484
+ fn=lambda: gr.update(visible=True),
485
+ outputs=[settings_modal]
486
+ )
487
+ close_settings_btn.click(
488
+ fn=lambda: gr.update(visible=False),
489
+ outputs=[settings_modal]
490
+ )
491
+ save_settings_btn.click(
492
+ fn=self.save_settings,
493
+ inputs=[google_maps_key, openweather_key, anthropic_key, model_choice],
494
+ outputs=[settings_status]
495
+ )
496
+
497
+ # ====== Theme Toggle ======
498
+ theme_btn.click(
499
+ fn=None,
500
+ js="""
501
+ () => {
502
+ const container = document.querySelector('.gradio-container');
503
+ if (container) {
504
+ container.classList.toggle('theme-dark');
505
+ const isDark = container.classList.contains('theme-dark');
506
+ localStorage.setItem('lifeflow-theme', isDark ? 'dark' : 'light');
507
+ console.log('Theme toggled:', isDark ? 'dark' : 'light');
508
+ }
509
+ }
510
+ """
511
+ )
512
+
513
+ # ====== Documentation ======
514
+ doc_btn.click(
515
+ fn=lambda: gr.update(visible=True),
516
+ outputs=[doc_modal]
517
+ )
518
+ close_doc_btn.click(
519
+ fn=lambda: gr.update(visible=False),
520
+ outputs=[doc_modal]
521
+ )
522
+
523
+ return demo
524
+
525
+
526
+ def main():
527
+ app = LifeFlowAI()
528
+ demo = app.build_interface()
529
+ demo.launch(server_name="0.0.0.0", server_port=7860, share=False, show_error=True)
530
+
531
+ if __name__ == "__main__":
532
+ main()