Marco310 commited on
Commit
33e308f
·
1 Parent(s): 4abc17c

rebuild app.py

Browse files
Files changed (2) hide show
  1. core/__init__.py +0 -0
  2. core/utils.py +627 -0
core/__init__.py ADDED
File without changes
core/utils.py ADDED
@@ -0,0 +1,627 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ LifeFlow AI - Core Utilities (Fixed)
3
+ 核心工具函數 - 修復撒花動畫
4
+ """
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ current_dir = Path(__file__).parent.parent
9
+ sys.path.insert(0, str(current_dir))
10
+
11
+ import plotly.graph_objects as go
12
+ from config import AGENTS_INFO
13
+
14
+
15
+ def create_agent_stream_output() -> str:
16
+ """創建 Agent 串流輸出 HTML"""
17
+ return """
18
+ <div class="stream-container">
19
+ <div class="stream-text">Ready to analyze your tasks...<span class="stream-cursor"></span></div>
20
+ </div>
21
+ """
22
+
23
+
24
+ def create_agent_card_enhanced(agent_key: str, status: str = "idle", message: str = "") -> str:
25
+ """
26
+ 創建增強的 Agent 卡片
27
+
28
+ Args:
29
+ agent_key: Agent 標識符
30
+ status: 狀態 (idle/working/complete)
31
+ message: 顯示訊息
32
+ """
33
+ agent = AGENTS_INFO.get(agent_key, {})
34
+ avatar = agent.get("avatar", "🤖")
35
+ name = agent.get("name", "Agent")
36
+ role = agent.get("role", "")
37
+ color = agent.get("color", "#4A90E2")
38
+ glow = agent.get("glow", "rgba(74, 144, 226, 0.3)")
39
+
40
+ status_class = f"status-{status}"
41
+ card_class = "agent-card active" if status == "working" else "agent-card"
42
+
43
+ status_text = {
44
+ "idle": "On Standby",
45
+ "working": "Working...",
46
+ "complete": "Complete ✓"
47
+ }.get(status, "Unknown")
48
+
49
+ message_html = f'<div class="agent-message">{message}</div>' if message else ""
50
+
51
+ return f"""
52
+ <div class="{card_class}" style="--agent-color: {color}; --agent-glow: {glow};">
53
+ <div class="agent-header">
54
+ <div class="agent-avatar">{avatar}</div>
55
+ <div class="agent-info">
56
+ <h3>{name}</h3>
57
+ <div class="agent-role">{role}</div>
58
+ </div>
59
+ </div>
60
+ <span class="agent-status {status_class}">{status_text}</span>
61
+ {message_html}
62
+ </div>
63
+ """
64
+
65
+
66
+ def create_task_card(task_num: int, task_title: str, priority: str,
67
+ time_window: str, duration: str, location: str, icon: str = "📋") -> str:
68
+ """創建任務卡片"""
69
+ priority_color_map = {
70
+ "HIGH": "#FF6B6B",
71
+ "MEDIUM": "#F5A623",
72
+ "LOW": "#7ED321"
73
+ }
74
+
75
+ priority_class = f"priority-{priority.lower()}"
76
+ task_color = priority_color_map.get(priority, "#999")
77
+
78
+ return f"""
79
+ <div class="task-card" style="--task-priority-color: {task_color};">
80
+ <div class="task-header">
81
+ <div class="task-title">
82
+ <span class="task-icon">{icon}</span>
83
+ <span>#{task_num}: {task_title}</span>
84
+ </div>
85
+ <span class="priority-badge {priority_class}">{priority}</span>
86
+ </div>
87
+ <div class="task-details">
88
+ <div class="task-detail-item">
89
+ <span>🕐</span>
90
+ <span>{time_window}</span>
91
+ </div>
92
+ <div class="task-detail-item">
93
+ <span>⏱️</span>
94
+ <span>{duration}</span>
95
+ </div>
96
+ <div class="task-detail-item">
97
+ <span>📍</span>
98
+ <span>{location}</span>
99
+ </div>
100
+ </div>
101
+ </div>
102
+ """
103
+
104
+
105
+ def create_summary_card(total_tasks: int, high_priority: int, total_time: int) -> str:
106
+ """創建摘要卡片"""
107
+ return f"""
108
+ <div class="summary-card">
109
+ <h3 style="margin: 0 0 15px 0; font-size: 20px;">📋 Task Summary</h3>
110
+ <div class="summary-stats">
111
+ <div class="stat-item">
112
+ <span class="stat-value">{total_tasks}</span>
113
+ <span class="stat-label">Tasks</span>
114
+ </div>
115
+ <div class="stat-item">
116
+ <span class="stat-value" style="color: #FF6B6B;">{high_priority}</span>
117
+ <span class="stat-label">High Priority</span>
118
+ </div>
119
+ <div class="stat-item">
120
+ <span class="stat-value">{total_time}</span>
121
+ <span class="stat-label">min total</span>
122
+ </div>
123
+ </div>
124
+ </div>
125
+ """
126
+
127
+
128
+ def create_animated_map():
129
+ """創建動畫地圖(模擬)"""
130
+ fig = go.Figure()
131
+
132
+ # 模擬路線點
133
+ lats = [25.033, 25.045, 25.055, 25.040]
134
+ lons = [121.565, 121.575, 121.585, 121.570]
135
+
136
+ fig.add_trace(go.Scattermapbox(
137
+ lat=lats,
138
+ lon=lons,
139
+ mode='markers+lines',
140
+ marker=dict(size=12, color='#4A90E2'),
141
+ line=dict(width=3, color='#50C878'),
142
+ text=['Start', 'Task 1', 'Task 2', 'End'],
143
+ hoverinfo='text'
144
+ ))
145
+
146
+ fig.update_layout(
147
+ mapbox=dict(
148
+ style='open-street-map',
149
+ center=dict(lat=25.043, lon=121.575),
150
+ zoom=12
151
+ ),
152
+ margin=dict(l=0, r=0, t=0, b=0),
153
+ height=500
154
+ )
155
+
156
+ return fig
157
+
158
+
159
+ def get_reasoning_html_reversed(reasoning_messages: list = None) -> str:
160
+ """獲取推理過程 HTML(反向排序,最新在上)"""
161
+ if not reasoning_messages:
162
+ return '<div class="reasoning-timeline"><p style="text-align: center; opacity: 0.6;">No reasoning messages yet...</p></div>'
163
+
164
+ items_html = ""
165
+ for msg in reversed(reasoning_messages[-20:]): # 只顯示最近20條,反向
166
+ agent_info = AGENTS_INFO.get(msg.get('agent', 'planner'), {})
167
+ color = agent_info.get('color', '#4A90E2')
168
+ avatar = agent_info.get('avatar', '🤖')
169
+
170
+ items_html += f"""
171
+ <div class="reasoning-item" style="--agent-color: {color};">
172
+ <div class="reasoning-header">
173
+ <span>{avatar}</span>
174
+ <span class="reasoning-agent">{msg.get('agent', 'Agent')}</span>
175
+ <span class="reasoning-time">{msg.get('time', '')}</span>
176
+ </div>
177
+ <div class="reasoning-content">{msg.get('message', '')}</div>
178
+ </div>
179
+ """
180
+
181
+ return f'<div class="reasoning-timeline">{items_html}</div>'
182
+
183
+
184
+ def create_celebration_animation() -> str:
185
+ """創建慶祝動畫(純 CSS 撒花效果)- Gradio 兼容版本"""
186
+ import random
187
+
188
+ # 生成 100 個隨機位置和動畫的撒花元素
189
+ confetti_html = ""
190
+ colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E2']
191
+
192
+ for i in range(100):
193
+ left = random.randint(0, 100)
194
+ delay = random.uniform(0, 2)
195
+ duration = random.uniform(3, 6)
196
+ rotation = random.randint(0, 360)
197
+ color = random.choice(colors)
198
+ size = random.randint(8, 15)
199
+
200
+ confetti_html += f"""
201
+ <div class="confetti" style="
202
+ left: {left}%;
203
+ animation-delay: {delay}s;
204
+ animation-duration: {duration}s;
205
+ background-color: {color};
206
+ width: {size}px;
207
+ height: {size}px;
208
+ transform: rotate({rotation}deg);
209
+ "></div>
210
+ """
211
+
212
+ return f"""
213
+ <div class="celebration-overlay">
214
+ {confetti_html}
215
+ </div>
216
+
217
+ <style>
218
+ @keyframes confetti-fall {{
219
+ 0% {{
220
+ transform: translateY(-100vh) rotate(0deg);
221
+ opacity: 1;
222
+ }}
223
+ 100% {{
224
+ transform: translateY(100vh) rotate(720deg);
225
+ opacity: 0;
226
+ }}
227
+ }}
228
+
229
+ @keyframes celebration-fade {{
230
+ 0% {{ opacity: 1; }}
231
+ 80% {{ opacity: 1; }}
232
+ 100% {{ opacity: 0; }}
233
+ }}
234
+
235
+ .celebration-overlay {{
236
+ position: fixed;
237
+ top: 0;
238
+ left: 0;
239
+ width: 100%;
240
+ height: 100%;
241
+ pointer-events: none;
242
+ z-index: 9999;
243
+ overflow: hidden;
244
+ animation: celebration-fade 6s forwards;
245
+ }}
246
+
247
+ .confetti {{
248
+ position: absolute;
249
+ top: -20px;
250
+ animation: confetti-fall linear infinite;
251
+ opacity: 0.9;
252
+ }}
253
+ </style>
254
+ """
255
+
256
+
257
+ def create_result_visualization(task_list: list) -> str:
258
+ """
259
+ 創建詳細的結果視覺化卡片
260
+ 包含:快速摘要、時間線預覽、優化指標、關鍵亮點
261
+ """
262
+ # 計算統計數據
263
+ total_tasks = len(task_list)
264
+ total_time_min = sum(int(t.get('duration', '0').split()[0]) for t in task_list)
265
+ total_time_hours = total_time_min // 60
266
+ total_time_remain = total_time_min % 60
267
+ time_str = f"{total_time_hours}h {total_time_remain}min" if total_time_hours > 0 else f"{total_time_min}min"
268
+
269
+ # 模擬數據(實際應該從優化結果獲取)
270
+ total_distance = 2.8
271
+ efficiency = 95
272
+ time_saved = 25
273
+ distance_reduced = 12
274
+
275
+ # 構建 HTML
276
+ html = f"""
277
+ <div class="result-visualization">
278
+ <!-- 標題 -->
279
+ <div class="result-header">
280
+ <h2 style="color: #50C878; margin: 0; font-size: 28px; display: flex; align-items: center; gap: 10px;">
281
+ <span class="celebration-icon" style="font-size: 36px; animation: bounce 1s infinite;">🎉</span>
282
+ <span>Planning Complete!</span>
283
+ </h2>
284
+ <p style="margin: 10px 0 0 0; opacity: 0.8; font-size: 14px;">Your optimized route is ready</p>
285
+ </div>
286
+
287
+ <!-- 快速摘要 -->
288
+ <div class="quick-summary" style="margin-top: 25px;">
289
+ <h3 style="font-size: 18px; margin: 0 0 15px 0; color: #333;">📊 Quick Summary</h3>
290
+ <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;">
291
+ <div class="summary-metric">
292
+ <div class="metric-icon">🎯</div>
293
+ <div class="metric-content">
294
+ <div class="metric-value">{total_tasks}</div>
295
+ <div class="metric-label">Total Tasks</div>
296
+ </div>
297
+ </div>
298
+ <div class="summary-metric">
299
+ <div class="metric-icon">⏱️</div>
300
+ <div class="metric-content">
301
+ <div class="metric-value">{time_str}</div>
302
+ <div class="metric-label">Total Time</div>
303
+ </div>
304
+ </div>
305
+ <div class="summary-metric">
306
+ <div class="metric-icon">🚗</div>
307
+ <div class="metric-content">
308
+ <div class="metric-value">{total_distance}km</div>
309
+ <div class="metric-label">Total Distance</div>
310
+ </div>
311
+ </div>
312
+ <div class="summary-metric success">
313
+ <div class="metric-icon">✅</div>
314
+ <div class="metric-content">
315
+ <div class="metric-value">All Met</div>
316
+ <div class="metric-label">Deadlines</div>
317
+ </div>
318
+ </div>
319
+ </div>
320
+ </div>
321
+
322
+ <!-- 時間線預覽 -->
323
+ <div class="timeline-preview" style="margin-top: 25px;">
324
+ <h3 style="font-size: 18px; margin: 0 0 15px 0; color: #333;">🗓️ Timeline Preview</h3>
325
+ <div class="timeline-container">
326
+ """
327
+
328
+ # 添加時間線項目
329
+ for i, task in enumerate(task_list):
330
+ task_time = task.get('time', 'Anytime')
331
+ task_title = task.get('title', 'Task')
332
+ task_icon = task.get('icon', '📋')
333
+
334
+ # 解析時間(簡化示例)
335
+ if '-' in task_time:
336
+ times = task_time.split('-')
337
+ start = times[0].strip()
338
+ end = times[1].strip()
339
+ else:
340
+ start = '10:00'
341
+ end = '10:30'
342
+
343
+ html += f"""
344
+ <div class="timeline-item">
345
+ <div class="timeline-time">{start}</div>
346
+ <div class="timeline-connector">━━━</div>
347
+ <div class="timeline-task">
348
+ <span class="timeline-icon">{task_icon}</span>
349
+ <span class="timeline-title">{task_title}</span>
350
+ </div>
351
+ <div class="timeline-connector">━━━</div>
352
+ <div class="timeline-time">{end}</div>
353
+ </div>
354
+ """
355
+
356
+ # 添加旅行時間(除了最後一個任務)
357
+ if i < len(task_list) - 1:
358
+ travel_time = ['15min', '20min', '10min'][i] if i < 3 else '15min'
359
+ html += f"""
360
+ <div class="timeline-travel">
361
+ <div class="travel-arrow">↓</div>
362
+ <div class="travel-time">{travel_time}</div>
363
+ </div>
364
+ """
365
+
366
+ html += """
367
+ </div>
368
+ </div>
369
+
370
+ <!-- 優化指標 -->
371
+ <div class="optimization-metrics" style="margin-top: 25px;">
372
+ <h3 style="font-size: 18px; margin: 0 0 15px 0; color: #333;">📈 Optimization Metrics</h3>
373
+ <div class="metrics-grid">
374
+ """
375
+
376
+ # 添加進度條指標
377
+ metrics = [
378
+ ("Route Efficiency", efficiency, "#50C878"),
379
+ ("Time Optimization", 100 - (time_saved / 60 * 100), "#4A90E2"),
380
+ ("Distance Reduction", distance_reduced, "#F5A623")
381
+ ]
382
+
383
+ for label, value, color in metrics:
384
+ html += f"""
385
+ <div class="metric-bar">
386
+ <div class="metric-bar-header">
387
+ <span class="metric-bar-label">{label}</span>
388
+ <span class="metric-bar-value">{int(value)}%</span>
389
+ </div>
390
+ <div class="metric-bar-track">
391
+ <div class="metric-bar-fill" style="width: {value}%; background: {color};"></div>
392
+ </div>
393
+ </div>
394
+ """
395
+
396
+ html += """
397
+ </div>
398
+ <div class="optimization-stats" style="margin-top: 15px; display: flex; gap: 20px; flex-wrap: wrap;">
399
+ <div class="stat-badge">
400
+ <span class="stat-badge-icon">⚡</span>
401
+ <span class="stat-badge-text">Time Saved: <strong>25 min</strong></span>
402
+ </div>
403
+ <div class="stat-badge">
404
+ <span class="stat-badge-icon">📏</span>
405
+ <span class="stat-badge-text">Distance Reduced: <strong>12%</strong></span>
406
+ </div>
407
+ </div>
408
+ </div>
409
+
410
+ <!-- 關鍵亮點 -->
411
+ <div class="key-highlights" style="margin-top: 25px;">
412
+ <h3 style="font-size: 18px; margin: 0 0 15px 0; color: #333;">⚡ Key Highlights</h3>
413
+ <div class="highlights-list">
414
+ <div class="highlight-item success">
415
+ <span class="highlight-icon">✓</span>
416
+ <span class="highlight-text">No traffic delays expected</span>
417
+ </div>
418
+ <div class="highlight-item success">
419
+ <span class="highlight-icon">✓</span>
420
+ <span class="highlight-text">All locations open during visit</span>
421
+ </div>
422
+ <div class="highlight-item success">
423
+ <span class="highlight-icon">✓</span>
424
+ <span class="highlight-text">Weather conditions: Favorable</span>
425
+ </div>
426
+ <div class="highlight-item info">
427
+ <span class="highlight-icon">ℹ</span>
428
+ <span class="highlight-text">Recommended departure: 08:45 AM</span>
429
+ </div>
430
+ </div>
431
+ </div>
432
+ </div>
433
+
434
+ <style>
435
+ @keyframes bounce {
436
+ 0%, 100% { transform: translateY(0); }
437
+ 50% { transform: translateY(-10px); }
438
+ }
439
+
440
+ .result-visualization {
441
+ padding: 25px;
442
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
443
+ border-radius: 15px;
444
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
445
+ }
446
+
447
+ .quick-summary .summary-metric {
448
+ background: white;
449
+ padding: 15px;
450
+ border-radius: 12px;
451
+ display: flex;
452
+ align-items: center;
453
+ gap: 12px;
454
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
455
+ transition: transform 0.2s;
456
+ }
457
+
458
+ .quick-summary .summary-metric:hover {
459
+ transform: translateY(-3px);
460
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
461
+ }
462
+
463
+ .quick-summary .summary-metric.success {
464
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
465
+ color: white;
466
+ }
467
+
468
+ .metric-icon {
469
+ font-size: 28px;
470
+ }
471
+
472
+ .metric-value {
473
+ font-size: 20px;
474
+ font-weight: bold;
475
+ line-height: 1.2;
476
+ }
477
+
478
+ .metric-label {
479
+ font-size: 12px;
480
+ opacity: 0.8;
481
+ }
482
+
483
+ .timeline-preview {
484
+ background: white;
485
+ padding: 20px;
486
+ border-radius: 12px;
487
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
488
+ }
489
+
490
+ .timeline-item {
491
+ display: flex;
492
+ align-items: center;
493
+ gap: 8px;
494
+ margin: 8px 0;
495
+ font-size: 14px;
496
+ }
497
+
498
+ .timeline-time {
499
+ font-weight: 600;
500
+ color: #4A90E2;
501
+ min-width: 50px;
502
+ }
503
+
504
+ .timeline-connector {
505
+ color: #ddd;
506
+ font-weight: bold;
507
+ }
508
+
509
+ .timeline-task {
510
+ background: #f0f4f8;
511
+ padding: 8px 12px;
512
+ border-radius: 8px;
513
+ display: flex;
514
+ align-items: center;
515
+ gap: 8px;
516
+ flex: 1;
517
+ }
518
+
519
+ .timeline-travel {
520
+ display: flex;
521
+ align-items: center;
522
+ gap: 8px;
523
+ margin-left: 60px;
524
+ font-size: 13px;
525
+ color: #666;
526
+ }
527
+
528
+ .travel-arrow {
529
+ color: #4A90E2;
530
+ font-weight: bold;
531
+ }
532
+
533
+ .optimization-metrics {
534
+ background: white;
535
+ padding: 20px;
536
+ border-radius: 12px;
537
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
538
+ }
539
+
540
+ .metric-bar {
541
+ margin-bottom: 15px;
542
+ }
543
+
544
+ .metric-bar-header {
545
+ display: flex;
546
+ justify-content: space-between;
547
+ margin-bottom: 6px;
548
+ font-size: 13px;
549
+ }
550
+
551
+ .metric-bar-label {
552
+ font-weight: 500;
553
+ }
554
+
555
+ .metric-bar-value {
556
+ font-weight: 600;
557
+ color: #50C878;
558
+ }
559
+
560
+ .metric-bar-track {
561
+ height: 8px;
562
+ background: #e0e0e0;
563
+ border-radius: 4px;
564
+ overflow: hidden;
565
+ }
566
+
567
+ .metric-bar-fill {
568
+ height: 100%;
569
+ border-radius: 4px;
570
+ transition: width 1s ease-out;
571
+ }
572
+
573
+ .stat-badge {
574
+ background: #f0f4f8;
575
+ padding: 8px 15px;
576
+ border-radius: 20px;
577
+ display: inline-flex;
578
+ align-items: center;
579
+ gap: 8px;
580
+ font-size: 13px;
581
+ }
582
+
583
+ .stat-badge-icon {
584
+ font-size: 16px;
585
+ }
586
+
587
+ .key-highlights {
588
+ background: white;
589
+ padding: 20px;
590
+ border-radius: 12px;
591
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
592
+ }
593
+
594
+ .highlight-item {
595
+ display: flex;
596
+ align-items: center;
597
+ gap: 12px;
598
+ padding: 10px;
599
+ margin: 6px 0;
600
+ border-radius: 8px;
601
+ font-size: 14px;
602
+ }
603
+
604
+ .highlight-item.success {
605
+ background: #e8f5e9;
606
+ }
607
+
608
+ .highlight-item.info {
609
+ background: #e3f2fd;
610
+ }
611
+
612
+ .highlight-icon {
613
+ font-size: 18px;
614
+ font-weight: bold;
615
+ }
616
+
617
+ .highlight-item.success .highlight-icon {
618
+ color: #4CAF50;
619
+ }
620
+
621
+ .highlight-item.info .highlight-icon {
622
+ color: #2196F3;
623
+ }
624
+ </style>
625
+ """
626
+
627
+ return html