Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -2,12 +2,13 @@ import gradio as gr
|
|
2 |
import torch
|
3 |
from transformers import AutoTokenizer, AutoModelForSequenceClassification
|
4 |
import re
|
5 |
-
from tokenizers import normalizers
|
6 |
from tokenizers.normalizers import Sequence, Replace, Strip
|
7 |
-
from tokenizers import Regex
|
8 |
import os
|
9 |
|
10 |
|
|
|
11 |
model1_path = "https://huggingface.co/spaces/SzegedAI/AI_Detector/resolve/main/modernbert.bin"
|
12 |
model2_path = "https://huggingface.co/mihalykiss/modernbert_2/resolve/main/Model_groups_3class_seed12"
|
13 |
model3_path = "https://huggingface.co/mihalykiss/modernbert_2/resolve/main/Model_groups_3class_seed22"
|
@@ -32,12 +33,7 @@ try:
|
|
32 |
|
33 |
except Exception as e:
|
34 |
print(f"Error during model loading: {e}")
|
35 |
-
|
36 |
-
# Handle the error, e.g., by exiting or displaying an error in the UI if Gradio is already running.
|
37 |
-
# For simplicity, we'll let it potentially crash if models can't load before Gradio starts.
|
38 |
-
# If Gradio is already running, you'd need a more sophisticated error display.
|
39 |
-
# For now, we'll just make sure the Gradio interface doesn't try to use non-existent models.
|
40 |
-
tokenizer = None # Prevent further errors if tokenizer failed
|
41 |
model_1, model_2, model_3 = None, None, None
|
42 |
|
43 |
|
@@ -59,15 +55,31 @@ def clean_text(text: str) -> str:
|
|
59 |
text = re.sub(r'\s+([,.;:?!])', r'\1', text)
|
60 |
return text
|
61 |
|
62 |
-
if tokenizer:
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
join_hyphen_break,
|
68 |
-
newline_to_space,
|
69 |
Strip()
|
70 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
|
72 |
|
73 |
title_md = """
|
@@ -82,7 +94,7 @@ description = """
|
|
82 |
<li><span class="icon">✅</span> <strong>Human Verification: </strong> Clearly identifies human-written content.</li>
|
83 |
<li><span class="icon">🔍</span> <strong>Model Detection: </strong> Capable of identifying content from over 40 AI models.</li>
|
84 |
<li><span class="icon">📈</span> <strong>Accuracy: </strong> Performs optimally with more extensive text inputs.</li>
|
85 |
-
<li><span class="icon">📄</span> <strong>Read more: </strong> Our methodology is detailed in our research paper:
|
86 |
<a href="https://aclanthology.org/2025.genaidetect-1.15/" target="_blank" class="learn-more-link"> <b> LINK </b></a>.
|
87 |
</li>
|
88 |
</ul>
|
@@ -104,21 +116,21 @@ def classify_text_interface(text):
|
|
104 |
return "<p style='text-align: center; color: var(--ai-color);'><strong>Error: Models not loaded. Please check the console.</strong></p>"
|
105 |
|
106 |
cleaned_text = clean_text(text)
|
107 |
-
if not cleaned_text.strip():
|
108 |
result_message = "<p style='text-align: center; color: var(--text-secondary);'>Please enter some text to analyze.</p>"
|
109 |
return result_message
|
110 |
|
111 |
-
inputs = tokenizer(cleaned_text, return_tensors="pt", truncation=True, padding=True, max_length=512).to(device)
|
112 |
-
|
113 |
with torch.no_grad():
|
114 |
logits_1 = model_1(**inputs).logits
|
115 |
logits_2 = model_2(**inputs).logits
|
116 |
logits_3 = model_3(**inputs).logits
|
117 |
-
|
118 |
softmax_1 = torch.softmax(logits_1, dim=1)
|
119 |
softmax_2 = torch.softmax(logits_2, dim=1)
|
120 |
softmax_3 = torch.softmax(logits_3, dim=1)
|
121 |
-
|
122 |
averaged_probabilities = (softmax_1 + softmax_2 + softmax_3) / 3
|
123 |
probabilities = averaged_probabilities[0]
|
124 |
|
@@ -128,21 +140,21 @@ def classify_text_interface(text):
|
|
128 |
if v.lower() == 'human':
|
129 |
human_label_index = k
|
130 |
break
|
131 |
-
|
132 |
if human_label_index != -1:
|
133 |
-
ai_probs[human_label_index] = 0
|
134 |
human_prob_value = probabilities[human_label_index].item() * 100
|
135 |
-
else:
|
136 |
-
human_prob_value = 0
|
137 |
print("Warning: 'human' label not found in label_mapping.")
|
138 |
|
139 |
ai_total_prob = ai_probs.sum().item() * 100
|
140 |
|
141 |
-
|
142 |
-
ai_argmax_index = torch.argmax(ai_probs).item()
|
143 |
ai_argmax_model = label_mapping.get(ai_argmax_index, "Unknown AI")
|
144 |
|
145 |
-
if human_prob_value > ai_total_prob :
|
146 |
result_message = (
|
147 |
f"<p><strong>The text is</strong> <span class='highlight-human'><strong>{human_prob_value:.2f}%</strong> likely <b>Human written</b>.</span></p>"
|
148 |
)
|
@@ -156,33 +168,86 @@ def classify_text_interface(text):
|
|
156 |
modern_css = """
|
157 |
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
158 |
|
|
|
159 |
:root {
|
160 |
-
--primary-bg: #
|
161 |
--app-bg: #FFFFFF;
|
162 |
--text-primary: #2C3E50;
|
163 |
--text-secondary: #7F8C8D;
|
164 |
--accent-color: #1ABC9C;
|
165 |
--accent-color-darker: #16A085;
|
166 |
-
--border-color: #
|
167 |
--input-bg: #FFFFFF;
|
168 |
--input-focus-border: var(--accent-color);
|
169 |
-
--human-color: #2ECC71;
|
170 |
--human-bg: rgba(46, 204, 113, 0.1);
|
171 |
--ai-color: #E74C3C;
|
172 |
--ai-bg: rgba(231, 76, 60, 0.1);
|
173 |
--shadow-color: rgba(44, 62, 80, 0.1);
|
174 |
-
--container-max-width: 800px;
|
175 |
--border-radius-md: 8px;
|
176 |
--border-radius-lg: 12px;
|
|
|
|
|
|
|
|
|
|
|
|
|
177 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
178 |
.features-list strong::after {
|
179 |
content: " ";
|
180 |
display: inline-block;
|
181 |
-
width: 0.2em;
|
182 |
}
|
|
|
183 |
body {
|
184 |
font-family: 'Inter', sans-serif;
|
185 |
-
background:
|
186 |
color: var(--text-primary);
|
187 |
margin: 0;
|
188 |
padding: 20px;
|
@@ -192,6 +257,7 @@ body {
|
|
192 |
min-height: 100vh;
|
193 |
box-sizing: border-box;
|
194 |
overflow-y: auto;
|
|
|
195 |
}
|
196 |
|
197 |
.gradio-container {
|
@@ -202,19 +268,21 @@ body {
|
|
202 |
max-width: var(--container-max-width);
|
203 |
width: 100%;
|
204 |
margin: 20px auto;
|
205 |
-
border: none;
|
|
|
206 |
}
|
207 |
|
208 |
-
.form.svelte-633qhp, .block.svelte-11xb1hd, .gradio-html .block {
|
209 |
background: none !important;
|
210 |
border: none !important;
|
211 |
box-shadow: none !important;
|
212 |
padding: 0 !important;
|
213 |
}
|
214 |
|
215 |
-
|
|
|
216 |
color: var(--text-primary);
|
217 |
-
font-size: clamp(24px, 5vw, 30px);
|
218 |
font-weight: 700;
|
219 |
text-align: center;
|
220 |
margin-bottom: 20px;
|
@@ -243,7 +311,7 @@ h1 {
|
|
243 |
|
244 |
.features-list li {
|
245 |
display: flex;
|
246 |
-
align-items: center;
|
247 |
font-size: clamp(14px, 2.5vw, 16px);
|
248 |
color: var(--text-secondary);
|
249 |
margin-bottom: 12px;
|
@@ -254,6 +322,7 @@ h1 {
|
|
254 |
margin-right: 12px;
|
255 |
font-size: 1.2em;
|
256 |
color: var(--accent-color);
|
|
|
257 |
}
|
258 |
|
259 |
.learn-more-link, .learn-more-link b {
|
@@ -275,22 +344,23 @@ h1 {
|
|
275 |
width: 100%;
|
276 |
box-sizing: border-box;
|
277 |
color: var(--text-primary);
|
278 |
-
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
279 |
min-height: 120px;
|
280 |
-
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
281 |
}
|
282 |
|
283 |
#text_input_box textarea::placeholder {
|
284 |
-
color:
|
|
|
285 |
}
|
286 |
|
287 |
#text_input_box textarea:focus {
|
288 |
border-color: var(--input-focus-border);
|
289 |
-
box-shadow: 0 0 0 3px
|
290 |
outline: none;
|
291 |
}
|
292 |
|
293 |
-
#result_output_box {
|
294 |
background-color: var(--input-bg);
|
295 |
border: 1px solid var(--border-color);
|
296 |
border-radius: var(--border-radius-md);
|
@@ -300,41 +370,46 @@ h1 {
|
|
300 |
box-sizing: border-box;
|
301 |
text-align: center;
|
302 |
font-size: clamp(16px, 3vw, 17px);
|
303 |
-
box-shadow: 0 4px 8px rgba(0,0,0,0.05);
|
304 |
-
min-height: 80px;
|
305 |
-
display: flex;
|
306 |
flex-direction: column;
|
307 |
justify-content: center;
|
|
|
308 |
}
|
309 |
-
#result_output_box p {
|
310 |
-
margin-bottom: 8px;
|
311 |
line-height: 1.6;
|
|
|
312 |
}
|
313 |
#result_output_box p:last-child {
|
314 |
margin-bottom: 0;
|
315 |
}
|
|
|
|
|
|
|
316 |
|
317 |
|
318 |
-
.highlight-human, .highlight-ai {
|
319 |
font-weight: 600;
|
320 |
-
padding: 5px 10px;
|
321 |
border-radius: var(--border-radius-md);
|
322 |
display: inline-block;
|
323 |
-
font-size: 1.05em;
|
|
|
324 |
}
|
325 |
|
326 |
.highlight-human {
|
327 |
color: var(--human-color);
|
328 |
background-color: var(--human-bg);
|
329 |
-
/* border: 1px solid var(--human-color);
|
330 |
}
|
331 |
|
332 |
.highlight-ai {
|
333 |
color: var(--ai-color);
|
334 |
background-color: var(--ai-bg);
|
335 |
-
/* border: 1px solid var(--ai-color);
|
336 |
}
|
337 |
|
|
|
338 |
.tabs > div:first-child button {
|
339 |
background-color: transparent !important;
|
340 |
color: var(--text-secondary) !important;
|
@@ -352,32 +427,56 @@ h1 {
|
|
352 |
font-weight: 600 !important;
|
353 |
}
|
354 |
|
355 |
-
|
356 |
-
|
357 |
-
border: 1px solid var(--border
|
358 |
-
border-radius: var(--border-radius-
|
359 |
-
|
360 |
-
|
|
|
|
|
|
|
361 |
}
|
362 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
363 |
border: 1px solid var(--border-color) !important;
|
364 |
border-radius: var(--border-radius-md) !important;
|
365 |
font-size: 14px !important;
|
|
|
|
|
|
|
366 |
}
|
367 |
-
.gr-
|
368 |
-
|
369 |
-
color: var(--text-primary) !important;
|
370 |
}
|
371 |
|
372 |
|
373 |
.footer-text, #bottom_text {
|
374 |
text-align: center;
|
375 |
-
margin-top: 40px;
|
376 |
font-size: clamp(13px, 2vw, 14px);
|
377 |
color: var(--text-secondary);
|
378 |
}
|
379 |
-
|
380 |
-
#bottom_text p {
|
381 |
margin: 0;
|
382 |
}
|
383 |
|
@@ -390,10 +489,11 @@ h1 {
|
|
390 |
padding: 20px;
|
391 |
margin: 10px;
|
392 |
}
|
393 |
-
h1 { font-size: 22px; }
|
394 |
.app-description p, .features-list li { font-size: 14px; }
|
395 |
#text_input_box textarea { font-size: 15px; min-height: 100px; }
|
396 |
#result_output_box { font-size: 15px; padding: 15px; }
|
|
|
397 |
}
|
398 |
"""
|
399 |
|
@@ -407,7 +507,7 @@ with iface:
|
|
407 |
label="",
|
408 |
placeholder="Type or paste your content here...",
|
409 |
elem_id="text_input_box",
|
410 |
-
lines=10
|
411 |
)
|
412 |
result_output = gr.HTML(elem_id="result_output_box")
|
413 |
|
@@ -421,13 +521,15 @@ with iface:
|
|
421 |
examples=AI_texts,
|
422 |
inputs=text_input,
|
423 |
label="",
|
|
|
424 |
)
|
425 |
|
426 |
with gr.Accordion("Human Text Examples", open=False):
|
427 |
gr.Examples(
|
428 |
examples=Human_texts,
|
429 |
inputs=text_input,
|
430 |
-
label="",
|
|
|
431 |
)
|
432 |
|
433 |
gr.Markdown(bottom_text, elem_id="bottom_text")
|
|
|
2 |
import torch
|
3 |
from transformers import AutoTokenizer, AutoModelForSequenceClassification
|
4 |
import re
|
5 |
+
from tokenizers import normalizers # For isinstance check
|
6 |
from tokenizers.normalizers import Sequence, Replace, Strip
|
7 |
+
from tokenizers import Regex # For the Regex class
|
8 |
import os
|
9 |
|
10 |
|
11 |
+
# --- Model & Tokenizer Configuration ---
|
12 |
model1_path = "https://huggingface.co/spaces/SzegedAI/AI_Detector/resolve/main/modernbert.bin"
|
13 |
model2_path = "https://huggingface.co/mihalykiss/modernbert_2/resolve/main/Model_groups_3class_seed12"
|
14 |
model3_path = "https://huggingface.co/mihalykiss/modernbert_2/resolve/main/Model_groups_3class_seed22"
|
|
|
33 |
|
34 |
except Exception as e:
|
35 |
print(f"Error during model loading: {e}")
|
36 |
+
tokenizer = None
|
|
|
|
|
|
|
|
|
|
|
37 |
model_1, model_2, model_3 = None, None, None
|
38 |
|
39 |
|
|
|
55 |
text = re.sub(r'\s+([,.;:?!])', r'\1', text)
|
56 |
return text
|
57 |
|
58 |
+
if tokenizer:
|
59 |
+
# Define the new normalizers to add
|
60 |
+
custom_normalizers_to_add = [
|
61 |
+
Replace(Regex(r'(\w+)[--]\s*\n\s*(\w+)'), r"\1\2"), # join_hyphen_break
|
62 |
+
Replace(Regex(r'\s*\n\s*'), " "), # newline_to_space
|
|
|
|
|
63 |
Strip()
|
64 |
+
]
|
65 |
+
|
66 |
+
# Get the current normalizer from the backend tokenizer
|
67 |
+
current_backend_normalizer = tokenizer.backend_tokenizer.normalizer
|
68 |
+
|
69 |
+
if current_backend_normalizer is None:
|
70 |
+
# If no existing normalizer, just use the new ones in a Sequence
|
71 |
+
tokenizer.backend_tokenizer.normalizer = Sequence(custom_normalizers_to_add)
|
72 |
+
elif isinstance(current_backend_normalizer, normalizers.Sequence):
|
73 |
+
# If existing is a Sequence, extend its list of normalizers
|
74 |
+
# The .normalizers attribute of a Sequence is a list, so we can extend it
|
75 |
+
current_backend_normalizer.normalizers.extend(custom_normalizers_to_add)
|
76 |
+
# Re-assign to ensure change is registered if Sequence behaves immutably (though it likely modifies in place)
|
77 |
+
tokenizer.backend_tokenizer.normalizer = Sequence(current_backend_normalizer.normalizers)
|
78 |
+
else:
|
79 |
+
# If existing is a single normalizer (not None and not a Sequence), create a new Sequence
|
80 |
+
tokenizer.backend_tokenizer.normalizer = Sequence([current_backend_normalizer] + custom_normalizers_to_add)
|
81 |
+
|
82 |
+
# --- End Model & Tokenizer Configuration ---
|
83 |
|
84 |
|
85 |
title_md = """
|
|
|
94 |
<li><span class="icon">✅</span> <strong>Human Verification: </strong> Clearly identifies human-written content.</li>
|
95 |
<li><span class="icon">🔍</span> <strong>Model Detection: </strong> Capable of identifying content from over 40 AI models.</li>
|
96 |
<li><span class="icon">📈</span> <strong>Accuracy: </strong> Performs optimally with more extensive text inputs.</li>
|
97 |
+
<li><span class="icon">📄</span> <strong>Read more: </strong> Our methodology is detailed in our research paper:
|
98 |
<a href="https://aclanthology.org/2025.genaidetect-1.15/" target="_blank" class="learn-more-link"> <b> LINK </b></a>.
|
99 |
</li>
|
100 |
</ul>
|
|
|
116 |
return "<p style='text-align: center; color: var(--ai-color);'><strong>Error: Models not loaded. Please check the console.</strong></p>"
|
117 |
|
118 |
cleaned_text = clean_text(text)
|
119 |
+
if not cleaned_text.strip():
|
120 |
result_message = "<p style='text-align: center; color: var(--text-secondary);'>Please enter some text to analyze.</p>"
|
121 |
return result_message
|
122 |
|
123 |
+
inputs = tokenizer(cleaned_text, return_tensors="pt", truncation=True, padding=True, max_length=512).to(device)
|
124 |
+
|
125 |
with torch.no_grad():
|
126 |
logits_1 = model_1(**inputs).logits
|
127 |
logits_2 = model_2(**inputs).logits
|
128 |
logits_3 = model_3(**inputs).logits
|
129 |
+
|
130 |
softmax_1 = torch.softmax(logits_1, dim=1)
|
131 |
softmax_2 = torch.softmax(logits_2, dim=1)
|
132 |
softmax_3 = torch.softmax(logits_3, dim=1)
|
133 |
+
|
134 |
averaged_probabilities = (softmax_1 + softmax_2 + softmax_3) / 3
|
135 |
probabilities = averaged_probabilities[0]
|
136 |
|
|
|
140 |
if v.lower() == 'human':
|
141 |
human_label_index = k
|
142 |
break
|
143 |
+
|
144 |
if human_label_index != -1:
|
145 |
+
ai_probs[human_label_index] = 0
|
146 |
human_prob_value = probabilities[human_label_index].item() * 100
|
147 |
+
else:
|
148 |
+
human_prob_value = 0
|
149 |
print("Warning: 'human' label not found in label_mapping.")
|
150 |
|
151 |
ai_total_prob = ai_probs.sum().item() * 100
|
152 |
|
153 |
+
|
154 |
+
ai_argmax_index = torch.argmax(ai_probs).item()
|
155 |
ai_argmax_model = label_mapping.get(ai_argmax_index, "Unknown AI")
|
156 |
|
157 |
+
if human_prob_value > ai_total_prob :
|
158 |
result_message = (
|
159 |
f"<p><strong>The text is</strong> <span class='highlight-human'><strong>{human_prob_value:.2f}%</strong> likely <b>Human written</b>.</span></p>"
|
160 |
)
|
|
|
168 |
modern_css = """
|
169 |
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
170 |
|
171 |
+
/* Light Theme (Default) */
|
172 |
:root {
|
173 |
+
--primary-bg: #F4F7FC; /* Lighter body background */
|
174 |
--app-bg: #FFFFFF;
|
175 |
--text-primary: #2C3E50;
|
176 |
--text-secondary: #7F8C8D;
|
177 |
--accent-color: #1ABC9C;
|
178 |
--accent-color-darker: #16A085;
|
179 |
+
--border-color: #E0E6ED; /* Slightly softer border */
|
180 |
--input-bg: #FFFFFF;
|
181 |
--input-focus-border: var(--accent-color);
|
182 |
+
--human-color: #2ECC71;
|
183 |
--human-bg: rgba(46, 204, 113, 0.1);
|
184 |
--ai-color: #E74C3C;
|
185 |
--ai-bg: rgba(231, 76, 60, 0.1);
|
186 |
--shadow-color: rgba(44, 62, 80, 0.1);
|
187 |
+
--container-max-width: 800px;
|
188 |
--border-radius-md: 8px;
|
189 |
--border-radius-lg: 12px;
|
190 |
+
--examples-bg: #F8F9FA; /* Light background for examples area */
|
191 |
+
--placeholder-color: #B0BEC5;
|
192 |
+
--accordion-label-color: var(--text-primary);
|
193 |
+
--accordion-bg: var(--app-bg); /* Accordion bg same as app-bg for seamless look */
|
194 |
+
--accordion-border: var(--border-color);
|
195 |
+
--sample-textbox-bg: var(--input-bg);
|
196 |
}
|
197 |
+
|
198 |
+
/* Dark Theme Overrides */
|
199 |
+
@media (prefers-color-scheme: dark) {
|
200 |
+
:root {
|
201 |
+
--primary-bg: #1A1D2E; /* Dark Blue/Charcoal for body */
|
202 |
+
--app-bg: #252936; /* Slightly lighter dark for main container */
|
203 |
+
--text-primary: #EAEAF1; /* Off-white for primary text */
|
204 |
+
--text-secondary: #A0A3AF; /* Lighter gray for secondary text */
|
205 |
+
--accent-color: #1DE9B6; /* Brighter Teal for dark mode */
|
206 |
+
--accent-color-darker: #00BFA5;
|
207 |
+
--border-color: #3A3E4F; /* Darker, less prominent border */
|
208 |
+
--input-bg: #2C3040; /* Darker input background */
|
209 |
+
/* --input-focus-border: var(--accent-color); /* Can remain the same or use a brighter version */
|
210 |
+
--human-color: #69F0AE; /* Brighter Green */
|
211 |
+
--human-bg: rgba(105, 240, 174, 0.15); /* Slightly more opaque for dark bg */
|
212 |
+
--ai-color: #FF8A80; /* Brighter Red */
|
213 |
+
--ai-bg: rgba(255, 138, 128, 0.15); /* Slightly more opaque for dark bg */
|
214 |
+
--shadow-color: rgba(0, 0, 0, 0.25); /* Shadow on dark bg */
|
215 |
+
--examples-bg: #2A2E3B; /* Dark background for examples area */
|
216 |
+
--placeholder-color: #7A7D8A;
|
217 |
+
--accordion-label-color: var(--text-primary);
|
218 |
+
--accordion-bg: var(--app-bg); /* Keep accordion bg consistent with app */
|
219 |
+
--accordion-border: var(--border-color);
|
220 |
+
--sample-textbox-bg: var(--input-bg);
|
221 |
+
}
|
222 |
+
|
223 |
+
body { /* Ensure body background changes for dark mode */
|
224 |
+
background: var(--primary-bg);
|
225 |
+
}
|
226 |
+
|
227 |
+
/* Specific component adjustments for dark mode if needed */
|
228 |
+
#text_input_box textarea::placeholder {
|
229 |
+
color: var(--placeholder-color);
|
230 |
+
}
|
231 |
+
.gr-accordion > .label-wrap button {
|
232 |
+
color: var(--accordion-label-color) !important; /* Ensure it uses the dark mode text color */
|
233 |
+
}
|
234 |
+
.learn-more-link, .learn-more-link b { /* Ensure link color adapts */
|
235 |
+
color: var(--accent-color) !important;
|
236 |
+
}
|
237 |
+
.learn-more-link:hover, .learn-more-link:hover b {
|
238 |
+
color: var(--accent-color-darker) !important;
|
239 |
+
}
|
240 |
+
}
|
241 |
+
|
242 |
.features-list strong::after {
|
243 |
content: " ";
|
244 |
display: inline-block;
|
245 |
+
width: 0.2em; /* Adds a small space after the bolded part like "Verification: " */
|
246 |
}
|
247 |
+
|
248 |
body {
|
249 |
font-family: 'Inter', sans-serif;
|
250 |
+
background: var(--primary-bg); /* Use CSS variable */
|
251 |
color: var(--text-primary);
|
252 |
margin: 0;
|
253 |
padding: 20px;
|
|
|
257 |
min-height: 100vh;
|
258 |
box-sizing: border-box;
|
259 |
overflow-y: auto;
|
260 |
+
transition: background-color 0.3s ease, color 0.3s ease; /* Smooth theme transition */
|
261 |
}
|
262 |
|
263 |
.gradio-container {
|
|
|
268 |
max-width: var(--container-max-width);
|
269 |
width: 100%;
|
270 |
margin: 20px auto;
|
271 |
+
border: none; /* Gradio might add its own border, ensure it's controlled or removed */
|
272 |
+
transition: background-color 0.3s ease, box-shadow 0.3s ease;
|
273 |
}
|
274 |
|
275 |
+
.form.svelte-633qhp, .block.svelte-11xb1hd, .gradio-html .block {
|
276 |
background: none !important;
|
277 |
border: none !important;
|
278 |
box-shadow: none !important;
|
279 |
padding: 0 !important;
|
280 |
}
|
281 |
|
282 |
+
/* Title and subtitle are handled by title_md */
|
283 |
+
h1 { /* Fallback or for other h1s if any */
|
284 |
color: var(--text-primary);
|
285 |
+
font-size: clamp(24px, 5vw, 30px);
|
286 |
font-weight: 700;
|
287 |
text-align: center;
|
288 |
margin-bottom: 20px;
|
|
|
311 |
|
312 |
.features-list li {
|
313 |
display: flex;
|
314 |
+
align-items: center; /* Align icon with the first line of text */
|
315 |
font-size: clamp(14px, 2.5vw, 16px);
|
316 |
color: var(--text-secondary);
|
317 |
margin-bottom: 12px;
|
|
|
322 |
margin-right: 12px;
|
323 |
font-size: 1.2em;
|
324 |
color: var(--accent-color);
|
325 |
+
flex-shrink: 0; /* Prevent icon from shrinking */
|
326 |
}
|
327 |
|
328 |
.learn-more-link, .learn-more-link b {
|
|
|
344 |
width: 100%;
|
345 |
box-sizing: border-box;
|
346 |
color: var(--text-primary);
|
347 |
+
transition: background-color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease;
|
348 |
min-height: 120px;
|
349 |
+
box-shadow: 0 2px 4px rgba(0,0,0,0.05); /* Subtle shadow for light mode */
|
350 |
}
|
351 |
|
352 |
#text_input_box textarea::placeholder {
|
353 |
+
color: var(--placeholder-color);
|
354 |
+
transition: color 0.3s ease;
|
355 |
}
|
356 |
|
357 |
#text_input_box textarea:focus {
|
358 |
border-color: var(--input-focus-border);
|
359 |
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--input-focus-border) 25%, transparent); /* Use color-mix for focus shadow */
|
360 |
outline: none;
|
361 |
}
|
362 |
|
363 |
+
#result_output_box {
|
364 |
background-color: var(--input-bg);
|
365 |
border: 1px solid var(--border-color);
|
366 |
border-radius: var(--border-radius-md);
|
|
|
370 |
box-sizing: border-box;
|
371 |
text-align: center;
|
372 |
font-size: clamp(16px, 3vw, 17px);
|
373 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.05); /* Subtle shadow for light mode */
|
374 |
+
min-height: 80px;
|
375 |
+
display: flex;
|
376 |
flex-direction: column;
|
377 |
justify-content: center;
|
378 |
+
transition: background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease;
|
379 |
}
|
380 |
+
#result_output_box p {
|
381 |
+
margin-bottom: 8px;
|
382 |
line-height: 1.6;
|
383 |
+
color: var(--text-primary); /* Ensure text inside result box also adapts */
|
384 |
}
|
385 |
#result_output_box p:last-child {
|
386 |
margin-bottom: 0;
|
387 |
}
|
388 |
+
#result_output_box strong { /* Ensure bold text also adapts color */
|
389 |
+
color: var(--text-primary);
|
390 |
+
}
|
391 |
|
392 |
|
393 |
+
.highlight-human, .highlight-ai { /* These are spans, color is set by their own var */
|
394 |
font-weight: 600;
|
395 |
+
padding: 5px 10px;
|
396 |
border-radius: var(--border-radius-md);
|
397 |
display: inline-block;
|
398 |
+
font-size: 1.05em;
|
399 |
+
transition: background-color 0.3s ease, color 0.3s ease;
|
400 |
}
|
401 |
|
402 |
.highlight-human {
|
403 |
color: var(--human-color);
|
404 |
background-color: var(--human-bg);
|
|
|
405 |
}
|
406 |
|
407 |
.highlight-ai {
|
408 |
color: var(--ai-color);
|
409 |
background-color: var(--ai-bg);
|
|
|
410 |
}
|
411 |
|
412 |
+
/* Gradio specific Tab styling (if you were to use Tabs) */
|
413 |
.tabs > div:first-child button {
|
414 |
background-color: transparent !important;
|
415 |
color: var(--text-secondary) !important;
|
|
|
427 |
font-weight: 600 !important;
|
428 |
}
|
429 |
|
430 |
+
/* Accordion and Examples Styling */
|
431 |
+
.gr-accordion {
|
432 |
+
border: 1px solid var(--accordion-border) !important;
|
433 |
+
border-radius: var(--border-radius-lg) !important;
|
434 |
+
box-shadow: none !important;
|
435 |
+
padding: 0 15px 15px 15px !important;
|
436 |
+
margin-bottom: 20px !important; /* Increased space below each accordion */
|
437 |
+
background-color: var(--accordion-bg) !important; /* Use variable for accordion background */
|
438 |
+
transition: background-color 0.3s ease, border-color 0.3s ease;
|
439 |
}
|
440 |
+
|
441 |
+
.gr-accordion > .label-wrap button {
|
442 |
+
font-weight: 600 !important;
|
443 |
+
color: var(--accordion-label-color) !important;
|
444 |
+
padding: 15px 0px !important; /* More padding for label */
|
445 |
+
font-size: 1.05em !important;
|
446 |
+
transition: color 0.3s ease;
|
447 |
+
}
|
448 |
+
.gr-accordion > .label-wrap { /* Remove default Gradio accordion label border */
|
449 |
+
border-bottom: none !important;
|
450 |
+
}
|
451 |
+
|
452 |
+
|
453 |
+
.gr-examples { /* Wrapper for the examples content */
|
454 |
+
padding: 15px 0px 0px 0px !important; /* Adjust padding, top padding handled by accordion */
|
455 |
+
border: none !important; /* Border is on accordion now */
|
456 |
+
border-radius: 0 !important; /* Rounded corners on accordion */
|
457 |
+
background-color: transparent !important; /* Examples area transparent, accordion has bg */
|
458 |
+
margin-top: 0px !important; /* No extra margin, accordion handles it */
|
459 |
+
}
|
460 |
+
.gr-sample-textbox { /* Individual example textboxes */
|
461 |
border: 1px solid var(--border-color) !important;
|
462 |
border-radius: var(--border-radius-md) !important;
|
463 |
font-size: 14px !important;
|
464 |
+
background-color: var(--sample-textbox-bg) !important;
|
465 |
+
color: var(--text-primary) !important; /* Text color for example text */
|
466 |
+
transition: background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease;
|
467 |
}
|
468 |
+
.gr-sample-textbox:hover {
|
469 |
+
border-color: var(--accent-color) !important; /* Highlight on hover */
|
|
|
470 |
}
|
471 |
|
472 |
|
473 |
.footer-text, #bottom_text {
|
474 |
text-align: center;
|
475 |
+
margin-top: 40px; /* Keep space above footer */
|
476 |
font-size: clamp(13px, 2vw, 14px);
|
477 |
color: var(--text-secondary);
|
478 |
}
|
479 |
+
#bottom_text p {
|
|
|
480 |
margin: 0;
|
481 |
}
|
482 |
|
|
|
489 |
padding: 20px;
|
490 |
margin: 10px;
|
491 |
}
|
492 |
+
h1 { font-size: 22px; }
|
493 |
.app-description p, .features-list li { font-size: 14px; }
|
494 |
#text_input_box textarea { font-size: 15px; min-height: 100px; }
|
495 |
#result_output_box { font-size: 15px; padding: 15px; }
|
496 |
+
.gr-accordion > .label-wrap button { padding: 12px 0 !important; }
|
497 |
}
|
498 |
"""
|
499 |
|
|
|
507 |
label="",
|
508 |
placeholder="Type or paste your content here...",
|
509 |
elem_id="text_input_box",
|
510 |
+
lines=10
|
511 |
)
|
512 |
result_output = gr.HTML(elem_id="result_output_box")
|
513 |
|
|
|
521 |
examples=AI_texts,
|
522 |
inputs=text_input,
|
523 |
label="",
|
524 |
+
elem_classes="gr-examples" # Added class for styling
|
525 |
)
|
526 |
|
527 |
with gr.Accordion("Human Text Examples", open=False):
|
528 |
gr.Examples(
|
529 |
examples=Human_texts,
|
530 |
inputs=text_input,
|
531 |
+
label="",
|
532 |
+
elem_classes="gr-examples" # Added class for styling
|
533 |
)
|
534 |
|
535 |
gr.Markdown(bottom_text, elem_id="bottom_text")
|