AliHashir commited on
Commit
4607dd0
Β·
1 Parent(s): d71fe3a

Enhance UI with modern, colorful, and engaging design

Browse files
app/web/templates/_result_block.html CHANGED
@@ -1,35 +1,331 @@
1
- <div class="card">
2
  <style>
3
- .badge { display:inline-block; padding:4px 10px; border-radius:999px; font-weight:600; font-size:14px; }
4
- .True { background:#e6ffed; color:#05620d; border:1px solid #b1f0b8; }
5
- .False { background:#ffe8e6; color:#7b1010; border:1px solid #f3b3ad; }
6
- .Misleading { background:#fff6e0; color:#6a4b00; border:1px solid #f3d49a; }
7
- .Unverified { background:#eef2f7; color:#3b4252; border:1px solid #d0d7e2; }
8
- .muted { color:#6b7280; font-size:14px; }
9
- .sources li{ margin: 6px 0; }
10
- pre { background:#f6f8fa; padding:8px; border-radius:8px; overflow:auto; }
11
- button { padding:8px 12px; border-radius:8px; border:1px solid #d1d5db; cursor:pointer; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  </style>
13
 
14
- <div><span class="badge {{ r.verdict }}">{{ r.verdict }}</span></div>
15
- <h3 style="margin:10px 0 6px 0;">Claim</h3>
16
- <p>{{ r.claim }}</p>
 
 
 
 
 
 
 
 
 
 
 
17
 
18
- <h3 style="margin:10px 0 6px 0;">Rationale</h3>
19
- <p>{{ r.rationale }}</p>
20
- <div class="muted">Confidence: {{ '%.2f' % r.confidence }} Β· <a href="/r/{{ r.id }}" target="_blank" rel="noopener">Share link</a></div>
 
 
 
 
21
 
22
- <h3 style="margin:16px 0 6px 0;">Ready-to-post</h3>
23
- <pre id="post">{{ r.post }}</pre>
24
- <button onclick="navigator.clipboard.writeText(document.getElementById('post').innerText)">Copy</button>
 
 
 
 
25
 
26
- <h3 style="margin:16px 0 6px 0;">Sources</h3>
27
- <ol class="sources">
28
- {% for s in r.sources %}
29
- <li>
30
- <a href="{{ s.url }}" target="_blank" rel="noopener">{{ s.title }}</a>
31
- {% if s.snippet %}<div class="muted">{{ s.snippet }}</div>{% endif %}
32
- </li>
33
- {% endfor %}
34
- </ol>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="result-card">
2
  <style>
3
+ .result-card {
4
+ background: white;
5
+ border-radius: 20px;
6
+ padding: 2rem;
7
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
8
+ border: 1px solid #e5e7eb;
9
+ margin-top: 2rem;
10
+ }
11
+
12
+ .verdict-section {
13
+ text-align: center;
14
+ margin-bottom: 2rem;
15
+ padding: 1.5rem;
16
+ border-radius: 16px;
17
+ background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
18
+ }
19
+
20
+ .verdict-badge {
21
+ display: inline-block;
22
+ padding: 0.75rem 2rem;
23
+ border-radius: 50px;
24
+ font-weight: 700;
25
+ font-size: 1.25rem;
26
+ text-transform: uppercase;
27
+ letter-spacing: 0.5px;
28
+ margin-bottom: 1rem;
29
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
30
+ }
31
+
32
+ .True {
33
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%);
34
+ color: white;
35
+ box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
36
+ }
37
+
38
+ .False {
39
+ background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
40
+ color: white;
41
+ box-shadow: 0 4px 15px rgba(239, 68, 68, 0.3);
42
+ }
43
+
44
+ .Misleading {
45
+ background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
46
+ color: white;
47
+ box-shadow: 0 4px 15px rgba(245, 158, 11, 0.3);
48
+ }
49
+
50
+ .Unverified {
51
+ background: linear-gradient(135deg, #6b7280 0%, #4b5563 100%);
52
+ color: white;
53
+ box-shadow: 0 4px 15px rgba(107, 114, 128, 0.3);
54
+ }
55
+
56
+ .confidence-bar {
57
+ margin-top: 1rem;
58
+ background: #e5e7eb;
59
+ height: 8px;
60
+ border-radius: 4px;
61
+ overflow: hidden;
62
+ }
63
+
64
+ .confidence-fill {
65
+ height: 100%;
66
+ background: linear-gradient(90deg, #3b82f6 0%, #1d4ed8 100%);
67
+ border-radius: 4px;
68
+ transition: width 1s ease-out;
69
+ }
70
+
71
+ .section {
72
+ margin: 2rem 0;
73
+ }
74
+
75
+ .section-title {
76
+ display: flex;
77
+ align-items: center;
78
+ gap: 0.5rem;
79
+ font-size: 1.25rem;
80
+ font-weight: 600;
81
+ color: #374151;
82
+ margin-bottom: 1rem;
83
+ padding-bottom: 0.5rem;
84
+ border-bottom: 2px solid #e5e7eb;
85
+ }
86
+
87
+ .claim-text {
88
+ background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
89
+ border-left: 4px solid #3b82f6;
90
+ padding: 1.5rem;
91
+ border-radius: 0 12px 12px 0;
92
+ font-size: 1.1rem;
93
+ line-height: 1.6;
94
+ font-style: italic;
95
+ margin-bottom: 1rem;
96
+ }
97
+
98
+ .rationale-text {
99
+ background: #f8fafc;
100
+ padding: 1.5rem;
101
+ border-radius: 12px;
102
+ line-height: 1.7;
103
+ color: #374151;
104
+ border: 1px solid #e2e8f0;
105
+ }
106
+
107
+ .post-container {
108
+ background: linear-gradient(135deg, #eef2ff 0%, #e0e7ff 100%);
109
+ border: 2px dashed #3b82f6;
110
+ border-radius: 16px;
111
+ padding: 1.5rem;
112
+ position: relative;
113
+ }
114
+
115
+ .post-text {
116
+ font-family: system-ui, sans-serif;
117
+ font-size: 1rem;
118
+ line-height: 1.6;
119
+ color: #1f2937;
120
+ margin: 0;
121
+ white-space: pre-wrap;
122
+ background: white;
123
+ padding: 1rem;
124
+ border-radius: 8px;
125
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
126
+ }
127
+
128
+ .copy-button {
129
+ background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
130
+ color: white;
131
+ border: none;
132
+ padding: 0.75rem 1.5rem;
133
+ border-radius: 25px;
134
+ font-weight: 600;
135
+ cursor: pointer;
136
+ margin-top: 1rem;
137
+ transition: all 0.3s ease;
138
+ box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3);
139
+ }
140
+
141
+ .copy-button:hover {
142
+ transform: translateY(-2px);
143
+ box-shadow: 0 6px 20px rgba(59, 130, 246, 0.4);
144
+ }
145
+
146
+ .copy-button:active {
147
+ transform: translateY(0);
148
+ }
149
+
150
+ .sources-grid {
151
+ display: grid;
152
+ gap: 1rem;
153
+ }
154
+
155
+ .source-card {
156
+ background: white;
157
+ border: 1px solid #e5e7eb;
158
+ border-radius: 12px;
159
+ padding: 1.25rem;
160
+ transition: all 0.3s ease;
161
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
162
+ }
163
+
164
+ .source-card:hover {
165
+ transform: translateY(-2px);
166
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
167
+ border-color: #3b82f6;
168
+ }
169
+
170
+ .source-title {
171
+ font-weight: 600;
172
+ color: #1f2937;
173
+ text-decoration: none;
174
+ font-size: 1.05rem;
175
+ line-height: 1.4;
176
+ display: block;
177
+ margin-bottom: 0.5rem;
178
+ }
179
+
180
+ .source-title:hover {
181
+ color: #3b82f6;
182
+ }
183
+
184
+ .source-snippet {
185
+ color: #6b7280;
186
+ font-size: 0.9rem;
187
+ line-height: 1.5;
188
+ margin-top: 0.5rem;
189
+ }
190
+
191
+ .source-url {
192
+ color: #3b82f6;
193
+ font-size: 0.85rem;
194
+ margin-top: 0.5rem;
195
+ word-break: break-all;
196
+ }
197
+
198
+ .meta-info {
199
+ display: flex;
200
+ justify-content: space-between;
201
+ align-items: center;
202
+ padding: 1rem;
203
+ background: #f8fafc;
204
+ border-radius: 12px;
205
+ font-size: 0.9rem;
206
+ color: #6b7280;
207
+ margin-top: 1rem;
208
+ }
209
+
210
+ .share-link {
211
+ color: #3b82f6;
212
+ text-decoration: none;
213
+ font-weight: 500;
214
+ }
215
+
216
+ .share-link:hover {
217
+ text-decoration: underline;
218
+ }
219
+
220
+ .icon {
221
+ font-size: 1.25rem;
222
+ }
223
+
224
+ @keyframes slideIn {
225
+ from { opacity: 0; transform: translateY(20px); }
226
+ to { opacity: 1; transform: translateY(0); }
227
+ }
228
+
229
+ .result-card {
230
+ animation: slideIn 0.6s ease-out;
231
+ }
232
  </style>
233
 
234
+ <div class="verdict-section">
235
+ <div class="verdict-badge {{ r.verdict }}">
236
+ {% if r.verdict == 'True' %}βœ… Verified True
237
+ {% elif r.verdict == 'False' %}❌ Verified False
238
+ {% elif r.verdict == 'Misleading' %}⚠️ Misleading
239
+ {% else %}❓ Unverified
240
+ {% endif %}
241
+ </div>
242
+ <div style="font-size: 1rem; color: #6b7280; margin-bottom: 0.5rem;">Confidence Score</div>
243
+ <div style="font-size: 1.5rem; font-weight: 700; color: #1f2937;">{{ '%.0f' % (r.confidence * 100) }}%</div>
244
+ <div class="confidence-bar">
245
+ <div class="confidence-fill" style="width: {{ '%.0f' % (r.confidence * 100) }}%"></div>
246
+ </div>
247
+ </div>
248
 
249
+ <div class="section">
250
+ <h3 class="section-title">
251
+ <span class="icon">πŸ’¬</span>
252
+ Claim Being Verified
253
+ </h3>
254
+ <div class="claim-text">{{ r.claim }}</div>
255
+ </div>
256
 
257
+ <div class="section">
258
+ <h3 class="section-title">
259
+ <span class="icon">🧠</span>
260
+ AI Analysis & Reasoning
261
+ </h3>
262
+ <div class="rationale-text">{{ r.rationale }}</div>
263
+ </div>
264
 
265
+ <div class="section">
266
+ <h3 class="section-title">
267
+ <span class="icon">πŸ“±</span>
268
+ Ready-to-Share Social Media Post
269
+ </h3>
270
+ <div class="post-container">
271
+ <div class="post-text" id="post-{{ r.id }}">{{ r.post }}</div>
272
+ <button class="copy-button" onclick="copyToClipboard('post-{{ r.id }}')">
273
+ πŸ“‹ Copy to Clipboard
274
+ </button>
275
+ </div>
276
+ </div>
277
+
278
+ <div class="section">
279
+ <h3 class="section-title">
280
+ <span class="icon">πŸ”</span>
281
+ Sources Analyzed ({{ r.sources|length }})
282
+ </h3>
283
+ <div class="sources-grid">
284
+ {% for s in r.sources %}
285
+ <div class="source-card">
286
+ <a href="{{ s.url }}" target="_blank" rel="noopener" class="source-title">
287
+ {{ s.title }}
288
+ </a>
289
+ <div class="source-url">{{ s.url }}</div>
290
+ {% if s.snippet %}
291
+ <div class="source-snippet">{{ s.snippet }}</div>
292
+ {% endif %}
293
+ </div>
294
+ {% endfor %}
295
+ </div>
296
+ </div>
297
+
298
+ <div class="meta-info">
299
+ <div>
300
+ <strong>Fact-check ID:</strong> {{ r.id }}
301
+ </div>
302
+ <div>
303
+ <a href="/r/{{ r.id }}" target="_blank" rel="noopener" class="share-link">
304
+ πŸ”— Share this fact-check
305
+ </a>
306
+ </div>
307
+ </div>
308
  </div>
309
+
310
+ <script>
311
+ function copyToClipboard(elementId) {
312
+ const element = document.getElementById(elementId);
313
+ const text = element.innerText;
314
+
315
+ navigator.clipboard.writeText(text).then(function() {
316
+ // Show success feedback
317
+ const button = event.target;
318
+ const originalText = button.innerHTML;
319
+ button.innerHTML = 'βœ… Copied!';
320
+ button.style.background = 'linear-gradient(135deg, #10b981 0%, #059669 100%)';
321
+
322
+ setTimeout(() => {
323
+ button.innerHTML = originalText;
324
+ button.style.background = 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)';
325
+ }, 2000);
326
+ }).catch(function(err) {
327
+ console.error('Failed to copy text: ', err);
328
+ alert('Failed to copy to clipboard');
329
+ });
330
+ }
331
+ </script>
app/web/templates/index.html CHANGED
@@ -2,33 +2,353 @@
2
  <html lang="en">
3
  <head>
4
  <meta charset="utf-8" />
5
- <title>Automated Fact-Checker</title>
6
  <meta name="viewport" content="width=device-width, initial-scale=1" />
7
  <script src="https://unpkg.com/[email protected]" defer></script>
 
8
  <style>
9
- :root { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; color-scheme: light dark; }
10
- body { margin:0; padding:24px; max-width:900px; }
11
- .wrap { display:grid; gap:16px; }
12
- textarea { width:100%; min-height:110px; padding:12px; border-radius:10px; border:1px solid #d1d5db; }
13
- button { padding:10px 14px; border-radius:10px; border:1px solid #d1d5db; cursor:pointer; }
14
- .muted { color:#6b7280; font-size:14px; }
15
- .card { border:1px solid #e5e7eb; border-radius:12px; padding:16px; }
16
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  </head>
18
  <body>
19
- <h1 class="muted">Automated Fact-Checker & Synthesizer</h1>
 
 
 
 
20
 
21
- <div class="card">
22
- <form class="wrap" hx-post="/ui/check" hx-target="#result" hx-swap="innerHTML">
23
- <label for="claim" class="muted">Paste a single claim</label>
24
- <textarea id="claim" name="claim" placeholder="e.g., The Earth orbits the Sun"></textarea>
25
- <div>
26
- <button type="submit">Check claim</button>
27
- <span class="muted">You'll get sources, a verdict, and a shareable post.</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  </div>
29
- </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  </div>
31
 
32
- <div id="result" class="wrap" style="margin-top:16px;"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  </body>
34
  </html>
 
2
  <html lang="en">
3
  <head>
4
  <meta charset="utf-8" />
5
+ <title>πŸ” AI Fact-Checker - Verify Claims Instantly</title>
6
  <meta name="viewport" content="width=device-width, initial-scale=1" />
7
  <script src="https://unpkg.com/[email protected]" defer></script>
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
9
  <style>
10
+ :root {
11
+ --primary: #3b82f6;
12
+ --primary-dark: #2563eb;
13
+ --secondary: #10b981;
14
+ --danger: #ef4444;
15
+ --warning: #f59e0b;
16
+ --gray-50: #f9fafb;
17
+ --gray-100: #f3f4f6;
18
+ --gray-200: #e5e7eb;
19
+ --gray-300: #d1d5db;
20
+ --gray-600: #4b5563;
21
+ --gray-700: #374151;
22
+ --gray-900: #111827;
23
+ font-family: 'Inter', system-ui, -apple-system, sans-serif;
24
+ }
25
+
26
+ * { box-sizing: border-box; }
27
+
28
+ body {
29
+ margin: 0;
30
+ padding: 0;
31
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
32
+ min-height: 100vh;
33
+ color: var(--gray-900);
34
+ }
35
+
36
+ .container {
37
+ max-width: 1000px;
38
+ margin: 0 auto;
39
+ padding: 2rem;
40
+ }
41
+
42
+ .hero {
43
+ text-align: center;
44
+ margin-bottom: 3rem;
45
+ color: white;
46
+ }
47
+
48
+ .hero h1 {
49
+ font-size: 3.5rem;
50
+ font-weight: 700;
51
+ margin: 0 0 1rem;
52
+ background: linear-gradient(45deg, #fff, #f0f9ff);
53
+ -webkit-background-clip: text;
54
+ -webkit-text-fill-color: transparent;
55
+ background-clip: text;
56
+ text-shadow: 0 4px 20px rgba(0,0,0,0.1);
57
+ }
58
+
59
+ .hero p {
60
+ font-size: 1.25rem;
61
+ font-weight: 300;
62
+ margin: 0;
63
+ opacity: 0.9;
64
+ }
65
+
66
+ .main-card {
67
+ background: white;
68
+ border-radius: 24px;
69
+ padding: 2.5rem;
70
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
71
+ border: 1px solid rgba(255, 255, 255, 0.1);
72
+ backdrop-filter: blur(10px);
73
+ }
74
+
75
+ .form-wrapper {
76
+ display: flex;
77
+ flex-direction: column;
78
+ gap: 1.5rem;
79
+ }
80
+
81
+ .input-group {
82
+ display: flex;
83
+ flex-direction: column;
84
+ gap: 0.75rem;
85
+ }
86
+
87
+ .input-label {
88
+ font-weight: 600;
89
+ color: var(--gray-700);
90
+ font-size: 1.1rem;
91
+ }
92
+
93
+ .claim-input {
94
+ width: 100%;
95
+ min-height: 140px;
96
+ padding: 1.5rem;
97
+ border: 2px solid var(--gray-200);
98
+ border-radius: 16px;
99
+ font-size: 1rem;
100
+ font-family: inherit;
101
+ resize: vertical;
102
+ transition: all 0.3s ease;
103
+ background: var(--gray-50);
104
+ }
105
+
106
+ .claim-input:focus {
107
+ outline: none;
108
+ border-color: var(--primary);
109
+ background: white;
110
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
111
+ transform: translateY(-2px);
112
+ }
113
+
114
+ .claim-input::placeholder {
115
+ color: var(--gray-600);
116
+ font-style: italic;
117
+ }
118
+
119
+ .submit-section {
120
+ display: flex;
121
+ flex-direction: column;
122
+ align-items: center;
123
+ gap: 1rem;
124
+ }
125
+
126
+ .submit-btn {
127
+ background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
128
+ color: white;
129
+ border: none;
130
+ padding: 1rem 3rem;
131
+ border-radius: 50px;
132
+ font-size: 1.1rem;
133
+ font-weight: 600;
134
+ cursor: pointer;
135
+ transition: all 0.3s ease;
136
+ box-shadow: 0 8px 25px rgba(59, 130, 246, 0.3);
137
+ text-transform: uppercase;
138
+ letter-spacing: 0.5px;
139
+ }
140
+
141
+ .submit-btn:hover {
142
+ transform: translateY(-3px);
143
+ box-shadow: 0 12px 35px rgba(59, 130, 246, 0.4);
144
+ background: linear-gradient(135deg, var(--primary-dark) 0%, #1d4ed8 100%);
145
+ }
146
+
147
+ .submit-btn:active {
148
+ transform: translateY(-1px);
149
+ }
150
+
151
+ .help-text {
152
+ color: var(--gray-600);
153
+ font-size: 0.95rem;
154
+ text-align: center;
155
+ line-height: 1.6;
156
+ }
157
+
158
+ .examples {
159
+ display: grid;
160
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
161
+ gap: 1rem;
162
+ margin-top: 2rem;
163
+ }
164
+
165
+ .example-card {
166
+ background: var(--gray-50);
167
+ border: 1px solid var(--gray-200);
168
+ border-radius: 12px;
169
+ padding: 1rem;
170
+ cursor: pointer;
171
+ transition: all 0.3s ease;
172
+ }
173
+
174
+ .example-card:hover {
175
+ background: var(--primary);
176
+ color: white;
177
+ transform: translateY(-2px);
178
+ box-shadow: 0 8px 25px rgba(59, 130, 246, 0.2);
179
+ }
180
+
181
+ .example-card strong {
182
+ display: block;
183
+ margin-bottom: 0.5rem;
184
+ font-size: 0.9rem;
185
+ }
186
+
187
+ .result-container {
188
+ margin-top: 2rem;
189
+ }
190
+
191
+ .loading {
192
+ display: none;
193
+ text-align: center;
194
+ padding: 2rem;
195
+ color: var(--gray-600);
196
+ }
197
+
198
+ .loading.htmx-request {
199
+ display: block;
200
+ }
201
+
202
+ .spinner {
203
+ display: inline-block;
204
+ width: 40px;
205
+ height: 40px;
206
+ border: 4px solid var(--gray-200);
207
+ border-top: 4px solid var(--primary);
208
+ border-radius: 50%;
209
+ animation: spin 1s linear infinite;
210
+ margin-bottom: 1rem;
211
+ }
212
+
213
+ @keyframes spin {
214
+ 0% { transform: rotate(0deg); }
215
+ 100% { transform: rotate(360deg); }
216
+ }
217
+
218
+ .feature-grid {
219
+ display: grid;
220
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
221
+ gap: 1.5rem;
222
+ margin-top: 3rem;
223
+ }
224
+
225
+ .feature {
226
+ text-align: center;
227
+ color: white;
228
+ padding: 1.5rem;
229
+ background: rgba(255, 255, 255, 0.1);
230
+ border-radius: 16px;
231
+ backdrop-filter: blur(10px);
232
+ border: 1px solid rgba(255, 255, 255, 0.2);
233
+ }
234
+
235
+ .feature-icon {
236
+ font-size: 2.5rem;
237
+ margin-bottom: 1rem;
238
+ display: block;
239
+ }
240
+
241
+ .feature h3 {
242
+ margin: 0 0 0.5rem;
243
+ font-weight: 600;
244
+ }
245
+
246
+ .feature p {
247
+ margin: 0;
248
+ opacity: 0.9;
249
+ font-size: 0.9rem;
250
+ }
251
+
252
+ @media (max-width: 768px) {
253
+ .container { padding: 1rem; }
254
+ .hero h1 { font-size: 2.5rem; }
255
+ .main-card { padding: 1.5rem; }
256
+ .submit-btn { padding: 0.875rem 2rem; }
257
+ }
258
+ </link>
259
  </head>
260
  <body>
261
+ <div class="container">
262
+ <div class="hero">
263
+ <h1>πŸ” AI Fact-Checker</h1>
264
+ <p>Verify claims instantly with AI-powered research and analysis</p>
265
+ </div>
266
 
267
+ <div class="main-card">
268
+ <form class="form-wrapper" hx-post="/ui/check" hx-target="#result" hx-swap="innerHTML">
269
+ <div class="input-group">
270
+ <label for="claim" class="input-label">πŸ’¬ Enter your claim to fact-check</label>
271
+ <textarea
272
+ id="claim"
273
+ name="claim"
274
+ class="claim-input"
275
+ placeholder="Type any claim you want to verify... e.g., 'The Earth orbits the Sun' or 'Climate change is caused by human activity'"
276
+ required
277
+ ></textarea>
278
+ </div>
279
+
280
+ <div class="submit-section">
281
+ <button type="submit" class="submit-btn">πŸš€ Fact-Check Now</button>
282
+ <p class="help-text">
283
+ Our AI will search multiple sources, analyze evidence, and provide you with a verdict plus a shareable social media post
284
+ </p>
285
+ </div>
286
+ </form>
287
+
288
+ <div class="examples">
289
+ <div class="example-card" onclick="fillExample('The Earth is flat and not spherical')">
290
+ <strong>🌍 Geography Claim</strong>
291
+ "The Earth is flat and not spherical"
292
+ </div>
293
+ <div class="example-card" onclick="fillExample('Vaccines cause autism in children')">
294
+ <strong>πŸ₯ Medical Claim</strong>
295
+ "Vaccines cause autism in children"
296
+ </div>
297
+ <div class="example-card" onclick="fillExample('Climate change is a natural phenomenon, not caused by humans')">
298
+ <strong>🌑️ Climate Claim</strong>
299
+ "Climate change is a natural phenomenon"
300
+ </div>
301
  </div>
302
+ </div>
303
+
304
+ <div class="feature-grid">
305
+ <div class="feature">
306
+ <span class="feature-icon">πŸ”</span>
307
+ <h3>Multi-Source Research</h3>
308
+ <p>Searches and analyzes multiple authoritative sources across the web</p>
309
+ </div>
310
+ <div class="feature">
311
+ <span class="feature-icon">🧠</span>
312
+ <h3>AI-Powered Analysis</h3>
313
+ <p>Uses advanced NLP models for semantic understanding and inference</p>
314
+ </div>
315
+ <div class="feature">
316
+ <span class="feature-icon">πŸ“Š</span>
317
+ <h3>Confidence Scoring</h3>
318
+ <p>Provides confidence levels and detailed reasoning for each verdict</p>
319
+ </div>
320
+ <div class="feature">
321
+ <span class="feature-icon">πŸ“±</span>
322
+ <h3>Social Media Ready</h3>
323
+ <p>Generates shareable posts perfect for social media platforms</p>
324
+ </div>
325
+ </div>
326
+
327
+ <div class="result-container">
328
+ <div class="loading" id="loading">
329
+ <div class="spinner"></div>
330
+ <p>πŸ€– AI is researching your claim across multiple sources...</p>
331
+ <p style="font-size: 0.9rem; opacity: 0.7;">This may take 10-15 seconds</p>
332
+ </div>
333
+ <div id="result"></div>
334
+ </div>
335
  </div>
336
 
337
+ <script>
338
+ function fillExample(text) {
339
+ document.getElementById('claim').value = text;
340
+ document.getElementById('claim').focus();
341
+ }
342
+
343
+ // Show loading state
344
+ document.body.addEventListener('htmx:beforeRequest', function() {
345
+ document.getElementById('loading').style.display = 'block';
346
+ document.getElementById('result').innerHTML = '';
347
+ });
348
+
349
+ document.body.addEventListener('htmx:afterRequest', function() {
350
+ document.getElementById('loading').style.display = 'none';
351
+ });
352
+ </script>
353
  </body>
354
  </html>
app/web/templates/result.html CHANGED
@@ -2,65 +2,377 @@
2
  <html lang="en">
3
  <head>
4
  <meta charset="utf-8">
5
- <title>{{ r.verdict }} β€’ Fact-Check</title>
6
  <meta name="viewport" content="width=device-width, initial-scale=1" />
 
7
  <style>
8
- :root { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; color-scheme: light dark; }
9
- body { margin: 0; padding: 24px; max-width: 900px; }
10
- .badge { display:inline-block; padding:4px 10px; border-radius:999px; font-weight:600; font-size:14px; }
11
- .True { background:#e6ffed; color:#05620d; border:1px solid #b1f0b8; }
12
- .False { background:#ffe8e6; color:#7b1010; border:1px solid #f3b3ad; }
13
- .Misleading { background:#fff6e0; color:#6a4b00; border:1px solid #f3d49a; }
14
- .Unverified { background:#eef2f7; color:#3b4252; border:1px solid #d0d7e2; }
15
- .card { border:1px solid #e5e7eb; border-radius:12px; padding:16px; margin:12px 0; }
16
- .muted { color:#6b7280; font-size:14px; }
17
- .sources li{ margin: 6px 0; }
18
- code, pre { background:#f6f8fa; padding:8px; border-radius:8px; display:block; overflow:auto; }
19
- button { padding:8px 12px; border-radius:8px; border:1px solid #d1d5db; cursor:pointer; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  </style>
21
  </head>
22
  <body>
23
- <h1 class="muted">Automated Fact-Check</h1>
24
- <div class="card">
25
- <div><span class="badge {{ r.verdict }}">{{ r.verdict }}</span></div>
26
- <h2 style="margin:10px 0 6px 0;">Claim</h2>
27
- <p>{{ r.claim }}</p>
28
- <h3 style="margin:10px 0 6px 0;">Rationale</h3>
29
- <p>{{ r.rationale }}</p>
30
- <div class="muted">Confidence: {{ '%.2f' % r.confidence }}</div>
31
- </div>
32
 
33
- <div class="card">
34
- <h3 style="margin:0 0 6px 0;">Ready-to-post</h3>
35
- <pre id="post">{{ r.post }}</pre>
36
- <button onclick="copyPost()">Copy</button>
37
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
- <div class="card">
40
- <h3 style="margin:0 0 6px 0;">Sources</h3>
41
- <ol class="sources">
42
- {% for s in r.sources %}
43
- <li>
44
- <a href="{{ s.url }}" target="_blank" rel="noopener">{{ s.title }}</a>
45
- {% if s.snippet %}<div class="muted">{{ s.snippet }}</div>{% endif %}
46
- {% if s.evidence and s.evidence|length > 0 %}
47
- <details><summary>evidence</summary>
48
- <ul>
49
- {% for ev in s.evidence %}
50
- <li>{{ ev }}</li>
51
- {% endfor %}
52
- </ul>
53
- </details>
54
- {% endif %}
55
- </li>
56
- {% endfor %}
57
- </ol>
58
  </div>
59
 
60
  <script>
61
  function copyPost() {
62
- const txt = document.getElementById('post').innerText;
63
- navigator.clipboard.writeText(txt);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  }
65
  </script>
66
  </body>
 
2
  <html lang="en">
3
  <head>
4
  <meta charset="utf-8">
5
+ <title>{{ r.verdict }} β€’ AI Fact-Check Results</title>
6
  <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
8
  <style>
9
+ :root {
10
+ --primary: #3b82f6;
11
+ --primary-dark: #2563eb;
12
+ --secondary: #10b981;
13
+ --danger: #ef4444;
14
+ --warning: #f59e0b;
15
+ --gray-50: #f9fafb;
16
+ --gray-100: #f3f4f6;
17
+ --gray-200: #e5e7eb;
18
+ --gray-300: #d1d5db;
19
+ --gray-600: #4b5563;
20
+ --gray-700: #374151;
21
+ --gray-900: #111827;
22
+ font-family: 'Inter', system-ui, -apple-system, sans-serif;
23
+ }
24
+
25
+ * { box-sizing: border-box; }
26
+
27
+ body {
28
+ margin: 0;
29
+ padding: 0;
30
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
31
+ min-height: 100vh;
32
+ color: var(--gray-900);
33
+ }
34
+
35
+ .container {
36
+ max-width: 1000px;
37
+ margin: 0 auto;
38
+ padding: 2rem;
39
+ }
40
+
41
+ .header {
42
+ text-align: center;
43
+ margin-bottom: 2rem;
44
+ color: white;
45
+ }
46
+
47
+ .header h1 {
48
+ font-size: 2.5rem;
49
+ font-weight: 700;
50
+ margin: 0 0 0.5rem;
51
+ background: linear-gradient(45deg, #fff, #f0f9ff);
52
+ -webkit-background-clip: text;
53
+ -webkit-text-fill-color: transparent;
54
+ background-clip: text;
55
+ }
56
+
57
+ .header p {
58
+ font-size: 1.1rem;
59
+ opacity: 0.9;
60
+ margin: 0;
61
+ }
62
+
63
+ .main-card {
64
+ background: white;
65
+ border-radius: 24px;
66
+ padding: 2.5rem;
67
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
68
+ border: 1px solid rgba(255, 255, 255, 0.1);
69
+ backdrop-filter: blur(10px);
70
+ }
71
+
72
+ .verdict-section {
73
+ text-align: center;
74
+ margin-bottom: 2rem;
75
+ padding: 2rem;
76
+ border-radius: 20px;
77
+ background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
78
+ }
79
+
80
+ .verdict-badge {
81
+ display: inline-block;
82
+ padding: 1rem 2.5rem;
83
+ border-radius: 50px;
84
+ font-weight: 700;
85
+ font-size: 1.5rem;
86
+ text-transform: uppercase;
87
+ letter-spacing: 0.5px;
88
+ margin-bottom: 1rem;
89
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
90
+ }
91
+
92
+ .True {
93
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%);
94
+ color: white;
95
+ }
96
+
97
+ .False {
98
+ background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
99
+ color: white;
100
+ }
101
+
102
+ .Misleading {
103
+ background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
104
+ color: white;
105
+ }
106
+
107
+ .Unverified {
108
+ background: linear-gradient(135deg, #6b7280 0%, #4b5563 100%);
109
+ color: white;
110
+ }
111
+
112
+ .confidence-display {
113
+ font-size: 2rem;
114
+ font-weight: 700;
115
+ color: var(--gray-700);
116
+ margin: 1rem 0;
117
+ }
118
+
119
+ .section {
120
+ margin: 2.5rem 0;
121
+ }
122
+
123
+ .section-title {
124
+ display: flex;
125
+ align-items: center;
126
+ gap: 0.75rem;
127
+ font-size: 1.5rem;
128
+ font-weight: 600;
129
+ color: #374151;
130
+ margin-bottom: 1.5rem;
131
+ padding-bottom: 0.75rem;
132
+ border-bottom: 3px solid #e5e7eb;
133
+ }
134
+
135
+ .claim-text {
136
+ background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
137
+ border-left: 6px solid var(--primary);
138
+ padding: 2rem;
139
+ border-radius: 0 16px 16px 0;
140
+ font-size: 1.2rem;
141
+ line-height: 1.7;
142
+ font-style: italic;
143
+ margin-bottom: 1.5rem;
144
+ box-shadow: 0 4px 15px rgba(59, 130, 246, 0.1);
145
+ }
146
+
147
+ .rationale-text {
148
+ background: #f8fafc;
149
+ padding: 2rem;
150
+ border-radius: 16px;
151
+ line-height: 1.8;
152
+ color: #374151;
153
+ border: 1px solid #e2e8f0;
154
+ font-size: 1.05rem;
155
+ }
156
+
157
+ .post-container {
158
+ background: linear-gradient(135deg, #eef2ff 0%, #e0e7ff 100%);
159
+ border: 3px dashed var(--primary);
160
+ border-radius: 20px;
161
+ padding: 2rem;
162
+ position: relative;
163
+ }
164
+
165
+ .post-text {
166
+ font-family: system-ui, sans-serif;
167
+ font-size: 1.1rem;
168
+ line-height: 1.7;
169
+ color: #1f2937;
170
+ margin: 0;
171
+ white-space: pre-wrap;
172
+ background: white;
173
+ padding: 1.5rem;
174
+ border-radius: 12px;
175
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
176
+ }
177
+
178
+ .copy-button {
179
+ background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
180
+ color: white;
181
+ border: none;
182
+ padding: 1rem 2rem;
183
+ border-radius: 30px;
184
+ font-weight: 600;
185
+ cursor: pointer;
186
+ margin-top: 1.5rem;
187
+ transition: all 0.3s ease;
188
+ box-shadow: 0 6px 20px rgba(59, 130, 246, 0.3);
189
+ font-size: 1rem;
190
+ }
191
+
192
+ .copy-button:hover {
193
+ transform: translateY(-3px);
194
+ box-shadow: 0 8px 25px rgba(59, 130, 246, 0.4);
195
+ }
196
+
197
+ .sources-grid {
198
+ display: grid;
199
+ gap: 1.5rem;
200
+ }
201
+
202
+ .source-card {
203
+ background: white;
204
+ border: 1px solid #e5e7eb;
205
+ border-radius: 16px;
206
+ padding: 1.5rem;
207
+ transition: all 0.3s ease;
208
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
209
+ }
210
+
211
+ .source-card:hover {
212
+ transform: translateY(-3px);
213
+ box-shadow: 0 12px 30px rgba(0, 0, 0, 0.15);
214
+ border-color: var(--primary);
215
+ }
216
+
217
+ .source-title {
218
+ font-weight: 600;
219
+ color: #1f2937;
220
+ text-decoration: none;
221
+ font-size: 1.1rem;
222
+ line-height: 1.5;
223
+ display: block;
224
+ margin-bottom: 0.75rem;
225
+ }
226
+
227
+ .source-title:hover {
228
+ color: var(--primary);
229
+ }
230
+
231
+ .source-snippet {
232
+ color: #6b7280;
233
+ font-size: 0.95rem;
234
+ line-height: 1.6;
235
+ margin-top: 0.75rem;
236
+ }
237
+
238
+ .source-url {
239
+ color: var(--primary);
240
+ font-size: 0.85rem;
241
+ margin-top: 0.75rem;
242
+ word-break: break-all;
243
+ background: #f8fafc;
244
+ padding: 0.5rem;
245
+ border-radius: 6px;
246
+ }
247
+
248
+ .footer {
249
+ text-align: center;
250
+ margin-top: 3rem;
251
+ padding: 2rem;
252
+ background: rgba(255, 255, 255, 0.1);
253
+ border-radius: 20px;
254
+ backdrop-filter: blur(10px);
255
+ border: 1px solid rgba(255, 255, 255, 0.2);
256
+ color: white;
257
+ }
258
+
259
+ .footer a {
260
+ color: white;
261
+ text-decoration: none;
262
+ font-weight: 500;
263
+ background: rgba(255, 255, 255, 0.2);
264
+ padding: 0.5rem 1rem;
265
+ border-radius: 25px;
266
+ transition: all 0.3s ease;
267
+ }
268
+
269
+ .footer a:hover {
270
+ background: rgba(255, 255, 255, 0.3);
271
+ transform: translateY(-2px);
272
+ }
273
+
274
+ @media (max-width: 768px) {
275
+ .container { padding: 1rem; }
276
+ .main-card { padding: 1.5rem; }
277
+ .verdict-badge { font-size: 1.2rem; padding: 0.75rem 1.5rem; }
278
+ .section-title { font-size: 1.25rem; }
279
+ }
280
  </style>
281
  </head>
282
  <body>
283
+ <div class="container">
284
+ <div class="header">
285
+ <h1>πŸ” Fact-Check Results</h1>
286
+ <p>AI-powered claim verification and analysis</p>
287
+ </div>
 
 
 
 
288
 
289
+ <div class="main-card">
290
+ <div class="verdict-section">
291
+ <div class="verdict-badge {{ r.verdict }}">
292
+ {% if r.verdict == 'True' %}βœ… Verified True
293
+ {% elif r.verdict == 'False' %}❌ Verified False
294
+ {% elif r.verdict == 'Misleading' %}⚠️ Misleading
295
+ {% else %}❓ Unverified
296
+ {% endif %}
297
+ </div>
298
+ <div class="confidence-display">{{ '%.0f' % (r.confidence * 100) }}% Confidence</div>
299
+ </div>
300
+
301
+ <div class="section">
302
+ <h2 class="section-title">
303
+ <span>πŸ’¬</span>
304
+ Claim Verified
305
+ </h2>
306
+ <div class="claim-text">{{ r.claim }}</div>
307
+ </div>
308
+
309
+ <div class="section">
310
+ <h2 class="section-title">
311
+ <span>🧠</span>
312
+ AI Analysis & Reasoning
313
+ </h2>
314
+ <div class="rationale-text">{{ r.rationale }}</div>
315
+ </div>
316
+
317
+ <div class="section">
318
+ <h2 class="section-title">
319
+ <span>πŸ“±</span>
320
+ Ready-to-Share Social Media Post
321
+ </h2>
322
+ <div class="post-container">
323
+ <div class="post-text" id="social-post">{{ r.post }}</div>
324
+ <button class="copy-button" onclick="copyPost()">
325
+ πŸ“‹ Copy to Clipboard
326
+ </button>
327
+ </div>
328
+ </div>
329
+
330
+ <div class="section">
331
+ <h2 class="section-title">
332
+ <span>πŸ”</span>
333
+ Sources Analyzed ({{ r.sources|length }})
334
+ </h2>
335
+ <div class="sources-grid">
336
+ {% for s in r.sources %}
337
+ <div class="source-card">
338
+ <a href="{{ s.url }}" target="_blank" rel="noopener" class="source-title">
339
+ {{ s.title }}
340
+ </a>
341
+ <div class="source-url">{{ s.url }}</div>
342
+ {% if s.snippet %}
343
+ <div class="source-snippet">{{ s.snippet }}</div>
344
+ {% endif %}
345
+ </div>
346
+ {% endfor %}
347
+ </div>
348
+ </div>
349
+ </div>
350
 
351
+ <div class="footer">
352
+ <p style="margin: 0 0 1rem;">Want to fact-check another claim?</p>
353
+ <a href="/">πŸš€ Try Another Fact-Check</a>
354
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
  </div>
356
 
357
  <script>
358
  function copyPost() {
359
+ const element = document.getElementById('social-post');
360
+ const text = element.innerText;
361
+
362
+ navigator.clipboard.writeText(text).then(function() {
363
+ const button = event.target;
364
+ const originalText = button.innerHTML;
365
+ button.innerHTML = 'βœ… Copied!';
366
+ button.style.background = 'linear-gradient(135deg, #10b981 0%, #059669 100%)';
367
+
368
+ setTimeout(() => {
369
+ button.innerHTML = originalText;
370
+ button.style.background = 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)';
371
+ }, 2000);
372
+ }).catch(function(err) {
373
+ console.error('Failed to copy text: ', err);
374
+ alert('Failed to copy to clipboard');
375
+ });
376
  }
377
  </script>
378
  </body>