Nipun Claude commited on
Commit
95f4c51
Β·
1 Parent(s): 7fdbff2

Fix session state initialization error and improve quick queries alignment

Browse files

- Remove duplicate session state initialization that was causing AttributeError
- Move session state initialization earlier in the app lifecycle
- Improve quick queries button styling with smaller font (0.7rem) and proper left alignment
- Add CSS flexbox properties for consistent button layout

πŸ€– Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

Files changed (3) hide show
  1. .DS_Store +0 -0
  2. app.py +12 -6
  3. mockup.html +784 -0
.DS_Store ADDED
Binary file (8.2 kB). View file
 
app.py CHANGED
@@ -230,7 +230,7 @@ st.markdown("""
230
  color: #475569;
231
  border: 1px solid #e2e8f0;
232
  padding: 0.375rem 0.75rem;
233
- font-size: 0.75rem;
234
  font-weight: normal;
235
  text-align: left;
236
  white-space: normal;
@@ -239,6 +239,9 @@ st.markdown("""
239
  transition: all 0.2s ease;
240
  cursor: pointer;
241
  margin-bottom: 0.25rem;
 
 
 
242
  }
243
 
244
  [data-testid="stSidebar"] .stButton > button:hover {
@@ -640,6 +643,14 @@ with st.sidebar:
640
  except AttributeError:
641
  st.experimental_rerun()
642
 
 
 
 
 
 
 
 
 
643
  # Helpful instructions section
644
  if not st.session_state.responses:
645
  st.markdown("""
@@ -674,11 +685,6 @@ st.markdown("""
674
  </div>
675
  """, unsafe_allow_html=True)
676
 
677
- # Initialize chat history and processing state
678
- if "responses" not in st.session_state:
679
- st.session_state.responses = []
680
- if "processing" not in st.session_state:
681
- st.session_state.processing = False
682
 
683
  def show_custom_response(response):
684
  """Custom response display function with improved styling"""
 
230
  color: #475569;
231
  border: 1px solid #e2e8f0;
232
  padding: 0.375rem 0.75rem;
233
+ font-size: 0.7rem;
234
  font-weight: normal;
235
  text-align: left;
236
  white-space: normal;
 
239
  transition: all 0.2s ease;
240
  cursor: pointer;
241
  margin-bottom: 0.25rem;
242
+ width: 100%;
243
+ display: flex;
244
+ justify-content: flex-start;
245
  }
246
 
247
  [data-testid="stSidebar"] .stButton > button:hover {
 
643
  except AttributeError:
644
  st.experimental_rerun()
645
 
646
+ # Initialize session state first
647
+ if "responses" not in st.session_state:
648
+ st.session_state.responses = []
649
+ if "processing" not in st.session_state:
650
+ st.session_state.processing = False
651
+ if "session_id" not in st.session_state:
652
+ st.session_state.session_id = str(uuid.uuid4())
653
+
654
  # Helpful instructions section
655
  if not st.session_state.responses:
656
  st.markdown("""
 
685
  </div>
686
  """, unsafe_allow_html=True)
687
 
 
 
 
 
 
688
 
689
  def show_custom_response(response):
690
  """Custom response display function with improved styling"""
mockup.html ADDED
@@ -0,0 +1,784 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>VayuChat - Clean Interface</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
16
+ background: #f8fafc;
17
+ color: #334155;
18
+ line-height: 1.5;
19
+ }
20
+
21
+ .header {
22
+ background: white;
23
+ border-bottom: 1px solid #e2e8f0;
24
+ padding: 1rem 2rem;
25
+ display: flex;
26
+ align-items: center;
27
+ justify-content: space-between;
28
+ }
29
+
30
+ .logo {
31
+ display: flex;
32
+ align-items: center;
33
+ gap: 0.75rem;
34
+ }
35
+
36
+ .logo-icon {
37
+ width: 32px;
38
+ height: 32px;
39
+ background: #3b82f6;
40
+ border-radius: 8px;
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: center;
44
+ color: white;
45
+ font-weight: bold;
46
+ }
47
+
48
+ .title {
49
+ font-size: 1.25rem;
50
+ font-weight: 600;
51
+ color: #1e293b;
52
+ }
53
+
54
+ .subtitle {
55
+ font-size: 0.875rem;
56
+ color: #64748b;
57
+ margin-left: 0.5rem;
58
+ }
59
+
60
+ .controls {
61
+ display: flex;
62
+ align-items: center;
63
+ gap: 1rem;
64
+ }
65
+
66
+ .model-select {
67
+ padding: 0.5rem 0.75rem;
68
+ border: 1px solid #d1d5db;
69
+ border-radius: 6px;
70
+ background: white;
71
+ font-size: 0.875rem;
72
+ }
73
+
74
+ .main-container {
75
+ display: grid;
76
+ grid-template-columns: 300px 1fr;
77
+ height: calc(100vh - 80px);
78
+ }
79
+
80
+ .sidebar {
81
+ background: white;
82
+ border-right: 1px solid #e2e8f0;
83
+ padding: 1.5rem;
84
+ overflow-y: auto;
85
+ }
86
+
87
+ .sidebar-section {
88
+ margin-bottom: 2rem;
89
+ }
90
+
91
+ .sidebar-title {
92
+ font-size: 0.875rem;
93
+ font-weight: 600;
94
+ color: #374151;
95
+ margin-bottom: 1rem;
96
+ text-transform: uppercase;
97
+ letter-spacing: 0.05em;
98
+ }
99
+
100
+ .quick-prompt {
101
+ padding: 0.75rem;
102
+ background: #f8fafc;
103
+ border: 1px solid #e2e8f0;
104
+ border-radius: 6px;
105
+ margin-bottom: 0.5rem;
106
+ cursor: pointer;
107
+ transition: all 0.2s;
108
+ font-size: 0.875rem;
109
+ }
110
+
111
+ .quick-prompt:hover {
112
+ background: #e0f2fe;
113
+ border-color: #0ea5e9;
114
+ }
115
+
116
+ .dataset-info {
117
+ background: #f1f5f9;
118
+ padding: 1rem;
119
+ border-radius: 8px;
120
+ font-size: 0.875rem;
121
+ }
122
+
123
+ .dataset-info h4 {
124
+ font-weight: 600;
125
+ margin-bottom: 0.5rem;
126
+ color: #1e293b;
127
+ }
128
+
129
+ .content-area {
130
+ display: flex;
131
+ flex-direction: column;
132
+ background: white;
133
+ }
134
+
135
+ .workflow-steps {
136
+ display: flex;
137
+ background: #f8fafc;
138
+ border-bottom: 1px solid #e2e8f0;
139
+ padding: 1rem 2rem;
140
+ }
141
+
142
+ .step {
143
+ display: flex;
144
+ align-items: center;
145
+ gap: 0.5rem;
146
+ padding: 0.5rem 1rem;
147
+ border-radius: 6px;
148
+ font-size: 0.875rem;
149
+ font-weight: 500;
150
+ }
151
+
152
+ .step.active {
153
+ background: #dbeafe;
154
+ color: #1d4ed8;
155
+ }
156
+
157
+ .step.completed {
158
+ background: #dcfce7;
159
+ color: #166534;
160
+ }
161
+
162
+ .step-number {
163
+ width: 20px;
164
+ height: 20px;
165
+ border-radius: 50%;
166
+ background: #e2e8f0;
167
+ display: flex;
168
+ align-items: center;
169
+ justify-content: center;
170
+ font-size: 0.75rem;
171
+ font-weight: 600;
172
+ }
173
+
174
+ .step.active .step-number {
175
+ background: #3b82f6;
176
+ color: white;
177
+ }
178
+
179
+ .step.completed .step-number {
180
+ background: #22c55e;
181
+ color: white;
182
+ }
183
+
184
+ .step-arrow {
185
+ margin: 0 1rem;
186
+ color: #9ca3af;
187
+ }
188
+
189
+ .chat-container {
190
+ flex: 1;
191
+ display: flex;
192
+ flex-direction: column;
193
+ max-height: calc(100vh - 180px);
194
+ }
195
+
196
+ .messages {
197
+ flex: 1;
198
+ padding: 2rem;
199
+ overflow-y: auto;
200
+ }
201
+
202
+ .message {
203
+ margin-bottom: 2rem;
204
+ }
205
+
206
+ .message.user {
207
+ display: flex;
208
+ justify-content: flex-end;
209
+ }
210
+
211
+ .message.assistant {
212
+ display: flex;
213
+ justify-content: flex-start;
214
+ }
215
+
216
+ .message-content {
217
+ max-width: 70%;
218
+ padding: 1rem 1.5rem;
219
+ border-radius: 12px;
220
+ }
221
+
222
+ .message.user .message-content {
223
+ background: #3b82f6;
224
+ color: white;
225
+ }
226
+
227
+ .message.assistant .message-content {
228
+ background: #f1f5f9;
229
+ color: #334155;
230
+ }
231
+
232
+ .code-container {
233
+ margin: 1rem 0;
234
+ border: 1px solid #e2e8f0;
235
+ border-radius: 8px;
236
+ background: #f8fafc;
237
+ }
238
+
239
+ .code-header {
240
+ display: flex;
241
+ justify-content: space-between;
242
+ align-items: center;
243
+ padding: 0.75rem 1rem;
244
+ background: #f1f5f9;
245
+ border-bottom: 1px solid #e2e8f0;
246
+ cursor: pointer;
247
+ transition: background-color 0.2s;
248
+ }
249
+
250
+ .code-header:hover {
251
+ background: #e2e8f0;
252
+ }
253
+
254
+ .code-title {
255
+ font-size: 0.875rem;
256
+ font-weight: 500;
257
+ color: #374151;
258
+ }
259
+
260
+ .toggle-text {
261
+ font-size: 0.75rem;
262
+ color: #6b7280;
263
+ }
264
+
265
+ .code-block {
266
+ background: #1e293b;
267
+ color: #e2e8f0;
268
+ padding: 1rem;
269
+ font-family: 'Monaco', 'Menlo', monospace;
270
+ font-size: 0.875rem;
271
+ overflow-x: auto;
272
+ display: none;
273
+ line-height: 1.5;
274
+ }
275
+
276
+ .code-block.expanded {
277
+ display: block;
278
+ }
279
+
280
+ .code-actions {
281
+ display: flex;
282
+ gap: 0.5rem;
283
+ padding: 0.75rem 1rem;
284
+ background: #f8fafc;
285
+ border-top: 1px solid #e2e8f0;
286
+ }
287
+
288
+ .code-action-btn {
289
+ padding: 0.25rem 0.75rem;
290
+ background: white;
291
+ border: 1px solid #d1d5db;
292
+ border-radius: 4px;
293
+ font-size: 0.75rem;
294
+ cursor: pointer;
295
+ transition: all 0.2s;
296
+ color: #6b7280;
297
+ }
298
+
299
+ .code-action-btn:hover {
300
+ background: #f3f4f6;
301
+ border-color: #9ca3af;
302
+ }
303
+
304
+ .chart-container {
305
+ background: white;
306
+ border: 1px solid #e2e8f0;
307
+ border-radius: 8px;
308
+ padding: 1.5rem;
309
+ margin: 1rem 0;
310
+ }
311
+
312
+ .answer-container {
313
+ background: #f8fafc;
314
+ border: 1px solid #e2e8f0;
315
+ border-radius: 8px;
316
+ padding: 1.5rem;
317
+ margin: 1rem 0;
318
+ }
319
+
320
+ .answer-text {
321
+ font-size: 1.125rem;
322
+ color: #1e293b;
323
+ line-height: 1.6;
324
+ margin-bottom: 1rem;
325
+ }
326
+
327
+ .answer-highlight {
328
+ background: #fef3c7;
329
+ padding: 0.125rem 0.375rem;
330
+ border-radius: 4px;
331
+ font-weight: 600;
332
+ color: #92400e;
333
+ }
334
+
335
+ .context-info {
336
+ background: #f1f5f9;
337
+ border-left: 4px solid #3b82f6;
338
+ padding: 0.75rem 1rem;
339
+ margin: 1rem 0;
340
+ font-size: 0.875rem;
341
+ color: #475569;
342
+ }
343
+
344
+ .feedback-container {
345
+ display: flex;
346
+ align-items: center;
347
+ gap: 0.75rem;
348
+ margin-top: 1rem;
349
+ padding-top: 1rem;
350
+ border-top: 1px solid #e2e8f0;
351
+ }
352
+
353
+ .feedback-label {
354
+ font-size: 0.875rem;
355
+ color: #6b7280;
356
+ font-weight: 500;
357
+ }
358
+
359
+ .feedback-buttons {
360
+ display: flex;
361
+ gap: 0.5rem;
362
+ }
363
+
364
+ .feedback-btn {
365
+ padding: 0.5rem 0.75rem;
366
+ background: transparent;
367
+ border: 1px solid #d1d5db;
368
+ border-radius: 6px;
369
+ cursor: pointer;
370
+ transition: all 0.2s;
371
+ font-size: 0.875rem;
372
+ color: #6b7280;
373
+ }
374
+
375
+ .feedback-btn:hover {
376
+ background: #f9fafb;
377
+ border-color: #9ca3af;
378
+ }
379
+
380
+ .feedback-btn.active.thumbs-up {
381
+ background: #dcfce7;
382
+ border-color: #22c55e;
383
+ color: #166534;
384
+ }
385
+
386
+ .feedback-btn.active.thumbs-down {
387
+ background: #fef2f2;
388
+ border-color: #ef4444;
389
+ color: #dc2626;
390
+ }
391
+
392
+ .quick-actions {
393
+ display: flex;
394
+ gap: 0.5rem;
395
+ margin-top: 1rem;
396
+ flex-wrap: wrap;
397
+ }
398
+
399
+ .quick-action-btn {
400
+ padding: 0.375rem 0.75rem;
401
+ background: #f8fafc;
402
+ border: 1px solid #e2e8f0;
403
+ border-radius: 6px;
404
+ font-size: 0.75rem;
405
+ cursor: pointer;
406
+ transition: all 0.2s;
407
+ color: #475569;
408
+ }
409
+
410
+ .quick-action-btn:hover {
411
+ background: #e0f2fe;
412
+ border-color: #0ea5e9;
413
+ color: #0c4a6e;
414
+ }
415
+
416
+ .input-container {
417
+ padding: 1.5rem 2rem;
418
+ border-top: 1px solid #e2e8f0;
419
+ background: white;
420
+ }
421
+
422
+ .input-wrapper {
423
+ display: flex;
424
+ gap: 1rem;
425
+ align-items: center;
426
+ }
427
+
428
+ .input-field {
429
+ flex: 1;
430
+ padding: 0.75rem 1rem;
431
+ border: 1px solid #d1d5db;
432
+ border-radius: 8px;
433
+ font-size: 1rem;
434
+ outline: none;
435
+ transition: border-color 0.2s;
436
+ }
437
+
438
+ .input-field:focus {
439
+ border-color: #3b82f6;
440
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
441
+ }
442
+
443
+ .send-button {
444
+ padding: 0.75rem 1.5rem;
445
+ background: #3b82f6;
446
+ color: white;
447
+ border: none;
448
+ border-radius: 8px;
449
+ font-weight: 500;
450
+ cursor: pointer;
451
+ transition: background-color 0.2s;
452
+ }
453
+
454
+ .send-button:hover {
455
+ background: #2563eb;
456
+ }
457
+
458
+ .send-button:disabled {
459
+ background: #9ca3af;
460
+ cursor: not-allowed;
461
+ }
462
+
463
+ .typing-indicator {
464
+ display: flex;
465
+ align-items: center;
466
+ gap: 0.5rem;
467
+ padding: 1rem 1.5rem;
468
+ color: #6b7280;
469
+ font-style: italic;
470
+ }
471
+
472
+ .typing-dots {
473
+ display: flex;
474
+ gap: 0.25rem;
475
+ }
476
+
477
+ .typing-dot {
478
+ width: 6px;
479
+ height: 6px;
480
+ border-radius: 50%;
481
+ background: #9ca3af;
482
+ animation: typing 1.4s infinite ease-in-out;
483
+ }
484
+
485
+ .typing-dot:nth-child(1) { animation-delay: -0.32s; }
486
+ .typing-dot:nth-child(2) { animation-delay: -0.16s; }
487
+
488
+ @keyframes typing {
489
+ 0%, 80%, 100% { transform: scale(0.8); opacity: 0.5; }
490
+ 40% { transform: scale(1); opacity: 1; }
491
+ }
492
+ </style>
493
+ </head>
494
+ <body>
495
+ <div class="header">
496
+ <div class="logo">
497
+ <div class="logo-icon">V</div>
498
+ <div>
499
+ <span class="title">VayuChat</span>
500
+ <span class="subtitle">Environmental Data Analysis</span>
501
+ </div>
502
+ </div>
503
+ <div class="controls">
504
+ <select class="model-select">
505
+ <option>gpt-4o-mini</option>
506
+ <option>claude-3.5-sonnet</option>
507
+ </select>
508
+ </div>
509
+ </div>
510
+
511
+ <div class="main-container">
512
+ <div class="sidebar">
513
+ <div class="sidebar-section">
514
+ <div class="sidebar-title">Dataset Info</div>
515
+ <div class="dataset-info">
516
+ <h4>PM2.5 Air Quality Data</h4>
517
+ <p><strong>Time Range:</strong> Jan 2023 - Dec 2023</p>
518
+ <p><strong>Locations:</strong> 15 cities in Gujarat</p>
519
+ <p><strong>Records:</strong> 13,140 measurements</p>
520
+ </div>
521
+ </div>
522
+
523
+ <div class="sidebar-section">
524
+ <div class="sidebar-title">Quick Queries</div>
525
+ <div class="quick-prompt">Which month had highest pollution?</div>
526
+ <div class="quick-prompt">Which city has worst air quality?</div>
527
+ <div class="quick-prompt">Show annual PM2.5 average</div>
528
+ <div class="quick-prompt">Compare winter vs summer pollution</div>
529
+ <div class="quick-prompt">List all cities by pollution level</div>
530
+ <div class="quick-prompt">Plot monthly average PM2.5 for 2023</div>
531
+ <div class="quick-prompt">Show seasonal pollution patterns</div>
532
+ </div>
533
+ </div>
534
+
535
+ <div class="content-area">
536
+ <div class="workflow-steps">
537
+ <div class="step completed">
538
+ <div class="step-number">1</div>
539
+ <span>Natural Language Query</span>
540
+ </div>
541
+ <div class="step-arrow">β†’</div>
542
+ <div class="step completed">
543
+ <div class="step-number">2</div>
544
+ <span>Code Generation</span>
545
+ </div>
546
+ <div class="step-arrow">β†’</div>
547
+ <div class="step active">
548
+ <div class="step-number">3</div>
549
+ <span>Visualization</span>
550
+ </div>
551
+ </div>
552
+
553
+ <div class="chat-container">
554
+ <div class="messages">
555
+ <!-- Chart Response Example -->
556
+ <div class="message user">
557
+ <div class="message-content">
558
+ Plot the monthly average PM2.5 for the year 2023
559
+ </div>
560
+ </div>
561
+
562
+ <div class="message assistant">
563
+ <div class="message-content">
564
+ <p>Here's the monthly average PM2.5 plot for 2023:</p>
565
+
566
+ <div class="code-container">
567
+ <div class="code-header" onclick="toggleCode(this)">
568
+ <div class="code-title">Generated Python Code</div>
569
+ <div class="toggle-text">Click to expand</div>
570
+ </div>
571
+ <div class="code-block">
572
+ <pre>import pandas as pd
573
+ import matplotlib.pyplot as plt
574
+
575
+ # Load and filter data for 2023
576
+ df_2023 = df[df['year'] == 2023]
577
+
578
+ # Calculate monthly averages
579
+ monthly_avg = df_2023.groupby('month')['pm25'].mean()
580
+
581
+ # Create the plot
582
+ plt.figure(figsize=(12, 6))
583
+ plt.plot(monthly_avg.index, monthly_avg.values,
584
+ marker='o', linewidth=3, markersize=8,
585
+ color='#3b82f6')
586
+
587
+ plt.title('Monthly Average PM2.5 - Gujarat 2023', fontsize=16, fontweight='bold')
588
+ plt.xlabel('Month', fontsize=12)
589
+ plt.ylabel('PM2.5 (ug/m3)', fontsize=12)
590
+ plt.grid(True, alpha=0.3)
591
+ plt.tight_layout()
592
+ plt.show()</pre>
593
+ </div>
594
+ <div class="code-actions">
595
+ <button class="code-action-btn" onclick="copyCode(event)">Copy Code</button>
596
+ <button class="code-action-btn" onclick="downloadCode(event)">Download</button>
597
+ </div>
598
+ </div>
599
+
600
+ <div class="chart-container">
601
+ <div style="width: 100%; height: 300px; background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); border: 2px solid #e2e8f0; border-radius: 8px; display: flex; flex-direction: column; align-items: center; justify-content: center; position: relative;">
602
+ <!-- Chart Title -->
603
+ <div style="position: absolute; top: 20px; font-weight: bold; font-size: 16px; color: #1e293b;">Monthly Average PM2.5 - Gujarat 2023</div>
604
+
605
+ <!-- Chart Area -->
606
+ <svg width="500" height="200" style="margin-top: 20px;">
607
+ <!-- Grid -->
608
+ <defs>
609
+ <pattern id="grid" width="50" height="40" patternUnits="userSpaceOnUse">
610
+ <path d="M 50 0 L 0 0 0 40" fill="none" stroke="#f1f5f9" stroke-width="1"/>
611
+ </pattern>
612
+ </defs>
613
+ <rect width="100%" height="100%" fill="url(#grid)" />
614
+
615
+ <!-- WHO Guideline -->
616
+ <line x1="60" y1="160" x2="460" y2="160" stroke="#ef4444" stroke-width="2" stroke-dasharray="5,5"/>
617
+ <text x="455" y="155" font-size="10" fill="#ef4444" text-anchor="end">WHO (15)</text>
618
+
619
+ <!-- Data Line -->
620
+ <polyline points="60,30 100,50 140,80 180,85 220,95 260,110 300,170 340,155 380,150 420,75 460,35 500,45"
621
+ fill="none" stroke="#3b82f6" stroke-width="3"/>
622
+
623
+ <!-- Data Points -->
624
+ <circle cx="60" cy="30" r="4" fill="#3b82f6" stroke="white" stroke-width="2"/>
625
+ <circle cx="100" cy="50" r="4" fill="#3b82f6" stroke="white" stroke-width="2"/>
626
+ <circle cx="140" cy="80" r="4" fill="#3b82f6" stroke="white" stroke-width="2"/>
627
+ <circle cx="180" cy="85" r="4" fill="#3b82f6" stroke="white" stroke-width="2"/>
628
+ <circle cx="220" cy="95" r="4" fill="#3b82f6" stroke="white" stroke-width="2"/>
629
+ <circle cx="260" cy="110" r="4" fill="#3b82f6" stroke="white" stroke-width="2"/>
630
+ <circle cx="300" cy="170" r="4" fill="#3b82f6" stroke="white" stroke-width="2"/>
631
+ <circle cx="340" cy="155" r="4" fill="#3b82f6" stroke="white" stroke-width="2"/>
632
+ <circle cx="380" cy="150" r="4" fill="#3b82f6" stroke="white" stroke-width="2"/>
633
+ <circle cx="420" cy="75" r="4" fill="#3b82f6" stroke="white" stroke-width="2"/>
634
+ <circle cx="460" cy="35" r="4" fill="#3b82f6" stroke="white" stroke-width="2"/>
635
+ <circle cx="500" cy="45" r="4" fill="#3b82f6" stroke="white" stroke-width="2"/>
636
+
637
+ <!-- Month Labels -->
638
+ <text x="60" y="195" font-size="11" fill="#64748b" text-anchor="middle">Jan</text>
639
+ <text x="100" y="195" font-size="11" fill="#64748b" text-anchor="middle">Feb</text>
640
+ <text x="140" y="195" font-size="11" fill="#64748b" text-anchor="middle">Mar</text>
641
+ <text x="180" y="195" font-size="11" fill="#64748b" text-anchor="middle">Apr</text>
642
+ <text x="220" y="195" font-size="11" fill="#64748b" text-anchor="middle">May</text>
643
+ <text x="260" y="195" font-size="11" fill="#64748b" text-anchor="middle">Jun</text>
644
+ <text x="300" y="195" font-size="11" fill="#64748b" text-anchor="middle">Jul</text>
645
+ <text x="340" y="195" font-size="11" fill="#64748b" text-anchor="middle">Aug</text>
646
+ <text x="380" y="195" font-size="11" fill="#64748b" text-anchor="middle">Sep</text>
647
+ <text x="420" y="195" font-size="11" fill="#64748b" text-anchor="middle">Oct</text>
648
+ <text x="460" y="195" font-size="11" fill="#64748b" text-anchor="middle">Nov</text>
649
+ <text x="500" y="195" font-size="11" fill="#64748b" text-anchor="middle">Dec</text>
650
+
651
+ <!-- Y-axis labels -->
652
+ <text x="50" y="175" font-size="11" fill="#64748b" text-anchor="end">0</text>
653
+ <text x="50" y="135" font-size="11" fill="#64748b" text-anchor="end">40</text>
654
+ <text x="50" y="95" font-size="11" fill="#64748b" text-anchor="end">80</text>
655
+ <text x="50" y="35" font-size="11" fill="#64748b" text-anchor="end">100</text>
656
+ </svg>
657
+
658
+ <!-- Axis Labels -->
659
+ <div style="position: absolute; bottom: 10px; font-size: 12px; color: #64748b;">Month</div>
660
+ <div style="position: absolute; left: 15px; top: 50%; transform: rotate(-90deg); font-size: 12px; color: #64748b;">PM2.5 (ug/m3)</div>
661
+ </div>
662
+ </div>
663
+
664
+ <div class="context-info">
665
+ The chart shows highest PM2.5 levels in winter months with January at 95.2 and November at 91.7. Lowest levels occur during monsoon season with July at 25.1. All months exceed WHO annual guideline of 15.
666
+ </div>
667
+
668
+ <div class="quick-actions">
669
+ <button class="quick-action-btn">Compare with 2022</button>
670
+ <button class="quick-action-btn">Show by city</button>
671
+ <button class="quick-action-btn">Seasonal breakdown</button>
672
+ </div>
673
+
674
+ <div class="feedback-container">
675
+ <span class="feedback-label">Was this helpful?</span>
676
+ <div class="feedback-buttons">
677
+ <button class="feedback-btn thumbs-up" onclick="giveFeedback(this, 'up')">πŸ‘ Helpful</button>
678
+ <button class="feedback-btn thumbs-down" onclick="giveFeedback(this, 'down')">πŸ‘Ž Not helpful</button>
679
+ </div>
680
+ </div>
681
+ </div>
682
+ </div>
683
+
684
+ <!-- Direct Answer Example -->
685
+ <div class="message user">
686
+ <div class="message-content">
687
+ Which month had the highest PM2.5 pollution in 2023?
688
+ </div>
689
+ </div>
690
+
691
+ <div class="message assistant">
692
+ <div class="message-content">
693
+ <div class="answer-container">
694
+ <div class="answer-text">
695
+ <span class="answer-highlight">January 2023</span> had the highest PM2.5 pollution with an average concentration of <span class="answer-highlight">95.2</span>.
696
+ </div>
697
+ <div class="context-info">
698
+ This value is 6.3 times higher than the WHO annual guideline of 15 and represents typical winter pollution patterns in Gujarat.
699
+ </div>
700
+ </div>
701
+
702
+ <div class="quick-actions">
703
+ <button class="quick-action-btn">Compare with other months</button>
704
+ <button class="quick-action-btn">Show January city breakdown</button>
705
+ <button class="quick-action-btn">Winter pollution causes</button>
706
+ </div>
707
+
708
+ <div class="feedback-container">
709
+ <span class="feedback-label">Was this helpful?</span>
710
+ <div class="feedback-buttons">
711
+ <button class="feedback-btn thumbs-up" onclick="giveFeedback(this, 'up')">πŸ‘ Helpful</button>
712
+ <button class="feedback-btn thumbs-down" onclick="giveFeedback(this, 'down')">πŸ‘Ž Not helpful</button>
713
+ </div>
714
+ </div>
715
+ </div>
716
+ </div>
717
+
718
+ <!-- List Answer Example -->
719
+ <div class="message user">
720
+ <div class="message-content">
721
+ Which cities in Gujarat have the worst air quality?
722
+ </div>
723
+ </div>
724
+
725
+ <div class="message assistant">
726
+ <div class="message-content">
727
+ <div class="answer-container">
728
+ <div class="answer-text">
729
+ <strong>1. Ahmedabad:</strong> <span class="answer-highlight">68.3</span><br>
730
+ <strong>2. Surat:</strong> <span class="answer-highlight">61.7</span><br>
731
+ <strong>3. Vadodara:</strong> <span class="answer-highlight">58.9</span><br>
732
+ <strong>4. Rajkot:</strong> <span class="answer-highlight">52.4</span><br>
733
+ <strong>5. Gandhinagar:</strong> <span class="answer-highlight">48.1</span>
734
+ </div>
735
+ <div class="context-info">
736
+ All cities exceed WHO guidelines by 3-4x. Industrial activity and vehicular emissions are primary contributors.
737
+ </div>
738
+ </div>
739
+
740
+ <div class="quick-actions">
741
+ <button class="quick-action-btn">Show seasonal variation by city</button>
742
+ <button class="quick-action-btn">Compare with national averages</button>
743
+ <button class="quick-action-btn">Plot city comparison chart</button>
744
+ </div>
745
+
746
+ <div class="feedback-container">
747
+ <span class="feedback-label">Was this helpful?</span>
748
+ <div class="feedback-buttons">
749
+ <button class="feedback-btn thumbs-up" onclick="giveFeedback(this, 'up')">πŸ‘ Helpful</button>
750
+ <button class="feedback-btn thumbs-down" onclick="giveFeedback(this, 'down')">πŸ‘Ž Not helpful</button>
751
+ </div>
752
+ </div>
753
+ </div>
754
+ </div>
755
+ </div>
756
+
757
+ <div class="input-container">
758
+ <div class="input-wrapper">
759
+ <input type="text" class="input-field" placeholder="Ask me anything about air quality data..." />
760
+ <button class="send-button">Send</button>
761
+ </div>
762
+ </div>
763
+ </div>
764
+ </div>
765
+ </div>
766
+
767
+ <script>
768
+ function toggleCode(header) {
769
+ const codeBlock = header.nextElementSibling;
770
+ const toggleText = header.querySelector('.toggle-text');
771
+
772
+ if (codeBlock.classList.contains('expanded')) {
773
+ codeBlock.classList.remove('expanded');
774
+ toggleText.textContent = 'Click to expand';
775
+ } else {
776
+ codeBlock.classList.add('expanded');
777
+ toggleText.textContent = 'Click to collapse';
778
+ }
779
+ }
780
+
781
+ function copyCode(event) {
782
+ event.stopPropagation();
783
+ const codeBlock = event.target.closest('.code-container').querySelector('.code-block pre');
784
+ navigator.clipboard.writeText(