aikenml commited on
Commit
9874a5c
Β·
1 Parent(s): 32ad221

changed buttons

Browse files
app/api/__pycache__/endpoints.cpython-310.pyc CHANGED
Binary files a/app/api/__pycache__/endpoints.cpython-310.pyc and b/app/api/__pycache__/endpoints.cpython-310.pyc differ
 
app/api/endpoints.py CHANGED
@@ -26,6 +26,9 @@ class DetectorResult(BaseModel):
26
  class DetectionResponse(BaseModel):
27
  results: List[DetectorResult]
28
 
 
 
 
29
  @router.post("/humanize")
30
  async def humanize_text_endpoint(input_data: HumanizeInput):
31
  try:
@@ -40,41 +43,6 @@ async def humanize_text_endpoint(input_data: HumanizeInput):
40
  except Exception as e:
41
  raise HTTPException(status_code=500, detail=str(e))
42
 
43
- # @router.post("/detect")
44
- # async def detect_ai_endpoint(input_data: DetectInput) -> DetectionResponse:
45
- # try:
46
- # # Call the AI detection function
47
- # detection_results = detectors.detect_ai(input_data.text)
48
-
49
- # # Ensure the results are in the expected format
50
- # # If detect_ai returns a different format, adjust accordingly
51
- # formatted_results = []
52
-
53
- # # Assuming detect_ai returns a list of dictionaries with detector info
54
- # # You may need to adjust this based on your actual detect_ai function output
55
- # if isinstance(detection_results, list):
56
- # for result in detection_results:
57
- # formatted_results.append(DetectorResult(
58
- # detector_name=result.get('name', 'Unknown Detector'),
59
- # ai_probability=result.get('probability', 0.0)
60
- # ))
61
- # elif isinstance(detection_results, dict):
62
- # # If it returns a single result or dict format
63
- # for detector_name, probability in detection_results.items():
64
- # formatted_results.append(DetectorResult(
65
- # detector_name=detector_name,
66
- # ai_probability=probability
67
- # ))
68
- # else:
69
- # # If it's a different format, you'll need to adjust this
70
- # raise ValueError("Unexpected detection results format")
71
-
72
- # return DetectionResponse(results=formatted_results)
73
-
74
- # except Exception as e:
75
- # raise HTTPException(status_code=500, detail=f"Detection failed: {str(e)}")
76
-
77
-
78
 
79
  @router.post("/detect")
80
  async def detect_ai_endpoint(input_data: DetectInput) -> DetectionResponse:
@@ -144,12 +112,50 @@ async def detect_ai_endpoint(input_data: DetectInput) -> DetectionResponse:
144
  print(f"Detection error: {str(e)}") # Debug log
145
  raise HTTPException(status_code=500, detail=f"Detection failed: {str(e)}")
146
 
147
- # Test endpoint to verify the detection endpoint is working
148
- @router.get("/detect/test")
149
- async def test_detect_endpoint():
150
- """Test endpoint to verify detection functionality"""
151
- return DetectionResponse(results=[
152
- DetectorResult(detector_name="Test Detector 1", ai_probability=0.85),
153
- DetectorResult(detector_name="Test Detector 2", ai_probability=0.42),
154
- DetectorResult(detector_name="Test Detector 3", ai_probability=0.91)
155
- ])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  class DetectionResponse(BaseModel):
27
  results: List[DetectorResult]
28
 
29
+
30
+
31
+
32
  @router.post("/humanize")
33
  async def humanize_text_endpoint(input_data: HumanizeInput):
34
  try:
 
43
  except Exception as e:
44
  raise HTTPException(status_code=500, detail=str(e))
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
  @router.post("/detect")
48
  async def detect_ai_endpoint(input_data: DetectInput) -> DetectionResponse:
 
112
  print(f"Detection error: {str(e)}") # Debug log
113
  raise HTTPException(status_code=500, detail=f"Detection failed: {str(e)}")
114
 
115
+ # # Test endpoint to verify the detection endpoint is working
116
+ # @router.get("/detect/test")
117
+ # async def test_detect_endpoint():
118
+ # """Test endpoint to verify detection functionality"""
119
+ # return DetectionResponse(results=[
120
+ # DetectorResult(detector_name="Test Detector 1", ai_probability=0.85),
121
+ # DetectorResult(detector_name="Test Detector 2", ai_probability=0.42),
122
+ # DetectorResult(detector_name="Test Detector 3", ai_probability=0.91)
123
+ # ])
124
+
125
+
126
+
127
+
128
+ # @router.post("/detect")
129
+ # async def detect_ai_endpoint(input_data: DetectInput) -> DetectionResponse:
130
+ # try:
131
+ # # Call the AI detection function
132
+ # detection_results = detectors.detect_ai(input_data.text)
133
+
134
+ # # Ensure the results are in the expected format
135
+ # # If detect_ai returns a different format, adjust accordingly
136
+ # formatted_results = []
137
+
138
+ # # Assuming detect_ai returns a list of dictionaries with detector info
139
+ # # You may need to adjust this based on your actual detect_ai function output
140
+ # if isinstance(detection_results, list):
141
+ # for result in detection_results:
142
+ # formatted_results.append(DetectorResult(
143
+ # detector_name=result.get('name', 'Unknown Detector'),
144
+ # ai_probability=result.get('probability', 0.0)
145
+ # ))
146
+ # elif isinstance(detection_results, dict):
147
+ # # If it returns a single result or dict format
148
+ # for detector_name, probability in detection_results.items():
149
+ # formatted_results.append(DetectorResult(
150
+ # detector_name=detector_name,
151
+ # ai_probability=probability
152
+ # ))
153
+ # else:
154
+ # # If it's a different format, you'll need to adjust this
155
+ # raise ValueError("Unexpected detection results format")
156
+
157
+ # return DetectionResponse(results=formatted_results)
158
+
159
+ # except Exception as e:
160
+ # raise HTTPException(status_code=500, detail=f"Detection failed: {str(e)}")
161
+
app/chains/__pycache__/detectors.cpython-310.pyc CHANGED
Binary files a/app/chains/__pycache__/detectors.cpython-310.pyc and b/app/chains/__pycache__/detectors.cpython-310.pyc differ
 
app/chains/__pycache__/humanizer_chain.cpython-310.pyc CHANGED
Binary files a/app/chains/__pycache__/humanizer_chain.cpython-310.pyc and b/app/chains/__pycache__/humanizer_chain.cpython-310.pyc differ
 
app/chains/detectors.py CHANGED
@@ -14,15 +14,15 @@ def detect_ai(text):
14
  return [
15
  {
16
  "name": "GPTZero",
17
- "probability": 0.85 # 85% AI probability
18
  },
19
  {
20
  "name": "Another Detector",
21
- "probability": 0.96 # 85% AI probability
22
  },
23
  {
24
  "name": "Another Detector ",
25
- "probability": 0.3 # 85% AI probability
26
  }
27
  ]
28
 
 
14
  return [
15
  {
16
  "name": "GPTZero",
17
+ "probability": 0.3 # 85% AI probability
18
  },
19
  {
20
  "name": "Another Detector",
21
+ "probability": 0.2 # 85% AI probability
22
  },
23
  {
24
  "name": "Another Detector ",
25
+ "probability": 0.9 # 85% AI probability
26
  }
27
  ]
28
 
app/static/index copy 2.html ADDED
@@ -0,0 +1,920 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>AI Text Humanizer</title>
6
+ <style>
7
+ * {
8
+ margin: 0;
9
+ padding: 0;
10
+ box-sizing: border-box;
11
+ }
12
+
13
+ html, body {
14
+ height: 100%;
15
+ }
16
+
17
+ body {
18
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
19
+ background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #ec4899 100%);
20
+ color: #1f2937;
21
+ overflow-y: auto; /* Allow page scrolling */
22
+ min-height: 100vh;
23
+ }
24
+
25
+ .container {
26
+ min-height: 100vh;
27
+ max-width: 1400px;
28
+ margin: 0 auto;
29
+ display: flex;
30
+ flex-direction: column;
31
+ padding: 16px;
32
+ }
33
+
34
+ /* ===== HEADER (Title + Subtitle) ===== */
35
+ .header {
36
+ text-align: center;
37
+ margin-bottom: 16px; /* Reduced bottom margin so header sits higher */
38
+ }
39
+
40
+ h1 {
41
+ font-size: 2.0em; /* Slightly smaller */
42
+ font-weight: 800;
43
+ background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
44
+ -webkit-background-clip: text;
45
+ -webkit-text-fill-color: transparent;
46
+ background-clip: text;
47
+ margin-bottom: 6px;
48
+ text-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
49
+ }
50
+
51
+ p.description {
52
+ color: rgba(255, 255, 255, 0.9);
53
+ font-size: 1.2em;
54
+ font-weight: 300;
55
+ }
56
+
57
+ /* ===== MAIN GRID (Input + Output Panels) ===== */
58
+ .main-content {
59
+ display: grid;
60
+ grid-template-columns: 1fr 1fr;
61
+ gap: 24px;
62
+ min-height: 70vh; /* Fixed height for the main panels */
63
+ margin-bottom: 24px;
64
+ }
65
+
66
+ @media (max-width: 1200px) {
67
+ .main-content {
68
+ grid-template-columns: 1fr;
69
+ grid-template-rows: 1fr 1fr;
70
+ gap: 20px;
71
+ }
72
+ h1 {
73
+ font-size: 1.8em;
74
+ }
75
+ }
76
+
77
+ @media (max-width: 768px) {
78
+ .container {
79
+ padding: 12px;
80
+ }
81
+ .panel {
82
+ padding: 16px;
83
+ }
84
+ h1 {
85
+ font-size: 1.6em;
86
+ }
87
+ p.description {
88
+ font-size: 1em;
89
+ }
90
+ }
91
+
92
+ /* ===== PANEL BASE STYLES ===== */
93
+ .panel {
94
+ background: rgba(255, 255, 255, 0.95);
95
+ backdrop-filter: blur(20px);
96
+ border-radius: 20px;
97
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
98
+ border: 1px solid rgba(255, 255, 255, 0.2);
99
+
100
+ display: flex;
101
+ flex-direction: column;
102
+ height: 100%;
103
+ overflow: hidden;
104
+ padding: 24px;
105
+ }
106
+
107
+ .input-panel {
108
+ background: rgba(255, 255, 255, 0.98);
109
+ }
110
+
111
+ .output-panel {
112
+ background: rgba(248, 250, 252, 0.98);
113
+ }
114
+
115
+ /* ===== PANEL HEADER (Title + Subtitle inside each panel) ===== */
116
+ .panel-header {
117
+ margin-bottom: 16px;
118
+ padding-bottom: 12px;
119
+ border-bottom: 2px solid rgba(99, 102, 241, 0.1);
120
+ }
121
+
122
+ .panel-title {
123
+ font-size: 1.3em;
124
+ font-weight: 600;
125
+ color: #374151;
126
+ margin-bottom: 6px;
127
+ }
128
+
129
+ .panel-subtitle {
130
+ color: #6b7280;
131
+ font-size: 0.9em;
132
+ }
133
+
134
+ /* ===== INPUT PANEL CONTROLS ===== */
135
+ textarea {
136
+ flex: 1; /* Takes all available vertical space */
137
+ font-size: 15px;
138
+ padding: 16px;
139
+ border: 2px solid #e5e7eb;
140
+ border-radius: 12px;
141
+ resize: none;
142
+ transition: all 0.3s ease;
143
+ font-family: inherit;
144
+ line-height: 1.6;
145
+ background: #fafafa;
146
+ overflow-y: auto;
147
+ }
148
+
149
+ textarea:focus {
150
+ outline: none;
151
+ border-color: #6366f1;
152
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
153
+ background: white;
154
+ }
155
+
156
+ textarea::placeholder {
157
+ color: #9ca3af;
158
+ }
159
+
160
+ /* ===== BUTTON STYLES ===== */
161
+ button {
162
+ background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
163
+ color: white;
164
+ padding: 8px 16px; /* Further reduced */
165
+ font-size: 13px; /* Further reduced */
166
+ font-weight: 600;
167
+ border: none;
168
+ border-radius: 8px; /* Smaller radius */
169
+ cursor: pointer;
170
+ transition: all 0.3s ease;
171
+ box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3);
172
+ max-width: 200px; /* Limit button width */
173
+ }
174
+
175
+ button:hover {
176
+ transform: translateY(-1px);
177
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);
178
+ }
179
+
180
+ button:active {
181
+ transform: translateY(0);
182
+ }
183
+
184
+ .copy-button {
185
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%);
186
+ box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3);
187
+ margin-top: 0;
188
+ flex-shrink: 0;
189
+ width: 100%;
190
+ max-width: none; /* Allow full width for output buttons */
191
+ }
192
+
193
+ .copy-button:hover {
194
+ box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4);
195
+ }
196
+
197
+ .copy-button.copied {
198
+ background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%);
199
+ }
200
+
201
+ .detector-button {
202
+ background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
203
+ box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3);
204
+ margin-top: 8px;
205
+ flex-shrink: 0;
206
+ width: 100%;
207
+ max-width: none; /* Allow full width for output buttons */
208
+ }
209
+
210
+ .detector-button:hover {
211
+ box-shadow: 0 4px 12px rgba(245, 158, 11, 0.4);
212
+ }
213
+
214
+ /* ===== FILE UPLOAD STYLES ===== */
215
+ .file-upload-area {
216
+ margin-top: 12px;
217
+ display: flex;
218
+ gap: 8px;
219
+ align-items: center;
220
+ justify-content: center;
221
+ }
222
+
223
+ .buttons-row {
224
+ display: flex;
225
+ gap: 8px;
226
+ align-items: center;
227
+ justify-content: center;
228
+ flex-wrap: wrap;
229
+ }
230
+
231
+ .file-input-wrapper {
232
+ position: relative;
233
+ }
234
+
235
+ .file-input {
236
+ position: absolute;
237
+ opacity: 0;
238
+ width: 100%;
239
+ height: 100%;
240
+ cursor: pointer;
241
+ }
242
+
243
+ .file-input-label {
244
+ display: flex;
245
+ align-items: center;
246
+ justify-content: center;
247
+ padding: 8px 16px;
248
+ background: linear-gradient(135deg, #8b5cf6 0%, #a855f7 100%);
249
+ color: white;
250
+ border-radius: 8px;
251
+ cursor: pointer;
252
+ transition: all 0.3s ease;
253
+ font-size: 13px;
254
+ font-weight: 600;
255
+ box-shadow: 0 2px 8px rgba(139, 92, 246, 0.3);
256
+ min-height: 32px;
257
+ max-width: 140px; /* Limit width */
258
+ }
259
+
260
+ .file-input-label:hover {
261
+ transform: translateY(-1px);
262
+ box-shadow: 0 4px 12px rgba(139, 92, 246, 0.4);
263
+ }
264
+
265
+ .clear-button {
266
+ background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
267
+ box-shadow: 0 2px 8px rgba(239, 68, 68, 0.3);
268
+ padding: 8px 16px;
269
+ max-width: 100px; /* Limit width */
270
+ }
271
+
272
+ .clear-button:hover {
273
+ box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
274
+ }
275
+
276
+ .file-info {
277
+ font-size: 12px;
278
+ color: #6b7280;
279
+ margin-top: 8px;
280
+ padding: 0 4px;
281
+ text-align: center;
282
+ }
283
+
284
+ /* ===== OUTPUT CONTENT CONTAINER ===== */
285
+ .output-container {
286
+ display: flex;
287
+ flex-direction: column;
288
+ height: 400px; /* Fixed height for the container */
289
+ overflow: hidden; /* Prevent container from expanding */
290
+ }
291
+
292
+ /* ===== OUTPUT CONTENT (scrollable) ===== */
293
+ .output-content {
294
+ flex: 1; /* Fill vertical space between header and bottom button */
295
+ padding: 16px;
296
+ background: #f8fafc;
297
+ border: 2px solid #e2e8f0;
298
+ border-radius: 12px;
299
+ font-size: 15px;
300
+ line-height: 1.7;
301
+ white-space: pre-wrap;
302
+ overflow-y: auto; /* Only this area scrolls */
303
+ color: #334155;
304
+ height: 400px; /* Fixed height instead of min-height */
305
+ max-height: 400px; /* Prevent expansion */
306
+ }
307
+
308
+ /* ===== BOTTOM BUTTON CONTAINER (flexible height) ===== */
309
+ .panel-bottom {
310
+ margin-top: 16px;
311
+ display: flex;
312
+ flex-direction: column;
313
+ gap: 8px;
314
+ flex-shrink: 0; /* Prevent buttons from shrinking */
315
+ }
316
+
317
+ /* ===== AI DETECTOR SECTION STYLES ===== */
318
+ .detector-section {
319
+ display: none; /* Hidden by default */
320
+ background: rgba(255, 255, 255, 0.95);
321
+ backdrop-filter: blur(20px);
322
+ border-radius: 20px;
323
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
324
+ border: 1px solid rgba(255, 255, 255, 0.2);
325
+ padding: 24px;
326
+ margin-bottom: 24px;
327
+ animation: slideIn 0.5s ease-out;
328
+ }
329
+
330
+ @keyframes slideIn {
331
+ from {
332
+ opacity: 0;
333
+ transform: translateY(20px);
334
+ }
335
+ to {
336
+ opacity: 1;
337
+ transform: translateY(0);
338
+ }
339
+ }
340
+
341
+ .detector-results {
342
+ padding: 16px;
343
+ background: #ffffff;
344
+ border: 2px solid #e2e8f0;
345
+ border-radius: 12px;
346
+ }
347
+
348
+ .detector-table-title {
349
+ font-size: 1.1em;
350
+ font-weight: 600;
351
+ color: #374151;
352
+ margin-bottom: 12px;
353
+ text-align: center;
354
+ }
355
+
356
+ .detector-table {
357
+ width: 100%;
358
+ border-collapse: collapse;
359
+ font-size: 14px;
360
+ }
361
+
362
+ .detector-table th {
363
+ background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
364
+ color: white;
365
+ padding: 12px 8px;
366
+ text-align: left;
367
+ font-weight: 600;
368
+ border-radius: 8px 8px 0 0;
369
+ }
370
+
371
+ .detector-table th:first-child {
372
+ border-radius: 8px 0 0 0;
373
+ }
374
+
375
+ .detector-table th:last-child {
376
+ border-radius: 0 8px 0 0;
377
+ }
378
+
379
+ .detector-table td {
380
+ padding: 10px 8px;
381
+ border-bottom: 1px solid #e5e7eb;
382
+ }
383
+
384
+ .detector-table tr:nth-child(even) {
385
+ background-color: #f8fafc;
386
+ }
387
+
388
+ .detector-table tr:hover {
389
+ background-color: #f1f5f9;
390
+ }
391
+
392
+ .ai-percentage {
393
+ font-weight: 600;
394
+ color: #ef4444;
395
+ }
396
+
397
+ .ai-percentage.low {
398
+ color: #10b981;
399
+ }
400
+
401
+ .ai-percentage.medium {
402
+ color: #f59e0b;
403
+ }
404
+
405
+ .ai-percentage.high {
406
+ color: #ef4444;
407
+ }
408
+
409
+ /* ===== LOADING & ERROR STATES ===== */
410
+ .loading {
411
+ color: #64748b;
412
+ font-style: italic;
413
+ text-align: center;
414
+ padding: 40px 20px;
415
+ display: flex;
416
+ align-items: center;
417
+ justify-content: center;
418
+ gap: 8px;
419
+ }
420
+
421
+ .loading::before {
422
+ content: "✨";
423
+ animation: pulse 1.5s infinite;
424
+ }
425
+
426
+ .detector-loading {
427
+ color: #64748b;
428
+ font-style: italic;
429
+ text-align: center;
430
+ padding: 20px;
431
+ display: flex;
432
+ align-items: center;
433
+ justify-content: center;
434
+ gap: 8px;
435
+ }
436
+
437
+ .detector-loading::before {
438
+ content: "πŸ”";
439
+ animation: pulse 1.5s infinite;
440
+ }
441
+
442
+ @keyframes pulse {
443
+ 0%, 100% {
444
+ opacity: 1;
445
+ }
446
+ 50% {
447
+ opacity: 0.5;
448
+ }
449
+ }
450
+
451
+ .error {
452
+ color: #ef4444;
453
+ font-weight: 500;
454
+ text-align: center;
455
+ padding: 24px 16px;
456
+ background: rgba(239, 68, 68, 0.05);
457
+ border-radius: 8px;
458
+ }
459
+
460
+ /* ===== CUSTOM SCROLLBAR for output ===== */
461
+ .output-content::-webkit-scrollbar {
462
+ width: 8px;
463
+ }
464
+ .output-content::-webkit-scrollbar-track {
465
+ background: #f1f5f9;
466
+ border-radius: 4px;
467
+ }
468
+ .output-content::-webkit-scrollbar-thumb {
469
+ background: #cbd5e1;
470
+ border-radius: 4px;
471
+ }
472
+ .output-content::-webkit-scrollbar-thumb:hover {
473
+ background: #94a3b8;
474
+ }
475
+
476
+ /* ===== DEBUG STYLES (temporary - remove in production) ===== */
477
+ .debug-info {
478
+ position: fixed;
479
+ top: 10px;
480
+ right: 10px;
481
+ background: rgba(0, 0, 0, 0.8);
482
+ color: white;
483
+ padding: 10px;
484
+ border-radius: 5px;
485
+ font-size: 12px;
486
+ z-index: 1000;
487
+ display: none; /* Hidden by default */
488
+ }
489
+ </style>
490
+ </head>
491
+ <body>
492
+ <div class="container">
493
+ <!-- Debug info (remove in production) -->
494
+ <div id="debugInfo" class="debug-info"></div>
495
+
496
+ <!-- ===== HEADER ===== -->
497
+ <div class="header">
498
+ <h1>🌟 AI Text Humanizer</h1>
499
+ <p class="description">Transform AI text into natural, human-like language</p>
500
+ </div>
501
+
502
+ <!-- ===== MAIN PANELS ===== -->
503
+ <div class="main-content">
504
+ <!-- ==================== INPUT PANEL ==================== -->
505
+ <div class="panel input-panel">
506
+ <div class="panel-header">
507
+ <div class="panel-title">πŸ“ Input Text</div>
508
+ <div class="panel-subtitle">Paste your AI-generated content here or upload a file</div>
509
+ </div>
510
+
511
+ <textarea
512
+ id="inputText"
513
+ placeholder="Paste your AI-generated text here and watch the magic happen..."
514
+ ></textarea>
515
+
516
+ <div class="panel-bottom">
517
+ <!-- All buttons in one row -->
518
+ <div class="buttons-row">
519
+ <button type="button" onclick="humanize()">πŸš€ Humanize Text</button>
520
+
521
+ <div class="file-input-wrapper">
522
+ <input type="file" id="fileInput" class="file-input" accept=".txt,.doc,.docx,.pdf" onchange="handleFileUpload(event)">
523
+ <label for="fileInput" class="file-input-label">
524
+ πŸ“ Choose File
525
+ </label>
526
+ </div>
527
+
528
+ <button type="button" class="clear-button" onclick="clearText()">πŸ—‘οΈ Clear</button>
529
+ </div>
530
+
531
+ <div id="fileInfo" class="file-info" style="display: none;"></div>
532
+ </div>
533
+ </div>
534
+
535
+ <!-- ==================== OUTPUT PANEL ==================== -->
536
+ <div class="panel output-panel">
537
+ <div class="panel-header">
538
+ <div class="panel-title">✨ Humanized Result</div>
539
+ <div class="panel-subtitle">Your transformed, natural text</div>
540
+ </div>
541
+
542
+ <div class="output-container">
543
+ <!-- #result holds loading / error / final text; scrollable -->
544
+ <div id="result" class="output-content">
545
+ <div class="loading">Your humanized text will appear here...</div>
546
+ </div>
547
+ </div>
548
+
549
+ <div class="panel-bottom">
550
+ <!-- Copy button hidden until text arrives -->
551
+ <button
552
+ id="copyBtn"
553
+ type="button"
554
+ class="copy-button"
555
+ style="display: none;"
556
+ onclick="copyText()"
557
+ >
558
+ πŸ“‹ Copy Text
559
+ </button>
560
+
561
+ <!-- AI Detector button hidden until text arrives -->
562
+ <button
563
+ id="detectorBtn"
564
+ type="button"
565
+ class="detector-button"
566
+ style="display: none;"
567
+ onclick="runDetector()"
568
+ >
569
+ πŸ” AI Detector Results
570
+ </button>
571
+ </div>
572
+ </div>
573
+ </div>
574
+
575
+ <!-- ===== AI DETECTOR SECTION (appears at bottom) ===== -->
576
+ <div id="detectorSection" class="detector-section">
577
+ <div class="panel-header">
578
+ <div class="panel-title">πŸ” AI Detector Analysis</div>
579
+ <div class="panel-subtitle">Detailed analysis of your humanized text</div>
580
+ </div>
581
+ <div id="detectorResults"></div>
582
+ </div>
583
+ </div>
584
+
585
+ <!-- ===== JAVASCRIPT ===== -->
586
+ <script>
587
+ let currentHumanizedText = '';
588
+ let debugMode = false; // Set to true for debugging
589
+
590
+ // Debug function
591
+ function updateDebugInfo(message) {
592
+ if (!debugMode) return;
593
+ const debugEl = document.getElementById('debugInfo');
594
+ debugEl.textContent = message;
595
+ debugEl.style.display = 'block';
596
+ console.log('DEBUG:', message);
597
+ }
598
+
599
+ // Toggle debug mode with Ctrl+D
600
+ document.addEventListener('keydown', (e) => {
601
+ if (e.ctrlKey && e.key === 'd') {
602
+ e.preventDefault();
603
+ debugMode = !debugMode;
604
+ const debugEl = document.getElementById('debugInfo');
605
+ debugEl.style.display = debugMode ? 'block' : 'none';
606
+ updateDebugInfo('Debug mode: ' + (debugMode ? 'ON' : 'OFF'));
607
+ }
608
+ });
609
+
610
+ // File upload handler
611
+ async function handleFileUpload(event) {
612
+ const file = event.target.files[0];
613
+ if (!file) return;
614
+
615
+ updateDebugInfo('File selected: ' + file.name);
616
+
617
+ const fileInfo = document.getElementById('fileInfo');
618
+ const textArea = document.getElementById('inputText');
619
+
620
+ // Show file info
621
+ fileInfo.textContent = `πŸ“„ ${file.name} (${(file.size / 1024).toFixed(1)} KB)`;
622
+ fileInfo.style.display = 'block';
623
+
624
+ try {
625
+ let text = '';
626
+
627
+ if (file.type === 'text/plain' || file.name.endsWith('.txt')) {
628
+ // Handle plain text files
629
+ text = await file.text();
630
+ } else if (file.name.endsWith('.docx')) {
631
+ // For DOCX files, show a message since mammoth requires external library
632
+ textArea.value = '⚠️ DOCX files are not yet supported. Please copy and paste the text manually or use a .txt file.';
633
+ fileInfo.textContent += ' - DOCX support coming soon!';
634
+ return;
635
+ } else if (file.name.endsWith('.pdf')) {
636
+ // For PDF files, show a message since PDF parsing is complex
637
+ textArea.value = '⚠️ PDF files are not yet supported. Please copy and paste the text manually or use a .txt file.';
638
+ fileInfo.textContent += ' - PDF support coming soon!';
639
+ return;
640
+ } else {
641
+ // Try to read as text for other formats
642
+ text = await file.text();
643
+ }
644
+
645
+ // Set the text in the textarea
646
+ textArea.value = text;
647
+
648
+ // Trigger the auto-resize
649
+ textArea.style.height = 'auto';
650
+ textArea.style.height = textArea.scrollHeight + 'px';
651
+
652
+ updateDebugInfo('File loaded successfully, text length: ' + text.length);
653
+ fileInfo.textContent += ' - βœ… Loaded successfully!';
654
+
655
+ } catch (error) {
656
+ updateDebugInfo('Error reading file: ' + error.message);
657
+ fileInfo.textContent = `❌ Error reading ${file.name}: ${error.message}`;
658
+ fileInfo.style.color = '#ef4444';
659
+ console.error('File reading error:', error);
660
+ }
661
+ }
662
+
663
+ // Clear text and file
664
+ function clearText() {
665
+ const textArea = document.getElementById('inputText');
666
+ const fileInput = document.getElementById('fileInput');
667
+ const fileInfo = document.getElementById('fileInfo');
668
+ const outputDiv = document.getElementById('result');
669
+ const copyBtn = document.getElementById('copyBtn');
670
+ const detectorBtn = document.getElementById('detectorBtn');
671
+ const detectorSection = document.getElementById('detectorSection');
672
+
673
+ // Clear all inputs and outputs
674
+ textArea.value = '';
675
+ fileInput.value = '';
676
+ fileInfo.style.display = 'none';
677
+ fileInfo.style.color = '#6b7280'; // Reset color
678
+
679
+ // Reset output area
680
+ outputDiv.innerHTML = '<div class="loading">Your humanized text will appear here...</div>';
681
+ copyBtn.style.display = 'none';
682
+ detectorBtn.style.display = 'none';
683
+ detectorSection.style.display = 'none';
684
+
685
+ // Reset stored text
686
+ currentHumanizedText = '';
687
+
688
+ // Reset textarea height
689
+ textArea.style.height = 'auto';
690
+
691
+ updateDebugInfo('All content cleared');
692
+ }
693
+
694
+ async function humanize() {
695
+ const textArea = document.getElementById("inputText");
696
+ const outputDiv = document.getElementById("result");
697
+ const copyBtn = document.getElementById("copyBtn");
698
+ const detectorBtn = document.getElementById("detectorBtn");
699
+ const detectorSection = document.getElementById("detectorSection");
700
+
701
+ const text = textArea.value.trim();
702
+
703
+ if (!text) {
704
+ alert("Please enter some text first!");
705
+ return;
706
+ }
707
+
708
+ updateDebugInfo('Starting humanize process...');
709
+
710
+ // Hide detector section when starting new humanization
711
+ detectorSection.style.display = "none";
712
+
713
+ // Clear previous results completely
714
+ outputDiv.innerHTML = "";
715
+
716
+ // Show loading spinner
717
+ const spinner = document.createElement("div");
718
+ spinner.className = "loading";
719
+ spinner.textContent = "Processing your text...";
720
+ outputDiv.appendChild(spinner);
721
+
722
+ // Hide buttons until we have a successful response
723
+ copyBtn.style.display = "none";
724
+ detectorBtn.style.display = "none";
725
+
726
+ try {
727
+ updateDebugInfo('Sending request to /humanize...');
728
+
729
+ const response = await fetch("/humanize", {
730
+ method: "POST",
731
+ headers: {
732
+ "Content-Type": "application/json",
733
+ },
734
+ body: JSON.stringify({ text }),
735
+ });
736
+
737
+ updateDebugInfo(`Response status: ${response.status}`);
738
+
739
+ if (!response.ok) {
740
+ throw new Error(`Server responded with status ${response.status}`);
741
+ }
742
+
743
+ const data = await response.json();
744
+ updateDebugInfo('Received humanized text, length: ' + data.humanized.length);
745
+
746
+ // Store the humanized text for detector
747
+ currentHumanizedText = data.humanized;
748
+
749
+ // Replace spinner with the actual humanized text
750
+ outputDiv.innerHTML = "";
751
+ const textNode = document.createTextNode(data.humanized);
752
+ outputDiv.appendChild(textNode);
753
+
754
+ // Show buttons
755
+ copyBtn.style.display = "block";
756
+ detectorBtn.style.display = "block";
757
+
758
+ updateDebugInfo('Humanize completed successfully');
759
+ } catch (error) {
760
+ updateDebugInfo('Error in humanize: ' + error.message);
761
+
762
+ outputDiv.innerHTML = "";
763
+ const errDiv = document.createElement("div");
764
+ errDiv.className = "error";
765
+ errDiv.textContent = `❌ Error: ${error.message}`;
766
+ outputDiv.appendChild(errDiv);
767
+ console.error("Error:", error);
768
+ }
769
+ }
770
+
771
+ async function runDetector() {
772
+ if (!currentHumanizedText) {
773
+ alert("No humanized text available for detection!");
774
+ return;
775
+ }
776
+
777
+ updateDebugInfo('Starting detector process...');
778
+
779
+ const detectorSection = document.getElementById("detectorSection");
780
+ const detectorResults = document.getElementById("detectorResults");
781
+ const detectorBtn = document.getElementById("detectorBtn");
782
+
783
+ // Show the detector section and scroll to it
784
+ detectorSection.style.display = "block";
785
+ setTimeout(() => {
786
+ detectorSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
787
+ }, 100);
788
+
789
+ // Show loading state in the detector results area
790
+ detectorResults.innerHTML = "";
791
+ const spinner = document.createElement("div");
792
+ spinner.className = "detector-loading";
793
+ spinner.textContent = "Running AI detection...";
794
+ detectorResults.appendChild(spinner);
795
+
796
+ // Disable button during processing
797
+ detectorBtn.disabled = true;
798
+ detectorBtn.textContent = "πŸ” Analyzing...";
799
+
800
+ try {
801
+ updateDebugInfo('Sending request to /detect...');
802
+
803
+ const response = await fetch("/detect", {
804
+ method: "POST",
805
+ headers: {
806
+ "Content-Type": "application/json",
807
+ },
808
+ body: JSON.stringify({ text: currentHumanizedText }),
809
+ });
810
+
811
+ updateDebugInfo(`Detect response status: ${response.status}`);
812
+
813
+ if (!response.ok) {
814
+ throw new Error(`Server responded with status ${response.status}`);
815
+ }
816
+
817
+ const data = await response.json();
818
+ updateDebugInfo('Received detector results, count: ' + data.results.length);
819
+
820
+ // Create the detector results table
821
+ detectorResults.innerHTML = `
822
+ <div class="detector-results">
823
+ <div class="detector-table-title">πŸ” AI Detector Tool Results</div>
824
+ <table class="detector-table">
825
+ <thead>
826
+ <tr>
827
+ <th>AI Detector Tool</th>
828
+ <th>AI Probability</th>
829
+ </tr>
830
+ </thead>
831
+ <tbody>
832
+ ${data.results.map(result => {
833
+ const percentage = Math.round(result.ai_probability * 100);
834
+ let colorClass = 'high';
835
+ if (percentage < 30) colorClass = 'low';
836
+ else if (percentage < 70) colorClass = 'medium';
837
+
838
+ return `
839
+ <tr>
840
+ <td>${result.detector_name}</td>
841
+ <td class="ai-percentage ${colorClass}">${percentage}% AI written</td>
842
+ </tr>
843
+ `;
844
+ }).join('')}
845
+ </tbody>
846
+ </table>
847
+ </div>
848
+ `;
849
+
850
+ updateDebugInfo('Detector results displayed successfully');
851
+
852
+ } catch (error) {
853
+ updateDebugInfo('Error in detector: ' + error.message);
854
+
855
+ // Add error message
856
+ detectorResults.innerHTML = "";
857
+ const errDiv = document.createElement("div");
858
+ errDiv.className = "error";
859
+ errDiv.textContent = `❌ Detection Error: ${error.message}`;
860
+ detectorResults.appendChild(errDiv);
861
+
862
+ console.error("Detection Error:", error);
863
+ } finally {
864
+ // Re-enable button
865
+ detectorBtn.disabled = false;
866
+ detectorBtn.textContent = "πŸ” AI Detector Results";
867
+ }
868
+ }
869
+
870
+ async function copyText() {
871
+ const copyBtn = document.getElementById("copyBtn");
872
+ const textToCopy = currentHumanizedText;
873
+
874
+ if (!textToCopy) {
875
+ alert("No text to copy!");
876
+ return;
877
+ }
878
+
879
+ try {
880
+ await navigator.clipboard.writeText(textToCopy);
881
+
882
+ // Instant feedback on the button
883
+ const original = copyBtn.innerHTML;
884
+ copyBtn.innerHTML = "βœ… Copied!";
885
+ copyBtn.classList.add("copied");
886
+ setTimeout(() => {
887
+ copyBtn.innerHTML = original;
888
+ copyBtn.classList.remove("copied");
889
+ }, 2000);
890
+ } catch {
891
+ // Fallback for older browsers
892
+ const tmp = document.createElement("textarea");
893
+ tmp.value = textToCopy;
894
+ document.body.appendChild(tmp);
895
+ tmp.select();
896
+ document.execCommand("copy");
897
+ document.body.removeChild(tmp);
898
+
899
+ copyBtn.innerHTML = "βœ… Copied!";
900
+ setTimeout(() => {
901
+ copyBtn.innerHTML = "πŸ“‹ Copy Text";
902
+ }, 2000);
903
+ }
904
+ }
905
+
906
+ // Ctrl+Enter = humanize
907
+ document.addEventListener("keydown", (e) => {
908
+ if (e.ctrlKey && e.key === "Enter") {
909
+ humanize();
910
+ }
911
+ });
912
+
913
+ // Auto-resize the input textarea as you type
914
+ document.getElementById("inputText").addEventListener("input", function () {
915
+ this.style.height = "auto";
916
+ this.style.height = this.scrollHeight + "px";
917
+ });
918
+ </script>
919
+ </body>
920
+ </html>
app/static/index.html CHANGED
@@ -186,8 +186,7 @@
186
  box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3);
187
  margin-top: 0;
188
  flex-shrink: 0;
189
- width: 100%;
190
- max-width: none; /* Allow full width for output buttons */
191
  }
192
 
193
  .copy-button:hover {
@@ -201,10 +200,9 @@
201
  .detector-button {
202
  background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
203
  box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3);
204
- margin-top: 8px;
205
  flex-shrink: 0;
206
- width: 100%;
207
- max-width: none; /* Allow full width for output buttons */
208
  }
209
 
210
  .detector-button:hover {
@@ -505,7 +503,7 @@
505
  <div class="panel input-panel">
506
  <div class="panel-header">
507
  <div class="panel-title">πŸ“ Input Text</div>
508
- <div class="panel-subtitle">Paste your AI-generated content here or upload a file</div>
509
  </div>
510
 
511
  <textarea
@@ -547,27 +545,28 @@
547
  </div>
548
 
549
  <div class="panel-bottom">
550
- <!-- Copy button hidden until text arrives -->
551
- <button
552
- id="copyBtn"
553
- type="button"
554
- class="copy-button"
555
- style="display: none;"
556
- onclick="copyText()"
557
- >
558
- πŸ“‹ Copy Text
559
- </button>
560
-
561
- <!-- AI Detector button hidden until text arrives -->
562
- <button
563
- id="detectorBtn"
564
- type="button"
565
- class="detector-button"
566
- style="display: none;"
567
- onclick="runDetector()"
568
- >
569
- πŸ” AI Detector Results
570
- </button>
 
571
  </div>
572
  </div>
573
  </div>
 
186
  box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3);
187
  margin-top: 0;
188
  flex-shrink: 0;
189
+ max-width: 140px; /* Same as file input button */
 
190
  }
191
 
192
  .copy-button:hover {
 
200
  .detector-button {
201
  background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
202
  box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3);
203
+ margin-top: 0; /* Remove top margin */
204
  flex-shrink: 0;
205
+ max-width: 180px; /* Slightly wider for longer text */
 
206
  }
207
 
208
  .detector-button:hover {
 
503
  <div class="panel input-panel">
504
  <div class="panel-header">
505
  <div class="panel-title">πŸ“ Input Text</div>
506
+ <div class="panel-subtitle">Paste your AI-generated content or upload a file</div>
507
  </div>
508
 
509
  <textarea
 
545
  </div>
546
 
547
  <div class="panel-bottom">
548
+ <!-- Copy and Detector buttons in one row -->
549
+ <div class="buttons-row">
550
+ <button
551
+ id="copyBtn"
552
+ type="button"
553
+ class="copy-button"
554
+ style="display: none;"
555
+ onclick="copyText()"
556
+ >
557
+ πŸ“‹ Copy Text
558
+ </button>
559
+
560
+ <button
561
+ id="detectorBtn"
562
+ type="button"
563
+ class="detector-button"
564
+ style="display: none;"
565
+ onclick="runDetector()"
566
+ >
567
+ πŸ” AI Detector Results
568
+ </button>
569
+ </div>
570
  </div>
571
  </div>
572
  </div>
app/test/test_models.ipynb CHANGED
@@ -2,7 +2,7 @@
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
- "execution_count": 1,
6
  "metadata": {},
7
  "outputs": [],
8
  "source": [
@@ -11,7 +11,7 @@
11
  },
12
  {
13
  "cell_type": "code",
14
- "execution_count": 2,
15
  "metadata": {},
16
  "outputs": [],
17
  "source": [
@@ -26,14 +26,14 @@
26
  },
27
  {
28
  "cell_type": "code",
29
- "execution_count": 3,
30
  "metadata": {},
31
  "outputs": [
32
  {
33
  "name": "stdout",
34
  "output_type": "stream",
35
  "text": [
36
- "Biology is all about exploring life and the living world, diving into areas that look at how things are built, how they work, how they grow, where they come from, and how they've changed over time. Molecular biology zooms in on the interactions between the tiny parts of cells like DNA, RNA, proteins, and other important molecules. Then there's cellular biology, which takes a closer look at cells themselves β€” the building blocks of life. Inside these cells are organelles like mitochondria for energy production, chloroplasts for photosynthesis, and the nucleus for managing genetic information. Processes such as mitosis and meiosis play crucial roles in helping organisms grow, reproduce, and pass on traits to their offspring. And thanks to cell signaling, cells in multicellular organisms can communicate and coordinate with each other effectively.\n"
37
  ]
38
  }
39
  ],
 
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
+ "execution_count": 8,
6
  "metadata": {},
7
  "outputs": [],
8
  "source": [
 
11
  },
12
  {
13
  "cell_type": "code",
14
+ "execution_count": 9,
15
  "metadata": {},
16
  "outputs": [],
17
  "source": [
 
26
  },
27
  {
28
  "cell_type": "code",
29
+ "execution_count": 10,
30
  "metadata": {},
31
  "outputs": [
32
  {
33
  "name": "stdout",
34
  "output_type": "stream",
35
  "text": [
36
+ "Biology is a science study about life and living things, which include many subfields that look at structure, function, growth, origin, and evolution. Molecular biology is about how parts of cells like DNA, RNA, proteins, and other biomolecules interact with each other. Cellular biology is more about the structure and function of cells because they are the basic units of life. Inside cells, there are organelles such as mitochondria, chloroplasts, and nucleus that show special roles for making energy, doing photosynthesis, and controlling genes. Processes like mitosis and meiosis are very important for growing, reproducing, and passing on traits to next generations. Also cell signaling helps in coordination between many cells in multicellular organisms.\n"
37
  ]
38
  }
39
  ],