pvanand commited on
Commit
c36e25a
·
verified ·
1 Parent(s): 4ea1e35

Upload 18 files

Browse files
static/css/apps-hub/ai-sidebar.css ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* ai-sidebar.css */
2
+ .ai-sidebar__container {
3
+ display: flex;
4
+ min-height: 100vh;
5
+ }
6
+
7
+ .ai-sidebar__sidebar {
8
+ width: 250px;
9
+ background-color: #ffffff;
10
+ padding: 20px;
11
+ box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
12
+ display: flex;
13
+ flex-direction: column;
14
+ }
15
+
16
+ .ai-sidebar__title {
17
+ font-size: 1.5rem;
18
+ font-weight: bold;
19
+ color: #3f72af;
20
+ margin-bottom: 20px;
21
+ }
22
+
23
+ .ai-sidebar__app-list {
24
+ list-style-type: none;
25
+ padding: 0;
26
+ margin: 0;
27
+ flex-grow: 1;
28
+ }
29
+
30
+ .ai-sidebar__app-item {
31
+ display: flex;
32
+ align-items: center;
33
+ padding: 10px;
34
+ margin-bottom: 10px;
35
+ border-radius: 8px;
36
+ cursor: pointer;
37
+ transition: background-color 0.2s ease;
38
+ }
39
+
40
+ .ai-sidebar__app-item:hover {
41
+ background-color: #3f72af;
42
+ color: white;
43
+ }
44
+
45
+ .ai-sidebar__app-icon {
46
+ width: 40px;
47
+ height: 40px;
48
+ border-radius: 50%;
49
+ display: flex;
50
+ justify-content: center;
51
+ align-items: center;
52
+ margin-right: 15px;
53
+ color: white;
54
+ }
55
+
56
+ .ai-sidebar__app-name {
57
+ font-weight: 500;
58
+ }
59
+
60
+ .ai-sidebar__content {
61
+ flex-grow: 1;
62
+ padding: 20px;
63
+ }
64
+
65
+ .ai-sidebar__footer {
66
+ margin-top: auto;
67
+ }
68
+
69
+ .ai-sidebar__footer .ai-sidebar__app-item {
70
+ text-decoration: none;
71
+ color: inherit;
72
+ }
73
+
74
+ .ai-sidebar__footer .ai-sidebar__app-icon {
75
+ width: 40px;
76
+ height: 40px;
77
+ }
static/css/apps-hub/app-selector.css ADDED
@@ -0,0 +1,431 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --bg-color: #f0f4f8;
3
+ --card-bg: #ffffff;
4
+ --text-color: #2d3748;
5
+ --text-secondary: #718096;
6
+ --accent-color: #3f72af;
7
+ --accent-hover: #3182ce;
8
+ }
9
+
10
+ .dark-mode {
11
+ --bg-color: #1a202c;
12
+ --card-bg: #2d3748;
13
+ --text-color: #e2e8f0;
14
+ --text-secondary: #a0aec0;
15
+ --accent-color: #4299e1;
16
+ --accent-hover: #63b3ed;
17
+ }
18
+
19
+ body {
20
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
21
+ margin: 0;
22
+ padding: 0;
23
+ background-color: var(--bg-color);
24
+ color: var(--text-color);
25
+ line-height: 1.6;
26
+ transition: background-color 0.3s ease, color 0.3s ease;
27
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400' viewBox='0 0 400 400'%3E%3Cg fill='%239C92AC' fill-opacity='0.05'%3E%3Ccircle cx='50' cy='50' r='2'/%3E%3Ccircle cx='150' cy='100' r='3'/%3E%3Ccircle cx='250' cy='50' r='2'/%3E%3Ccircle cx='350' cy='150' r='3'/%3E%3Ccircle cx='300' cy='250' r='2'/%3E%3Ccircle cx='200' cy='300' r='3'/%3E%3Ccircle cx='100' cy='250' r='2'/%3E%3Cpath d='M50 50L150 100L250 50L350 150L300 250L200 300L100 250Z' stroke='%239C92AC' stroke-opacity='0.05' fill='none'/%3E%3C/g%3E%3C/svg%3E");
28
+ }
29
+
30
+ .dark-mode body {
31
+ background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23FFFFFF' fill-opacity='0.05'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
32
+ }
33
+
34
+ .background-elements {
35
+ position: fixed;
36
+ top: 0;
37
+ left: 0;
38
+ width: 100%;
39
+ height: 100%;
40
+ pointer-events: none;
41
+ z-index: -1;
42
+ opacity: 0.3;
43
+ }
44
+
45
+ .background-elements::before,
46
+ .background-elements::after {
47
+ content: '';
48
+ position: absolute;
49
+ width: 400px;
50
+ height: 400px;
51
+ border-radius: 50%;
52
+ background-color: var(--accent-color);
53
+ filter: blur(150px);
54
+ }
55
+
56
+ .background-elements::before {
57
+ top: -200px;
58
+ left: -200px;
59
+ }
60
+
61
+ .background-elements::after {
62
+ bottom: -200px;
63
+ right: -200px;
64
+ }
65
+
66
+ .title-bar {
67
+ background-color: var(--accent-color);
68
+ color: white;
69
+ padding: 6rem 0 8rem;
70
+ text-align: center;
71
+ position: relative;
72
+ overflow: hidden;
73
+ clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%);
74
+ }
75
+
76
+ .title {
77
+ font-size: 4.5rem;
78
+ font-weight: 400;
79
+ margin-bottom: 0.5rem;
80
+ text-transform: uppercase;
81
+ letter-spacing: 4px;
82
+ position: relative;
83
+ z-index: 1;
84
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
85
+ background: linear-gradient(45deg, #ffffff, #e0e0e0);
86
+ -webkit-background-clip: text;
87
+ -webkit-text-fill-color: transparent;
88
+ display: inline-block;
89
+ }
90
+
91
+ .subtitle {
92
+ font-size: 1.6rem;
93
+ font-weight: 300;
94
+ position: relative;
95
+ z-index: 1;
96
+ max-width: 600px;
97
+ margin: 1.5rem auto 0;
98
+ opacity: 0.9;
99
+ line-height: 1.4;
100
+ }
101
+
102
+ .particles {
103
+ position: absolute;
104
+ top: 0;
105
+ left: 0;
106
+ width: 100%;
107
+ height: 100%;
108
+ overflow: hidden;
109
+ }
110
+
111
+ .particle {
112
+ position: absolute;
113
+ background-color: rgba(255, 255, 255, 0.7);
114
+ border-radius: 50%;
115
+ animation: orbit 20s linear infinite;
116
+ }
117
+
118
+ @keyframes orbit {
119
+ 0% { transform: translate(-50%, -50%) rotate(0deg) translateX(-100px) translateY(200px) rotate(0deg); }
120
+ 25% { transform: translate(-50%, -50%) translateX(100px) rotate(5deg); }
121
+ 50% { transform: translate(-50%, -50%) translateX(200px); }
122
+ 75% { transform: translate(-50%, -50%) translateX(600px); }
123
+ 100% { transform: translate(-50%, -50%) translateX(1000px); }
124
+ }
125
+
126
+ .particle:nth-child(1) { animation-duration: 15s; }
127
+ .particle:nth-child(2) { animation-duration: 25s; }
128
+ .particle:nth-child(3) { animation-duration: 20s; }
129
+ .particle:nth-child(4) { animation-duration: 30s; }
130
+ .particle:nth-child(5) { animation-duration: 18s; }
131
+
132
+ .discover-ai-apps {
133
+ max-width: 1200px;
134
+ margin: 0 auto;
135
+ padding: 4rem 2rem;
136
+ }
137
+
138
+ .app-grid {
139
+ display: grid;
140
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
141
+ gap: 2.5rem;
142
+ margin-top: 2rem;
143
+ }
144
+
145
+ .app-card {
146
+ background-color: var(--card-bg);
147
+ border-radius: 20px;
148
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
149
+ padding: 2rem;
150
+ cursor: pointer;
151
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
152
+ display: flex;
153
+ flex-direction: column;
154
+ align-items: center;
155
+ }
156
+
157
+ .app-card:hover {
158
+ transform: translateY(-10px);
159
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
160
+ }
161
+
162
+ .app-icon {
163
+ width: 100px;
164
+ height: 100px;
165
+ border-radius: 50%;
166
+ display: flex;
167
+ justify-content: center;
168
+ align-items: center;
169
+ margin-bottom: 1.5rem;
170
+ transition: transform 0.3s ease;
171
+ }
172
+
173
+ .app-card:hover .app-icon {
174
+ transform: scale(1.1);
175
+ }
176
+
177
+ .app-icon i {
178
+ font-size: 2.5rem;
179
+ color: #fff;
180
+ }
181
+
182
+ .app-name {
183
+ font-size: 1.8rem;
184
+ margin-bottom: 1rem;
185
+ color: var(--accent-color);
186
+ text-align: center;
187
+ }
188
+
189
+ .app-description {
190
+ font-size: 1rem;
191
+ color: var(--text-secondary);
192
+ text-align: center;
193
+ flex-grow: 1;
194
+ }
195
+
196
+ .app-details {
197
+ position: fixed;
198
+ top: 0;
199
+ left: 0;
200
+ width: 100%;
201
+ height: 100%;
202
+ background-color: rgba(0, 0, 0, 0.5);
203
+ backdrop-filter: blur(10px);
204
+ display: flex;
205
+ justify-content: center;
206
+ align-items: center;
207
+ z-index: 1000;
208
+ }
209
+
210
+ .app-details-content {
211
+ background-color: var(--card-bg);
212
+ border-radius: 20px;
213
+ padding: 3rem;
214
+ max-width: 600px;
215
+ width: 90%;
216
+ position: relative;
217
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
218
+ display: flex;
219
+ flex-direction: column;
220
+ align-items: center;
221
+ }
222
+
223
+ .app-details-icon {
224
+ width: 120px;
225
+ height: 120px;
226
+ border-radius: 50%;
227
+ display: flex;
228
+ justify-content: center;
229
+ align-items: center;
230
+ margin-bottom: 2rem;
231
+ }
232
+
233
+ .app-details-icon i {
234
+ font-size: 3rem;
235
+ color: #fff;
236
+ }
237
+
238
+ .app-details h2 {
239
+ font-size: 2.5rem;
240
+ margin-bottom: 1.5rem;
241
+ color: var(--accent-color);
242
+ text-align: center;
243
+ }
244
+
245
+ .app-details p {
246
+ font-size: 1.1rem;
247
+ line-height: 1.6;
248
+ color: var(--text-color);
249
+ margin-bottom: 2rem;
250
+ text-align: center;
251
+ }
252
+
253
+ .close-button {
254
+ position: absolute;
255
+ top: 20px;
256
+ right: 20px;
257
+ font-size: 2rem;
258
+ background: none;
259
+ border: none;
260
+ color: var(--text-secondary);
261
+ cursor: pointer;
262
+ transition: transform 0.3s ease, color 0.3s ease;
263
+ }
264
+
265
+ .close-button:hover {
266
+ transform: scale(1.1);
267
+ color: var(--accent-color);
268
+ }
269
+
270
+ .try-button {
271
+ display: inline-block;
272
+ background-color: var(--accent-color);
273
+ color: #ffffff;
274
+ padding: 1rem 2rem;
275
+ border-radius: 30px;
276
+ text-decoration: none;
277
+ transition: background-color 0.3s ease, transform 0.3s ease;
278
+ text-transform: uppercase;
279
+ letter-spacing: 1px;
280
+ font-weight: bold;
281
+ }
282
+
283
+ .try-button:hover {
284
+ background-color: var(--accent-hover);
285
+ transform: translateY(-3px);
286
+ }
287
+
288
+ .fade-enter-active, .fade-leave-active {
289
+ transition: opacity 0.3s, transform 0.3s;
290
+ }
291
+
292
+ .fade-enter, .fade-leave-to {
293
+ opacity: 0;
294
+ transform: scale(0.95);
295
+ }
296
+
297
+ .fade-enter-to, .fade-leave {
298
+ opacity: 1;
299
+ transform: scale(1);
300
+ }
301
+
302
+ ::-webkit-scrollbar {
303
+ width: 12px;
304
+ }
305
+
306
+ ::-webkit-scrollbar-track {
307
+ background: var(--bg-color);
308
+ }
309
+
310
+ ::-webkit-scrollbar-thumb {
311
+ background-color: var(--accent-color);
312
+ border-radius: 6px;
313
+ border: 3px solid var(--bg-color);
314
+ }
315
+
316
+ ::-webkit-scrollbar-thumb:hover {
317
+ background-color: var(--accent-hover);
318
+ }
319
+
320
+ .dark-mode-toggle {
321
+ position: fixed;
322
+ top: 20px;
323
+ right: 20px;
324
+ background-color: var(--accent-color);
325
+ color: white;
326
+ border: none;
327
+ border-radius: 50%;
328
+ width: 50px;
329
+ height: 50px;
330
+ font-size: 1.5rem;
331
+ cursor: pointer;
332
+ transition: background-color 0.3s ease;
333
+ z-index: 1000;
334
+ }
335
+
336
+ .dark-mode-toggle:hover {
337
+ background-color: var(--accent-hover);
338
+ }
339
+
340
+ @media (max-width: 768px) {
341
+ .title-bar {
342
+ padding: 4rem 0 6rem;
343
+ }
344
+
345
+ .title {
346
+ font-size: 3rem;
347
+ letter-spacing: 2px;
348
+ }
349
+
350
+ .subtitle {
351
+ font-size: 1.2rem;
352
+ }
353
+
354
+ .app-grid {
355
+ grid-template-columns: 1fr;
356
+ }
357
+
358
+ .app-card {
359
+ padding: 1.5rem;
360
+ }
361
+
362
+ .app-name {
363
+ font-size: 1.5rem;
364
+ }
365
+
366
+ .app-description {
367
+ font-size: 0.9rem;
368
+ }
369
+
370
+ .app-details-content {
371
+ padding: 2rem;
372
+ max-width: 90%;
373
+ }
374
+
375
+ .app-details h2 {
376
+ font-size: 2rem;
377
+ }
378
+ }
379
+
380
+ /* Top buttons for login and dark mode*/
381
+ .top-buttons {
382
+ position: fixed;
383
+ top: 20px;
384
+ right: 20px;
385
+ display: flex;
386
+ gap: 10px;
387
+ z-index: 1000;
388
+ }
389
+
390
+ .login-button,
391
+ .dark-mode-toggle {
392
+ background-color: var(--accent-color);
393
+ color: white;
394
+ border: none;
395
+ border-radius: 25px;
396
+ padding: 10px 20px;
397
+ font-size: 1rem;
398
+ cursor: pointer;
399
+ transition: background-color 0.3s ease, transform 0.3s ease;
400
+ display: flex;
401
+ align-items: center;
402
+ justify-content: center;
403
+ gap: 8px;
404
+ height: 40px;
405
+ }
406
+
407
+ .login-button:hover,
408
+ .dark-mode-toggle:hover {
409
+ background-color: var(--accent-hover);
410
+ transform: translateY(-3px);
411
+ }
412
+
413
+ .login-button i,
414
+ .dark-mode-toggle i {
415
+ font-size: 1.2rem;
416
+ }
417
+
418
+ /* Remove the fixed positioning from dark-mode-toggle */
419
+ .dark-mode-toggle {
420
+ position: static;
421
+ width: auto;
422
+ }
423
+
424
+ /* For mobile responsiveness */
425
+ @media (max-width: 768px) {
426
+ .top-buttons {
427
+ flex-direction: column;
428
+ top: 10px;
429
+ right: 10px;
430
+ }
431
+ }
static/css/apps-hub/profile-page.css ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* profile-page.css */
2
+ .profile-page__container {
3
+ max-width: 800px;
4
+ margin: 0 auto;
5
+ padding: 20px;
6
+ background-color: var(--ai-sidebar-bg-color, #ffffff);
7
+ border-radius: 8px;
8
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
9
+ }
10
+
11
+ .profile-page__title {
12
+ color: var(--ai-sidebar-text-color, #333333);
13
+ font-size: 24px;
14
+ margin-bottom: 20px;
15
+ }
16
+
17
+ .profile-page__info {
18
+ display: grid;
19
+ grid-template-columns: 1fr 1fr;
20
+ gap: 15px;
21
+ }
22
+
23
+ .profile-page__info-item {
24
+ background-color: var(--ai-sidebar-hover-bg-color, #f0f0f0);
25
+ padding: 10px;
26
+ border-radius: 4px;
27
+ }
28
+
29
+ .profile-page__info-label {
30
+ font-weight: bold;
31
+ color: var(--ai-sidebar-active-text-color, #007bff);
32
+ }
33
+
34
+ .profile-page__info-value {
35
+ color: var(--ai-sidebar-text-color, #333333);
36
+ }
37
+
38
+ .profile-page__logout-btn {
39
+ margin-top: 20px;
40
+ padding: 10px 20px;
41
+ background-color: var(--ai-sidebar-active-bg-color, #007bff);
42
+ color: #ffffff;
43
+ border: none;
44
+ border-radius: 4px;
45
+ cursor: pointer;
46
+ transition: background-color 0.3s ease;
47
+ }
48
+
49
+ .profile-page__logout-btn:hover {
50
+ background-color: var(--ai-sidebar-hover-bg-color, #0056b3);
51
+ }
static/css/apps-hub/research-pro-style.css ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --accent-color: #003366; /* Navy blue */
3
+ }
4
+
5
+ body {
6
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
7
+ line-height: 1.6;
8
+ margin: 0;
9
+ padding: 0;
10
+ background-color: #f0f2f5;
11
+ color: #333;
12
+ }
13
+
14
+ #input-container {
15
+ margin-bottom: 20px;
16
+ background-color: #ffffff;
17
+ padding: 20px;
18
+ border-radius: 8px;
19
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
20
+ }
21
+
22
+ .button-container {
23
+ justify-content: space-between;
24
+ height: 30px;
25
+ }
26
+
27
+ #output-container {
28
+ display: flex;
29
+ flex-direction: column;
30
+ gap: 20px;
31
+ }
32
+
33
+ #report-container, #sources-container {
34
+ background-color: #ffffff;
35
+ padding: 30px;
36
+ border-radius: 8px;
37
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
38
+ overflow-wrap: break-word;
39
+ word-wrap: break-word;
40
+ word-break: break-word;
41
+ }
42
+
43
+ textarea {
44
+ width: 100%;
45
+ padding: 12px;
46
+ margin-bottom: 15px;
47
+ border: 1px solid #ccc;
48
+ border-radius: 4px;
49
+ font-size: 16px;
50
+ resize: vertical;
51
+ box-sizing: border-box;
52
+ }
53
+
54
+ button {
55
+ padding: 12px 24px;
56
+ background-color: var(--accent-color);
57
+ color: white;
58
+ border: none;
59
+ border-radius: 4px;
60
+ cursor: pointer;
61
+ font-size: 16px;
62
+ transition: background-color 0.3s;
63
+ }
64
+
65
+ button:hover {
66
+ background-color: #002244;
67
+ }
68
+
69
+ .source-item {
70
+ margin-bottom: 20px;
71
+ padding: 15px;
72
+ background-color: #f9f9f9;
73
+ border: 1px solid #e0e0e0;
74
+ border-radius: 8px;
75
+ position: relative;
76
+ cursor: pointer;
77
+ transition: box-shadow 0.3s;
78
+ }
79
+
80
+ .source-item:hover {
81
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
82
+ }
83
+
84
+ .source-url {
85
+ color: var(--accent-color);
86
+ text-decoration: none;
87
+ word-break: break-all;
88
+ font-weight: bold;
89
+ display: block;
90
+ margin-bottom: 10px;
91
+ cursor: pointer;
92
+ }
93
+
94
+ .source-content {
95
+ margin-top: 10px;
96
+ position: relative;
97
+ overflow: hidden;
98
+ }
99
+
100
+ .source-snippet {
101
+ max-height: 150px;
102
+ overflow: hidden;
103
+ }
104
+
105
+ .source-full {
106
+ display: none;
107
+ }
108
+
109
+ .expand-indicator {
110
+ position: absolute;
111
+ bottom: 0;
112
+ left: 0;
113
+ right: 0;
114
+ height: 30px;
115
+ background: linear-gradient(to bottom, rgba(249,249,249,0), rgba(249,249,249,1));
116
+ display: flex;
117
+ align-items: center;
118
+ justify-content: center;
119
+ }
120
+
121
+ .expand-indicator::after {
122
+ content: '▼';
123
+ font-size: 14px;
124
+ color: #666;
125
+ }
126
+
127
+ .expanded .expand-indicator::after {
128
+ content: '▲';
129
+ }
130
+
131
+ .source-item:hover .expand-indicator {
132
+ background: linear-gradient(to bottom, rgba(249,249,249,0), rgba(249,249,249,0.9));
133
+ }
134
+
135
+ h2 {
136
+ color: var(--accent-color);
137
+ border-bottom: 2px solid var(--accent-color);
138
+ padding-bottom: 10px;
139
+ margin-top: 0;
140
+ }
141
+
142
+ @media (min-width: 768px) {
143
+ #output-container {
144
+ flex-direction: row;
145
+ }
146
+ #report-container {
147
+ flex: 3;
148
+ }
149
+ #sources-container {
150
+ flex: 2;
151
+ }
152
+ }
153
+
154
+ @media (max-width: 767px) {
155
+ body {
156
+ padding: 10px;
157
+ }
158
+ #input-container, #report-container, #sources-container {
159
+ padding: 15px;
160
+ }
161
+ }
162
+
163
+ .centered-container {
164
+ display: flex;
165
+ flex-direction: column;
166
+ align-items: center;
167
+ justify-content: center;
168
+ min-height: calc(100vh - 40px);
169
+ }
170
+
171
+ .search-container {
172
+ display: flex;
173
+ margin-top: 20px;
174
+ }
175
+
176
+ .search-container input {
177
+ width: 500px;
178
+ padding: 10px 20px;
179
+ font-size: 16px;
180
+ border: 1px solid #bfcceb;
181
+ border-radius: 15px 0 0 15px;
182
+ outline: none;
183
+ color: #0e1f4b;
184
+ }
185
+
186
+ .search-container button {
187
+ width: 50px;
188
+ padding: 10px;
189
+ background: #ffffff;
190
+ border: none;
191
+ cursor: pointer;
192
+ transition: opacity 0.3s ease;
193
+ display: flex;
194
+ align-items: center;
195
+ justify-content: center;
196
+ border-radius: 0 15px 15px 0;
197
+ }
198
+
199
+ .search-container button:hover {
200
+ opacity: 0.5;
201
+ }
202
+
203
+ .search-container button svg {
204
+ width: 24px;
205
+ height: 24px;
206
+ }
207
+
208
+ ::placeholder {
209
+ font-style: italic;
210
+ font: 1em sans-serif;
211
+ color: #7088c1;
212
+ opacity: 0.5;
213
+ }
214
+
215
+ .title {
216
+ font-weight: 700;
217
+ font-size: 64px !important;
218
+ background: linear-gradient(45deg, #3b82f6, #10b981);
219
+ -webkit-background-clip: text;
220
+ background-clip: text;
221
+ color: transparent;
222
+ text-align: center;
223
+ margin: 0;
224
+ padding: 0 0 0.5rem;
225
+ }
226
+
227
+ .generate-btn {
228
+ white-space: nowrap;
229
+ }
230
+
231
+ .download-btn {
232
+ background-color: #30764c;
233
+ margin-top: 10px;
234
+ }
235
+
236
+ .download-btn:hover {
237
+ background-color: #2c8d55;
238
+ }
static/js/apps-hub/app-selector.js ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function toggleDarkMode() {
2
+ document.body.classList.toggle('dark-mode');
3
+ }
4
+
5
+ new Vue({
6
+ el: '#app',
7
+ data: {
8
+ selectedApp: null,
9
+ aiApps: [
10
+ {
11
+ id: 1,
12
+ name: 'MindScribe',
13
+ shortDescription: 'AI-powered journal and mood tracker',
14
+ longDescription: 'MindScribe is your personal AI therapist and journal. It analyzes your daily entries to provide insights into your emotional well-being and offers personalized suggestions for improving your mental health.',
15
+ icon: 'fas fa-brain',
16
+ color: '#FF6B6B',
17
+ link: '/login'
18
+ },
19
+ {
20
+ id: 2,
21
+ name: 'EcoSense',
22
+ shortDescription: 'Smart home energy optimization',
23
+ longDescription: 'EcoSense uses advanced AI algorithms to optimize your homes energy usage. It learns your habits and adjusts your smart home devices to maximize efficiency without sacrificing comfort.',
24
+ icon: 'fas fa-leaf',
25
+ color: '#4ECDC4',
26
+ link: '/login'
27
+ },
28
+ {
29
+ id: 3,
30
+ name: 'LinguaGenius',
31
+ shortDescription: 'AI language learning assistant',
32
+ longDescription: 'LinguaGenius revolutionizes language learning by adapting to your personal learning style. It creates custom lesson plans, provides real-time pronunciation feedback, and engages you in AI-powered conversations.',
33
+ icon: 'fas fa-language',
34
+ color: '#45B7D1',
35
+ link: '/login'
36
+ },
37
+ {
38
+ id: 4,
39
+ name: 'NutriAI',
40
+ shortDescription: 'Personalized nutrition and meal planning',
41
+ longDescription: 'NutriAI is your personal nutritionist powered by artificial intelligence. It analyzes your dietary preferences, health goals, and biometrics to create tailored meal plans and provide real-time nutritional advice.',
42
+ icon: 'fas fa-utensils',
43
+ color: '#FF9FF3',
44
+ link: '/login'
45
+ },
46
+ {
47
+ id: 5,
48
+ name: 'CreativeForge',
49
+ shortDescription: 'AI-assisted creative content generator',
50
+ longDescription: 'CreativeForge is an AI-powered tool for artists, writers, and musicians. It provides inspiration, generates ideas, and even collaborates with you to create unique pieces of art, stories, or musical compositions.',
51
+ icon: 'fas fa-paint-brush',
52
+ color: '#FFC75F',
53
+ link: '/login'
54
+ },
55
+ {
56
+ id: 6,
57
+ name: 'SafeGuardAI',
58
+ shortDescription: 'Intelligent home and personal security',
59
+ longDescription: 'SafeGuardAI uses advanced computer vision and anomaly detection to keep your home and loved ones safe. It integrates with your existing security systems and provides real-time alerts and recommendations.',
60
+ icon: 'fas fa-shield-alt',
61
+ color: '#6A5ACD',
62
+ link: '/login'
63
+ }
64
+ ]
65
+ },
66
+ methods: {
67
+ selectApp(app) {
68
+ this.selectedApp = app;
69
+ },
70
+ closeDetails() {
71
+ this.selectedApp = null;
72
+ }
73
+ }
74
+ });
static/js/apps-hub/app1.js ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', async () => {
2
+ const session = await checkAuth();
3
+ if (!session) {
4
+ redirectToLogin();
5
+ }
6
+ });
7
+
8
+ document.getElementById('logoutBtn').addEventListener('click', logout);
static/js/apps-hub/auth.js ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const client = new Appwrite.Client();
2
+ client.setEndpoint('https://cloud.appwrite.io/v1')
3
+ .setProject('66c2f6c7000abed7f1f9');
4
+
5
+ const account = new Appwrite.Account(client);
6
+
7
+ async function checkAuth() {
8
+ try {
9
+ const session = await account.getSession('current');
10
+ return session;
11
+ } catch (error) {
12
+ console.error("Not authenticated", error);
13
+ return null;
14
+ }
15
+ }
16
+
17
+ function redirectToLogin() {
18
+ window.location.href = '/';
19
+ }
20
+
21
+ async function logout() {
22
+ // Show loading indicator
23
+ const loadingElement = document.createElement('div');
24
+ loadingElement.textContent = 'Logging out...';
25
+ document.body.appendChild(loadingElement);
26
+
27
+ try {
28
+ // Set a timeout for the logout process
29
+ const logoutPromise = account.deleteSession('current');
30
+ const timeoutPromise = new Promise((_, reject) =>
31
+ setTimeout(() => reject(new Error('Logout timed out')), 5000) // 5 second timeout
32
+ );
33
+
34
+ await Promise.race([logoutPromise, timeoutPromise]);
35
+
36
+ // Perform any client-side cleanup here
37
+ // For example, clear any local storage items
38
+ localStorage.clear();
39
+
40
+ // Redirect to login page
41
+ window.location.href = '/';
42
+ } catch (error) {
43
+ console.error("Logout failed:", error);
44
+ alert("Logout failed. Please try again.");
45
+ } finally {
46
+ // Remove loading indicator
47
+ document.body.removeChild(loadingElement);
48
+ }
49
+ }
static/js/apps-hub/login.js ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.getElementById('loginBtn').addEventListener('click', initiateOAuth2Login);
2
+
3
+ function initiateOAuth2Login() {
4
+ const success = `${window.location.origin}/research-pro`;
5
+ const failure = `${window.location.origin}/login`;
6
+
7
+ account.createOAuth2Session(
8
+ 'google',
9
+ success,
10
+ failure,
11
+ ['https://www.googleapis.com/auth/userinfo.email',
12
+ 'https://www.googleapis.com/auth/userinfo.profile',
13
+ 'openid']
14
+ );
15
+ }
16
+
17
+ // Check if already logged in
18
+ (async function() {
19
+ const session = await checkAuth();
20
+ if (session) {
21
+ window.location.href = '/profile';
22
+ }
23
+ })();
static/js/apps-hub/profile-page.js ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // profile-page.js
2
+ document.addEventListener('DOMContentLoaded', async () => {
3
+ try {
4
+ const session = await checkAuth();
5
+ if (!session) {
6
+ redirectToLogin();
7
+ return;
8
+ }
9
+
10
+ const user = await account.get();
11
+ displayProfilePageInfo(user, session);
12
+ } catch (error) {
13
+ console.error("Error fetching profile:", error);
14
+ document.getElementById('profilePageInfo').textContent = 'Error loading profile information.';
15
+ }
16
+ });
17
+
18
+ function displayProfilePageInfo(user, session) {
19
+ const profileInfoElement = document.getElementById('profilePageInfo');
20
+ profileInfoElement.innerHTML = `
21
+ <div class="profile-page__info-item">
22
+ <span class="profile-page__info-label">Name:</span>
23
+ <span class="profile-page__info-value">${user.name}</span>
24
+ </div>
25
+ <div class="profile-page__info-item">
26
+ <span class="profile-page__info-label">Email:</span>
27
+ <span class="profile-page__info-value">${user.email}</span>
28
+ </div>
29
+ <div class="profile-page__info-item">
30
+ <span class="profile-page__info-label">User ID:</span>
31
+ <span class="profile-page__info-value">${user.$id}</span>
32
+ </div>
33
+ <div class="profile-page__info-item">
34
+ <span class="profile-page__info-label">Provider:</span>
35
+ <span class="profile-page__info-value">${session.provider}</span>
36
+ </div>
37
+ <div class="profile-page__info-item">
38
+ <span class="profile-page__info-label">Provider UID:</span>
39
+ <span class="profile-page__info-value">${session.providerUid}</span>
40
+ </div>
41
+ <div class="profile-page__info-item">
42
+ <span class="profile-page__info-label">Session Expires:</span>
43
+ <span class="profile-page__info-value">${new Date(session.expire).toLocaleString()}</span>
44
+ </div>
45
+ `;
46
+ }
47
+
48
+ document.getElementById('profilePageLogoutBtn').addEventListener('click', logout);
static/js/apps-hub/profile.js ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', async () => {
2
+ try {
3
+ const session = await checkAuth();
4
+ if (!session) {
5
+ redirectToLogin();
6
+ return;
7
+ }
8
+
9
+ const user = await account.get();
10
+ displayProfileInfo(user, session);
11
+ } catch (error) {
12
+ console.error("Error fetching profile:", error);
13
+ document.getElementById('profileInfo').textContent = 'Error loading profile information.';
14
+ }
15
+ });
16
+
17
+ function displayProfileInfo(user, session) {
18
+ const profileInfoElement = document.getElementById('profileInfo');
19
+ profileInfoElement.innerHTML = `
20
+ <p><strong>Name:</strong> ${user.name}</p>
21
+ <p><strong>Email:</strong> ${user.email}</p>
22
+ <p><strong>User ID:</strong> ${user.$id}</p>
23
+ <p><strong>Provider:</strong> ${session.provider}</p>
24
+ <p><strong>Provider UID:</strong> ${session.providerUid}</p>
25
+ <p><strong>Session Expires:</strong> ${new Date(session.expire).toLocaleString()}</p>
26
+ `;
27
+ }
28
+
29
+ document.getElementById('logoutBtn').addEventListener('click', logout);
static/js/apps-hub/research-pro.js ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ async function generateReport() {
2
+ const description = document.getElementById('description').value || document.getElementById('description-textarea').value;
3
+ document.getElementById('initial-view').style.display = 'none';
4
+ document.getElementById('report-view').style.display = 'block';
5
+ document.getElementById('description-textarea').value = description;
6
+ const reportContainer = document.getElementById('report-container');
7
+ const sourcesContainer = document.getElementById('sources-container');
8
+ const downloadBtn = document.getElementById('downloadBtn');
9
+ reportContainer.innerHTML = 'Generating report...';
10
+ sourcesContainer.innerHTML = '';
11
+ downloadBtn.style.display = 'none';
12
+
13
+ try {
14
+ const response = await fetch('https://pvanand-search-generate-prod.hf.space/generate_report', {
15
+ method: 'POST',
16
+ headers: {
17
+ 'Content-Type': 'application/json',
18
+ 'Accept': 'text/plain'
19
+ },
20
+ body: JSON.stringify({
21
+ description: description,
22
+ user_id: "",
23
+ user_name: "multi-agent-research",
24
+ internet: true,
25
+ output_format: "report_table",
26
+ data_format: "Structured data",
27
+ generate_charts: true,
28
+ output_as_md: true
29
+ })
30
+ });
31
+
32
+ if (!response.ok) {
33
+ throw new Error(`HTTP error! status: ${response.status}`);
34
+ }
35
+
36
+ const reader = response.body.getReader();
37
+ const decoder = new TextDecoder();
38
+ let markdown = '';
39
+ let metadata = '';
40
+ let isReadingMetadata = false;
41
+
42
+ while (true) {
43
+ const { value, done } = await reader.read();
44
+ if (done) break;
45
+
46
+ const chunk = decoder.decode(value, { stream: true });
47
+
48
+ if (chunk.includes('<report-metadata>')) {
49
+ isReadingMetadata = true;
50
+ metadata = '';
51
+ }
52
+
53
+ if (isReadingMetadata) {
54
+ metadata += chunk;
55
+ if (chunk.includes('</report-metadata>')) {
56
+ isReadingMetadata = false;
57
+ processMetadata(metadata);
58
+ }
59
+ } else {
60
+ markdown += chunk;
61
+ renderMarkdown(markdown);
62
+ }
63
+ }
64
+
65
+ // Show the download button after the report is generated
66
+ downloadBtn.style.display = 'inline-block';
67
+ } catch (error) {
68
+ reportContainer.innerHTML = `Error generating report: ${error.message}`;
69
+ }
70
+ }
71
+
72
+ function renderMarkdown(markdown) {
73
+ const reportContainer = document.getElementById('report-container');
74
+ const previousHeight = reportContainer.scrollHeight;
75
+ const reportContent = markdown.match(/<report>([\s\S]*)<\/report>/);
76
+
77
+ if (reportContent) {
78
+ reportContainer.innerHTML = marked.parse(reportContent[1]);
79
+ } else {
80
+ reportContainer.innerHTML = marked.parse(markdown);
81
+ }
82
+
83
+ const scripts = reportContainer.getElementsByTagName('script');
84
+ Array.from(scripts).forEach(script => {
85
+ const newScript = document.createElement('script');
86
+ newScript.textContent = script.textContent;
87
+ script.parentNode.replaceChild(newScript, script);
88
+ });
89
+
90
+ // Make Plotly charts responsive
91
+ const plots = reportContainer.querySelectorAll('.js-plotly-plot');
92
+ plots.forEach(plot => {
93
+ Plotly.Plots.resize(plot);
94
+ });
95
+
96
+ // Scroll to new content
97
+ scrollToNewContent(previousHeight);
98
+ }
99
+
100
+ function processMetadata(metadata) {
101
+ const sourcesContainer = document.getElementById('sources-container');
102
+ const metadataMatch = metadata.match(/all-text-with-urls: (.+)/);
103
+
104
+ if (metadataMatch) {
105
+ const metadataObj = JSON.parse(metadataMatch[1]);
106
+
107
+ sourcesContainer.innerHTML = '<h2>Sources</h2>';
108
+ metadataObj.forEach(([content, url]) => {
109
+ if (content.trim() !== "") {
110
+ const sourceItem = document.createElement('div');
111
+ sourceItem.className = 'source-item';
112
+ const snippet = content.length > 400 ? content.substring(0, 400) + '...' : content;
113
+ sourceItem.innerHTML = `
114
+ <a href="${url}" target="_blank" class="source-url">${url}</a>
115
+ <div class="source-content">
116
+ <div class="source-snippet">${marked.parse(snippet)}</div>
117
+ <div class="source-full">${marked.parse(content)}</div>
118
+ <div class="expand-indicator"></div>
119
+ </div>
120
+ `;
121
+ sourcesContainer.appendChild(sourceItem);
122
+
123
+ const sourceUrl = sourceItem.querySelector('.source-url');
124
+ const sourceContent = sourceItem.querySelector('.source-content');
125
+ const snippetDiv = sourceItem.querySelector('.source-snippet');
126
+ const fullDiv = sourceItem.querySelector('.source-full');
127
+
128
+ sourceContent.addEventListener('click', function(e) {
129
+ if (!sourceItem.classList.contains('expanded')) {
130
+ sourceItem.classList.add('expanded');
131
+ snippetDiv.style.display = 'none';
132
+ fullDiv.style.display = 'block';
133
+ } else if (e.clientY > sourceContent.getBoundingClientRect().bottom - 30) {
134
+ sourceItem.classList.remove('expanded');
135
+ snippetDiv.style.display = 'block';
136
+ fullDiv.style.display = 'none';
137
+ }
138
+ });
139
+
140
+ sourceUrl.addEventListener('click', function(e) {
141
+ e.stopPropagation();
142
+ });
143
+ }
144
+ });
145
+ } else {
146
+ sourcesContainer.innerHTML = '<h2>Sources</h2><p>No source information available.</p>';
147
+ }
148
+ }
149
+
150
+ function scrollToNewContent(previousHeight) {
151
+ const reportContainer = document.getElementById('report-container');
152
+ const newHeight = reportContainer.scrollHeight;
153
+ const scrollDifference = newHeight - previousHeight;
154
+
155
+ if (scrollDifference > 0) {
156
+ const viewportHeight = window.innerHeight;
157
+ const currentScrollPosition = window.pageYOffset;
158
+ const contentBottom = reportContainer.offsetTop + newHeight;
159
+
160
+ // Start scrolling when new content is within 200px of viewport bottom
161
+ if (contentBottom > currentScrollPosition + viewportHeight - 200) {
162
+ window.scrollBy({
163
+ top: scrollDifference,
164
+ behavior: 'smooth'
165
+ });
166
+ }
167
+ }
168
+ }
169
+
170
+ function scrollToTop() {
171
+ window.scrollTo({
172
+ top: 0,
173
+ behavior: 'smooth'
174
+ });
175
+ }
176
+
177
+ async function downloadHTML() {
178
+ try {
179
+ // Fetch the CSS
180
+ const response = await fetch('styles.css');
181
+ let css = await response.text();
182
+
183
+ // Add styles for expanded sources and body max-width
184
+ css += `
185
+ body.report-body {
186
+ max-width: 804px;
187
+ margin: 0 auto;
188
+ }
189
+ .source-item {
190
+ margin-bottom: 20px;
191
+ }
192
+ .source-content {
193
+ margin-top: 10px;
194
+ }
195
+ .source-snippet, .expand-indicator {
196
+ display: none;
197
+ }
198
+ .source-full {
199
+ display: block;
200
+ }
201
+ `;
202
+
203
+ // Clone the document
204
+ const htmlContent = document.implementation.createHTMLDocument('Report');
205
+
206
+ // Set up the basic structure
207
+ htmlContent.documentElement.lang = 'en';
208
+ const head = htmlContent.head;
209
+ const body = htmlContent.body;
210
+ body.className = 'report-body';
211
+
212
+ // Add meta tags
213
+ const meta = htmlContent.createElement('meta');
214
+ meta.charset = 'UTF-8';
215
+ head.appendChild(meta);
216
+
217
+ const viewport = htmlContent.createElement('meta');
218
+ viewport.name = 'viewport';
219
+ viewport.content = 'width=device-width, initial-scale=1.0';
220
+ head.appendChild(viewport);
221
+
222
+ // Add title
223
+ const title = htmlContent.createElement('title');
224
+ title.textContent = 'Generated Report';
225
+ head.appendChild(title);
226
+
227
+ // Add style
228
+ const style = htmlContent.createElement('style');
229
+ style.textContent = css;
230
+ head.appendChild(style);
231
+
232
+ // Add necessary scripts
233
+ const markedScript = htmlContent.createElement('script');
234
+ markedScript.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js';
235
+ head.appendChild(markedScript);
236
+
237
+ const plotlyScript = htmlContent.createElement('script');
238
+ plotlyScript.src = 'https://cdn.plot.ly/plotly-latest.min.js';
239
+ head.appendChild(plotlyScript);
240
+
241
+ // Copy the report content
242
+ const reportContainer = document.getElementById('report-container');
243
+ body.innerHTML = reportContainer.innerHTML;
244
+
245
+ // Copy and modify the sources content
246
+ const sourcesContainer = document.getElementById('sources-container');
247
+ const sourcesDiv = htmlContent.createElement('div');
248
+ sourcesDiv.innerHTML = sourcesContainer.innerHTML;
249
+
250
+ // Expand all sources
251
+ const sourceItems = sourcesDiv.querySelectorAll('.source-item');
252
+ sourceItems.forEach(item => {
253
+ item.classList.add('expanded');
254
+ const snippetDiv = item.querySelector('.source-snippet');
255
+ const fullDiv = item.querySelector('.source-full');
256
+ if (snippetDiv) snippetDiv.style.display = 'none';
257
+ if (fullDiv) fullDiv.style.display = 'block';
258
+ });
259
+
260
+ body.appendChild(sourcesDiv);
261
+
262
+ // Create blob and download
263
+ const blob = new Blob([htmlContent.documentElement.outerHTML], { type: 'text/html' });
264
+ const url = URL.createObjectURL(blob);
265
+ const a = document.createElement('a');
266
+ a.href = url;
267
+ a.download = 'report.html';
268
+ document.body.appendChild(a);
269
+ a.click();
270
+ document.body.removeChild(a);
271
+ URL.revokeObjectURL(url);
272
+ } catch (error) {
273
+ console.error('Error downloading HTML:', error);
274
+ }
275
+ }
276
+ // Make Plotly charts responsive on window resize
277
+ window.addEventListener('resize', function() {
278
+ const plots = document.querySelectorAll('.js-plotly-plot');
279
+ plots.forEach(plot => {
280
+ Plotly.Plots.resize(plot);
281
+ });
282
+ });
static/js/apps-hub/sidebar-component.js ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // sidebar-component.js
2
+ const SidebarComponent = {
3
+ template: `
4
+ <aside class="ai-sidebar__sidebar">
5
+ <div class="ai-sidebar__title">AI Apps</div>
6
+ <ul class="ai-sidebar__app-list">
7
+ <li v-for="app in apps" :key="app.id" class="ai-sidebar__app-item" @click="navigateToApp(app)">
8
+ <div class="ai-sidebar__app-icon" :style="{ backgroundColor: app.color }">
9
+ <i :class="app.icon"></i>
10
+ </div>
11
+ <span class="ai-sidebar__app-name">{{ app.name }}</span>
12
+ </li>
13
+ </ul>
14
+ <div class="ai-sidebar__footer">
15
+ <a href="/profile" class="ai-sidebar__app-item">
16
+ <div class="ai-sidebar__app-icon" style="backgroundColor: #3f72af">
17
+ <i class="fas fa-user"></i>
18
+ </div>
19
+ <span class="ai-sidebar__app-name">Profile</span>
20
+ </a>
21
+ </div>
22
+ </aside>
23
+ `,
24
+ setup() {
25
+ const apps = Vue.ref([
26
+ { id: 1, name: 'iResearcher', icon: 'fas fa-flask', color: '#6C5CE7', url: '/research-pro' },
27
+ { id: 2, name: 'MindScribe', icon: 'fas fa-brain', color: '#FF6B6B', url: '/research-pro' },
28
+ { id: 3, name: 'LinguaGenius', icon: 'fas fa-language', color: '#45B7D1', url: '/research-pro' },
29
+ { id: 4, name: 'NutriAI', icon: 'fas fa-utensils', color: '#FF9FF3', url: '/research-pro' },
30
+ ]);
31
+
32
+ const navigateToApp = (app) => {
33
+ window.location.href = app.url;
34
+ };
35
+
36
+ return {
37
+ apps,
38
+ navigateToApp
39
+ };
40
+ }
41
+ };
templates/apps-hub/app-selector.html ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Discover AI Apps</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
9
+ <link rel="stylesheet" href="/css/app-selector.css">
10
+ </head>
11
+ <body>
12
+ <div class="background-elements"></div>
13
+
14
+ <div class="top-buttons">
15
+ <button class="login-button" onclick="window.location.href = '/login'">
16
+ <i class="fab fa-google"></i> Login
17
+ </button>
18
+ <button class="dark-mode-toggle" onclick="toggleDarkMode()">
19
+ <i class="fas fa-moon"></i>
20
+ </button>
21
+ </div>
22
+ <div id="app">
23
+ <div class="title-bar">
24
+ <h1 class="title">DISCOVER AI•APPS</h1>
25
+ <p class="subtitle">Explore the future of artificial intelligence with our curated collection of cutting-edge AI applications</p>
26
+ <div class="particles">
27
+ <div class="particle" style="width: 12px; height: 12px; left: 20%; top: 20%;"></div>
28
+ <div class="particle" style="width: 18px; height: 18px; left: 40%; top: 30%;"></div>
29
+ <div class="particle" style="width: 10px; height: 10px; left: 60%; top: 40%;"></div>
30
+ <div class="particle" style="width: 15px; height: 15px; left: 80%; top: 50%;"></div>
31
+ <div class="particle" style="width: 8px; height: 8px; left: 30%; top: 60%;"></div>
32
+ </div>
33
+ </div>
34
+ <div class="discover-ai-apps">
35
+ {% raw %}
36
+ <div class="app-grid">
37
+ <div v-for="app in aiApps" :key="app.id" class="app-card" @click="selectApp(app)">
38
+ <div class="app-icon" :style="{ backgroundColor: app.color }">
39
+ <i :class="app.icon"></i>
40
+ </div>
41
+ <h2 class="app-name">{{ app.name }}</h2>
42
+ <p class="app-description">{{ app.shortDescription }}</p>
43
+ </div>
44
+ </div>
45
+
46
+ <transition name="fade">
47
+ <div v-if="selectedApp" class="app-details">
48
+ <div class="app-details-content">
49
+ <button class="close-button" @click="closeDetails">&times;</button>
50
+ <div class="app-details-icon" :style="{ backgroundColor: selectedApp.color }">
51
+ <i :class="selectedApp.icon"></i>
52
+ </div>
53
+ <h2>{{ selectedApp.name }}</h2>
54
+ <p>{{ selectedApp.longDescription }}</p>
55
+ <a :href="selectedApp.link" class="try-button">Try Now</a>
56
+ </div>
57
+ </div>
58
+ </transition>
59
+ {% endraw %}
60
+ </div>
61
+ </div>
62
+ <script src="/js/app-selector.js"></script>
63
+ </body>
64
+ </html>
templates/apps-hub/base.html ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>{{ title }} - My App</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/appwrite@15.0.0"></script>
8
+ <script src="/js/auth.js"></script>
9
+ <script src="https://cdn.tailwindcss.com"></script>
10
+ </head>
11
+ <body class="bg-gray-100 min-h-screen flex items-center justify-center">
12
+ <div class="bg-white p-8 rounded-lg shadow-md w-full max-w-md">
13
+ {% block content %}{% endblock %}
14
+ </div>
15
+ {% block scripts %}{% endblock %}
16
+ </body>
17
+ </html>
templates/apps-hub/login.html ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Login</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/appwrite@15.0.0"></script>
8
+ <script src="/js/auth.js"></script>
9
+ <style>
10
+ body {
11
+ font-family: Arial, sans-serif;
12
+ background: linear-gradient(to right, #4a90e2, #7e57c2);
13
+ display: flex;
14
+ justify-content: center;
15
+ align-items: center;
16
+ height: 100vh;
17
+ margin: 0;
18
+ }
19
+ .container {
20
+ background-color: white;
21
+ padding: 2rem;
22
+ border-radius: 8px;
23
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
24
+ text-align: center;
25
+ width: 300px;
26
+ }
27
+ h1 {
28
+ color: #333;
29
+ margin-bottom: 1rem;
30
+ }
31
+ p {
32
+ color: #666;
33
+ margin-bottom: 1rem;
34
+ }
35
+ .spinner {
36
+ border: 4px solid #f3f3f3;
37
+ border-top: 4px solid #3498db;
38
+ border-radius: 50%;
39
+ width: 40px;
40
+ height: 40px;
41
+ animation: spin 1s linear infinite;
42
+ margin: 0 auto 1rem;
43
+ }
44
+ @keyframes spin {
45
+ 0% { transform: rotate(0deg); }
46
+ 100% { transform: rotate(360deg); }
47
+ }
48
+ </style>
49
+ </head>
50
+ <body>
51
+ <div class="container">
52
+ <h1>Welcome</h1>
53
+ <div class="spinner"></div>
54
+ <p>Logging in with Google...</p>
55
+ <p style="font-size: 0.9em; color: #888;">Please wait while we redirect you.</p>
56
+ </div>
57
+
58
+ <script>
59
+ // Check if already logged in
60
+ (async function() {
61
+ const session = await checkAuth();
62
+ if (session) {
63
+ window.location.href = '/research-pro';
64
+ } else {
65
+ initiateOAuth2Login();
66
+ }
67
+ })();
68
+
69
+ function initiateOAuth2Login() {
70
+ const success = `${window.location.origin}/research-pro`;
71
+ const failure = `${window.location.origin}/login`;
72
+
73
+ account.createOAuth2Session(
74
+ 'google',
75
+ success,
76
+ failure,
77
+ ['https://www.googleapis.com/auth/userinfo.email',
78
+ 'https://www.googleapis.com/auth/userinfo.profile',
79
+ 'openid']
80
+ );
81
+ }
82
+ </script>
83
+ </body>
84
+ </html>
templates/apps-hub/mindscribe.html ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>MindScribe</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
8
+ <link rel="stylesheet" href="ai-sidebar.css">
9
+ <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
10
+ <script src="sidebar-component.js"></script>
11
+ </head>
12
+ <body>
13
+ <div id="app" class="ai-sidebar__container">
14
+ <sidebar-component></sidebar-component>
15
+ <main class="ai-sidebar__content">
16
+ <h1>MindScribe</h1>
17
+ <p>Welcome to MindScribe, your AI-powered journal and mood tracker.</p>
18
+ <!-- Add more HTML content for the MindScribe app here -->
19
+ </main>
20
+ </div>
21
+
22
+ <script>
23
+ const { createApp } = Vue;
24
+
25
+ const app = createApp({
26
+ components: {
27
+ 'sidebar-component': SidebarComponent
28
+ }
29
+ // Your app-specific Vue code here
30
+ });
31
+
32
+ app.mount('#app');
33
+ </script>
34
+ </body>
35
+ </html>
templates/apps-hub/profile.html ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Profile</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/appwrite@15.0.0"></script>
8
+ <script src="/js/auth.js"></script>
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
10
+ <link rel="stylesheet" href="/css/ai-sidebar.css">
11
+ <link rel="stylesheet" href="/css/profile-page.css">
12
+ <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
13
+ <script src="/js/sidebar-component.js"></script>
14
+ </head>
15
+ <body>
16
+ <div id="app" class="ai-sidebar__container">
17
+ <sidebar-component></sidebar-component>
18
+ <main class="ai-sidebar__content">
19
+ <div class="profile-page__container">
20
+ <h1 class="profile-page__title">Profile</h1>
21
+ <div id="profilePageInfo" class="profile-page__info"></div>
22
+ <button @click="handleLogout" class="profile-page__logout-btn">Logout</button>
23
+ </div>
24
+ </main>
25
+ </div>
26
+
27
+ <script src="/js/profile-page.js"></script>
28
+ <script>
29
+ const { createApp } = Vue;
30
+
31
+ const app = createApp({
32
+ components: {
33
+ 'sidebar-component': SidebarComponent
34
+ },
35
+ methods: {
36
+ async checkProfileAuthentication() {
37
+ const session = await checkAuth();
38
+ if (!session) {
39
+ redirectToLogin();
40
+ }
41
+ },
42
+ handleLogout() {
43
+ logout();
44
+ }
45
+ },
46
+ mounted() {
47
+ this.checkProfileAuthentication();
48
+ }
49
+ });
50
+
51
+ app.mount('#app');
52
+ </script>
53
+ </body>
54
+ </html>
templates/apps-hub/research-pro.html ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Research Pro</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/appwrite@15.0.0"></script>
8
+ <script src="/js/auth.js"></script>
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
10
+ <link rel="stylesheet" href="/css/ai-sidebar.css">
11
+ <link rel="stylesheet" href="/css/research-pro-style.css">
12
+ <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
13
+ <script src="/js/sidebar-component.js"></script>
14
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
15
+ <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
16
+ <style>
17
+ #main-container {
18
+ position: relative;
19
+ min-height: 100vh;
20
+ }
21
+ .button-container {
22
+ position: fixed;
23
+ bottom: 20px;
24
+ right: 20px;
25
+ justify-content: flex-end;
26
+ height: 30px;
27
+ }
28
+ </style>
29
+ </head>
30
+ <body>
31
+ <div id="app" class="ai-sidebar__container">
32
+ <sidebar-component></sidebar-component>
33
+ <main class="ai-sidebar__content">
34
+ <div id="main-container">
35
+ <div id="initial-view" class="centered-container">
36
+ <h1 class="title">iResearcher</h1>
37
+ <div class="search-container">
38
+ <input type="text" id="description" placeholder="Your question/topic...">
39
+ <button onclick="generateReport()">
40
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="url(#gradient)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
41
+ <defs>
42
+ <linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
43
+ <stop offset="0%" stop-color="#3b82f6" />
44
+ <stop offset="100%" stop-color="#10b981" />
45
+ </linearGradient>
46
+ </defs>
47
+ <circle cx="11" cy="11" r="8"></circle>
48
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
49
+ </svg>
50
+ </button>
51
+ </div>
52
+ </div>
53
+
54
+ <!-- This will be shown when a report is generated -->
55
+ <div id="report-view" style="display: none;">
56
+ <div id="input-container" style="display: none;">
57
+ <textarea id="description-textarea" rows="1" placeholder="Your question/topics"></textarea>
58
+ </div>
59
+ <div id="output-container">
60
+ <div id="report-container"></div>
61
+ <div id="sources-container"></div>
62
+ </div>
63
+ <div class="button-container">
64
+ <button id="downloadBtn" onclick="downloadHTML()" style="display: none;" title="Download HTML Report">
65
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
66
+ <path d="M3 15v4c0 1.1.9 2 2 2h14a2 2 0 0 0 2-2v-4M17 9l-5 5-5-5M12 12.8V2.5"/>
67
+ </svg>
68
+ </button>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ </main>
73
+ </div>
74
+
75
+ <script src="/js/research-pro.js"></script>
76
+ <script>
77
+ const { createApp } = Vue;
78
+
79
+ const app = createApp({
80
+ components: {
81
+ 'sidebar-component': SidebarComponent
82
+ },
83
+ methods: {
84
+ async checkAuthentication() {
85
+ const session = await checkAuth();
86
+ if (!session) {
87
+ redirectToLogin();
88
+ }
89
+ }
90
+ },
91
+ mounted() {
92
+ this.checkAuthentication();
93
+ }
94
+ });
95
+
96
+ app.mount('#app');
97
+ </script>
98
+ </body>
99
+ </html>