GopalKrushnaMahapatra commited on
Commit
b000c9c
·
verified ·
1 Parent(s): cc4ce9d

Upload 14 files

Browse files
Files changed (15) hide show
  1. .env.local +1 -0
  2. .gitattributes +5 -0
  3. Plagiarism2.png +3 -0
  4. ai-check.html +692 -0
  5. ai.png +3 -0
  6. dashboard-main.png +3 -0
  7. dashboard.html +321 -0
  8. grammar.html +823 -0
  9. grammar.png +3 -0
  10. history.html +122 -0
  11. index.html +77 -0
  12. login.html +76 -0
  13. logo.png +3 -0
  14. plagiarism.html +740 -0
  15. signup.html +82 -0
.env.local ADDED
@@ -0,0 +1 @@
 
 
1
+ NEXT_PUBLIC_API_URL=https://gopalkrushnamahapatra-truewrite-scan.hf.space
.gitattributes CHANGED
@@ -33,3 +33,8 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ ai.png filter=lfs diff=lfs merge=lfs -text
37
+ dashboard-main.png filter=lfs diff=lfs merge=lfs -text
38
+ grammar.png filter=lfs diff=lfs merge=lfs -text
39
+ logo.png filter=lfs diff=lfs merge=lfs -text
40
+ Plagiarism2.png filter=lfs diff=lfs merge=lfs -text
Plagiarism2.png ADDED

Git LFS Details

  • SHA256: 3a7b1b4bbae354df634b0a7f2921fe855656ab8a3891321cb8d6ef9c58e697ec
  • Pointer size: 132 Bytes
  • Size of remote file: 6.99 MB
ai-check.html ADDED
@@ -0,0 +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>
ai.png ADDED

Git LFS Details

  • SHA256: 7c785e9a8e46c30e57e600594569252042164f94972db09512e880a80c50df7e
  • Pointer size: 132 Bytes
  • Size of remote file: 7.9 MB
dashboard-main.png ADDED

Git LFS Details

  • SHA256: ddddcab2f23c0d550fd9b6dd677b390fb7e2ef0b7dbfe39c42bfc9a93fab6c02
  • Pointer size: 132 Bytes
  • Size of remote file: 1.25 MB
dashboard.html ADDED
@@ -0,0 +1,321 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Dashboard – TrueWrite Scan</title>
6
+ <script src="https://cdn.tailwindcss.com"></script>
7
+ </head>
8
+ <body class="bg-slate-950 text-white min-h-screen flex flex-col">
9
+ <!-- Top bar -->
10
+ <header class="border-b border-slate-800 bg-slate-950/90 backdrop-blur sticky top-0 z-20">
11
+ <div class="max-w-6xl mx-auto px-4 py-3 flex items-center justify-between">
12
+ <!-- Logo + name -->
13
+ <div class="flex items-center gap-3">
14
+ <img src="logo.png" alt="TrueWrite Scan logo"
15
+ class="w-10 h-10 rounded-2xl border border-slate-700 object-cover" />
16
+ <div>
17
+ <p class="text-xl md:text-2xl font-extrabold tracking-tight">
18
+ <span class="bg-gradient-to-r from-indigo-400 via-fuchsia-400 to-emerald-400 bg-clip-text text-transparent">
19
+ TrueWrite
20
+ </span>
21
+ <span class="text-slate-100"> Scan</span>
22
+ </p>
23
+ <p class="text-[11px] text-slate-400 uppercase tracking-[0.22em]">
24
+ Honest. Original. Authenticated
25
+ </p>
26
+ </div>
27
+ </div>
28
+ <div class="flex items-center gap-3">
29
+ <span id="userName" class="text-xs text-slate-300"></span>
30
+ <a href="history.html"
31
+ class="text-xs px-3 py-1 rounded-full border border-slate-600 hover:bg-slate-800">
32
+ History
33
+ </a>
34
+ <button id="logoutBtn"
35
+ class="text-xs px-3 py-1 rounded-full border border-slate-600 hover:bg-slate-800">
36
+ Logout
37
+ </button>
38
+ </div>
39
+ </div>
40
+ </header>
41
+
42
+ <main class="flex-1 max-w-6xl mx-auto px-4 py-8 space-y-12">
43
+ <!-- About the website -->
44
+ <section class="grid md:grid-cols-[3fr,2fr] gap-8 items-center">
45
+ <div>
46
+ <h1 class="text-3xl md:text-4xl font-bold mb-3">
47
+ Write it once. <span class="text-indigo-400">Check it all.</span>
48
+ </h1>
49
+ <p class="text-sm md:text-base text-slate-300 mb-4">
50
+ TrueWrite Scan is your all-in-one writing assistant inspired by different platforms and alsp it is different. From spotting grammar mistakes to estimating plagiarism and and get a rough signal
51
+ of AI-generated content — all in one unified dashboard — right in your browser.
52
+ </p>
53
+ <ul class="text-sm text-slate-300 space-y-1 mb-2 list-disc list-inside">
54
+ <li>Check grammar for up to 1000 words in a single click.</li>
55
+ <li>Paste or upload text for plagiarism estimation.</li>
56
+ <li>Get a heuristic AI-content probability with clear summaries.</li>
57
+ <li>Download PDF reports for every scan.</li>
58
+ </ul>
59
+ </div>
60
+
61
+ <!-- Illustration side image -->
62
+ <div class="relative">
63
+ <div class="absolute inset-0 bg-gradient-to-tr from-indigo-500/30 via-purple-500/20 to-emerald-400/30 blur-3xl -z-10"></div>
64
+ <img
65
+ src="dashboard-main.png"
66
+ alt="TrueWrite Scan dashboard preview"
67
+ class="rounded-3xl border border-slate-800 shadow-2xl shadow-indigo-500/30"
68
+ />
69
+ </div>
70
+ </section>
71
+
72
+ <!-- Three tools (buttons + images) -->
73
+ <section class="space-y-4">
74
+ <h2 class="text-xl md:text-2xl font-semibold">Choose a tool</h2>
75
+
76
+ <!-- Grammar card -->
77
+ <a href="grammar.html"
78
+ class="group flex flex-col md:flex-row items-center gap-6 bg-slate-900/70 border border-slate-800 rounded-2xl p-5
79
+ hover:border-fuchsia-400 hover:-translate-y-1 hover:shadow-xl hover:shadow-fuchsia-500/30 transition">
80
+ <div class="flex-1">
81
+ <p class="text-xs uppercase tracking-wide text-fuchsia-300 mb-1">Tool 1</p>
82
+ <h3 class="text-lg md:text-xl font-semibold mb-1">Grammar Check</h3>
83
+ <p class="text-sm text-slate-300 mb-3">
84
+ Instantly highlight basic grammar, punctuation, and spelling issues for up to
85
+ <strong>1000 words</strong>. Perfect for assignments and quick edits.
86
+ </p>
87
+ <button
88
+ class="px-4 py-2 rounded-full bg-fuchsia-500 group-hover:bg-fuchsia-600 text-xs font-medium">
89
+ Scan Text →
90
+ </button>
91
+ </div>
92
+ <img
93
+ src="grammar.png"
94
+ alt="Grammar checker preview"
95
+ class="w-full md:w-72 rounded-xl border border-slate-700"
96
+ />
97
+ </a>
98
+
99
+ <!-- Plagiarism card -->
100
+ <!-- Plagiarism -->
101
+ <a href="plagiarism.html"
102
+ class="group flex flex-col md:flex-row items-center gap-6 bg-gradient-to-br from-emerald-500/20 via-slate-900 to-slate-900 border border-emerald-500/40 rounded-2xl p-5 hover:-translate-y-1 hover:shadow-xl hover:shadow-emerald-500/30 transition">
103
+
104
+ <!-- Image on LEFT -->
105
+ <img
106
+ src="Plagiarism2.png"
107
+ alt="Plagiarism checker preview"
108
+ class="w-full md:w-72 rounded-xl border border-slate-700 object-cover"
109
+ />
110
+
111
+ <!-- Text + button on RIGHT -->
112
+ <div class="flex-1">
113
+ <p class="text-xs uppercase tracking-wide text-emerald-300 mb-1">Tool 2</p>
114
+ <h3 class="text-lg md:text-xl font-semibold mb-1">Plagiarism Check</h3>
115
+ <p class="text-sm text-slate-200 mb-3">
116
+ Paste your text or upload a file and compare it against documents to get a real plagiarism percentage with a detailed <strong>PDF report</strong>.
117
+ </p>
118
+ <div class="mt-3 flex justify-end">
119
+ <button
120
+ class="px-4 py-2 rounded-full bg-emerald-500 group-hover:bg-emerald-600 text-xs font-medium">
121
+ Scan document →
122
+ </button>
123
+ </div>
124
+ </div>
125
+ </a>
126
+
127
+
128
+ <!-- AI Content card -->
129
+ <a href="ai-check.html"
130
+ class="group flex flex-col md:flex-row items-center gap-6 bg-slate-900/70 border border-slate-800 rounded-2xl p-5
131
+ hover:border-indigo-400 hover:-translate-y-1 hover:shadow-xl hover:shadow-indigo-500/30 transition">
132
+ <div class="flex-1">
133
+ <p class="text-xs uppercase tracking-wide text-indigo-300 mb-1">Tool 3</p>
134
+ <h3 class="text-lg md:text-xl font-semibold mb-1">AI Content Check</h3>
135
+ <p class="text-sm text-slate-300 mb-3">
136
+ Analyze your writing using different models to estimate how likely it appears
137
+ AI-generated vs human-written, and save the result as a PDF report.
138
+ </p>
139
+ <button
140
+ class="px-4 py-2 rounded-full bg-indigo-500 group-hover:bg-indigo-600 text-xs font-medium">
141
+ Analyze AI Content →
142
+ </button>
143
+ </div>
144
+ <img
145
+ src="ai.png"
146
+ alt="AI content checker preview"
147
+ class="w-full md:w-72 rounded-xl border border-slate-700"
148
+ />
149
+ </a>
150
+ </section>
151
+
152
+ <!-- Reviews slider -->
153
+ <section class="mt-4">
154
+ <h2 class="text-xl md:text-2xl font-semibold mb-3">What users are saying</h2>
155
+ <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-5 md:p-6 relative overflow-hidden">
156
+ <div id="reviewCard" class="transition-all duration-500">
157
+ <!-- Filled by JS -->
158
+ </div>
159
+
160
+ <div class="flex items-center justify-between mt-4">
161
+ <div id="reviewDots" class="flex gap-1.5"></div>
162
+ <div class="flex gap-2">
163
+ <button id="prevReview"
164
+ class="w-8 h-8 rounded-full border border-slate-600 flex items-center justify-center text-xs hover:bg-slate-800">
165
+
166
+ </button>
167
+ <button id="nextReview"
168
+ class="w-8 h-8 rounded-full border border-slate-600 flex items-center justify-center text-xs hover:bg-slate-800">
169
+
170
+ </button>
171
+ </div>
172
+ </div>
173
+ </div>
174
+ </section>
175
+ </main>
176
+
177
+ <!-- Footer -->
178
+ <footer class="border-t border-slate-800 mt-8">
179
+ <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">
180
+ <p>© 2025 TrueWrite Scan. All rights reserved.</p>
181
+ <p>Designed & developed by <span class="text-indigo-300 font-medium">Gopal Krushna Mahapatra</span>.</p>
182
+ </div>
183
+ </footer>
184
+
185
+ <script>
186
+ // Simple protection: if not “logged in”, redirect to login
187
+ if (!localStorage.getItem('truewriteUser')) {
188
+ window.location.href = 'login.html';
189
+ }
190
+
191
+ document.getElementById('logoutBtn').onclick = () => {
192
+ localStorage.removeItem('truewriteUser');
193
+ window.location.href = 'index.html';
194
+ };
195
+
196
+ // ------- Reviews slider with star ratings -------
197
+ const reviews = [
198
+ {
199
+ name: "Aarav S.",
200
+ role: "B.Tech Student",
201
+ text: "TrueWrite Scan makes it easy to quickly check my lab reports for basic errors before submitting.",
202
+ stars: 5
203
+ },
204
+ {
205
+ name: "Priya K.",
206
+ role: "Research Scholar",
207
+ text: "I love the clean dashboard. Switching between grammar, plagiarism, and AI checks feels effortless.",
208
+ stars: 5
209
+ },
210
+ {
211
+ name: "Rahul M.",
212
+ role: "Content Writer",
213
+ text: "The PDF reports are very handy when I want to keep track of changes and share results with my team.",
214
+ stars: 4
215
+ },
216
+ {
217
+ name: "Sneha R.",
218
+ role: "M.Sc. Student",
219
+ text: "Great for understanding how plagiarism and AI detectors roughly work under the hood.",
220
+ stars: 5
221
+ },
222
+ {
223
+ name: "Vikram J.",
224
+ role: "Developer",
225
+ text: "Perfect demo project to learn front-end + basic text processing. The UI looks super polished.",
226
+ stars: 4
227
+ },
228
+ {
229
+ name: "Ananya P.",
230
+ role: "Teacher",
231
+ text: "I show this tool to my students so they understand the importance of original writing.",
232
+ stars: 5
233
+ },
234
+ {
235
+ name: "Kunal D.",
236
+ role: "Freelance Writer",
237
+ text: "The grammar checker catches little issues that I tend to miss when I’m in a hurry.",
238
+ stars: 4
239
+ },
240
+ {
241
+ name: "Meera L.",
242
+ role: "College Student",
243
+ text: "Having grammar, plagiarism and AI content checkers in one place saves me a lot of time.",
244
+ stars: 5
245
+ },
246
+ {
247
+ name: "Siddharth G.",
248
+ role: "Tech Enthusiast",
249
+ text: "A nice example of how text tools can be built using only front-end technologies and JavaScript.",
250
+ stars: 4
251
+ },
252
+ {
253
+ name: "Riya S.",
254
+ role: "BCA Student",
255
+ text: "Very intuitive layout. Feels similar to popular tools like QuillBot but customised for learning.",
256
+ stars: 5
257
+ }
258
+ ];
259
+
260
+ let currentReview = 0;
261
+ const reviewCard = document.getElementById('reviewCard');
262
+ const reviewDots = document.getElementById('reviewDots');
263
+
264
+ function starRow(stars) {
265
+ let html = "";
266
+ for (let i = 0; i < 5; i++) {
267
+ html += `<span class="${i < stars ? 'text-yellow-400' : 'text-slate-600'} text-sm">★</span>`;
268
+ }
269
+ return html;
270
+ }
271
+
272
+ function renderReview() {
273
+ const r = reviews[currentReview];
274
+ reviewCard.innerHTML = `
275
+ <div class="flex items-center gap-2 mb-2">
276
+ ${starRow(r.stars)}
277
+ </div>
278
+ <p class="text-sm text-slate-200 mb-3">"${r.text}"</p>
279
+ <p class="text-sm font-semibold">${r.name}</p>
280
+ <p class="text-xs text-slate-400">${r.role}</p>
281
+ `;
282
+
283
+ reviewDots.innerHTML = reviews
284
+ .map(
285
+ (_, i) =>
286
+ `<span class="w-2 h-2 rounded-full ${
287
+ i === currentReview ? 'bg-indigo-400' : 'bg-slate-600'
288
+ }"></span>`
289
+ )
290
+ .join('');
291
+ }
292
+
293
+ function nextReview() {
294
+ currentReview = (currentReview + 1) % reviews.length;
295
+ renderReview();
296
+ }
297
+
298
+ function prevReview() {
299
+ currentReview = (currentReview - 1 + reviews.length) % reviews.length;
300
+ renderReview();
301
+ }
302
+
303
+ document.getElementById('nextReview').onclick = nextReview;
304
+ document.getElementById('prevReview').onclick = prevReview;
305
+
306
+ // Auto-slide every 5 seconds
307
+ let reviewTimer = setInterval(nextReview, 5000);
308
+ // Reset timer on manual click
309
+ document.getElementById('nextReview').addEventListener('click', () => {
310
+ clearInterval(reviewTimer);
311
+ reviewTimer = setInterval(nextReview, 5000);
312
+ });
313
+ document.getElementById('prevReview').addEventListener('click', () => {
314
+ clearInterval(reviewTimer);
315
+ reviewTimer = setInterval(nextReview, 5000);
316
+ });
317
+
318
+ renderReview();
319
+ </script>
320
+ </body>
321
+ </html>
grammar.html ADDED
@@ -0,0 +1,823 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Grammar Check – TrueWrite Scan</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <!-- jsPDF (no integrity, to avoid SRI issues locally) -->
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
10
+ </head>
11
+ <body class="bg-gradient-to-br from-slate-950 via-slate-900 to-indigo-950 text-white min-h-screen flex flex-col">
12
+ <!-- Glow background -->
13
+ <div class="pointer-events-none fixed inset-0 opacity-40 blur-3xl"
14
+ style="background: radial-gradient(circle at 0% 0%, rgba(59,130,246,0.35), transparent 55%), radial-gradient(circle at 80% 80%, rgba(16,185,129,0.28), transparent 55%);">
15
+ </div>
16
+
17
+ <main class="relative flex-1 max-w-6xl mx-auto px-4 py-8 z-10">
18
+ <!-- Top bar -->
19
+ <div class="flex items-center justify-between mb-6">
20
+ <div class="flex items-center gap-3">
21
+ <div>
22
+ <img src="logo.png" alt="TrueWrite Scan Logo" class="w-10 h-10 rounded-xl shadow-lg shadow-indigo-500/40">
23
+ </div>
24
+ <div>
25
+ <h1 class="text-2xl md:text-3xl font-extrabold tracking-tight">
26
+ TrueWrite <span class="text-emerald-300">Scan</span>
27
+ </h1>
28
+ <p class="text-[11px] text-slate-300 uppercase tracking-[0.25em]">
29
+ Grammar Rectifier
30
+ </p>
31
+ </div>
32
+ </div>
33
+ <a href="dashboard.html"
34
+ 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">
35
+ ← Back to dashboard
36
+ </a>
37
+ </div>
38
+
39
+ <!-- Tool navigation -->
40
+ <nav class="mb-6">
41
+ <div class="inline-flex items-center gap-1 rounded-full bg-slate-900/80 border border-slate-800 p-1 text-xs">
42
+ <a href="grammar.html"
43
+ class="px-3 py-1.5 rounded-full bg-emerald-500 text-white font-medium shadow shadow-emerald-500/40"
44
+ aria-current="page">
45
+ Grammar
46
+ </a>
47
+ <a href="plagiarism.html"
48
+ class="px-3 py-1.5 rounded-full text-slate-300 hover:bg-slate-800">
49
+ Plagiarism
50
+ </a>
51
+ <a href="ai-check.html"
52
+ class="px-3 py-1.5 rounded-full text-slate-300 hover:bg-slate-800">
53
+ AI Content
54
+ </a>
55
+ </div>
56
+ </nav>
57
+
58
+ <div class="grid md:grid-cols-2 gap-6">
59
+ <!-- LEFT: input -->
60
+ <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">
61
+ <header class="mb-4">
62
+ <h2 class="text-lg md:text-xl font-semibold">Input text or upload file</h2>
63
+ <p class="text-xs text-slate-300 mt-1">
64
+ Large files are trimmed server-side for safety. Upload size limit is 15 MB.
65
+ </p>
66
+ </header>
67
+
68
+ <!-- Drag & drop -->
69
+ <div id="dropZone"
70
+ 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-emerald-400/80 hover:bg-slate-900/70">
71
+ <div class="flex items-center gap-3">
72
+ <div class="w-8 h-8 rounded-full bg-slate-800/80 flex items-center justify-center">
73
+ <span class="text-lg">📂</span>
74
+ </div>
75
+ <div>
76
+ <p class="font-medium text-slate-100">Drag & drop your file here</p>
77
+ <p class="text-[11px] text-slate-400">
78
+ Supported: .txt, .pdf, .docx (max 15MB)
79
+ </p>
80
+ </div>
81
+ </div>
82
+ <div class="flex flex-col items-end gap-1">
83
+ <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">
84
+ Browse
85
+ <input id="fileInput" type="file" accept=".txt,.pdf,.docx" class="hidden" />
86
+ </label>
87
+ <span id="fileName" class="text-[10px] text-slate-400 max-w-[150px] truncate"></span>
88
+ </div>
89
+ </div>
90
+
91
+ <div id="statusTiny" class="text-[11px] text-slate-400 mb-2">
92
+ Ready · No file selected
93
+ </div>
94
+
95
+ <!-- Paste / textarea -->
96
+ <div class="flex gap-2 items-center mb-1">
97
+ <button id="pasteBtn"
98
+ class="text-[11px] px-3 py-1 rounded-full border border-slate-600/80 bg-slate-900/70 hover:bg-slate-800/90">
99
+ Paste text from clipboard
100
+ </button>
101
+ <span class="text-[11px] text-slate-500">or type directly below</span>
102
+ </div>
103
+
104
+ <!-- Word count -->
105
+ <div class="flex justify-end mb-2">
106
+ <span id="wordCount" class="text-[11px] text-slate-400">0 / 1000 words</span>
107
+ </div>
108
+
109
+ <textarea id="inputText" rows="10"
110
+ 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-emerald-500/70 focus:border-emerald-500/60 placeholder:text-slate-500"
111
+ placeholder="Paste your content here or use the drag-and-drop box above..."></textarea>
112
+
113
+ <!-- Buttons -->
114
+ <div class="mt-4 flex flex-wrap gap-2 items-center">
115
+ <button id="checkBtn"
116
+ class="px-5 py-2 rounded-xl bg-emerald-500 hover:bg-emerald-600 text-sm font-semibold shadow-lg shadow-emerald-500/40 flex items-center gap-2">
117
+ <svg id="spinner"
118
+ class="hidden animate-spin h-4 w-4 text-white"
119
+ xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
120
+ <circle class="opacity-25" cx="12" cy="12" r="10"
121
+ stroke="currentColor" stroke-width="4"></circle>
122
+ <path class="opacity-75" fill="currentColor"
123
+ d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"></path>
124
+ </svg>
125
+ <span id="checkLabel">Check Grammar</span>
126
+ </button>
127
+
128
+ <!-- Download report button: behavior from file 2 -->
129
+ <button id="downloadBtn"
130
+ 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">
131
+ Download report as PDF
132
+ </button>
133
+ </div>
134
+ </section>
135
+
136
+ <!-- RIGHT: result -->
137
+ <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 text-sm">
138
+ <header class="mb-3">
139
+ <h2 class="text-lg font-semibold">Result</h2>
140
+ <p class="text-[11px] text-slate-400">
141
+ Corrected text and statistics appear here after each scan.
142
+ </p>
143
+ </header>
144
+
145
+ <!-- Progress bar -->
146
+ <div class="mb-3">
147
+ <div class="h-1.5 w-full rounded-full bg-slate-800/80 overflow-hidden">
148
+ <div id="progressBar" class="h-full w-0 bg-gradient-to-r from-emerald-400 via-teal-400 to-sky-400 transition-[width] duration-200 ease-out"></div>
149
+ </div>
150
+ <div id="progressText" class="text-[11px] text-slate-400 mt-1">Idle</div>
151
+ </div>
152
+
153
+ <div class="grid gap-3 mb-2">
154
+ <div class="flex flex-wrap gap-4 text-[12px] text-slate-200">
155
+ <div>Words analysed: <span id="statWords" class="font-semibold text-emerald-300">0</span></div>
156
+ <div>Corrections: <span id="statCorrections" class="font-semibold text-emerald-300">0</span></div>
157
+ </div>
158
+ <p id="summary" class="text-[13px] text-slate-200">
159
+ Run a check to see corrected text and a brief summary of edits.
160
+ </p>
161
+ <!-- Extra corrections info area from file 1 -->
162
+ <div id="corrections" class="text-[11px] text-slate-300"></div>
163
+ </div>
164
+
165
+ <div class="flex-1">
166
+ <div class="bg-slate-950/60 border border-slate-800/80 rounded-2xl p-3 flex flex-col h-full">
167
+ <p class="text-[11px] text-slate-400 mb-1">Corrected text</p>
168
+ <textarea id="correctedText"
169
+ class="flex-1 w-full bg-transparent text-xs md:text-[13px] resize-none focus:outline-none min-h-[260px]"
170
+ placeholder="Corrected version will appear here..." readonly></textarea>
171
+ </div>
172
+
173
+ <!-- Hidden original preview holder so JS still works, but nothing is shown -->
174
+ <div id="originalPreview" class="hidden"></div>
175
+ </div>
176
+ </section>
177
+ </div>
178
+
179
+ <!-- ========= Info sections from file 1 ========= -->
180
+
181
+ <section class="mt-10 space-y-4">
182
+ <h2 class="text-xl md:text-2xl font-semibold">Why choose this tool?</h2>
183
+ <p class="text-sm text-slate-300">
184
+ Inspired by tools like QuillBot, this page gives you a simple, focused space to check
185
+ short pieces of writing quickly before you submit or share them.
186
+ </p>
187
+
188
+ <div class="grid md:grid-cols-3 gap-5 mt-3">
189
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
190
+ <div class="flex items-center gap-3 mb-2">
191
+ <div class="w-9 h-9 rounded-full bg-indigo-500/20 flex items-center justify-center">
192
+ <span class="text-lg">⚡</span>
193
+ </div>
194
+ <p class="font-semibold text-sm">Fast one-click review</p>
195
+ </div>
196
+ <p class="text-xs text-slate-300">
197
+ Paste, click, and instantly get a cleaner version of your text without any sign-up
198
+ or complicated options.
199
+ </p>
200
+ </div>
201
+
202
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
203
+ <div class="flex items-center gap-3 mb-2">
204
+ <div class="w-9 h-9 rounded-full bg-emerald-500/20 flex items-center justify-center">
205
+ <span class="text-lg">🎯</span>
206
+ </div>
207
+ <p class="font-semibold text-sm">Focus on basics that matter</p>
208
+ </div>
209
+ <p class="text-xs text-slate-300">
210
+ Targets the most common issues students face: extra spaces, lowercase “i”,
211
+ sentence starts, and missing punctuation.
212
+ </p>
213
+ </div>
214
+
215
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
216
+ <div class="flex items-center gap-3 mb-2">
217
+ <div class="w-9 h-9 rounded-full bg-fuchsia-500/20 flex items-center justify-center">
218
+ <span class="text-lg">📄</span>
219
+ </div>
220
+ <p class="font-semibold text-sm">Instant PDF summaries</p>
221
+ </div>
222
+ <p class="text-xs text-slate-300">
223
+ Export a quick PDF report for your records or to attach with your assignment
224
+ submissions.
225
+ </p>
226
+ </div>
227
+ </div>
228
+ </section>
229
+
230
+ <section class="mt-10 space-y-4">
231
+ <h2 class="text-xl md:text-2xl font-semibold">Who can use this tool?</h2>
232
+ <p class="text-sm text-slate-300">
233
+ This grammar checker is designed as a lightweight helper for anyone who wants a final
234
+ polish before sending or submitting text.
235
+ </p>
236
+
237
+ <div class="grid md:grid-cols-3 gap-5 mt-3">
238
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
239
+ <div class="flex items-center gap-3 mb-2">
240
+ <div class="w-9 h-9 rounded-full bg-sky-500/20 flex items-center justify-center">
241
+ <span class="text-lg">👨‍🎓</span>
242
+ </div>
243
+ <p class="font-semibold text-sm">Students</p>
244
+ </div>
245
+ <p class="text-xs text-slate-300">
246
+ Quickly check lab reports, assignments, mini-projects, and emails to faculty or
247
+ supervisors.
248
+ </p>
249
+ </div>
250
+
251
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
252
+ <div class="flex items-center gap-3 mb-2">
253
+ <div class="w-9 h-9 rounded-full bg-amber-500/20 flex items-center justify-center">
254
+ <span class="text-lg">👩‍🏫</span>
255
+ </div>
256
+ <p class="font-semibold text-sm">Teachers & mentors</p>
257
+ </div>
258
+ <p class="text-xs text-slate-300">
259
+ Use it as a quick demo in class to show students how common grammar issues can be
260
+ auto-detected.
261
+ </p>
262
+ </div>
263
+
264
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
265
+ <div class="flex items-center gap-3 mb-2">
266
+ <div class="w-9 h-9 rounded-full bg-rose-500/20 flex items-center justify-center">
267
+ <span class="text-lg">💼</span>
268
+ </div>
269
+ <p class="font-semibold text-sm">Professionals & creators</p>
270
+ </div>
271
+ <p class="text-xs text-slate-300">
272
+ Clean up short posts, descriptions, and emails before sharing them with clients,
273
+ teams, or on social media.
274
+ </p>
275
+ </div>
276
+ </div>
277
+ </section>
278
+
279
+ <!-- How it works (frontend-focused) from file 1 -->
280
+ <section class="mt-10 space-y-4">
281
+ <h2 class="text-xl md:text-2xl font-semibold">How does this work?</h2>
282
+ <p class="text-sm text-slate-300">
283
+ Behind the scenes, prefers a neural model (GECToR), then falls back to LanguageTool, and finally to a
284
+ lightweight heuristic that do the necessary corrections.
285
+ </p>
286
+
287
+ <div class="grid md:grid-cols-3 gap-5 mt-3">
288
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
289
+ <div class="flex items-center gap-3 mb-2">
290
+ <div class="w-9 h-9 rounded-full bg-indigo-500/20 flex items-center justify-center">
291
+ <span class="text-lg">1️⃣</span>
292
+ </div>
293
+ <p class="font-semibold text-sm">Paste & analyse</p>
294
+ </div>
295
+ <p class="text-xs text-slate-300">
296
+ You paste your text (up to ~1000 words). The tool counts words and prepares it for
297
+ analysis.
298
+ </p>
299
+ </div>
300
+
301
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
302
+ <div class="flex items-center gap-3 mb-2">
303
+ <div class="w-9 h-9 rounded-full bg-emerald-500/20 flex items-center justify-center">
304
+ <span class="text-lg">2️⃣</span>
305
+ </div>
306
+ <p class="font-semibold text-sm">Apply smart rules and models</p>
307
+ </div>
308
+ <p class="text-xs text-slate-300">
309
+ GECToR model rewrites sentences word-by-word, focusing on grammar, agreement, and spelling.
310
+ JavaScript rules can fix double spaces, lowercase “i”, sentence capitalization, and
311
+ end punctuation. Then the text is sent to a neural grammar model on the server for deeper fixes.
312
+ </p>
313
+ </div>
314
+
315
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
316
+ <div class="flex items-center gap-3 mb-2">
317
+ <div class="w-9 h-9 rounded-full bg-fuchsia-500/20 flex items-center justify-center">
318
+ <span class="text-lg">3️⃣</span>
319
+ </div>
320
+ <p class="font-semibold text-sm">Review & export</p>
321
+ </div>
322
+ <p class="text-xs text-slate-300">
323
+ You compare the original and corrected text, then optionally export a PDF summary
324
+ for future reference.
325
+ </p>
326
+ </div>
327
+ </div>
328
+ </section>
329
+
330
+ <!-- Reviews slider from file 1 -->
331
+ <section class="mt-12">
332
+ <h2 class="text-xl md:text-2xl font-semibold mb-3">What users say about this grammar tool</h2>
333
+ <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-5 md:p-6 relative overflow-hidden">
334
+ <div id="reviewCard" class="transition-all duration-500">
335
+ <!-- filled by JS -->
336
+ </div>
337
+
338
+ <div class="flex items-center justify-between mt-4">
339
+ <div id="reviewDots" class="flex gap-1.5"></div>
340
+ <div class="flex gap-2">
341
+ <button id="prevReview"
342
+ class="w-8 h-8 rounded-full border border-slate-600 flex items-center justify-center text-xs hover:bg-slate-800">
343
+
344
+ </button>
345
+ <button id="nextReview"
346
+ class="w-8 h-8 rounded-full border border-slate-600 flex items-center justify-center text-xs hover:bg-slate-800">
347
+
348
+ </button>
349
+ </div>
350
+ </div>
351
+ </div>
352
+ </section>
353
+
354
+ </main>
355
+
356
+ <!-- Footer -->
357
+ <footer class="relative border-t border-slate-800/80 bg-slate-950/70 backdrop-blur">
358
+ <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">
359
+ <p>© 2025 TrueWrite Scan. All rights reserved.</p>
360
+ <p>Designed & developed by <span class="text-emerald-300 font-medium">Gopal Krushna Mahapatra</span>.</p>
361
+ </div>
362
+ </footer>
363
+
364
+ <script>
365
+ const BACKEND_URL = "http://localhost:8000";
366
+ const token = localStorage.getItem("truewriteToken");
367
+ const user = localStorage.getItem("truewriteUser");
368
+ if (!token || !user) {
369
+ window.location.href = "login.html";
370
+ }
371
+
372
+ // jsPDF helper (from file 2)
373
+ function getJsPDF() {
374
+ if (window.jspdf && window.jspdf.jsPDF) return window.jspdf.jsPDF;
375
+ if (window.jsPDF) return window.jsPDF;
376
+ alert("PDF library (jsPDF) did not load. Check your internet connection or CDN access.");
377
+ return null;
378
+ }
379
+
380
+ // DOM refs (superset)
381
+ const dropZone = document.getElementById("dropZone");
382
+ const fileInput = document.getElementById("fileInput");
383
+ const fileNameSpan = document.getElementById("fileName");
384
+ const statusTiny = document.getElementById("statusTiny");
385
+ const pasteBtn = document.getElementById("pasteBtn");
386
+ const textarea = document.getElementById("inputText");
387
+ const checkBtn = document.getElementById("checkBtn");
388
+ const checkLabel = document.getElementById("checkLabel");
389
+ const spinner = document.getElementById("spinner");
390
+ const progressBar = document.getElementById("progressBar");
391
+ const progressText = document.getElementById("progressText");
392
+ const statWords = document.getElementById("statWords");
393
+ const statCorrections = document.getElementById("statCorrections");
394
+ const summaryEl = document.getElementById("summary");
395
+ const correctionsDiv = document.getElementById("corrections");
396
+ const correctedTextEl = document.getElementById("correctedText");
397
+ const originalPreview = document.getElementById("originalPreview");
398
+ const downloadBtn = document.getElementById("downloadBtn");
399
+ const wordCountSpan = document.getElementById("wordCount");
400
+
401
+ let lastResult = null;
402
+ let progressTimer = null;
403
+
404
+ function setLoading(on) {
405
+ if (on) {
406
+ spinner.classList.remove("hidden");
407
+ checkLabel.textContent = "Checking...";
408
+ checkBtn.disabled = true;
409
+ checkBtn.classList.add("opacity-60", "cursor-not-allowed");
410
+
411
+ let pct = 0;
412
+ progressBar.style.width = "0%";
413
+ progressText.textContent = "Uploading / processing...";
414
+ progressTimer = setInterval(() => {
415
+ pct += Math.random() * 10;
416
+ if (pct > 90) pct = 90;
417
+ progressBar.style.width = pct + "%";
418
+ }, 200);
419
+ } else {
420
+ spinner.classList.add("hidden");
421
+ checkLabel.textContent = "Check Grammar";
422
+ checkBtn.disabled = false;
423
+ checkBtn.classList.remove("opacity-60", "cursor-not-allowed");
424
+ if (progressTimer) {
425
+ clearInterval(progressTimer);
426
+ progressTimer = null;
427
+ }
428
+ progressBar.style.width = "100%";
429
+ progressText.textContent = "Done";
430
+ setTimeout(() => {
431
+ progressBar.style.width = "0%";
432
+ progressText.textContent = "Idle";
433
+ }, 700);
434
+ }
435
+ }
436
+
437
+ // Word counting & 1000-word indicator (from file 1)
438
+ function countWords(text) {
439
+ if (!text || !text.trim()) return 0;
440
+ return text.trim().split(/\s+/).length;
441
+ }
442
+
443
+ function updateWordCount() {
444
+ const words = countWords(textarea.value);
445
+ if (wordCountSpan) {
446
+ wordCountSpan.textContent = `${words} / 1000 words`;
447
+ }
448
+ if (words > 1000) {
449
+ statusTiny.textContent = "Text longer than 1000 words — extra words will be ignored by the server.";
450
+ }
451
+ }
452
+
453
+ textarea.addEventListener("input", updateWordCount);
454
+
455
+ // Simple local heuristic rules (from file 1)
456
+ function applyLocalGrammarRules(rawText) {
457
+ let corrections = 0;
458
+ let text = rawText || "";
459
+
460
+ const beforeDoubleSpace = text;
461
+ text = text.replace(/\s{2,}/g, " ");
462
+ if (text !== beforeDoubleSpace) corrections++;
463
+
464
+ const beforeI = text;
465
+ text = text.replace(/\bi\b/g, "I");
466
+ if (text !== beforeI) corrections++;
467
+
468
+ const beforeSentence = text;
469
+ text = text.replace(/(^\s*\w|[.!?]\s+\w)/g, c => c.toUpperCase());
470
+ if (text !== beforeSentence) corrections++;
471
+
472
+ if (text.trim() && !/[.!?]\s*$/.test(text.trim())) {
473
+ text = text.trim() + ".";
474
+ corrections++;
475
+ }
476
+
477
+ return { corrected: text, corrections };
478
+ }
479
+
480
+ // Drag & drop (from file 2)
481
+ ["dragenter", "dragover"].forEach(evt => {
482
+ dropZone.addEventListener(evt, e => {
483
+ e.preventDefault();
484
+ e.stopPropagation();
485
+ dropZone.classList.add("border-emerald-400/80", "bg-slate-900/80");
486
+ });
487
+ });
488
+
489
+ ["dragleave", "drop"].forEach(evt => {
490
+ dropZone.addEventListener(evt, e => {
491
+ e.preventDefault();
492
+ e.stopPropagation();
493
+ dropZone.classList.remove("border-emerald-400/80", "bg-slate-900/80");
494
+ });
495
+ });
496
+
497
+ dropZone.addEventListener("drop", e => {
498
+ const dt = e.dataTransfer;
499
+ const files = dt.files;
500
+ if (!files || !files.length) return;
501
+ const file = files[0];
502
+ fileInput.files = files;
503
+ handleFileSelected(file);
504
+ });
505
+
506
+ fileInput.addEventListener("change", e => {
507
+ const file = e.target.files[0];
508
+ if (!file) {
509
+ fileNameSpan.textContent = "";
510
+ statusTiny.textContent = "Ready · No file selected";
511
+ textarea.value = "";
512
+ originalPreview.textContent = "";
513
+ updateWordCount();
514
+ return;
515
+ }
516
+ handleFileSelected(file);
517
+ });
518
+
519
+ function handleFileSelected(file) {
520
+ fileNameSpan.textContent = file.name;
521
+ const fname = file.name.toLowerCase();
522
+ if (file.type === "text/plain" || fname.endsWith(".txt")) {
523
+ const reader = new FileReader();
524
+ reader.onload = () => {
525
+ textarea.value = reader.result;
526
+ statusTiny.textContent = `${file.name} loaded as text`;
527
+ originalPreview.textContent = textarea.value.slice(0, 1500);
528
+ updateWordCount();
529
+ };
530
+ reader.readAsText(file);
531
+ } else {
532
+ textarea.value = "";
533
+ originalPreview.textContent = "";
534
+ statusTiny.textContent = `${file.name} selected. Will be parsed on backend.`;
535
+ updateWordCount();
536
+ }
537
+ }
538
+
539
+ // Paste
540
+ pasteBtn.onclick = async () => {
541
+ try {
542
+ const txt = await navigator.clipboard.readText();
543
+ textarea.value = txt;
544
+ originalPreview.textContent = txt.slice(0, 1500);
545
+ statusTiny.textContent = "Text pasted from clipboard";
546
+ updateWordCount();
547
+ } catch {
548
+ alert("Clipboard access blocked by browser.");
549
+ }
550
+ };
551
+
552
+ // API calls (from file 2)
553
+ async function callGrammarText(text) {
554
+ const res = await fetch(`${BACKEND_URL}/api/grammar-check`, {
555
+ method: "POST",
556
+ headers: {
557
+ "Content-Type": "application/json",
558
+ "Authorization": `Bearer ${token}`
559
+ },
560
+ body: JSON.stringify({ text })
561
+ });
562
+ const data = await res.json();
563
+ if (!res.ok) throw new Error(data.detail || data.error || "Server error");
564
+ return data;
565
+ }
566
+
567
+ async function callGrammarFile(file) {
568
+ const form = new FormData();
569
+ form.append("file", file, file.name);
570
+ const res = await fetch(`${BACKEND_URL}/api/grammar-check-file`, {
571
+ method: "POST",
572
+ headers: { "Authorization": `Bearer ${token}` },
573
+ body: form
574
+ });
575
+ const data = await res.json();
576
+ if (!res.ok) throw new Error(data.detail || data.error || "Server error");
577
+ return data;
578
+ }
579
+
580
+ // Main check: file 2 logic + file 1 fallback
581
+ checkBtn.onclick = async () => {
582
+ const text = textarea.value.trim();
583
+ const file = fileInput.files[0];
584
+
585
+ if (!text && !file) {
586
+ alert("Please paste text or upload a file first.");
587
+ return;
588
+ }
589
+
590
+ setLoading(true);
591
+ summaryEl.textContent = "Checking...";
592
+ correctionsDiv.textContent = "";
593
+ correctedTextEl.value = "";
594
+ statWords.textContent = "0";
595
+ statCorrections.textContent = "0";
596
+ lastResult = null;
597
+
598
+ // Local heuristic used only as fallback
599
+ const localWords = countWords(text);
600
+ const localTrimmedText = localWords > 1000
601
+ ? text.split(/\s+/).slice(0, 1000).join(" ")
602
+ : text;
603
+ const localRes = applyLocalGrammarRules(localTrimmedText);
604
+
605
+ try {
606
+ let data;
607
+ if (file && !(file.type === "text/plain" || file.name.toLowerCase().endsWith(".txt"))) {
608
+ data = await callGrammarFile(file);
609
+ } else {
610
+ const payloadText = text || (file ? await file.text() : "");
611
+ data = await callGrammarText(payloadText);
612
+ }
613
+
614
+ const words = data.original_words ?? localWords ?? 0;
615
+ const corrections = data.corrections ?? 0;
616
+ const corrected = data.corrected_text ?? localRes.corrected ?? "";
617
+ const summary = data.summary ?? "";
618
+
619
+ statWords.textContent = String(words);
620
+ statCorrections.textContent = String(corrections);
621
+ correctedTextEl.value = corrected;
622
+ summaryEl.textContent = summary || "Grammar check completed.";
623
+
624
+ correctionsDiv.innerHTML = `
625
+ <div>Corrections made (server): <strong>${corrections}</strong></div>
626
+ `;
627
+
628
+ if (!originalPreview.textContent) {
629
+ originalPreview.textContent = (textarea.value || "").slice(0, 1500);
630
+ }
631
+
632
+ lastResult = {
633
+ original: textarea.value || (file ? "[uploaded file]" : ""),
634
+ corrected,
635
+ words,
636
+ corrections,
637
+ summary
638
+ };
639
+ statusTiny.textContent = "Done";
640
+ } catch (err) {
641
+ console.error(err);
642
+ // Fallback to local heuristic result from file 1
643
+ const words = localWords;
644
+ const corrections = localRes.corrections;
645
+ const corrected = localRes.corrected;
646
+
647
+ statWords.textContent = String(words);
648
+ statCorrections.textContent = String(corrections);
649
+ correctedTextEl.value = corrected;
650
+ summaryEl.textContent =
651
+ "Server error — showing local heuristic corrections instead. " + (err.message || "");
652
+
653
+ correctionsDiv.innerHTML = `
654
+ <div class="text-xs text-amber-300">Server error: ${escapeHtml(err.message || String(err))}</div>
655
+ <div class="mt-2">Local heuristic corrections applied: <strong>${corrections}</strong></div>
656
+ `;
657
+
658
+ if (!originalPreview.textContent) {
659
+ originalPreview.textContent = (textarea.value || "").slice(0, 1500);
660
+ }
661
+
662
+ lastResult = {
663
+ original: textarea.value || (file ? "[uploaded file]" : ""),
664
+ corrected,
665
+ words,
666
+ corrections,
667
+ summary: "Local heuristic corrections only"
668
+ };
669
+ statusTiny.textContent = "Error (using local corrections)";
670
+ } finally {
671
+ setLoading(false);
672
+ }
673
+ };
674
+
675
+ // PDF download: logic from file 2
676
+ downloadBtn.onclick = () => {
677
+ if (!lastResult) {
678
+ alert("Run at least one grammar check before downloading a report.");
679
+ return;
680
+ }
681
+
682
+ const jsPDF = getJsPDF();
683
+ if (!jsPDF) return;
684
+
685
+ const doc = new jsPDF({ unit: "pt", format: "a4" });
686
+ let y = 40;
687
+
688
+ doc.setFontSize(16);
689
+ doc.text("TrueWrite Scan — Grammar Report", 40, y); y += 22;
690
+
691
+ doc.setFontSize(11);
692
+ doc.text("Generated: " + new Date().toLocaleString(), 40, y); y += 18;
693
+ doc.text("User: " + (user || "N/A"), 40, y); y += 18;
694
+
695
+ doc.setFontSize(12);
696
+ doc.text(`Words analysed: ${lastResult.words}`, 40, y); y += 16;
697
+ doc.text(`Corrections: ${lastResult.corrections}`, 40, y); y += 20;
698
+
699
+ if (lastResult.summary) {
700
+ doc.text("Summary:", 40, y); y += 16;
701
+ doc.setFontSize(10);
702
+ let lines = doc.splitTextToSize(lastResult.summary, 520);
703
+ doc.text(lines, 40, y);
704
+ y += lines.length * 12 + 10;
705
+ }
706
+
707
+ doc.setFontSize(11);
708
+ doc.text("--- Original text (truncated) ---", 40, y); y += 16;
709
+ doc.setFontSize(9);
710
+ const original = lastResult.original || "";
711
+ const originalTrunc = original.length > 2500 ? original.slice(0, 2500) + "\n\n[TRUNCATED]" : original;
712
+ let lines = doc.splitTextToSize(originalTrunc, 520);
713
+ doc.text(lines, 40, y);
714
+ y += lines.length * 10 + 12;
715
+
716
+ doc.setFontSize(11);
717
+ doc.text("--- Corrected text (truncated) ---", 40, y); y += 16;
718
+ doc.setFontSize(9);
719
+ const corrected = lastResult.corrected || "";
720
+ const correctedTrunc = corrected.length > 2500 ? corrected.slice(0, 2500) + "\n\n[TRUNCATED]" : corrected;
721
+ lines = doc.splitTextToSize(correctedTrunc, 520);
722
+ doc.text(lines, 40, y);
723
+
724
+ doc.save("truewrite-grammar-report.pdf");
725
+ };
726
+
727
+ function escapeHtml(str) {
728
+ if (!str) return "";
729
+ return String(str).replace(/[&<>"'`]/g, s => ({
730
+ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;", "`": "&#96;"
731
+ }[s]));
732
+ }
733
+
734
+ // ===== Reviews slider (from file 1) =====
735
+ const reviews = [
736
+ {
737
+ name: "Aarav S.",
738
+ role: "Final-year B.Tech student",
739
+ text: "I use this grammar page before every assignment submission. It quickly cleans up the most obvious mistakes.",
740
+ stars: 5
741
+ },
742
+ {
743
+ name: "Priya K.",
744
+ role: "M.Tech researcher",
745
+ text: "The corrected text + PDF report help me keep a record of changes when I work on my thesis chapters.",
746
+ stars: 5
747
+ },
748
+ {
749
+ name: "Rahul M.",
750
+ role: "Content creator",
751
+ text: "Simple, fast, no distractions. Perfect when I just want to sanity-check captions and short posts.",
752
+ stars: 4
753
+ },
754
+ {
755
+ name: "Sneha R.",
756
+ role: "English learner",
757
+ text: "Seeing how my sentences are automatically fixed is helping me understand grammar rules better.",
758
+ stars: 5
759
+ },
760
+ {
761
+ name: "Vikram J.",
762
+ role: "Developer",
763
+ text: "Nice example of a purely front-end grammar tool. The UI feels inspired by popular tools like QuillBot.",
764
+ stars: 4
765
+ }
766
+ ];
767
+
768
+ let currentReview = 0;
769
+ const reviewCard = document.getElementById("reviewCard");
770
+ const reviewDots = document.getElementById("reviewDots");
771
+
772
+ function createStarRow(stars) {
773
+ let html = "";
774
+ for (let i = 0; i < 5; i++) {
775
+ html += `<span class="${i < stars ? "text-yellow-400" : "text-slate-600"} text-sm">★</span>`;
776
+ }
777
+ return html;
778
+ }
779
+
780
+ function renderReview() {
781
+ const r = reviews[currentReview];
782
+ reviewCard.innerHTML = `
783
+ <div class="flex items-center gap-2 mb-2">
784
+ ${createStarRow(r.stars)}
785
+ </div>
786
+ <p class="text-sm text-slate-200 mb-3">"${r.text}"</p>
787
+ <p class="text-sm font-semibold">${r.name}</p>
788
+ <p class="text-xs text-slate-400">${r.role}</p>
789
+ `;
790
+ reviewDots.innerHTML = reviews.map((_, i) =>
791
+ `<span class="w-2 h-2 rounded-full ${i === currentReview ? "bg-emerald-400" : "bg-slate-600"}"></span>`
792
+ ).join("");
793
+ }
794
+
795
+ function nextReview() {
796
+ currentReview = (currentReview + 1) % reviews.length;
797
+ renderReview();
798
+ }
799
+
800
+ function prevReview() {
801
+ currentReview = (currentReview - 1 + reviews.length) % reviews.length;
802
+ renderReview();
803
+ }
804
+
805
+ document.getElementById("nextReview").addEventListener("click", () => {
806
+ clearInterval(reviewTimer);
807
+ nextReview();
808
+ reviewTimer = setInterval(nextReview, 6000);
809
+ });
810
+ document.getElementById("prevReview").addEventListener("click", () => {
811
+ clearInterval(reviewTimer);
812
+ prevReview();
813
+ reviewTimer = setInterval(nextReview, 6000);
814
+ });
815
+
816
+ let reviewTimer = setInterval(nextReview, 6000);
817
+ renderReview();
818
+
819
+ // Initial word count
820
+ updateWordCount();
821
+ </script>
822
+ </body>
823
+ </html>
grammar.png ADDED

Git LFS Details

  • SHA256: b5f823909fc10877eecbc660adfeb215df051c6654d0093ffaf4362455badbea
  • Pointer size: 132 Bytes
  • Size of remote file: 6.2 MB
history.html ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>My History – TrueWrite Scan</title>
6
+ <script src="https://cdn.tailwindcss.com"></script>
7
+ </head>
8
+ <body class="bg-slate-950 text-white min-h-screen flex flex-col">
9
+ <header class="border-b border-slate-800 bg-slate-950/90 backdrop-blur sticky top-0 z-20">
10
+ <div class="max-w-6xl mx-auto px-4 py-3 flex items-center justify-between">
11
+ <div class="flex items-center gap-3">
12
+ <img src="images/logo.png" alt="TrueWrite Scan logo"
13
+ class="w-9 h-9 rounded-2xl border border-slate-700 object-cover" />
14
+ <div>
15
+ <p class="text-lg md:text-xl font-extrabold tracking-tight">
16
+ <span class="bg-gradient-to-r from-indigo-400 via-fuchsia-400 to-emerald-400 bg-clip-text text-transparent">
17
+ TrueWrite
18
+ </span> History
19
+ </p>
20
+ <p class="text-[11px] text-slate-400 uppercase tracking-[0.22em]">
21
+ Recent grammar, plagiarism & AI scans
22
+ </p>
23
+ </div>
24
+ </div>
25
+ <a href="dashboard.html"
26
+ class="text-xs px-3 py-1 rounded-full border border-slate-600 hover:bg-slate-800">
27
+ ← Back to dashboard
28
+ </a>
29
+ </div>
30
+ </header>
31
+
32
+ <main class="flex-1 max-w-6xl mx-auto px-4 py-8">
33
+ <h1 class="text-xl md:text-2xl font-semibold mb-3">My scan history</h1>
34
+ <p class="text-sm text-slate-300 mb-4">
35
+ Each entry represents one run of Grammar, Plagiarism, or AI content check. Stored per account
36
+ on the backend using SQLite.
37
+ </p>
38
+
39
+ <div id="historyContainer"
40
+ class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4 text-sm">
41
+ <p id="loading" class="text-xs text-slate-400">Loading history...</p>
42
+ <table id="historyTable" class="w-full text-xs hidden">
43
+ <thead class="border-b border-slate-800 text-slate-400">
44
+ <tr>
45
+ <th class="py-2 text-left">Time (UTC)</th>
46
+ <th class="py-2 text-left">Tool</th>
47
+ <th class="py-2 text-left">Summary</th>
48
+ <th class="py-2 text-left">Snippet</th>
49
+ </tr>
50
+ </thead>
51
+ <tbody id="historyBody" class="divide-y divide-slate-800"></tbody>
52
+ </table>
53
+ <p id="emptyMsg" class="text-xs text-slate-400 hidden">No history yet. Run a check to see it here.</p>
54
+ </div>
55
+ </main>
56
+
57
+ <footer class="border-t border-slate-800">
58
+ <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">
59
+ <p>© 2025 TrueWrite Scan. All rights reserved.</p>
60
+ <p>Designed & developed by <span class="text-indigo-300 font-medium">Gopal Krushna Mahapatra</span>.</p>
61
+ </div>
62
+ </footer>
63
+
64
+ <script>
65
+ const BACKEND_URL = "http://localhost:8000";
66
+ const token = localStorage.getItem("truewriteToken");
67
+ const user = localStorage.getItem("truewriteUser");
68
+ if (!token || !user) {
69
+ window.location.href = "login.html";
70
+ }
71
+
72
+ const loading = document.getElementById("loading");
73
+ const table = document.getElementById("historyTable");
74
+ const bodyEl = document.getElementById("historyBody");
75
+ const emptyMsg = document.getElementById("emptyMsg");
76
+
77
+ async function loadHistory() {
78
+ try {
79
+ const res = await fetch(`${BACKEND_URL}/api/history`, {
80
+ headers: { "Authorization": `Bearer ${token}` }
81
+ });
82
+ const data = await res.json();
83
+ if (!res.ok) {
84
+ loading.textContent = data.detail || "Failed to load history.";
85
+ return;
86
+ }
87
+
88
+ const items = data.items || [];
89
+ if (items.length === 0) {
90
+ loading.classList.add("hidden");
91
+ emptyMsg.classList.remove("hidden");
92
+ return;
93
+ }
94
+
95
+ bodyEl.innerHTML = items.map(item => {
96
+ const toolLabel =
97
+ item.tool === "grammar" ? "Grammar" :
98
+ item.tool === "plagiarism" ? "Plagiarism" :
99
+ item.tool === "ai" ? "AI Content" :
100
+ item.tool;
101
+ const snippet = (item.input_text || "").slice(0, 60).replace(/\s+/g, " ");
102
+ return `
103
+ <tr>
104
+ <td class="py-2 pr-2 align-top text-slate-300">${item.created_at}</td>
105
+ <td class="py-2 pr-2 align-top text-slate-200">${toolLabel}</td>
106
+ <td class="py-2 pr-2 align-top text-slate-300">${item.summary}</td>
107
+ <td class="py-2 pr-2 align-top text-slate-400">${snippet}${snippet.length === 60 ? "..." : ""}</td>
108
+ </tr>
109
+ `;
110
+ }).join("");
111
+
112
+ loading.classList.add("hidden");
113
+ table.classList.remove("hidden");
114
+ } catch (e) {
115
+ loading.textContent = "Failed to contact backend.";
116
+ }
117
+ }
118
+
119
+ loadHistory();
120
+ </script>
121
+ </body>
122
+ </html>
index.html ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>TrueWrite Scan – Welcome</title>
6
+ <script src="https://cdn.tailwindcss.com"></script>
7
+ <style>
8
+ @keyframes float {
9
+ 0%, 100% { transform: translateY(0); }
10
+ 50% { transform: translateY(-12px); }
11
+ }
12
+ </style>
13
+ </head>
14
+ <body class="bg-slate-950 text-white min-h-screen flex items-center justify-center">
15
+ <div class="absolute inset-0 bg-gradient-to-br from-indigo-500/20 via-purple-500/10 to-emerald-500/20 blur-3xl -z-10"></div>
16
+
17
+ <div class="max-w-3xl w-full mx-4 text-center">
18
+ <!-- Logo + brand -->
19
+ <div class="flex items-center justify-center gap-3 mb-6">
20
+ <!-- 🔹 Your logo image here -->
21
+ <img
22
+ src="logo.png"
23
+ alt="TrueWrite Scan logo"
24
+ class="w-11 h-11 rounded-2xl border border-slate-700 object-cover shadow-lg shadow-indigo-500/40"
25
+ />
26
+ <div class="text-left">
27
+ <h1 class="text-3xl md:text-4xl font-extrabold tracking-tight">
28
+ <span class="bg-gradient-to-r from-indigo-400 via-fuchsia-400 to-emerald-400 bg-clip-text text-transparent">
29
+ TrueWrite
30
+ </span>
31
+ <span class="text-slate-100"> Scan</span>
32
+ </h1>
33
+ <p class="text-sm text-slate-300">
34
+ Honest. Original. Authenticated
35
+ </p>
36
+ </div>
37
+ </div>
38
+
39
+ <!-- Hero text -->
40
+ <h2 class="text-2xl md:text-3xl font-semibold mb-3">
41
+ Write with confidence. Check with precision.
42
+ </h2>
43
+ <p class="text-slate-300 max-w-xl mx-auto mb-8">
44
+ Securely scan your assignments, research papers, and articles for Grammar issues,
45
+ Plagiarism, and AI-generated content – all in a single dashboard.
46
+ </p>
47
+
48
+ <!-- Buttons -->
49
+ <div class="flex flex-col sm:flex-row items-center justify-center gap-4 mb-10">
50
+ <a href="signup.html"
51
+ class="px-6 py-3 rounded-full bg-indigo-500 hover:bg-indigo-600 transition shadow-lg shadow-indigo-500/30 font-medium">
52
+ Sign up (New users)
53
+ </a>
54
+ <a href="login.html"
55
+ class="px-6 py-3 rounded-full border border-slate-500 hover:border-white hover:bg-white/5 transition font-medium">
56
+ Login (Existing users)
57
+ </a>
58
+ </div>
59
+
60
+ <!-- Floating feature cards -->
61
+ <div class="grid md:grid-cols-3 gap-4">
62
+ <div class="bg-slate-900/60 rounded-2xl p-4 border border-slate-700/60 animate-[float_5s_ease-in-out_infinite]">
63
+ <h3 class="font-semibold mb-1">Grammar Check</h3>
64
+ <p class="text-xs text-slate-300">Fix spelling, punctuation & all type of grammatical errors.</p>
65
+ </div>
66
+ <div class="bg-slate-900/60 rounded-2xl p-4 border border-slate-700/60 animate-[float_6s_ease-in-out_infinite]">
67
+ <h3 class="font-semibold mb-1">Plagiarism Scan</h3>
68
+ <p class="text-xs text-slate-300">Compare your text against existing text.</p>
69
+ </div>
70
+ <div class="bg-slate-900/60 rounded-2xl p-4 border border-slate-700/60 animate-[float_7s_ease-in-out_infinite]">
71
+ <h3 class="font-semibold mb-1">AI Content Check</h3>
72
+ <p class="text-xs text-slate-300">Estimate whether text looks human-written.</p>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </body>
77
+ </html>
login.html ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Login – TrueWrite Scan</title>
6
+ <script src="https://cdn.tailwindcss.com"></script>
7
+ </head>
8
+ <body class="bg-slate-950 text-white min-h-screen flex items-center justify-center">
9
+ <div class="max-w-md w-full mx-4 bg-slate-900/70 border border-slate-800 rounded-2xl p-6 shadow-xl">
10
+ <h1 class="text-2xl font-bold mb-1">Welcome back</h1>
11
+ <p class="text-xs text-slate-400 mb-4">Login to continue using TrueWrite Scan.</p>
12
+
13
+ <form id="loginForm" class="space-y-4">
14
+ <div>
15
+ <label class="block text-xs mb-1">Email</label>
16
+ <input id="email" type="email" required
17
+ class="w-full px-3 py-2 rounded-lg bg-slate-950 border border-slate-700 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500" />
18
+ </div>
19
+ <div>
20
+ <label class="block text-xs mb-1">Password</label>
21
+ <input id="password" type="password" required
22
+ class="w-full px-3 py-2 rounded-lg bg-slate-950 border border-slate-700 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500" />
23
+ </div>
24
+ <p id="loginError" class="text-xs text-red-400 h-4"></p>
25
+ <button type="submit"
26
+ class="w-full py-2 rounded-lg bg-indigo-500 hover:bg-indigo-600 text-sm font-semibold">
27
+ Login
28
+ </button>
29
+ </form>
30
+
31
+ <p class="mt-4 text-xs text-slate-400 text-center">
32
+ New to TrueWrite Scan?
33
+ <a href="signup.html" class="text-indigo-300 underline underline-offset-2">Create account</a>
34
+ </p>
35
+ </div>
36
+
37
+ <script>
38
+ const BACKEND_URL = "http://localhost:8000";
39
+
40
+ const form = document.getElementById("loginForm");
41
+ const err = document.getElementById("loginError");
42
+
43
+ form.addEventListener("submit", async (e) => {
44
+ e.preventDefault();
45
+ err.textContent = "";
46
+
47
+ const email = document.getElementById("email").value.trim();
48
+ const password = document.getElementById("password").value;
49
+
50
+ try {
51
+ const res = await fetch(`${BACKEND_URL}/api/login`, {
52
+ method: "POST",
53
+ headers: { "Content-Type": "application/json" },
54
+ body: JSON.stringify({ email, password })
55
+ });
56
+
57
+ const data = await res.json();
58
+ if (!res.ok) {
59
+ err.textContent = data.detail || "Login failed";
60
+ return;
61
+ }
62
+
63
+ localStorage.setItem("truewriteToken", data.token);
64
+ localStorage.setItem("truewriteUser", JSON.stringify({
65
+ name: data.name,
66
+ email: data.email
67
+ }));
68
+
69
+ window.location.href = "dashboard.html";
70
+ } catch (e2) {
71
+ err.textContent = "Server error. Is backend running?";
72
+ }
73
+ });
74
+ </script>
75
+ </body>
76
+ </html>
logo.png ADDED

Git LFS Details

  • SHA256: 16219bf337a71de8339299e21502742955bb6b9d01408db07af92a92870b02fb
  • Pointer size: 132 Bytes
  • Size of remote file: 1.86 MB
plagiarism.html ADDED
@@ -0,0 +1,740 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>Plagiarism Check – TrueWrite Scan</title>
6
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <!-- jsPDF (no integrity to avoid local SRI issues) -->
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
10
+ </head>
11
+ <body class="bg-gradient-to-br from-slate-950 via-slate-900 to-violet-950 text-white min-h-screen flex flex-col">
12
+ <main class="flex-1 max-w-6xl mx-auto px-4 py-8">
13
+ <!-- Top bar with logo -->
14
+ <div class="flex items-center justify-between mb-4">
15
+ <div class="flex items-center gap-3">
16
+ <div>
17
+ <img src="logo.png" alt="TrueWrite Scan Logo" class="w-10 h-10 rounded-xl shadow-lg shadow-indigo-500/40">
18
+ </div>
19
+ <div>
20
+ <h1 class="text-2xl md:text-3xl font-extrabold">TrueWrite <span class="text-blue-400">Scan</span></h1>
21
+ <p class="text-[11px] text-slate-300 uppercase tracking-[0.25em]">
22
+ Plagiarism Checker
23
+ </p>
24
+ </div>
25
+ </div>
26
+ <a href="dashboard.html"
27
+ 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">
28
+ ← Back to dashboard
29
+ </a>
30
+ </div>
31
+
32
+ <!-- Tool navigation -->
33
+ <nav class="mb-6">
34
+ <div class="inline-flex items-center gap-1 rounded-full bg-slate-900/80 border border-slate-800 p-1 text-xs">
35
+ <a href="grammar.html"
36
+ class="px-3 py-1.5 rounded-full text-slate-300 hover:bg-slate-800">
37
+ Grammar
38
+ </a>
39
+ <a href="plagiarism.html"
40
+ class="px-3 py-1.5 rounded-full bg-blue-500 text-white font-medium shadow shadow-blue-500/40"
41
+ aria-current="page">
42
+ Plagiarism
43
+ </a>
44
+ <a href="ai-check.html"
45
+ class="px-3 py-1.5 rounded-full text-slate-300 hover:bg-slate-800">
46
+ AI Content
47
+ </a>
48
+ </div>
49
+ </nav>
50
+
51
+ <!-- Main checker grid -->
52
+ <div class="grid md:grid-cols-2 gap-6">
53
+ <!-- LEFT PANEL -->
54
+ <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">
55
+ <header class="mb-4">
56
+ <h2 class="text-lg md:text-xl font-semibold">Analyze your content</h2>
57
+ <p class="text-xs text-slate-300 mt-1">
58
+ Paste text or drop a <span class="font-semibold">.txt / .pdf / .docx</span> file. This gives an idea of ​​how much Plagiarized content there is.
59
+ </p>
60
+ </header>
61
+ <!-- Drag & drop -->
62
+ <div id="dropZone"
63
+ 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-blue-400/80 hover:bg-slate-900/70">
64
+ <div class="flex items-center gap-3">
65
+ <div class="w-8 h-8 rounded-full bg-slate-800/80 flex items-center justify-center">
66
+ <span class="text-lg">📂</span>
67
+ </div>
68
+ <div>
69
+ <div class="font-semibold text-sm">Drag & drop file</div>
70
+ <div class="text-xs text-slate-400">Supported: .txt, .pdf, .docx (max 15MB)</div>
71
+ </div>
72
+ </div>
73
+ <div class="text-right">
74
+ <label class="px-3 py-1 rounded-full border border-slate-600 cursor-pointer bg-slate-800/60 text-xs">
75
+ Browse <input id="fileInput" type="file" accept=".txt,.pdf,.docx" class="hidden" />
76
+ </label>
77
+ <div id="fileName" class="text-xs text-slate-400 mt-1 max-w-[160px] truncate"></div>
78
+ </div>
79
+ </div>
80
+
81
+ <div class="mt-3 text-xs text-slate-400">
82
+ Tip: .txt files preview locally; .pdf/.docx will be uploaded and parsed by the server.
83
+ </div>
84
+
85
+ <div class="mt-3 flex items-center justify-between gap-3">
86
+ <button id="pasteBtn"
87
+ class="text-xs px-3 py-1 rounded-full border border-slate-600 hover:bg-slate-800">
88
+ Paste text
89
+ </button>
90
+ <span id="statusTiny" class="text-xs text-slate-400">Ready</span>
91
+ </div>
92
+
93
+ <textarea id="inputText" rows="12"
94
+ class="w-full mt-3 p-3 bg-slate-950/60 border border-slate-800 rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-[#0487D9]
95
+ placeholder="Paste text here or load a .txt file (preview)."></textarea>
96
+
97
+ <div class="mt-4 flex flex-wrap gap-2 items-center">
98
+ <button id="checkBtn"
99
+ class="px-5 py-2 rounded-xl bg-[#0487D9] hover:bg-[#0487D9] text-sm font-medium flex items-center gap-2">
100
+ <svg id="spinner" class="hidden animate-spin h-4 w-4" viewBox="0 0 24 24" fill="none">
101
+ <circle cx="12" cy="12" r="10" stroke="white" stroke-opacity="0.25" stroke-width="4"></circle>
102
+ <path d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" fill="white" fill-opacity="0.8"></path>
103
+ </svg>
104
+ <span id="checkLabel">Check Plagiarism</span>
105
+ </button>
106
+
107
+ <button id="downloadBtn"
108
+ class="px-5 py-2 rounded-xl bg-slate-800 border border-slate-700 text-sm font-medium">
109
+ Download report as PDF
110
+ </button>
111
+ </div>
112
+ </section>
113
+
114
+ <!-- RIGHT: Result & Progress -->
115
+ <section class="p-5 rounded-3xl bg-slate-900/40 border border-white/5 backdrop-blur text-sm">
116
+ <h2 class="font-semibold text-lg mb-2">Result</h2>
117
+
118
+ <!-- Progress bar -->
119
+ <div class="h-2.5 w-full rounded-full bg-slate-800 overflow-hidden mb-2">
120
+ <div id="progressBar" class="h-full w-0 bg-[#0487D9] transition-all"></div>
121
+ </div>
122
+ <div id="progressText" class="text-xs text-slate-400 mb-3">Idle</div>
123
+
124
+ <!-- Big score line -->
125
+ <p id="score" class="text-lg font-bold mb-1 text-[#0487D9]">No scan yet.</p>
126
+
127
+ <!-- Gauge + risk badge -->
128
+ <div class="flex items-center gap-4 mb-4">
129
+ <div class="relative w-24 h-24">
130
+ <svg viewBox="0 0 100 100" class="w-24 h-24 transform -rotate-90">
131
+ <!-- background circle -->
132
+ <circle cx="50" cy="50" r="40"
133
+ stroke="rgba(148, 163, 184, 0.35)"
134
+ stroke-width="8"
135
+ fill="none" />
136
+ <!-- progress circle -->
137
+ <circle id="gaugeCircle" cx="50" cy="50" r="40"
138
+ stroke="rgb(59, 130, 246)"
139
+ stroke-width="8"
140
+ fill="none"
141
+ stroke-linecap="round"
142
+ stroke-dasharray="251.2"
143
+ stroke-dashoffset="251.2" />
144
+ </svg>
145
+ <div class="absolute inset-0 flex items-center justify-center">
146
+ <span id="gaugeLabel" class="text-xs font-semibold text-slate-100">0%</span>
147
+ </div>
148
+ </div>
149
+
150
+ <div>
151
+ <span id="riskBadge"
152
+ class="inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold bg-slate-800 text-slate-200 border border-slate-600">
153
+ No risk yet
154
+ </span>
155
+ <p class="mt-1 text-[11px] text-slate-400">
156
+ Risk level updates after each scan based on plagiarism percentage.
157
+ </p>
158
+ </div>
159
+ </div>
160
+
161
+ <p id="summary" class="text-sm text-slate-200 mb-3">
162
+ Run a scan to see estimated plagiarism percentage based on the demo corpus.
163
+ </p>
164
+
165
+ <div class="text-xs text-slate-400 mb-3">
166
+ <ul class="list-disc list-inside space-y-1">
167
+ <li>Educational demo – not connected to real academic databases.</li>
168
+ <li>Backend uses TF-IDF and set-based similarity against local corpus files.</li>
169
+ </ul>
170
+ </div>
171
+
172
+ <h3 class="font-semibold text-sm mt-2 mb-1">Top matches in corpus</h3>
173
+ <div id="matches" class="mt-1 text-xs text-slate-300 space-y-2">
174
+ <!-- filled by JS -->
175
+ </div>
176
+ </section>
177
+ </div>
178
+
179
+ <!-- Extra sections (unchanged UI text) -->
180
+ <section class="mt-10 space-y-4">
181
+ <h2 class="text-xl md:text-2xl font-semibold">Why choose this plagiarism checker?</h2>
182
+ <p class="text-sm text-slate-300">
183
+ TrueWrite Scan’s plagiarism page is built to help you understand how similarity checks work,
184
+ from input text to percentage score and PDF report.
185
+ </p>
186
+
187
+ <div class="grid md:grid-cols-3 gap-5 mt-3">
188
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
189
+ <div class="flex items-center gap-3 mb-2">
190
+ <div class="w-9 h-9 rounded-full bg-emerald-500/20 flex items-center justify-center">
191
+ <span class="text-lg">📊</span>
192
+ </div>
193
+ <p class="font-semibold text-sm">Clear percentage score</p>
194
+ </div>
195
+ <p class="text-xs text-slate-300">
196
+ See an easy-to-understand plagiarism percentage instead of raw technical metrics.
197
+ </p>
198
+ </div>
199
+
200
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
201
+ <div class="flex items-center gap-3 mb-2">
202
+ <div class="w-9 h-9 rounded-full bg-indigo-500/20 flex items-center justify-center">
203
+ <span class="text-lg">🌐</span>
204
+ </div>
205
+ <p class="font-semibold text-sm">Backend similarity engine</p>
206
+ </div>
207
+ <p class="text-xs text-slate-300">
208
+ The backend uses TF-IDF and set-based similarity to compare your text with a demo corpus.
209
+ </p>
210
+ </div>
211
+
212
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
213
+ <div class="flex items-center gap-3 mb-2">
214
+ <div class="w-9 h-9 rounded-full bg-fuchsia-500/20 flex items-center justify-center">
215
+ <span class="text-lg">📄</span>
216
+ </div>
217
+ <p class="font-semibold text-sm">Instant PDF reports</p>
218
+ </div>
219
+ <p class="text-xs text-slate-300">
220
+ Export a mini plagiarism report so you can attach it with your draft or keep it for records.
221
+ </p>
222
+ </div>
223
+ </div>
224
+ </section>
225
+
226
+ <section class="mt-10 space-y-4">
227
+ <h2 class="text-xl md:text-2xl font-semibold">Who can use this tool?</h2>
228
+ <p class="text-sm text-slate-300">
229
+ Anyone who wants a first originality check before using heavy enterprise tools.
230
+ </p>
231
+
232
+ <div class="grid md:grid-cols-3 gap-5 mt-3">
233
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
234
+ <div class="flex items-center gap-3 mb-2">
235
+ <div class="w-9 h-9 rounded-full bg-sky-500/20 flex items-center justify-center">
236
+ <span class="text-lg">🎓</span>
237
+ </div>
238
+ <p class="font-semibold text-sm">Students & project teams</p>
239
+ </div>
240
+ <p class="text-xs text-slate-300">
241
+ Check reports, abstracts, and essays for overlap with sample academic-style text.
242
+ </p>
243
+ </div>
244
+
245
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
246
+ <div class="flex items-center gap-3 mb-2">
247
+ <div class="w-9 h-9 rounded-full bg-amber-500/20 flex items-center justify-center">
248
+ <span class="text-lg">👩‍🏫</span>
249
+ </div>
250
+ <p class="font-semibold text-sm">Educators</p>
251
+ </div>
252
+ <p class="text-xs text-slate-300">
253
+ Demonstrate the concept of similarity checking in class with a clear, visual UI.
254
+ </p>
255
+ </div>
256
+
257
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
258
+ <div class="flex items-center gap-3 mb-2">
259
+ <div class="w-9 h-9 rounded-full bg-rose-500/20 flex items-center justify-center">
260
+ <span class="text-lg">✍️</span>
261
+ </div>
262
+ <p class="font-semibold text-sm">Bloggers & writers</p>
263
+ </div>
264
+ <p class="text-xs text-slate-300">
265
+ Quickly see if short articles look too similar to built-in reference styles.
266
+ </p>
267
+ </div>
268
+ </div>
269
+ </section>
270
+
271
+ <section class="mt-10 space-y-4">
272
+ <h2 class="text-xl md:text-2xl font-semibold">How does this plagiarism checker work?</h2>
273
+ <p class="text-sm text-slate-300">
274
+ The logic is intentionally simple and transparent so you can follow and later extend it with
275
+ more advanced databases or APIs.
276
+ </p>
277
+
278
+ <div class="grid md:grid-cols-3 gap-5 mt-3">
279
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
280
+ <div class="flex items-center gap-3 mb-2">
281
+ <div class="w-9 h-9 rounded-full bg-emerald-500/20 flex items-center justify-center">
282
+ <span class="text-lg">1️⃣</span>
283
+ </div>
284
+ <p class="font-semibold text-sm">Clean and vectorise</p>
285
+ </div>
286
+ <p class="text-xs text-slate-300">
287
+ Your text is cleaned and transformed into TF-IDF vectors on the server.
288
+ </p>
289
+ </div>
290
+
291
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
292
+ <div class="flex items-center gap-3 mb-2">
293
+ <div class="w-9 h-9 rounded-full bg-indigo-500/20 flex items-center justify-center">
294
+ <span class="text-lg">2️⃣</span>
295
+ </div>
296
+ <p class="font-semibold text-sm">Compare with corpus</p>
297
+ </div>
298
+ <p class="text-xs text-slate-300">
299
+ The backend compares your text with stored documents using cosine and set-based similarity.
300
+ </p>
301
+ </div>
302
+
303
+ <div class="bg-slate-900/70 border border-slate-800 rounded-2xl p-4">
304
+ <div class="flex items-center gap-3 mb-2">
305
+ <div class="w-9 h-9 rounded-full bg-fuchsia-500/20 flex items-center justify-center">
306
+ <span class="text-lg">3️⃣</span>
307
+ </div>
308
+ <p class="font-semibold text-sm">Return a percentage</p>
309
+ </div>
310
+ <p class="text-xs text-slate-300">
311
+ The highest overlap is converted into a simple percentage and returned to this page with top matches.
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">Top reviews for this plagiarism tool</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="border-t border-slate-800">
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-blue-300 font-medium">Gopal Krushna Mahapatra</span>.</p>
340
+ </div>
341
+ </footer>
342
+
343
+ <!-- ===================== SCRIPTS ===================== -->
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
+ // Elements
361
+ const dropZone = document.getElementById("dropZone");
362
+ const fileInput = document.getElementById("fileInput");
363
+ const fileName = document.getElementById("fileName");
364
+ const textarea = document.getElementById("inputText");
365
+ const checkBtn = document.getElementById("checkBtn");
366
+ const spinner = document.getElementById("spinner");
367
+ const checkLabel = document.getElementById("checkLabel");
368
+ const statusTiny = document.getElementById("statusTiny");
369
+ const progressBar = document.getElementById("progressBar");
370
+ const progressText = document.getElementById("progressText");
371
+ const summaryP = document.getElementById("summary");
372
+ const matchesDiv = document.getElementById("matches");
373
+ const downloadBtn = document.getElementById("downloadBtn");
374
+ const scoreP = document.getElementById("score");
375
+ const pasteBtn = document.getElementById("pasteBtn");
376
+
377
+ const gaugeCircle = document.getElementById("gaugeCircle");
378
+ const gaugeLabel = document.getElementById("gaugeLabel");
379
+ const riskBadge = document.getElementById("riskBadge");
380
+ const GAUGE_CIRCUMFERENCE = 251.2; // 2πr for r=40
381
+
382
+ let lastResult = null;
383
+ let progressTimer = null;
384
+
385
+ // Gauge & risk helpers
386
+ function updateGauge(percent) {
387
+ const p = Math.max(0, Math.min(100, Number(percent) || 0));
388
+ const offset = GAUGE_CIRCUMFERENCE * (1 - p / 100);
389
+ gaugeCircle.style.strokeDashoffset = offset;
390
+ gaugeLabel.textContent = p.toFixed(0) + "%";
391
+ }
392
+
393
+ function updateRiskBadge(percent) {
394
+ const p = Math.max(0, Math.min(100, Number(percent) || 0));
395
+ let label, extraClasses;
396
+
397
+ if (p <= 20) {
398
+ label = "Safe";
399
+ extraClasses = "bg-blue-500/20 text-blue-300 border border-blue-500/60";
400
+ } else if (p <= 50) {
401
+ label = "Medium risk";
402
+ extraClasses = "bg-amber-500/20 text-amber-300 border border-amber-500/60";
403
+ } else {
404
+ label = "High risk";
405
+ extraClasses = "bg-rose-500/20 text-rose-300 border border-rose-500/60";
406
+ }
407
+
408
+ riskBadge.className =
409
+ "inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold " + extraClasses;
410
+ riskBadge.textContent = label;
411
+ }
412
+
413
+ // Loading & progress
414
+ function setLoading(on) {
415
+ if (on) {
416
+ spinner.classList.remove("hidden");
417
+ checkLabel.textContent = "Checking...";
418
+ checkBtn.disabled = true;
419
+ checkBtn.classList.add("opacity-80");
420
+ statusTiny.textContent = "Working...";
421
+ progressBar.style.width = "5%";
422
+ progressText.textContent = "Uploading/processing...";
423
+ progressTimer = setInterval(() => {
424
+ let cur = parseFloat(progressBar.style.width) || 5;
425
+ cur = Math.min(90, cur + Math.random() * 8);
426
+ progressBar.style.width = cur + "%";
427
+ }, 250);
428
+ } else {
429
+ spinner.classList.add("hidden");
430
+ checkLabel.textContent = "Check Plagiarism";
431
+ checkBtn.disabled = false;
432
+ checkBtn.classList.remove("opacity-80");
433
+ if (progressTimer) clearInterval(progressTimer);
434
+ progressBar.style.width = "100%";
435
+ progressText.textContent = "Done";
436
+ setTimeout(() => {
437
+ progressBar.style.width = "0%";
438
+ progressText.textContent = "Idle";
439
+ }, 700);
440
+ }
441
+ }
442
+
443
+ // Drag & drop + file handling
444
+ ["dragenter", "dragover"].forEach(e =>
445
+ dropZone.addEventListener(e, ev => {
446
+ ev.preventDefault();
447
+ dropZone.classList.add("ring-2", "ring-[#0487D9]");
448
+ })
449
+ );
450
+ ["dragleave", "drop"].forEach(e =>
451
+ dropZone.addEventListener(e, ev => {
452
+ ev.preventDefault();
453
+ dropZone.classList.remove("ring-2", "ring-[#0487D9]");
454
+ })
455
+ );
456
+
457
+ dropZone.addEventListener("drop", ev => {
458
+ ev.preventDefault();
459
+ const f = ev.dataTransfer.files[0];
460
+ if (!f) return;
461
+ fileInput.files = ev.dataTransfer.files;
462
+ fileName.textContent = f.name;
463
+ handleSelectedFile(f);
464
+ });
465
+
466
+ fileInput.addEventListener("change", ev => {
467
+ const f = ev.target.files[0];
468
+ if (!f) return;
469
+ fileName.textContent = f.name;
470
+ handleSelectedFile(f);
471
+ });
472
+
473
+ function handleSelectedFile(file) {
474
+ const fname = file.name || "file";
475
+ if (file.type === "text/plain" || fname.toLowerCase().endsWith(".txt")) {
476
+ const reader = new FileReader();
477
+ reader.onload = () => {
478
+ textarea.value = reader.result;
479
+ statusTiny.textContent = `${fname} loaded for preview`;
480
+ };
481
+ reader.readAsText(file);
482
+ } else {
483
+ textarea.value = "";
484
+ statusTiny.textContent = `${fname} selected — will be uploaded and parsed on server when you click Check`;
485
+ }
486
+ }
487
+
488
+ // Paste button
489
+ pasteBtn.addEventListener("click", async () => {
490
+ try {
491
+ const text = await navigator.clipboard.readText();
492
+ textarea.value = text;
493
+ statusTiny.textContent = "Pasted from clipboard.";
494
+ } catch (err) {
495
+ alert("Clipboard access blocked by browser.");
496
+ }
497
+ });
498
+
499
+ // Backend API helpers
500
+ async function callPlagiarismText(text) {
501
+ const res = await fetch(`${BACKEND_URL}/api/plagiarism-check`, {
502
+ method: "POST",
503
+ headers: {
504
+ "Content-Type": "application/json",
505
+ "Authorization": `Bearer ${token}`
506
+ },
507
+ body: JSON.stringify({ text })
508
+ });
509
+ if (!res.ok) {
510
+ const txt = await res.text();
511
+ throw new Error(txt || `Server responded ${res.status}`);
512
+ }
513
+ return res.json();
514
+ }
515
+
516
+ async function callPlagiarismFile(file) {
517
+ const form = new FormData();
518
+ form.append("file", file, file.name);
519
+ const res = await fetch(`${BACKEND_URL}/api/plagiarism-check-file`, {
520
+ method: "POST",
521
+ headers: { "Authorization": `Bearer ${token}` },
522
+ body: form
523
+ });
524
+ if (!res.ok) {
525
+ const txt = await res.text();
526
+ throw new Error(txt || `Server responded ${res.status}`);
527
+ }
528
+ return res.json();
529
+ }
530
+
531
+ // Main plagiarism action
532
+ checkBtn.addEventListener("click", async () => {
533
+ const file = fileInput.files[0];
534
+ const text = (textarea.value || "").trim();
535
+
536
+ if (!file && !text) {
537
+ alert("Please paste text or select a file.");
538
+ return;
539
+ }
540
+
541
+ setLoading(true);
542
+ summaryP.textContent = "Checking...";
543
+ scoreP.textContent = "Scanning...";
544
+ matchesDiv.innerHTML = "";
545
+ lastResult = null;
546
+
547
+ try {
548
+ let data;
549
+ if (file && !(file.type === "text/plain" || file.name.toLowerCase().endsWith(".txt"))) {
550
+ data = await callPlagiarismFile(file);
551
+ } else {
552
+ const payloadText = text || (file ? await file.text() : "");
553
+ data = await callPlagiarismText(payloadText);
554
+ }
555
+
556
+ if (!data) throw new Error("Empty response from server");
557
+ if (data.plagiarism_percent === undefined) {
558
+ throw new Error(data.detail || data.error || "Unexpected response");
559
+ }
560
+
561
+ const percent = data.plagiarism_percent;
562
+ scoreP.textContent = `Estimated Plagiarism: ${percent}%`;
563
+ summaryP.textContent = data.summary || `Plagiarism estimate: ${percent}%`;
564
+
565
+ updateGauge(percent);
566
+ updateRiskBadge(percent);
567
+
568
+ if (data.matches && data.matches.length) {
569
+ matchesDiv.innerHTML = data.matches.map(m => `
570
+ <div class="p-2 rounded bg-slate-800/40 flex items-center justify-between gap-2">
571
+ <div>
572
+ <div class="font-semibold">${m.title}</div>
573
+ <div class="text-[11px] text-slate-400">Similarity score: ${m.score}%</div>
574
+ </div>
575
+ </div>
576
+ `).join("");
577
+ } else {
578
+ matchesDiv.innerHTML =
579
+ "<div class='text-xs text-slate-400'>No close matches found in the current demo corpus.</div>";
580
+ }
581
+
582
+ lastResult = {
583
+ input: text || (file ? "[uploaded file]" : ""),
584
+ result: data
585
+ };
586
+ statusTiny.textContent = "Done";
587
+ } catch (err) {
588
+ console.error(err);
589
+ summaryP.textContent = "Error: " + (err.message || err);
590
+ scoreP.textContent = "Scan failed.";
591
+ statusTiny.textContent = "Error";
592
+ } finally {
593
+ setLoading(false);
594
+ }
595
+ });
596
+
597
+ // Download PDF report
598
+ downloadBtn.addEventListener("click", () => {
599
+ if (!lastResult) {
600
+ alert("Run a check first.");
601
+ return;
602
+ }
603
+
604
+ const jsPDF = getJsPDF();
605
+ if (!jsPDF) return;
606
+
607
+ const doc = new jsPDF({ unit: "pt", format: "a4" });
608
+ let y = 40;
609
+
610
+ doc.setFontSize(16);
611
+ doc.text("TrueWrite Scan — Plagiarism Report", 40, y); y += 22;
612
+
613
+ doc.setFontSize(11);
614
+ doc.text("Generated: " + new Date().toLocaleString(), 40, y); y += 18;
615
+ doc.text("User: " + (user || "N/A"), 40, y); y += 18;
616
+
617
+ const pr = lastResult.result.plagiarism_percent ?? "N/A";
618
+ doc.setFontSize(12);
619
+ doc.text(`Plagiarism: ${pr}%`, 40, y); y += 16;
620
+
621
+ if (lastResult.result.summary) {
622
+ doc.setFontSize(10);
623
+ const summaryLines = doc.splitTextToSize("Summary: " + lastResult.result.summary, 520);
624
+ doc.text(summaryLines, 40, y);
625
+ y += summaryLines.length * 12 + 10;
626
+ }
627
+
628
+ if (lastResult.result.matches && lastResult.result.matches.length) {
629
+ doc.setFontSize(11);
630
+ doc.text("Top matches:", 40, y); y += 14;
631
+ lastResult.result.matches.forEach(m => {
632
+ const line = `• ${m.title} — ${m.score}%`;
633
+ doc.text(line, 48, y);
634
+ y += 12;
635
+ });
636
+ }
637
+
638
+ y += 10;
639
+ doc.setFontSize(11);
640
+ doc.text("--- Original text (truncated) ---", 40, y); y += 16;
641
+ doc.setFontSize(9);
642
+ const raw = lastResult.input || "";
643
+ const truncated = raw.length > 3000 ? raw.slice(0, 3000) + "\n\n[TRUNCATED]" : raw;
644
+ const textLines = doc.splitTextToSize(truncated, 520);
645
+ doc.text(textLines, 40, y);
646
+
647
+ doc.save("plagiarism-report.pdf");
648
+ });
649
+
650
+ // Reviews slider
651
+ const reviews = [
652
+ {
653
+ name: "Aarav S.",
654
+ role: "B.Tech Student",
655
+ text: "Great for a quick originality check before using heavy tools like Turnitin.",
656
+ stars: 5
657
+ },
658
+ {
659
+ name: "Priya K.",
660
+ role: "Research Scholar",
661
+ text: "Helps me understand how similarity scores work in a simple, visible way.",
662
+ stars: 5
663
+ },
664
+ {
665
+ name: "Rahul M.",
666
+ role: "Content Writer",
667
+ text: "Nice to get an approximate plagiarism percentage while drafting blog posts.",
668
+ stars: 4
669
+ },
670
+ {
671
+ name: "Sneha R.",
672
+ role: "M.Sc. Student",
673
+ text: "Perfect educational example to show classmates how plagiarism detection is implemented.",
674
+ stars: 5
675
+ },
676
+ {
677
+ name: "Vikram J.",
678
+ role: "Developer",
679
+ text: "Clean UI and easy to tweak the logic for my own experiments.",
680
+ stars: 4
681
+ }
682
+ ];
683
+
684
+ let currentReview = 0;
685
+ const reviewCard = document.getElementById("reviewCard");
686
+ const reviewDots = document.getElementById("reviewDots");
687
+
688
+ function starRow(stars) {
689
+ let html = "";
690
+ for (let i = 0; i < 5; i++) {
691
+ html += `<span class="${i < stars ? "text-yellow-400" : "text-slate-600"} text-sm">★</span>`;
692
+ }
693
+ return html;
694
+ }
695
+
696
+ function renderReview() {
697
+ const r = reviews[currentReview];
698
+ reviewCard.innerHTML = `
699
+ <div class="flex items-center gap-2 mb-2">
700
+ ${starRow(r.stars)}
701
+ </div>
702
+ <p class="text-sm text-slate-200 mb-3">"${r.text}"</p>
703
+ <p class="text-sm font-semibold">${r.name}</p>
704
+ <p class="text-xs text-slate-400">${r.role}</p>
705
+ `;
706
+ reviewDots.innerHTML = reviews.map((_, i) =>
707
+ `<span class="w-2 h-2 rounded-full ${i === currentReview ? "bg-[#0487D9]" : "bg-slate-600"}"></span>`
708
+ ).join("");
709
+ }
710
+
711
+ function nextReview() {
712
+ currentReview = (currentReview + 1) % reviews.length;
713
+ renderReview();
714
+ }
715
+
716
+ function prevReview() {
717
+ currentReview = (currentReview - 1 + reviews.length) % reviews.length;
718
+ renderReview();
719
+ }
720
+
721
+ document.getElementById("nextReview").onclick = () => {
722
+ clearInterval(reviewTimer);
723
+ nextReview();
724
+ reviewTimer = setInterval(nextReview, 6000);
725
+ };
726
+ document.getElementById("prevReview").onclick = () => {
727
+ clearInterval(reviewTimer);
728
+ prevReview();
729
+ reviewTimer = setInterval(nextReview, 6000);
730
+ };
731
+
732
+ let reviewTimer = setInterval(nextReview, 6000);
733
+ renderReview();
734
+
735
+ // Initialise gauge/risk with 0
736
+ updateGauge(0);
737
+ updateRiskBadge(0);
738
+ </script>
739
+ </body>
740
+ </html>
signup.html ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Sign Up – TrueWrite Scan</title>
6
+ <script src="https://cdn.tailwindcss.com"></script>
7
+ </head>
8
+ <body class="bg-slate-950 text-white min-h-screen flex items-center justify-center">
9
+ <div class="max-w-md w-full mx-4 bg-slate-900/70 border border-slate-800 rounded-2xl p-6 shadow-xl">
10
+ <h1 class="text-2xl font-bold mb-1">Create your account</h1>
11
+ <p class="text-xs text-slate-400 mb-4">Sign up to use Grammar, Plagiarism, and AI checks.</p>
12
+
13
+ <form id="signupForm" class="space-y-4">
14
+ <div>
15
+ <label class="block text-xs mb-1">Full name</label>
16
+ <input id="name" type="text" required
17
+ class="w-full px-3 py-2 rounded-lg bg-slate-950 border border-slate-700 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500" />
18
+ </div>
19
+ <div>
20
+ <label class="block text-xs mb-1">Email</label>
21
+ <input id="email" type="email" required
22
+ class="w-full px-3 py-2 rounded-lg bg-slate-950 border border-slate-700 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500" />
23
+ </div>
24
+ <div>
25
+ <label class="block text-xs mb-1">Password</label>
26
+ <input id="password" type="password" required minlength="6"
27
+ class="w-full px-3 py-2 rounded-lg bg-slate-950 border border-slate-700 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500" />
28
+ </div>
29
+ <p id="signupError" class="text-xs text-red-400 h-4"></p>
30
+ <button type="submit"
31
+ class="w-full py-2 rounded-lg bg-indigo-500 hover:bg-indigo-600 text-sm font-semibold">
32
+ Sign up
33
+ </button>
34
+ </form>
35
+
36
+ <p class="mt-4 text-xs text-slate-400 text-center">
37
+ Already have an account?
38
+ <a href="login.html" class="text-indigo-300 underline underline-offset-2">Login</a>
39
+ </p>
40
+ </div>
41
+
42
+ <script>
43
+ const BACKEND_URL = "http://localhost:8000";
44
+
45
+ const form = document.getElementById("signupForm");
46
+ const err = document.getElementById("signupError");
47
+
48
+ form.addEventListener("submit", async (e) => {
49
+ e.preventDefault();
50
+ err.textContent = "";
51
+
52
+ const name = document.getElementById("name").value.trim();
53
+ const email = document.getElementById("email").value.trim();
54
+ const password = document.getElementById("password").value;
55
+
56
+ try {
57
+ const res = await fetch(`${BACKEND_URL}/api/signup`, {
58
+ method: "POST",
59
+ headers: { "Content-Type": "application/json" },
60
+ body: JSON.stringify({ name, email, password })
61
+ });
62
+
63
+ const data = await res.json();
64
+ if (!res.ok) {
65
+ err.textContent = data.detail || "Signup failed";
66
+ return;
67
+ }
68
+
69
+ localStorage.setItem("truewriteToken", data.token);
70
+ localStorage.setItem("truewriteUser", JSON.stringify({
71
+ name: data.name,
72
+ email: data.email
73
+ }));
74
+
75
+ window.location.href = "dashboard.html";
76
+ } catch (e2) {
77
+ err.textContent = "Server error. Is backend running?";
78
+ }
79
+ });
80
+ </script>
81
+ </body>
82
+ </html>