GopalKrushnaMahapatra commited on
Commit
3e7ccee
·
verified ·
1 Parent(s): 1c37992

Update ai-check.html

Browse files
Files changed (1) hide show
  1. ai-check.html +692 -692
ai-check.html CHANGED
@@ -1,692 +1,692 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <title>AI Content Check – TrueWrite Scan</title>
6
- <meta name="viewport" content="width=device-width, initial-scale=1" />
7
- <script src="https://cdn.tailwindcss.com"></script>
8
-
9
- <!-- jsPDF for report generation (no integrity) -->
10
- <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
11
- </head>
12
- <body class="bg-gradient-to-br from-slate-950 via-slate-900 to-violet-950 text-white min-h-screen flex flex-col">
13
-
14
- <!-- Glow background -->
15
- <div class="pointer-events-none fixed inset-0 opacity-40 blur-3xl"
16
- style="background: radial-gradient(circle at 10% 20%, rgba(174, 101, 241, 0.35), transparent 55%), radial-gradient(circle at 80% 80%, rgba(56,189,248,0.28), transparent 55%);">
17
- </div>
18
-
19
- <main class="relative flex-1 max-w-6xl mx-auto px-4 py-8 z-10">
20
- <!-- Top bar -->
21
- <div class="flex items-center justify-between mb-4">
22
- <div class="flex items-center gap-3">
23
- <div>
24
- <img src="logo.png" alt="TrueWrite Scan Logo" class="w-10 h-10 rounded-xl shadow-lg shadow-indigo-500/40">
25
- </div>
26
- <div>
27
- <h1 class="text-2xl md:text-3xl font-extrabold tracking-tight">
28
- TrueWrite <span class="text-fuchsia-400">Scan</span>
29
- </h1>
30
- <p class="text-[11px] text-slate-300 uppercase tracking-[0.25em]">
31
- AI-Content Detector
32
- </p>
33
- </div>
34
- </div>
35
- <a href="dashboard.html"
36
- class="text-xs text-slate-200 px-3 py-1.5 rounded-full border border-slate-600/70 bg-slate-900/40 backdrop-blur hover:bg-slate-800/70 transition">
37
- ← Back to dashboard
38
- </a>
39
- </div>
40
-
41
- <!-- Tool navigation -->
42
- <nav class="mb-6">
43
- <div class="inline-flex items-center gap-1 rounded-full bg-slate-900/80 border border-slate-800 p-1 text-xs">
44
- <a href="grammar.html"
45
- class="px-3 py-1.5 rounded-full text-slate-300 hover:bg-slate-800">
46
- Grammar
47
- </a>
48
- <a href="plagiarism.html"
49
- class="px-3 py-1.5 rounded-full text-slate-300 hover:bg-slate-800">
50
- Plagiarism
51
- </a>
52
- <a href="ai-check.html"
53
- class="px-3 py-1.5 rounded-full bg-fuchsia-500 text-white font-medium shadow shadow-fuchsia-500/40"
54
- aria-current="page">
55
- AI Content
56
- </a>
57
- </div>
58
- </nav>
59
-
60
- <!-- Glass main card -->
61
- <div class="grid md:grid-cols-2 gap-6">
62
- <!-- LEFT PANEL -->
63
- <section class="rounded-3xl border border-white/10 bg-slate-900/40 backdrop-blur-xl shadow-2xl shadow-black/50 p-5 md:p-6 flex flex-col">
64
- <header class="mb-4">
65
- <h2 class="text-lg md:text-xl font-semibold">Analyze your content</h2>
66
- <p class="text-xs text-slate-300 mt-1">
67
- Paste text or drop a <span class="font-semibold">.txt / .pdf / .docx</span> file. We’ll estimate how AI-generated it looks.
68
- </p>
69
- </header>
70
-
71
- <!-- Drag & Drop zone -->
72
- <div id="dropZone"
73
- class="mb-3 border-2 border-dashed border-slate-600/80 rounded-2xl bg-slate-900/50 backdrop-blur-md px-4 py-3 text-xs flex items-center justify-between transition hover:border-fuchsia-400/80 hover:bg-slate-900/70">
74
- <div class="flex items-center gap-3">
75
- <div class="w-8 h-8 rounded-full bg-slate-800/80 flex items-center justify-center">
76
- <span class="text-lg">📂</span>
77
- </div>
78
- <div>
79
- <p class="font-medium text-slate-100">Drag & drop your file here</p>
80
- <p class="text-[11px] text-slate-400">
81
- Supported: .txt, .pdf, .docx (max 15MB)
82
- </p>
83
- </div>
84
- </div>
85
- <div class="flex flex-col items-end gap-1">
86
- <label class="px-3 py-1 rounded-full border border-slate-500/80 text-[11px] cursor-pointer bg-slate-800/60 hover:bg-slate-700/80">
87
- Browse
88
- <input id="fileInput" type="file" accept=".txt,.pdf,.docx" class="hidden" />
89
- </label>
90
- <span id="fileName" class="text-[10px] text-slate-400 max-w-[150px] truncate"></span>
91
- </div>
92
- </div>
93
-
94
- <!-- Tiny status -->
95
- <div id="statusTiny" class="text-[11px] text-slate-400 mb-2">
96
- Ready · No file selected
97
- </div>
98
-
99
- <!-- Paste / textarea -->
100
- <div class="flex gap-2 items-center mb-2">
101
- <button id="pasteBtn"
102
- class="text-[11px] px-3 py-1 rounded-full border border-slate-600/80 bg-slate-900/70 hover:bg-slate-800/90">
103
- Paste text from clipboard
104
- </button>
105
- <span class="text-[11px] text-slate-500">or type directly below</span>
106
- </div>
107
-
108
- <textarea id="inputText" rows="12"
109
- class="flex-1 w-full rounded-2xl bg-slate-950/70 border border-slate-700/80 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-fuchsia-500/70 focus:border-fuchsia-500/60 placeholder:text-slate-500"
110
- placeholder="Paste your content here or use the drag-and-drop box above..."></textarea>
111
-
112
- <!-- Buttons -->
113
- <div class="mt-4 flex flex-wrap gap-2 items-center">
114
- <button id="checkAI"
115
- class="px-5 py-2 rounded-xl bg-gradient-to-r from-fuchsia-500 via-violet-500 to-sky-400 hover:brightness-110 text-sm font-semibold shadow-lg shadow-fuchsia-500/40 flex items-center gap-2">
116
- <svg id="spinner"
117
- class="hidden animate-spin h-4 w-4 text-white"
118
- xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
119
- <circle class="opacity-25" cx="12" cy="12" r="10"
120
- stroke="currentColor" stroke-width="4"></circle>
121
- <path class="opacity-75" fill="currentColor"
122
- d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"></path>
123
- </svg>
124
- <span id="checkLabel">Check AI Content</span>
125
- </button>
126
-
127
- <button id="downloadReport"
128
- class="px-5 py-2 rounded-xl bg-slate-900/70 border border-slate-600/80 text-sm font-medium hover:bg-slate-800/90">
129
- Download report as PDF
130
- </button>
131
- </div>
132
- </section>
133
-
134
- <!-- RIGHT PANEL -->
135
- <section class="rounded-3xl border border-white/10 bg-slate-900/40 backdrop-blur-xl shadow-2xl shadow-black/50 p-5 md:p-6 flex flex-col">
136
- <header class="mb-3">
137
- <h2 class="text-lg font-semibold">Detection result</h2>
138
- <p class="text-[11px] text-slate-400">
139
- Scores are estimates – always review with your own judgment.
140
- </p>
141
- </header>
142
-
143
- <!-- Progress bar -->
144
- <div class="mb-3">
145
- <div class="h-1.5 w-full rounded-full bg-slate-800/80 overflow-hidden">
146
- <div id="progressBar" class="h-full w-0 bg-gradient-to-r from-fuchsia-400 via-violet-400 to-sky-400 transition-[width] duration-200 ease-out"></div>
147
- </div>
148
- <div id="progressText" class="text-[11px] text-slate-400 mt-1">Idle</div>
149
- </div>
150
-
151
- <!-- Main result -->
152
- <div class="flex-1 rounded-2xl bg-slate-950/60 border border-slate-800/80 p-4 text-sm">
153
- <p id="score" class="text-xl font-bold mb-1 text-fuchsia-300">
154
- No scan yet.
155
- </p>
156
- <p id="detail" class="text-[13px] text-slate-200 mb-3">
157
- Paste text or upload a file, then click “Check AI Content”.
158
- </p>
159
-
160
- <div id="meta" class="text-[11px] text-slate-400 space-y-1">
161
- <!-- Filled by JS -->
162
- </div>
163
- </div>
164
-
165
- <!-- Mini legend -->
166
- <div class="mt-4 grid grid-cols-2 gap-3 text-[11px] text-slate-300">
167
- <div class="bg-slate-950/60 border border-slate-800/80 rounded-xl p-3">
168
- <p class="font-semibold mb-1">Model-based detection</p>
169
- <p>Uses a RoBERTa classifier when available. Falls back to heuristic if model load fails.</p>
170
- </div>
171
- <div class="bg-slate-950/60 border border-slate-800/80 rounded-xl p-3">
172
- <p class="font-semibold mb-1">Saved in history</p>
173
- <p>Every scan is logged in your <span class="font-semibold">My History</span> for future reference.</p>
174
- </div>
175
- </div>
176
- </section>
177
- </div>
178
-
179
- <!-- Extra sections from second file -->
180
- <section class="mt-10 space-y-4">
181
- <h2 class="text-xl md:text-2xl font-semibold">Why choose this AI content checker?</h2>
182
- <p class="text-sm text-slate-300">
183
- With AI writing everywhere, this page helps you quickly see how “machine-like” your text may look.
184
- </p>
185
-
186
- <div class="grid md:grid-cols-3 gap-5 mt-3">
187
- <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4 text-xs">
188
- <div class="flex items-center gap-3 mb-2">
189
- <div class="w-9 h-9 rounded-full bg-fuchsia-500/20 flex items-center justify-center">
190
- <span class="text-lg">📈</span>
191
- </div>
192
- <p class="font-semibold text-sm">Instant AI probability</p>
193
- </div>
194
- <p>
195
- Get an estimated AI-generated probability (%) along with the opposite human-written score.
196
- </p>
197
- </div>
198
-
199
- <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4 text-xs">
200
- <div class="flex items-center gap-3 mb-2">
201
- <div class="w-9 h-9 rounded-full bg-indigo-500/20 flex items-center justify-center">
202
- <span class="text-lg">🔍</span>
203
- </div>
204
- <p class="font-semibold text-sm">Transparent heuristics</p>
205
- </div>
206
- <p>
207
- Uses simple, explainable metrics like sentence length and vocabulary variety — great for demos.
208
- </p>
209
- </div>
210
-
211
- <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4 text-xs">
212
- <div class="flex items-center gap-3 mb-2">
213
- <div class="w-9 h-9 rounded-full bg-emerald-500/20 flex items-center justify-center">
214
- <span class="text-lg">📄</span>
215
- </div>
216
- <p class="font-semibold text-sm">Downloadable PDF</p>
217
- </div>
218
- <p>
219
- Save the AI-analysis result as a PDF snapshot along with your text.
220
- </p>
221
- </div>
222
- </div>
223
- </section>
224
-
225
- <section class="mt-10 space-y-4">
226
- <h2 class="text-xl md:text-2xl font-semibold">Who can use this tool?</h2>
227
- <p class="text-sm text-slate-300">
228
- Ideal for anyone curious about AI detection or building their own detector interface.
229
- </p>
230
-
231
- <div class="grid md:grid-cols-3 gap-5 mt-3 text-xs">
232
- <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
233
- <div class="flex items-center gap-3 mb-2">
234
- <div class="w-9 h-9 rounded-full bg-sky-500/20 flex items-center justify-center">
235
- <span class="text-lg">🎓</span>
236
- </div>
237
- <p class="font-semibold text-sm">Students & researchers</p>
238
- </div>
239
- <p>
240
- Test your assignments or papers and see how “AI-like” they appear based on simple rules.
241
- </p>
242
- </div>
243
-
244
- <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
245
- <div class="flex items-center gap-3 mb-2">
246
- <div class="w-9 h-9 rounded-full bg-amber-500/20 flex items-center justify-center">
247
- <span class="text-lg">🧑‍🏫</span>
248
- </div>
249
- <p class="font-semibold text-sm">Teachers & trainers</p>
250
- </div>
251
- <p>
252
- Show learners how AI detection can be approximated using simple statistics on text.
253
- </p>
254
- </div>
255
-
256
- <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
257
- <div class="flex items-center gap-3 mb-2">
258
- <div class="w-9 h-9 rounded-full bg-rose-500/20 flex items-center justify-center">
259
- <span class="text-lg">💻</span>
260
- </div>
261
- <p class="font-semibold text-sm">Developers & hobbyists</p>
262
- </div>
263
- <p>
264
- Use this as a frontend template and plug in your own backend AI-detection models later.
265
- </p>
266
- </div>
267
- </div>
268
- </section>
269
-
270
- <section class="mt-10 space-y-4">
271
- <h2 class="text-xl md:text-2xl font-semibold">How does this AI content checker work?</h2>
272
- <p class="text-sm text-slate-300">
273
- The scoring is intentionally simple, so you can read the JavaScript and fully understand it.
274
- </p>
275
-
276
- <div class="grid md:grid-cols-3 gap-5 mt-3 text-xs">
277
- <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
278
- <div class="flex items-center gap-3 mb-2">
279
- <div class="w-9 h-9 rounded-full bg-fuchsia-500/20 flex items-center justify-center">
280
- <span class="text-lg">1️⃣</span>
281
- </div>
282
- <p class="font-semibold text-sm">Tokenise the text</p>
283
- </div>
284
- <p>
285
- Text is lower-cased and split into words and sentences for basic statistics.
286
- </p>
287
- </div>
288
-
289
- <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
290
- <div class="flex items-center gap-3 mb-2">
291
- <div class="w-9 h-9 rounded-full bg-indigo-500/20 flex items-center justify-center">
292
- <span class="text-lg">2️⃣</span>
293
- </div>
294
- <p class="font-semibold text-sm">Measure patterns</p>
295
- </div>
296
- <p>
297
- It looks at vocabulary variety and average sentence length — long, repetitive text can
298
- look more AI-like.
299
- </p>
300
- </div>
301
-
302
- <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
303
- <div class="flex items-center gap-3 mb-2">
304
- <div class="w-9 h-9 rounded-full bg-emerald-500/20 flex items-center justify-center">
305
- <span class="text-lg">3️⃣</span>
306
- </div>
307
- <p class="font-semibold text-sm">Compute a score</p>
308
- </div>
309
- <p>
310
- Those metrics are combined into an AI-generated probability (0–100%), displayed and
311
- saved in your PDF report.
312
- </p>
313
- </div>
314
- </div>
315
- </section>
316
-
317
- <section class="mt-12">
318
- <h2 class="text-xl md:text-2xl font-semibold mb-3">Reviews for this AI checker</h2>
319
- <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-5 md:p-6 relative overflow-hidden">
320
- <div id="reviewCard" class="transition-all duration-500"></div>
321
-
322
- <div class="flex items-center justify-between mt-4">
323
- <div id="reviewDots" class="flex gap-1.5"></div>
324
- <div class="flex gap-2">
325
- <button id="prevReview"
326
- class="w-8 h-8 rounded-full border border-slate-600 flex items-center justify-center text-xs hover:bg-slate-800">‹</button>
327
- <button id="nextReview"
328
- class="w-8 h-8 rounded-full border border-slate-600 flex items-center justify-center text-xs hover:bg-slate-800">›</button>
329
- </div>
330
- </div>
331
- </div>
332
- </section>
333
- </main>
334
-
335
- <!-- Footer -->
336
- <footer class="relative border-t border-slate-800 bg-slate-950/80 backdrop-blur">
337
- <div class="max-w-6xl mx-auto px-4 py-4 text-[11px] text-slate-400 flex flex-col md:flex-row items-center justify-between gap-2">
338
- <p>© 2025 TrueWrite Scan. All rights reserved.</p>
339
- <p>Designed & developed by <span class="text-fuchsia-300 font-medium">Gopal Krushna Mahapatra</span>.</p>
340
- </div>
341
- </footer>
342
-
343
- <!-- SCRIPT -->
344
- <script>
345
- const BACKEND_URL = "http://localhost:8000";
346
- const token = localStorage.getItem("truewriteToken");
347
- const user = localStorage.getItem("truewriteUser");
348
- if (!token || !user) {
349
- window.location.href = "login.html";
350
- }
351
-
352
- // jsPDF helper
353
- function getJsPDF() {
354
- if (window.jspdf && window.jspdf.jsPDF) return window.jspdf.jsPDF;
355
- if (window.jsPDF) return window.jsPDF;
356
- alert("PDF library (jsPDF) did not load. Check your internet connection or CDN access.");
357
- return null;
358
- }
359
-
360
- // DOM references
361
- const textarea = document.getElementById("inputText");
362
- const fileInput = document.getElementById("fileInput");
363
- const fileNameSpan = document.getElementById("fileName");
364
- const pasteBtn = document.getElementById("pasteBtn");
365
- const statusTiny = document.getElementById("statusTiny");
366
- const checkBtn = document.getElementById("checkAI");
367
- const checkLabel = document.getElementById("checkLabel");
368
- const spinner = document.getElementById("spinner");
369
- const scoreP = document.getElementById("score");
370
- const detailP = document.getElementById("detail");
371
- const metaDiv = document.getElementById("meta");
372
- const downloadBtn = document.getElementById("downloadReport");
373
- const dropZone = document.getElementById("dropZone");
374
- const progressBar = document.getElementById("progressBar");
375
- const progressText = document.getElementById("progressText");
376
-
377
- let lastResult = null;
378
- let progressTimer = null;
379
-
380
- function setLoading(on) {
381
- if (on) {
382
- spinner.classList.remove("hidden");
383
- checkLabel.textContent = "Analyzing...";
384
- checkBtn.disabled = true;
385
- checkBtn.classList.add("opacity-60", "cursor-not-allowed");
386
-
387
- let pct = 0;
388
- progressBar.style.width = "0%";
389
- progressText.textContent = "Uploading / processing...";
390
- progressTimer = setInterval(() => {
391
- pct += Math.random() * 10;
392
- if (pct > 90) pct = 90;
393
- progressBar.style.width = pct + "%";
394
- }, 200);
395
- } else {
396
- spinner.classList.add("hidden");
397
- checkLabel.textContent = "Check AI Content";
398
- checkBtn.disabled = false;
399
- checkBtn.classList.remove("opacity-60", "cursor-not-allowed");
400
- if (progressTimer) {
401
- clearInterval(progressTimer);
402
- progressTimer = null;
403
- }
404
- progressBar.style.width = "100%";
405
- progressText.textContent = "Done";
406
- setTimeout(() => {
407
- progressBar.style.width = "0%";
408
- progressText.textContent = "Idle";
409
- }, 700);
410
- }
411
- }
412
-
413
- // Drag & drop handlers
414
- ["dragenter", "dragover"].forEach(evt => {
415
- dropZone.addEventListener(evt, e => {
416
- e.preventDefault();
417
- e.stopPropagation();
418
- dropZone.classList.add("border-fuchsia-400/80", "bg-slate-900/80");
419
- });
420
- });
421
-
422
- ["dragleave", "drop"].forEach(evt => {
423
- dropZone.addEventListener(evt, e => {
424
- e.preventDefault();
425
- e.stopPropagation();
426
- dropZone.classList.remove("border-fuchsia-400/80", "bg-slate-900/80");
427
- });
428
- });
429
-
430
- dropZone.addEventListener("drop", e => {
431
- const dt = e.dataTransfer;
432
- const files = dt.files;
433
- if (!files || !files.length) return;
434
- const file = files[0];
435
- fileInput.files = files;
436
- handleFileSelected(file);
437
- });
438
-
439
- // File selection via button
440
- fileInput.addEventListener("change", e => {
441
- const file = e.target.files[0];
442
- if (!file) {
443
- fileNameSpan.textContent = "";
444
- statusTiny.textContent = "Ready · No file selected";
445
- return;
446
- }
447
- handleFileSelected(file);
448
- });
449
-
450
- function handleFileSelected(file) {
451
- fileNameSpan.textContent = file.name;
452
-
453
- if (file.type === "text/plain" || file.name.toLowerCase().endsWith(".txt")) {
454
- const reader = new FileReader();
455
- reader.onload = () => textarea.value = reader.result;
456
- reader.readAsText(file);
457
- statusTiny.textContent = `${file.name} loaded as text`;
458
- } else {
459
- textarea.value = "";
460
- statusTiny.textContent = `${file.name} selected. Will be parsed on backend.`;
461
- }
462
- }
463
-
464
- // Paste button
465
- pasteBtn.onclick = async () => {
466
- try {
467
- const txt = await navigator.clipboard.readText();
468
- textarea.value = txt;
469
- statusTiny.textContent = "Text pasted from clipboard";
470
- } catch {
471
- alert("Clipboard access blocked by browser.");
472
- }
473
- };
474
-
475
- async function callAiCheck(text) {
476
- const res = await fetch(`${BACKEND_URL}/api/ai-check`, {
477
- method: "POST",
478
- headers: {
479
- "Content-Type": "application/json",
480
- "Authorization": `Bearer ${token}`
481
- },
482
- body: JSON.stringify({ text })
483
- });
484
- const data = await res.json();
485
- if (!res.ok) throw new Error(data.detail || data.error || "Server error");
486
- return data;
487
- }
488
-
489
- async function uploadFileAndCheck(file) {
490
- const form = new FormData();
491
- form.append("file", file, file.name);
492
- const res = await fetch(`${BACKEND_URL}/api/ai-check-file`, {
493
- method: "POST",
494
- headers: { "Authorization": `Bearer ${token}` },
495
- body: form
496
- });
497
- const data = await res.json();
498
- if (!res.ok) throw new Error(data.detail || data.error || "Server error");
499
- return data;
500
- }
501
-
502
- // Main check
503
- checkBtn.onclick = async () => {
504
- const text = textarea.value.trim();
505
- const file = fileInput.files[0];
506
-
507
- if (!text && !file) {
508
- alert("Please paste text or upload a file first.");
509
- return;
510
- }
511
-
512
- setLoading(true);
513
- scoreP.textContent = "Analyzing...";
514
- detailP.textContent = "Contacting backend...";
515
- metaDiv.innerHTML = "";
516
- lastResult = null;
517
-
518
- try {
519
- let data;
520
- if (file) data = await uploadFileAndCheck(file);
521
- else data = await callAiCheck(text);
522
-
523
- const ai = data.ai_percent ?? 0;
524
- const human = data.human_percent ?? (100 - ai);
525
- const wc = data.word_count ?? 0;
526
- const avg = data.avg_sentence_length ?? null;
527
- const summary = data.summary ?? "";
528
-
529
- scoreP.textContent = `AI probability: ${ai}% · Human: ${human}%`;
530
- detailP.textContent = summary || (ai > 60
531
- ? `Text appears more AI-like (human ~${human}%).`
532
- : `Text appears more human-like (human ~${human}%).`
533
- );
534
-
535
- metaDiv.innerHTML = `
536
- <div>Words: <strong>${wc}</strong></div>
537
- ${avg !== null ? `<div>Avg sentence length: <strong>${avg}</strong></div>` : ""}
538
- ${data.unique_ratio !== undefined ? `<div>Unique word ratio: <strong>${data.unique_ratio}</strong></div>` : ""}
539
- <div class="mt-1 text-[10px] text-slate-500">This scan is stored in your history.</div>
540
- `;
541
-
542
- lastResult = {
543
- ai_percent: ai,
544
- human_percent: human,
545
- word_count: wc,
546
- avg_sentence_length: avg,
547
- summary,
548
- text: textarea.value
549
- };
550
- } catch (err) {
551
- console.error(err);
552
- scoreP.textContent = "Error";
553
- detailP.textContent = err.message || "Failed to contact backend.";
554
- } finally {
555
- setLoading(false);
556
- }
557
- };
558
-
559
- // PDF download
560
- downloadBtn.onclick = () => {
561
- if (!lastResult) {
562
- alert("Run at least one scan before downloading a report.");
563
- return;
564
- }
565
-
566
- const jsPDF = getJsPDF();
567
- if (!jsPDF) return;
568
-
569
- const doc = new jsPDF({ unit: "pt", format: "a4" });
570
-
571
- let y = 40;
572
- doc.setFontSize(16);
573
- doc.text("TrueWrite Scan — AI Content Report", 40, y); y += 22;
574
-
575
- doc.setFontSize(11);
576
- doc.text("Generated: " + new Date().toLocaleString(), 40, y); y += 18;
577
- doc.text("User: " + (user || "N/A"), 40, y); y += 18;
578
-
579
- doc.setFontSize(12);
580
- doc.text(`AI probability: ${lastResult.ai_percent}%`, 40, y); y += 16;
581
- doc.text(`Human probability: ${lastResult.human_percent}%`, 40, y); y += 16;
582
- doc.text(`Word count: ${lastResult.word_count}`, 40, y); y += 18;
583
-
584
- if (lastResult.avg_sentence_length !== null && lastResult.avg_sentence_length !== undefined) {
585
- doc.text(`Avg sentence length: ${lastResult.avg_sentence_length}`, 40, y); y += 18;
586
- }
587
-
588
- if (lastResult.summary) {
589
- doc.text("Summary:", 40, y); y += 16;
590
- doc.setFontSize(10);
591
- const lines = doc.splitTextToSize(lastResult.summary, 520);
592
- doc.text(lines, 40, y);
593
- y += lines.length * 12 + 10;
594
- }
595
-
596
- doc.setFontSize(11);
597
- doc.text("--- Original text (truncated) ---", 40, y); y += 18;
598
- const original = lastResult.text || "";
599
- const truncated = original.length > 4000 ? original.slice(0, 4000) + "\n\n[TRUNCATED]" : original;
600
- const textLines = doc.splitTextToSize(truncated, 520);
601
- doc.setFontSize(10);
602
- doc.text(textLines, 40, y);
603
-
604
- doc.save("truewrite-ai-report.pdf");
605
- };
606
-
607
- // Reviews slider
608
- const reviews = [
609
- {
610
- name: "Aarav S.",
611
- role: "B.Tech Student",
612
- text: "Helps me understand how AI-generated my project reports might look before submission.",
613
- stars: 5
614
- },
615
- {
616
- name: "Priya K.",
617
- role: "AI Research Enthusiast",
618
- text: "The transparent scoring is perfect for explaining AI detection concepts to juniors.",
619
- stars: 5
620
- },
621
- {
622
- name: "Rahul M.",
623
- role: "Content Creator",
624
- text: "Gives a quick signal when my text feels too uniform and AI-like.",
625
- stars: 4
626
- },
627
- {
628
- name: "Sneha R.",
629
- role: "M.Sc. Student",
630
- text: "Very handy example for my seminar on AI ethics and content authenticity.",
631
- stars: 5
632
- },
633
- {
634
- name: "Vikram J.",
635
- role: "Developer",
636
- text: "Exactly the kind of simple, hackable AI-detector UI I wanted for my side project.",
637
- stars: 4
638
- }
639
- ];
640
-
641
- let currentReview = 0;
642
- const reviewCard = document.getElementById("reviewCard");
643
- const reviewDots = document.getElementById("reviewDots");
644
-
645
- function starRow(stars) {
646
- let html = "";
647
- for (let i = 0; i < 5; i++) {
648
- html += `<span class="${i < stars ? 'text-yellow-400' : 'text-slate-600'} text-sm">★</span>`;
649
- }
650
- return html;
651
- }
652
-
653
- function renderReview() {
654
- const r = reviews[currentReview];
655
- reviewCard.innerHTML = `
656
- <div class="flex items-center gap-2 mb-2">
657
- ${starRow(r.stars)}
658
- </div>
659
- <p class="text-sm text-slate-200 mb-3">"${r.text}"</p>
660
- <p class="text-sm font-semibold">${r.name}</p>
661
- <p class="text-xs text-slate-400">${r.role}</p>
662
- `;
663
- reviewDots.innerHTML = reviews.map((_, i) =>
664
- `<span class="w-2 h-2 rounded-full ${i === currentReview ? 'bg-fuchsia-400' : 'bg-slate-600'}"></span>`
665
- ).join('');
666
- }
667
-
668
- function nextReview() {
669
- currentReview = (currentReview + 1) % reviews.length;
670
- renderReview();
671
- }
672
-
673
- function prevReview() {
674
- currentReview = (currentReview - 1 + reviews.length) % reviews.length;
675
- renderReview();
676
- }
677
-
678
- document.getElementById('nextReview').onclick = nextReview;
679
- document.getElementById('prevReview').onclick = prevReview;
680
-
681
- let timer = setInterval(nextReview, 6000);
682
- document.getElementById('nextReview').addEventListener('click', () => {
683
- clearInterval(timer); timer = setInterval(nextReview, 6000);
684
- });
685
- document.getElementById('prevReview').addEventListener('click', () => {
686
- clearInterval(timer); timer = setInterval(nextReview, 6000);
687
- });
688
-
689
- renderReview();
690
- </script>
691
- </body>
692
- </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>AI Content Check – TrueWrite Scan</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+
9
+ <!-- jsPDF for report generation (no integrity) -->
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
11
+ </head>
12
+ <body class="bg-gradient-to-br from-slate-950 via-slate-900 to-violet-950 text-white min-h-screen flex flex-col">
13
+
14
+ <!-- Glow background -->
15
+ <div class="pointer-events-none fixed inset-0 opacity-40 blur-3xl"
16
+ style="background: radial-gradient(circle at 10% 20%, rgba(174, 101, 241, 0.35), transparent 55%), radial-gradient(circle at 80% 80%, rgba(56,189,248,0.28), transparent 55%);">
17
+ </div>
18
+
19
+ <main class="relative flex-1 max-w-6xl mx-auto px-4 py-8 z-10">
20
+ <!-- Top bar -->
21
+ <div class="flex items-center justify-between mb-4">
22
+ <div class="flex items-center gap-3">
23
+ <div>
24
+ <img src="logo.png" alt="TrueWrite Scan Logo" class="w-10 h-10 rounded-xl shadow-lg shadow-indigo-500/40">
25
+ </div>
26
+ <div>
27
+ <h1 class="text-2xl md:text-3xl font-extrabold tracking-tight">
28
+ TrueWrite <span class="text-fuchsia-400">Scan</span>
29
+ </h1>
30
+ <p class="text-[11px] text-slate-300 uppercase tracking-[0.25em]">
31
+ AI-Content Detector
32
+ </p>
33
+ </div>
34
+ </div>
35
+ <a href="dashboard.html"
36
+ class="text-xs text-slate-200 px-3 py-1.5 rounded-full border border-slate-600/70 bg-slate-900/40 backdrop-blur hover:bg-slate-800/70 transition">
37
+ ← Back to dashboard
38
+ </a>
39
+ </div>
40
+
41
+ <!-- Tool navigation -->
42
+ <nav class="mb-6">
43
+ <div class="inline-flex items-center gap-1 rounded-full bg-slate-900/80 border border-slate-800 p-1 text-xs">
44
+ <a href="grammar.html"
45
+ class="px-3 py-1.5 rounded-full text-slate-300 hover:bg-slate-800">
46
+ Grammar
47
+ </a>
48
+ <a href="plagiarism.html"
49
+ class="px-3 py-1.5 rounded-full text-slate-300 hover:bg-slate-800">
50
+ Plagiarism
51
+ </a>
52
+ <a href="ai-check.html"
53
+ class="px-3 py-1.5 rounded-full bg-fuchsia-500 text-white font-medium shadow shadow-fuchsia-500/40"
54
+ aria-current="page">
55
+ AI Content
56
+ </a>
57
+ </div>
58
+ </nav>
59
+
60
+ <!-- Glass main card -->
61
+ <div class="grid md:grid-cols-2 gap-6">
62
+ <!-- LEFT PANEL -->
63
+ <section class="rounded-3xl border border-white/10 bg-slate-900/40 backdrop-blur-xl shadow-2xl shadow-black/50 p-5 md:p-6 flex flex-col">
64
+ <header class="mb-4">
65
+ <h2 class="text-lg md:text-xl font-semibold">Analyze your content</h2>
66
+ <p class="text-xs text-slate-300 mt-1">
67
+ Paste text or drop a <span class="font-semibold">.txt / .pdf / .docx</span> file. We’ll estimate how AI-generated it looks.
68
+ </p>
69
+ </header>
70
+
71
+ <!-- Drag & Drop zone -->
72
+ <div id="dropZone"
73
+ class="mb-3 border-2 border-dashed border-slate-600/80 rounded-2xl bg-slate-900/50 backdrop-blur-md px-4 py-3 text-xs flex items-center justify-between transition hover:border-fuchsia-400/80 hover:bg-slate-900/70">
74
+ <div class="flex items-center gap-3">
75
+ <div class="w-8 h-8 rounded-full bg-slate-800/80 flex items-center justify-center">
76
+ <span class="text-lg">📂</span>
77
+ </div>
78
+ <div>
79
+ <p class="font-medium text-slate-100">Drag & drop your file here</p>
80
+ <p class="text-[11px] text-slate-400">
81
+ Supported: .txt, .pdf, .docx (max 15MB)
82
+ </p>
83
+ </div>
84
+ </div>
85
+ <div class="flex flex-col items-end gap-1">
86
+ <label class="px-3 py-1 rounded-full border border-slate-500/80 text-[11px] cursor-pointer bg-slate-800/60 hover:bg-slate-700/80">
87
+ Browse
88
+ <input id="fileInput" type="file" accept=".txt,.pdf,.docx" class="hidden" />
89
+ </label>
90
+ <span id="fileName" class="text-[10px] text-slate-400 max-w-[150px] truncate"></span>
91
+ </div>
92
+ </div>
93
+
94
+ <!-- Tiny status -->
95
+ <div id="statusTiny" class="text-[11px] text-slate-400 mb-2">
96
+ Ready · No file selected
97
+ </div>
98
+
99
+ <!-- Paste / textarea -->
100
+ <div class="flex gap-2 items-center mb-2">
101
+ <button id="pasteBtn"
102
+ class="text-[11px] px-3 py-1 rounded-full border border-slate-600/80 bg-slate-900/70 hover:bg-slate-800/90">
103
+ Paste text from clipboard
104
+ </button>
105
+ <span class="text-[11px] text-slate-500">or type directly below</span>
106
+ </div>
107
+
108
+ <textarea id="inputText" rows="12"
109
+ class="flex-1 w-full rounded-2xl bg-slate-950/70 border border-slate-700/80 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-fuchsia-500/70 focus:border-fuchsia-500/60 placeholder:text-slate-500"
110
+ placeholder="Paste your content here or use the drag-and-drop box above..."></textarea>
111
+
112
+ <!-- Buttons -->
113
+ <div class="mt-4 flex flex-wrap gap-2 items-center">
114
+ <button id="checkAI"
115
+ class="px-5 py-2 rounded-xl bg-gradient-to-r from-fuchsia-500 via-violet-500 to-sky-400 hover:brightness-110 text-sm font-semibold shadow-lg shadow-fuchsia-500/40 flex items-center gap-2">
116
+ <svg id="spinner"
117
+ class="hidden animate-spin h-4 w-4 text-white"
118
+ xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
119
+ <circle class="opacity-25" cx="12" cy="12" r="10"
120
+ stroke="currentColor" stroke-width="4"></circle>
121
+ <path class="opacity-75" fill="currentColor"
122
+ d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"></path>
123
+ </svg>
124
+ <span id="checkLabel">Check AI Content</span>
125
+ </button>
126
+
127
+ <button id="downloadReport"
128
+ class="px-5 py-2 rounded-xl bg-slate-900/70 border border-slate-600/80 text-sm font-medium hover:bg-slate-800/90">
129
+ Download report as PDF
130
+ </button>
131
+ </div>
132
+ </section>
133
+
134
+ <!-- RIGHT PANEL -->
135
+ <section class="rounded-3xl border border-white/10 bg-slate-900/40 backdrop-blur-xl shadow-2xl shadow-black/50 p-5 md:p-6 flex flex-col">
136
+ <header class="mb-3">
137
+ <h2 class="text-lg font-semibold">Detection result</h2>
138
+ <p class="text-[11px] text-slate-400">
139
+ Scores are estimates – always review with your own judgment.
140
+ </p>
141
+ </header>
142
+
143
+ <!-- Progress bar -->
144
+ <div class="mb-3">
145
+ <div class="h-1.5 w-full rounded-full bg-slate-800/80 overflow-hidden">
146
+ <div id="progressBar" class="h-full w-0 bg-gradient-to-r from-fuchsia-400 via-violet-400 to-sky-400 transition-[width] duration-200 ease-out"></div>
147
+ </div>
148
+ <div id="progressText" class="text-[11px] text-slate-400 mt-1">Idle</div>
149
+ </div>
150
+
151
+ <!-- Main result -->
152
+ <div class="flex-1 rounded-2xl bg-slate-950/60 border border-slate-800/80 p-4 text-sm">
153
+ <p id="score" class="text-xl font-bold mb-1 text-fuchsia-300">
154
+ No scan yet.
155
+ </p>
156
+ <p id="detail" class="text-[13px] text-slate-200 mb-3">
157
+ Paste text or upload a file, then click “Check AI Content”.
158
+ </p>
159
+
160
+ <div id="meta" class="text-[11px] text-slate-400 space-y-1">
161
+ <!-- Filled by JS -->
162
+ </div>
163
+ </div>
164
+
165
+ <!-- Mini legend -->
166
+ <div class="mt-4 grid grid-cols-2 gap-3 text-[11px] text-slate-300">
167
+ <div class="bg-slate-950/60 border border-slate-800/80 rounded-xl p-3">
168
+ <p class="font-semibold mb-1">Model-based detection</p>
169
+ <p>Uses a RoBERTa classifier when available. Falls back to heuristic if model load fails.</p>
170
+ </div>
171
+ <div class="bg-slate-950/60 border border-slate-800/80 rounded-xl p-3">
172
+ <p class="font-semibold mb-1">Saved in history</p>
173
+ <p>Every scan is logged in your <span class="font-semibold">My History</span> for future reference.</p>
174
+ </div>
175
+ </div>
176
+ </section>
177
+ </div>
178
+
179
+ <!-- Extra sections from second file -->
180
+ <section class="mt-10 space-y-4">
181
+ <h2 class="text-xl md:text-2xl font-semibold">Why choose this AI content checker?</h2>
182
+ <p class="text-sm text-slate-300">
183
+ With AI writing everywhere, this page helps you quickly see how “machine-like” your text may look.
184
+ </p>
185
+
186
+ <div class="grid md:grid-cols-3 gap-5 mt-3">
187
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4 text-xs">
188
+ <div class="flex items-center gap-3 mb-2">
189
+ <div class="w-9 h-9 rounded-full bg-fuchsia-500/20 flex items-center justify-center">
190
+ <span class="text-lg">📈</span>
191
+ </div>
192
+ <p class="font-semibold text-sm">Instant AI probability</p>
193
+ </div>
194
+ <p>
195
+ Get an estimated AI-generated probability (%) along with the opposite human-written score.
196
+ </p>
197
+ </div>
198
+
199
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4 text-xs">
200
+ <div class="flex items-center gap-3 mb-2">
201
+ <div class="w-9 h-9 rounded-full bg-indigo-500/20 flex items-center justify-center">
202
+ <span class="text-lg">🔍</span>
203
+ </div>
204
+ <p class="font-semibold text-sm">Transparent heuristics</p>
205
+ </div>
206
+ <p>
207
+ Uses simple, explainable metrics like sentence length and vocabulary variety — great for demos.
208
+ </p>
209
+ </div>
210
+
211
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4 text-xs">
212
+ <div class="flex items-center gap-3 mb-2">
213
+ <div class="w-9 h-9 rounded-full bg-emerald-500/20 flex items-center justify-center">
214
+ <span class="text-lg">📄</span>
215
+ </div>
216
+ <p class="font-semibold text-sm">Downloadable PDF</p>
217
+ </div>
218
+ <p>
219
+ Save the AI-analysis result as a PDF snapshot along with your text.
220
+ </p>
221
+ </div>
222
+ </div>
223
+ </section>
224
+
225
+ <section class="mt-10 space-y-4">
226
+ <h2 class="text-xl md:text-2xl font-semibold">Who can use this tool?</h2>
227
+ <p class="text-sm text-slate-300">
228
+ Ideal for anyone curious about AI detection or building their own detector interface.
229
+ </p>
230
+
231
+ <div class="grid md:grid-cols-3 gap-5 mt-3 text-xs">
232
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
233
+ <div class="flex items-center gap-3 mb-2">
234
+ <div class="w-9 h-9 rounded-full bg-sky-500/20 flex items-center justify-center">
235
+ <span class="text-lg">🎓</span>
236
+ </div>
237
+ <p class="font-semibold text-sm">Students & researchers</p>
238
+ </div>
239
+ <p>
240
+ Test your assignments or papers and see how “AI-like” they appear based on simple rules.
241
+ </p>
242
+ </div>
243
+
244
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
245
+ <div class="flex items-center gap-3 mb-2">
246
+ <div class="w-9 h-9 rounded-full bg-amber-500/20 flex items-center justify-center">
247
+ <span class="text-lg">🧑‍🏫</span>
248
+ </div>
249
+ <p class="font-semibold text-sm">Teachers & trainers</p>
250
+ </div>
251
+ <p>
252
+ Show learners how AI detection can be approximated using simple statistics on text.
253
+ </p>
254
+ </div>
255
+
256
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
257
+ <div class="flex items-center gap-3 mb-2">
258
+ <div class="w-9 h-9 rounded-full bg-rose-500/20 flex items-center justify-center">
259
+ <span class="text-lg">💻</span>
260
+ </div>
261
+ <p class="font-semibold text-sm">Developers & hobbyists</p>
262
+ </div>
263
+ <p>
264
+ Use this as a frontend template and plug in your own backend AI-detection models later.
265
+ </p>
266
+ </div>
267
+ </div>
268
+ </section>
269
+
270
+ <section class="mt-10 space-y-4">
271
+ <h2 class="text-xl md:text-2xl font-semibold">How does this AI content checker work?</h2>
272
+ <p class="text-sm text-slate-300">
273
+ The scoring is intentionally simple, so you can read the JavaScript and fully understand it.
274
+ </p>
275
+
276
+ <div class="grid md:grid-cols-3 gap-5 mt-3 text-xs">
277
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
278
+ <div class="flex items-center gap-3 mb-2">
279
+ <div class="w-9 h-9 rounded-full bg-fuchsia-500/20 flex items-center justify-center">
280
+ <span class="text-lg">1️⃣</span>
281
+ </div>
282
+ <p class="font-semibold text-sm">Tokenise the text</p>
283
+ </div>
284
+ <p>
285
+ Text is lower-cased and split into words and sentences for basic statistics.
286
+ </p>
287
+ </div>
288
+
289
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
290
+ <div class="flex items-center gap-3 mb-2">
291
+ <div class="w-9 h-9 rounded-full bg-indigo-500/20 flex items-center justify-center">
292
+ <span class="text-lg">2️⃣</span>
293
+ </div>
294
+ <p class="font-semibold text-sm">Measure patterns</p>
295
+ </div>
296
+ <p>
297
+ It looks at vocabulary variety and average sentence length — long, repetitive text can
298
+ look more AI-like.
299
+ </p>
300
+ </div>
301
+
302
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
303
+ <div class="flex items-center gap-3 mb-2">
304
+ <div class="w-9 h-9 rounded-full bg-emerald-500/20 flex items-center justify-center">
305
+ <span class="text-lg">3️⃣</span>
306
+ </div>
307
+ <p class="font-semibold text-sm">Compute a score</p>
308
+ </div>
309
+ <p>
310
+ Those metrics are combined into an AI-generated probability (0–100%), displayed and
311
+ saved in your PDF report.
312
+ </p>
313
+ </div>
314
+ </div>
315
+ </section>
316
+
317
+ <section class="mt-12">
318
+ <h2 class="text-xl md:text-2xl font-semibold mb-3">Reviews for this AI checker</h2>
319
+ <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-5 md:p-6 relative overflow-hidden">
320
+ <div id="reviewCard" class="transition-all duration-500"></div>
321
+
322
+ <div class="flex items-center justify-between mt-4">
323
+ <div id="reviewDots" class="flex gap-1.5"></div>
324
+ <div class="flex gap-2">
325
+ <button id="prevReview"
326
+ class="w-8 h-8 rounded-full border border-slate-600 flex items-center justify-center text-xs hover:bg-slate-800">‹</button>
327
+ <button id="nextReview"
328
+ class="w-8 h-8 rounded-full border border-slate-600 flex items-center justify-center text-xs hover:bg-slate-800">›</button>
329
+ </div>
330
+ </div>
331
+ </div>
332
+ </section>
333
+ </main>
334
+
335
+ <!-- Footer -->
336
+ <footer class="relative border-t border-slate-800 bg-slate-950/80 backdrop-blur">
337
+ <div class="max-w-6xl mx-auto px-4 py-4 text-[11px] text-slate-400 flex flex-col md:flex-row items-center justify-between gap-2">
338
+ <p>© 2025 TrueWrite Scan. All rights reserved.</p>
339
+ <p>Designed & developed by <span class="text-fuchsia-300 font-medium">Gopal Krushna Mahapatra</span>.</p>
340
+ </div>
341
+ </footer>
342
+
343
+ <!-- SCRIPT -->
344
+ <script>
345
+ const BACKEND_URL = "https://gopalkrushnamahapatra-truewrite-scan.hf.space";
346
+ const token = localStorage.getItem("truewriteToken");
347
+ const user = localStorage.getItem("truewriteUser");
348
+ if (!token || !user) {
349
+ window.location.href = "login.html";
350
+ }
351
+
352
+ // jsPDF helper
353
+ function getJsPDF() {
354
+ if (window.jspdf && window.jspdf.jsPDF) return window.jspdf.jsPDF;
355
+ if (window.jsPDF) return window.jsPDF;
356
+ alert("PDF library (jsPDF) did not load. Check your internet connection or CDN access.");
357
+ return null;
358
+ }
359
+
360
+ // DOM references
361
+ const textarea = document.getElementById("inputText");
362
+ const fileInput = document.getElementById("fileInput");
363
+ const fileNameSpan = document.getElementById("fileName");
364
+ const pasteBtn = document.getElementById("pasteBtn");
365
+ const statusTiny = document.getElementById("statusTiny");
366
+ const checkBtn = document.getElementById("checkAI");
367
+ const checkLabel = document.getElementById("checkLabel");
368
+ const spinner = document.getElementById("spinner");
369
+ const scoreP = document.getElementById("score");
370
+ const detailP = document.getElementById("detail");
371
+ const metaDiv = document.getElementById("meta");
372
+ const downloadBtn = document.getElementById("downloadReport");
373
+ const dropZone = document.getElementById("dropZone");
374
+ const progressBar = document.getElementById("progressBar");
375
+ const progressText = document.getElementById("progressText");
376
+
377
+ let lastResult = null;
378
+ let progressTimer = null;
379
+
380
+ function setLoading(on) {
381
+ if (on) {
382
+ spinner.classList.remove("hidden");
383
+ checkLabel.textContent = "Analyzing...";
384
+ checkBtn.disabled = true;
385
+ checkBtn.classList.add("opacity-60", "cursor-not-allowed");
386
+
387
+ let pct = 0;
388
+ progressBar.style.width = "0%";
389
+ progressText.textContent = "Uploading / processing...";
390
+ progressTimer = setInterval(() => {
391
+ pct += Math.random() * 10;
392
+ if (pct > 90) pct = 90;
393
+ progressBar.style.width = pct + "%";
394
+ }, 200);
395
+ } else {
396
+ spinner.classList.add("hidden");
397
+ checkLabel.textContent = "Check AI Content";
398
+ checkBtn.disabled = false;
399
+ checkBtn.classList.remove("opacity-60", "cursor-not-allowed");
400
+ if (progressTimer) {
401
+ clearInterval(progressTimer);
402
+ progressTimer = null;
403
+ }
404
+ progressBar.style.width = "100%";
405
+ progressText.textContent = "Done";
406
+ setTimeout(() => {
407
+ progressBar.style.width = "0%";
408
+ progressText.textContent = "Idle";
409
+ }, 700);
410
+ }
411
+ }
412
+
413
+ // Drag & drop handlers
414
+ ["dragenter", "dragover"].forEach(evt => {
415
+ dropZone.addEventListener(evt, e => {
416
+ e.preventDefault();
417
+ e.stopPropagation();
418
+ dropZone.classList.add("border-fuchsia-400/80", "bg-slate-900/80");
419
+ });
420
+ });
421
+
422
+ ["dragleave", "drop"].forEach(evt => {
423
+ dropZone.addEventListener(evt, e => {
424
+ e.preventDefault();
425
+ e.stopPropagation();
426
+ dropZone.classList.remove("border-fuchsia-400/80", "bg-slate-900/80");
427
+ });
428
+ });
429
+
430
+ dropZone.addEventListener("drop", e => {
431
+ const dt = e.dataTransfer;
432
+ const files = dt.files;
433
+ if (!files || !files.length) return;
434
+ const file = files[0];
435
+ fileInput.files = files;
436
+ handleFileSelected(file);
437
+ });
438
+
439
+ // File selection via button
440
+ fileInput.addEventListener("change", e => {
441
+ const file = e.target.files[0];
442
+ if (!file) {
443
+ fileNameSpan.textContent = "";
444
+ statusTiny.textContent = "Ready · No file selected";
445
+ return;
446
+ }
447
+ handleFileSelected(file);
448
+ });
449
+
450
+ function handleFileSelected(file) {
451
+ fileNameSpan.textContent = file.name;
452
+
453
+ if (file.type === "text/plain" || file.name.toLowerCase().endsWith(".txt")) {
454
+ const reader = new FileReader();
455
+ reader.onload = () => textarea.value = reader.result;
456
+ reader.readAsText(file);
457
+ statusTiny.textContent = `${file.name} loaded as text`;
458
+ } else {
459
+ textarea.value = "";
460
+ statusTiny.textContent = `${file.name} selected. Will be parsed on backend.`;
461
+ }
462
+ }
463
+
464
+ // Paste button
465
+ pasteBtn.onclick = async () => {
466
+ try {
467
+ const txt = await navigator.clipboard.readText();
468
+ textarea.value = txt;
469
+ statusTiny.textContent = "Text pasted from clipboard";
470
+ } catch {
471
+ alert("Clipboard access blocked by browser.");
472
+ }
473
+ };
474
+
475
+ async function callAiCheck(text) {
476
+ const res = await fetch(`${BACKEND_URL}/api/ai-check`, {
477
+ method: "POST",
478
+ headers: {
479
+ "Content-Type": "application/json",
480
+ "Authorization": `Bearer ${token}`
481
+ },
482
+ body: JSON.stringify({ text })
483
+ });
484
+ const data = await res.json();
485
+ if (!res.ok) throw new Error(data.detail || data.error || "Server error");
486
+ return data;
487
+ }
488
+
489
+ async function uploadFileAndCheck(file) {
490
+ const form = new FormData();
491
+ form.append("file", file, file.name);
492
+ const res = await fetch(`${BACKEND_URL}/api/ai-check-file`, {
493
+ method: "POST",
494
+ headers: { "Authorization": `Bearer ${token}` },
495
+ body: form
496
+ });
497
+ const data = await res.json();
498
+ if (!res.ok) throw new Error(data.detail || data.error || "Server error");
499
+ return data;
500
+ }
501
+
502
+ // Main check
503
+ checkBtn.onclick = async () => {
504
+ const text = textarea.value.trim();
505
+ const file = fileInput.files[0];
506
+
507
+ if (!text && !file) {
508
+ alert("Please paste text or upload a file first.");
509
+ return;
510
+ }
511
+
512
+ setLoading(true);
513
+ scoreP.textContent = "Analyzing...";
514
+ detailP.textContent = "Contacting backend...";
515
+ metaDiv.innerHTML = "";
516
+ lastResult = null;
517
+
518
+ try {
519
+ let data;
520
+ if (file) data = await uploadFileAndCheck(file);
521
+ else data = await callAiCheck(text);
522
+
523
+ const ai = data.ai_percent ?? 0;
524
+ const human = data.human_percent ?? (100 - ai);
525
+ const wc = data.word_count ?? 0;
526
+ const avg = data.avg_sentence_length ?? null;
527
+ const summary = data.summary ?? "";
528
+
529
+ scoreP.textContent = `AI probability: ${ai}% · Human: ${human}%`;
530
+ detailP.textContent = summary || (ai > 60
531
+ ? `Text appears more AI-like (human ~${human}%).`
532
+ : `Text appears more human-like (human ~${human}%).`
533
+ );
534
+
535
+ metaDiv.innerHTML = `
536
+ <div>Words: <strong>${wc}</strong></div>
537
+ ${avg !== null ? `<div>Avg sentence length: <strong>${avg}</strong></div>` : ""}
538
+ ${data.unique_ratio !== undefined ? `<div>Unique word ratio: <strong>${data.unique_ratio}</strong></div>` : ""}
539
+ <div class="mt-1 text-[10px] text-slate-500">This scan is stored in your history.</div>
540
+ `;
541
+
542
+ lastResult = {
543
+ ai_percent: ai,
544
+ human_percent: human,
545
+ word_count: wc,
546
+ avg_sentence_length: avg,
547
+ summary,
548
+ text: textarea.value
549
+ };
550
+ } catch (err) {
551
+ console.error(err);
552
+ scoreP.textContent = "Error";
553
+ detailP.textContent = err.message || "Failed to contact backend.";
554
+ } finally {
555
+ setLoading(false);
556
+ }
557
+ };
558
+
559
+ // PDF download
560
+ downloadBtn.onclick = () => {
561
+ if (!lastResult) {
562
+ alert("Run at least one scan before downloading a report.");
563
+ return;
564
+ }
565
+
566
+ const jsPDF = getJsPDF();
567
+ if (!jsPDF) return;
568
+
569
+ const doc = new jsPDF({ unit: "pt", format: "a4" });
570
+
571
+ let y = 40;
572
+ doc.setFontSize(16);
573
+ doc.text("TrueWrite Scan — AI Content Report", 40, y); y += 22;
574
+
575
+ doc.setFontSize(11);
576
+ doc.text("Generated: " + new Date().toLocaleString(), 40, y); y += 18;
577
+ doc.text("User: " + (user || "N/A"), 40, y); y += 18;
578
+
579
+ doc.setFontSize(12);
580
+ doc.text(`AI probability: ${lastResult.ai_percent}%`, 40, y); y += 16;
581
+ doc.text(`Human probability: ${lastResult.human_percent}%`, 40, y); y += 16;
582
+ doc.text(`Word count: ${lastResult.word_count}`, 40, y); y += 18;
583
+
584
+ if (lastResult.avg_sentence_length !== null && lastResult.avg_sentence_length !== undefined) {
585
+ doc.text(`Avg sentence length: ${lastResult.avg_sentence_length}`, 40, y); y += 18;
586
+ }
587
+
588
+ if (lastResult.summary) {
589
+ doc.text("Summary:", 40, y); y += 16;
590
+ doc.setFontSize(10);
591
+ const lines = doc.splitTextToSize(lastResult.summary, 520);
592
+ doc.text(lines, 40, y);
593
+ y += lines.length * 12 + 10;
594
+ }
595
+
596
+ doc.setFontSize(11);
597
+ doc.text("--- Original text (truncated) ---", 40, y); y += 18;
598
+ const original = lastResult.text || "";
599
+ const truncated = original.length > 4000 ? original.slice(0, 4000) + "\n\n[TRUNCATED]" : original;
600
+ const textLines = doc.splitTextToSize(truncated, 520);
601
+ doc.setFontSize(10);
602
+ doc.text(textLines, 40, y);
603
+
604
+ doc.save("truewrite-ai-report.pdf");
605
+ };
606
+
607
+ // Reviews slider
608
+ const reviews = [
609
+ {
610
+ name: "Aarav S.",
611
+ role: "B.Tech Student",
612
+ text: "Helps me understand how AI-generated my project reports might look before submission.",
613
+ stars: 5
614
+ },
615
+ {
616
+ name: "Priya K.",
617
+ role: "AI Research Enthusiast",
618
+ text: "The transparent scoring is perfect for explaining AI detection concepts to juniors.",
619
+ stars: 5
620
+ },
621
+ {
622
+ name: "Rahul M.",
623
+ role: "Content Creator",
624
+ text: "Gives a quick signal when my text feels too uniform and AI-like.",
625
+ stars: 4
626
+ },
627
+ {
628
+ name: "Sneha R.",
629
+ role: "M.Sc. Student",
630
+ text: "Very handy example for my seminar on AI ethics and content authenticity.",
631
+ stars: 5
632
+ },
633
+ {
634
+ name: "Vikram J.",
635
+ role: "Developer",
636
+ text: "Exactly the kind of simple, hackable AI-detector UI I wanted for my side project.",
637
+ stars: 4
638
+ }
639
+ ];
640
+
641
+ let currentReview = 0;
642
+ const reviewCard = document.getElementById("reviewCard");
643
+ const reviewDots = document.getElementById("reviewDots");
644
+
645
+ function starRow(stars) {
646
+ let html = "";
647
+ for (let i = 0; i < 5; i++) {
648
+ html += `<span class="${i < stars ? 'text-yellow-400' : 'text-slate-600'} text-sm">★</span>`;
649
+ }
650
+ return html;
651
+ }
652
+
653
+ function renderReview() {
654
+ const r = reviews[currentReview];
655
+ reviewCard.innerHTML = `
656
+ <div class="flex items-center gap-2 mb-2">
657
+ ${starRow(r.stars)}
658
+ </div>
659
+ <p class="text-sm text-slate-200 mb-3">"${r.text}"</p>
660
+ <p class="text-sm font-semibold">${r.name}</p>
661
+ <p class="text-xs text-slate-400">${r.role}</p>
662
+ `;
663
+ reviewDots.innerHTML = reviews.map((_, i) =>
664
+ `<span class="w-2 h-2 rounded-full ${i === currentReview ? 'bg-fuchsia-400' : 'bg-slate-600'}"></span>`
665
+ ).join('');
666
+ }
667
+
668
+ function nextReview() {
669
+ currentReview = (currentReview + 1) % reviews.length;
670
+ renderReview();
671
+ }
672
+
673
+ function prevReview() {
674
+ currentReview = (currentReview - 1 + reviews.length) % reviews.length;
675
+ renderReview();
676
+ }
677
+
678
+ document.getElementById('nextReview').onclick = nextReview;
679
+ document.getElementById('prevReview').onclick = prevReview;
680
+
681
+ let timer = setInterval(nextReview, 6000);
682
+ document.getElementById('nextReview').addEventListener('click', () => {
683
+ clearInterval(timer); timer = setInterval(nextReview, 6000);
684
+ });
685
+ document.getElementById('prevReview').addEventListener('click', () => {
686
+ clearInterval(timer); timer = setInterval(nextReview, 6000);
687
+ });
688
+
689
+ renderReview();
690
+ </script>
691
+ </body>
692
+ </html>