vicliv commited on
Commit
f95013c
·
1 Parent(s): b08bc0e

Improve report form: video support, expanded reasons with detail fields, professional disclaimer wording, motion blur limitation

Browse files
Files changed (2) hide show
  1. app/main.py +11 -8
  2. app/static/index.html +102 -39
app/main.py CHANGED
@@ -136,24 +136,25 @@ async def report(
136
  is_real: str = Form(...),
137
  reason: str = Form(...),
138
  reason_other: str = Form(""),
 
139
  comment: str = Form(""),
140
  p_fake: float = Form(...),
141
  consent: str = Form(...),
142
  ):
143
- """Save an error report (form answers + image) to a Hugging Face dataset repo."""
144
  if consent != "true":
145
- raise HTTPException(400, "Image saving consent is required.")
146
 
147
  if not HF_TOKEN:
148
  raise HTTPException(
149
  503, "Reporting is not configured (missing HF_TOKEN)."
150
  )
151
 
152
- # Read the uploaded image
153
  raw = await file.read()
154
  content_type = (file.content_type or "").lower()
155
- if content_type not in IMAGE_TYPES:
156
- raise HTTPException(415, "Only images can be reported.")
157
 
158
  # Build report payload
159
  ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H-%M-%S")
@@ -165,8 +166,10 @@ async def report(
165
  "is_real": is_real,
166
  "reason": reason,
167
  "reason_other": reason_other if reason == "other" else "",
 
168
  "comment": comment,
169
  "p_fake": p_fake,
 
170
  "original_filename": file.filename or "unknown",
171
  }
172
 
@@ -180,9 +183,9 @@ async def report(
180
  json.dumps(report_data, indent=2, ensure_ascii=False)
181
  )
182
 
183
- # Save image with original extension
184
- ext = Path(file.filename or "image.jpg").suffix or ".jpg"
185
- (report_dir / f"image{ext}").write_bytes(raw)
186
 
187
  # Upload to HF dataset repo
188
  try:
 
136
  is_real: str = Form(...),
137
  reason: str = Form(...),
138
  reason_other: str = Form(""),
139
+ reason_details: str = Form(""),
140
  comment: str = Form(""),
141
  p_fake: float = Form(...),
142
  consent: str = Form(...),
143
  ):
144
+ """Save an error report (form answers + media file) to a Hugging Face dataset repo."""
145
  if consent != "true":
146
+ raise HTTPException(400, "Consent to save the file is required.")
147
 
148
  if not HF_TOKEN:
149
  raise HTTPException(
150
  503, "Reporting is not configured (missing HF_TOKEN)."
151
  )
152
 
153
+ # Read the uploaded file
154
  raw = await file.read()
155
  content_type = (file.content_type or "").lower()
156
+ if content_type not in IMAGE_TYPES | VIDEO_TYPES:
157
+ raise HTTPException(415, "Unsupported file type for reporting.")
158
 
159
  # Build report payload
160
  ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H-%M-%S")
 
166
  "is_real": is_real,
167
  "reason": reason,
168
  "reason_other": reason_other if reason == "other" else "",
169
+ "reason_details": reason_details,
170
  "comment": comment,
171
  "p_fake": p_fake,
172
+ "content_type": content_type,
173
  "original_filename": file.filename or "unknown",
174
  }
175
 
 
183
  json.dumps(report_data, indent=2, ensure_ascii=False)
184
  )
185
 
186
+ # Save media file with original extension
187
+ ext = Path(file.filename or "file.bin").suffix or ".bin"
188
+ (report_dir / f"media{ext}").write_bytes(raw)
189
 
190
  # Upload to HF dataset repo
191
  try:
app/static/index.html CHANGED
@@ -152,12 +152,13 @@
152
  </svg>
153
  <div class="text-sm text-gray-500 space-y-2">
154
  <p data-i18n="privacy_note">Files are processed in memory and not stored.</p>
155
- <p class="font-medium text-gray-600" data-i18n="disclaimer_mistakes">This model can make mistakes.</p>
156
  <p class="font-medium text-gray-600" data-i18n="disclaimer_known_issues_title">Known limitations:</p>
157
  <ul class="list-disc list-inside space-y-1 text-gray-500">
158
- <li data-i18n="disclaimer_lipsync">Lip sync and small inpainting may not be reliably detected.</li>
159
- <li data-i18n="disclaimer_text_overlay">Images with text overlay are often incorrectly flagged as AI-generated.</li>
160
- <li data-i18n="disclaimer_full_gen">The model is very good at detecting fully AI-generated images.</li>
 
161
  </ul>
162
  </div>
163
  </div>
@@ -188,9 +189,9 @@
188
  <p class="mt-2 text-sm text-gray-500" data-i18n="report_description">Help us improve by reporting an incorrect result.</p>
189
 
190
  <form id="report-form" class="mt-6 space-y-6">
191
- <!-- Is the image real or AI? -->
192
  <fieldset>
193
- <legend class="text-sm font-semibold text-gray-900" data-i18n="report_is_real_label">Is this image real or AI-generated?</legend>
194
  <div class="mt-3 space-y-2">
195
  <label class="flex items-center gap-2 cursor-pointer">
196
  <input type="radio" name="is_real" value="real" class="w-4 h-4 text-blue-600" required />
@@ -209,15 +210,27 @@
209
  <div class="mt-3 space-y-2">
210
  <label class="flex items-center gap-2 cursor-pointer">
211
  <input type="radio" name="reason" value="self_made" class="w-4 h-4 text-blue-600" required />
212
- <span data-i18n="report_reason_self">I generated or took this image myself</span>
213
  </label>
214
  <label class="flex items-center gap-2 cursor-pointer">
215
  <input type="radio" name="reason" value="evidence" class="w-4 h-4 text-blue-600" />
216
- <span data-i18n="report_reason_evidence">Clear evidence of manipulation</span>
217
  </label>
218
  <label class="flex items-center gap-2 cursor-pointer">
219
  <input type="radio" name="reason" value="known_source" class="w-4 h-4 text-blue-600" />
220
- <span data-i18n="report_reason_source">Known source of the image</span>
 
 
 
 
 
 
 
 
 
 
 
 
221
  </label>
222
  <label class="flex items-center gap-2 cursor-pointer">
223
  <input type="radio" name="reason" value="other" class="w-4 h-4 text-blue-600" />
@@ -228,6 +241,11 @@
228
  <input id="reason-other-input" type="text" placeholder="" data-i18n-placeholder="report_reason_other_placeholder"
229
  class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none" />
230
  </div>
 
 
 
 
 
231
  </fieldset>
232
 
233
  <!-- Comment -->
@@ -240,7 +258,7 @@
240
  <!-- Consent -->
241
  <label class="flex items-start gap-3 cursor-pointer">
242
  <input id="report-consent" type="checkbox" class="w-4 h-4 mt-0.5 text-blue-600 rounded" />
243
- <span class="text-sm text-gray-700" data-i18n="report_consent">I allow saving this image for research purposes. This is required to submit the report.</span>
244
  </label>
245
 
246
  <!-- Error / success banners -->
@@ -298,30 +316,41 @@
298
  how_calculated_body: "We use a Swin Transformer V2 model fine-tuned to distinguish real photographs from AI-generated images. For videos, we sample 5 frames evenly across the duration and average the model's confidence. The score shown is the model's estimated probability that the content was generated by AI.",
299
  close: "Close",
300
  privacy_note: "Files are processed in memory and not stored.",
301
- disclaimer_mistakes: "This model can make mistakes.",
302
  disclaimer_known_issues_title: "Known limitations:",
303
- disclaimer_lipsync: "Lip sync and small inpainting may not be reliably detected.",
304
- disclaimer_text_overlay: "Images with text overlay are often incorrectly flagged as AI-generated.",
305
- disclaimer_full_gen: "The model is very good at detecting fully AI-generated images.",
 
306
  report_error: "Signal an error",
307
  report_title: "Signal an error",
308
  report_description: "Help us improve by reporting an incorrect result.",
309
- report_is_real_label: "Is this image real or AI-generated?",
310
  report_real: "Real (authentic)",
311
  report_ai: "AI-generated or manipulated",
312
  report_reason_label: "How do you know?",
313
- report_reason_self: "I generated or took this image myself",
314
- report_reason_evidence: "Clear evidence of manipulation",
315
- report_reason_source: "Known source of the image",
 
 
 
316
  report_reason_other: "Other",
317
  report_reason_other_placeholder: "Please specify…",
 
 
 
 
 
 
 
318
  report_comment_label: "Additional comments (optional)",
319
- report_consent: "I allow saving this image for research purposes. This is required to submit the report.",
320
  report_cancel: "Cancel",
321
  report_submit: "Submit report",
322
  report_submitting: "Submitting...",
323
  report_success: "Thank you! Your report has been submitted.",
324
- report_error_consent: "You must allow saving the image to submit a report.",
325
  report_error_fields: "Please fill in all required fields.",
326
  report_error_generic: "Failed to submit the report. Please try again.",
327
  },
@@ -359,30 +388,41 @@
359
  how_calculated_body: "Nous utilisons un modèle Swin Transformer V2 entraîné pour distinguer les vraies photographies des images générées par IA. Pour les vidéos, nous échantillonnons 5 images réparties uniformément sur la durée et faisons la moyenne de la confiance du modèle. Le score affiché correspond à la probabilité estimée que le contenu ait été généré par IA.",
360
  close: "Fermer",
361
  privacy_note: "Les fichiers sont traités en mémoire et ne sont pas conservés.",
362
- disclaimer_mistakes: "Ce modèle peut faire des erreurs.",
363
  disclaimer_known_issues_title: "Limitations connues :",
364
- disclaimer_lipsync: "La synchronisation labiale et les petits inpaintings peuvent ne pas être détectés de manière fiable.",
365
- disclaimer_text_overlay: "Les images avec du texte superposé sont souvent incorrectement signalées comme générées par IA.",
366
- disclaimer_full_gen: "Le modèle est très performant pour détecter les images entièrement générées par IA.",
 
367
  report_error: "Signaler une erreur",
368
  report_title: "Signaler une erreur",
369
  report_description: "Aidez-nous à nous améliorer en signalant un résultat incorrect.",
370
- report_is_real_label: "Cette image est-elle réelle ou générée par IA ?",
371
- report_real: "Réelle (authentique)",
372
- report_ai: "Générée par IA ou manipulée",
373
  report_reason_label: "Comment le savez-vous ?",
374
- report_reason_self: "J'ai généré ou pris cette image moi-même",
375
- report_reason_evidence: "Preuve claire de manipulation",
376
- report_reason_source: "Source connue de l'image",
 
 
 
377
  report_reason_other: "Autre",
378
  report_reason_other_placeholder: "Veuillez préciser…",
 
 
 
 
 
 
 
379
  report_comment_label: "Commentaires supplémentaires (optionnel)",
380
- report_consent: "J'autorise la sauvegarde de cette image à des fins de recherche. Ceci est requis pour soumettre le signalement.",
381
  report_cancel: "Annuler",
382
  report_submit: "Envoyer le signalement",
383
  report_submitting: "Envoi en cours...",
384
  report_success: "Merci ! Votre signalement a été soumis.",
385
- report_error_consent: "Vous devez autoriser la sauvegarde de l'image pour soumettre un signalement.",
386
  report_error_fields: "Veuillez remplir tous les champs obligatoires.",
387
  report_error_generic: "Échec de l'envoi du signalement. Veuillez réessayer.",
388
  },
@@ -667,10 +707,37 @@
667
  }
668
 
669
  /* --- Report modal --- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
670
  function openReportModal() {
671
  // Reset the form
672
  $("report-form").reset();
673
  $("reason-other-wrap").classList.add("hidden");
 
674
  $("report-error").classList.add("hidden");
675
  $("report-success").classList.add("hidden");
676
  $("report-submit").disabled = false;
@@ -726,6 +793,7 @@
726
  formData.append("is_real", isRealRadio.value);
727
  formData.append("reason", reasonValue);
728
  formData.append("reason_other", reasonOther);
 
729
  formData.append("comment", $("report-comment").value.trim());
730
  formData.append("p_fake", state.result ? state.result.p_fake : 0);
731
  formData.append("consent", "true");
@@ -808,15 +876,10 @@
808
  $("report-backdrop").addEventListener("click", closeReportModal);
809
  $("report-form").addEventListener("submit", submitReport);
810
 
811
- // Toggle "other" reason text input
812
  document.querySelectorAll('input[name="reason"]').forEach((radio) => {
813
  radio.addEventListener("change", () => {
814
- const wrap = $("reason-other-wrap");
815
- if (radio.value === "other" && radio.checked) {
816
- wrap.classList.remove("hidden");
817
- } else {
818
- wrap.classList.add("hidden");
819
- }
820
  });
821
  });
822
 
 
152
  </svg>
153
  <div class="text-sm text-gray-500 space-y-2">
154
  <p data-i18n="privacy_note">Files are processed in memory and not stored.</p>
155
+ <p class="font-medium text-gray-600" data-i18n="disclaimer_mistakes">Results are probabilistic and should not be treated as definitive.</p>
156
  <p class="font-medium text-gray-600" data-i18n="disclaimer_known_issues_title">Known limitations:</p>
157
  <ul class="list-disc list-inside space-y-1 text-gray-500">
158
+ <li data-i18n="disclaimer_full_gen">Performance is strongest on fully AI-generated images.</li>
159
+ <li data-i18n="disclaimer_lipsync">Subtle manipulations such as lip sync and localized inpainting may not be reliably detected.</li>
160
+ <li data-i18n="disclaimer_text_overlay">Images with text overlays are frequently misclassified as AI-generated.</li>
161
+ <li data-i18n="disclaimer_motion_blur">Video analysis may be less accurate in scenes with heavy motion blur.</li>
162
  </ul>
163
  </div>
164
  </div>
 
189
  <p class="mt-2 text-sm text-gray-500" data-i18n="report_description">Help us improve by reporting an incorrect result.</p>
190
 
191
  <form id="report-form" class="mt-6 space-y-6">
192
+ <!-- Is the content real or AI? -->
193
  <fieldset>
194
+ <legend class="text-sm font-semibold text-gray-900" data-i18n="report_is_real_label">Is this content real or AI-generated?</legend>
195
  <div class="mt-3 space-y-2">
196
  <label class="flex items-center gap-2 cursor-pointer">
197
  <input type="radio" name="is_real" value="real" class="w-4 h-4 text-blue-600" required />
 
210
  <div class="mt-3 space-y-2">
211
  <label class="flex items-center gap-2 cursor-pointer">
212
  <input type="radio" name="reason" value="self_made" class="w-4 h-4 text-blue-600" required />
213
+ <span data-i18n="report_reason_self">I created or captured this content myself</span>
214
  </label>
215
  <label class="flex items-center gap-2 cursor-pointer">
216
  <input type="radio" name="reason" value="evidence" class="w-4 h-4 text-blue-600" />
217
+ <span data-i18n="report_reason_evidence">Visible evidence of manipulation (e.g. artifacts, distortions)</span>
218
  </label>
219
  <label class="flex items-center gap-2 cursor-pointer">
220
  <input type="radio" name="reason" value="known_source" class="w-4 h-4 text-blue-600" />
221
+ <span data-i18n="report_reason_source">Known and verifiable original source</span>
222
+ </label>
223
+ <label class="flex items-center gap-2 cursor-pointer">
224
+ <input type="radio" name="reason" value="professional" class="w-4 h-4 text-blue-600" />
225
+ <span data-i18n="report_reason_professional">Professional or domain expertise</span>
226
+ </label>
227
+ <label class="flex items-center gap-2 cursor-pointer">
228
+ <input type="radio" name="reason" value="forensic" class="w-4 h-4 text-blue-600" />
229
+ <span data-i18n="report_reason_forensic">Metadata or forensic analysis</span>
230
+ </label>
231
+ <label class="flex items-center gap-2 cursor-pointer">
232
+ <input type="radio" name="reason" value="reverse_search" class="w-4 h-4 text-blue-600" />
233
+ <span data-i18n="report_reason_reverse_search">Reverse image/video search</span>
234
  </label>
235
  <label class="flex items-center gap-2 cursor-pointer">
236
  <input type="radio" name="reason" value="other" class="w-4 h-4 text-blue-600" />
 
241
  <input id="reason-other-input" type="text" placeholder="" data-i18n-placeholder="report_reason_other_placeholder"
242
  class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none" />
243
  </div>
244
+ <div id="reason-details-wrap" class="hidden mt-3">
245
+ <label class="text-xs font-medium text-gray-600" id="reason-details-label" data-i18n="report_details_label">Please provide details</label>
246
+ <input id="reason-details-input" type="text" placeholder="" data-i18n-placeholder="report_details_placeholder"
247
+ class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none" />
248
+ </div>
249
  </fieldset>
250
 
251
  <!-- Comment -->
 
258
  <!-- Consent -->
259
  <label class="flex items-start gap-3 cursor-pointer">
260
  <input id="report-consent" type="checkbox" class="w-4 h-4 mt-0.5 text-blue-600 rounded" />
261
+ <span class="text-sm text-gray-700" data-i18n="report_consent">I allow saving this file for research purposes. This is required to submit the report.</span>
262
  </label>
263
 
264
  <!-- Error / success banners -->
 
316
  how_calculated_body: "We use a Swin Transformer V2 model fine-tuned to distinguish real photographs from AI-generated images. For videos, we sample 5 frames evenly across the duration and average the model's confidence. The score shown is the model's estimated probability that the content was generated by AI.",
317
  close: "Close",
318
  privacy_note: "Files are processed in memory and not stored.",
319
+ disclaimer_mistakes: "Results are probabilistic and should not be treated as definitive.",
320
  disclaimer_known_issues_title: "Known limitations:",
321
+ disclaimer_full_gen: "Performance is strongest on fully AI-generated images.",
322
+ disclaimer_lipsync: "Subtle manipulations such as lip sync and localized inpainting may not be reliably detected.",
323
+ disclaimer_text_overlay: "Images with text overlays are frequently misclassified as AI-generated.",
324
+ disclaimer_motion_blur: "Video analysis may be less accurate in scenes with heavy motion blur.",
325
  report_error: "Signal an error",
326
  report_title: "Signal an error",
327
  report_description: "Help us improve by reporting an incorrect result.",
328
+ report_is_real_label: "Is this content real or AI-generated?",
329
  report_real: "Real (authentic)",
330
  report_ai: "AI-generated or manipulated",
331
  report_reason_label: "How do you know?",
332
+ report_reason_self: "I created or captured this content myself",
333
+ report_reason_evidence: "Visible evidence of manipulation (e.g. artifacts, distortions)",
334
+ report_reason_source: "Known and verifiable original source",
335
+ report_reason_professional: "Professional or domain expertise",
336
+ report_reason_forensic: "Metadata or forensic analysis",
337
+ report_reason_reverse_search: "Reverse image/video search",
338
  report_reason_other: "Other",
339
  report_reason_other_placeholder: "Please specify…",
340
+ report_details_label: "Please provide details",
341
+ report_details_placeholder: "e.g. source URL, description of artifacts, tool used…",
342
+ report_details_label_evidence: "Describe the evidence you observed",
343
+ report_details_label_source: "Provide the source URL or reference",
344
+ report_details_label_professional: "Describe your area of expertise",
345
+ report_details_label_forensic: "Describe the analysis performed",
346
+ report_details_label_reverse_search: "Provide the search tool or URL used",
347
  report_comment_label: "Additional comments (optional)",
348
+ report_consent: "I allow saving this file for research purposes. This is required to submit the report.",
349
  report_cancel: "Cancel",
350
  report_submit: "Submit report",
351
  report_submitting: "Submitting...",
352
  report_success: "Thank you! Your report has been submitted.",
353
+ report_error_consent: "You must allow saving the file to submit a report.",
354
  report_error_fields: "Please fill in all required fields.",
355
  report_error_generic: "Failed to submit the report. Please try again.",
356
  },
 
388
  how_calculated_body: "Nous utilisons un modèle Swin Transformer V2 entraîné pour distinguer les vraies photographies des images générées par IA. Pour les vidéos, nous échantillonnons 5 images réparties uniformément sur la durée et faisons la moyenne de la confiance du modèle. Le score affiché correspond à la probabilité estimée que le contenu ait été généré par IA.",
389
  close: "Fermer",
390
  privacy_note: "Les fichiers sont traités en mémoire et ne sont pas conservés.",
391
+ disclaimer_mistakes: "Les résultats sont probabilistes et ne doivent pas être considérés comme définitifs.",
392
  disclaimer_known_issues_title: "Limitations connues :",
393
+ disclaimer_full_gen: "Les performances sont optimales sur les images entièrement générées par IA.",
394
+ disclaimer_lipsync: "Les manipulations subtiles telles que la synchronisation labiale et l'inpainting localisé peuvent ne pas être détectées de manière fiable.",
395
+ disclaimer_text_overlay: "Les images comportant du texte superposé sont fréquemment classées à tort comme générées par IA.",
396
+ disclaimer_motion_blur: "L'analyse vidéo peut être moins précise dans les scènes présentant un fort flou de mouvement.",
397
  report_error: "Signaler une erreur",
398
  report_title: "Signaler une erreur",
399
  report_description: "Aidez-nous à nous améliorer en signalant un résultat incorrect.",
400
+ report_is_real_label: "Ce contenu est-il réel ou généré par IA ?",
401
+ report_real: "Réel (authentique)",
402
+ report_ai: "Généré par IA ou manipulé",
403
  report_reason_label: "Comment le savez-vous ?",
404
+ report_reason_self: "J'ai créé ou capturé ce contenu moi-même",
405
+ report_reason_evidence: "Preuves visibles de manipulation (ex. : artefacts, distorsions)",
406
+ report_reason_source: "Source originale connue et vérifiable",
407
+ report_reason_professional: "Expertise professionnelle ou de domaine",
408
+ report_reason_forensic: "Analyse des métadonnées ou analyse forensique",
409
+ report_reason_reverse_search: "Recherche inversée d'image ou de vidéo",
410
  report_reason_other: "Autre",
411
  report_reason_other_placeholder: "Veuillez préciser…",
412
+ report_details_label: "Veuillez fournir des détails",
413
+ report_details_placeholder: "ex. : URL source, description des artefacts, outil utilisé…",
414
+ report_details_label_evidence: "Décrivez les preuves observées",
415
+ report_details_label_source: "Fournissez l'URL source ou la référence",
416
+ report_details_label_professional: "Décrivez votre domaine d'expertise",
417
+ report_details_label_forensic: "Décrivez l'analyse effectuée",
418
+ report_details_label_reverse_search: "Indiquez l'outil ou l'URL de recherche utilisé",
419
  report_comment_label: "Commentaires supplémentaires (optionnel)",
420
+ report_consent: "J'autorise la sauvegarde de ce fichier à des fins de recherche. Ceci est requis pour soumettre le signalement.",
421
  report_cancel: "Annuler",
422
  report_submit: "Envoyer le signalement",
423
  report_submitting: "Envoi en cours...",
424
  report_success: "Merci ! Votre signalement a été soumis.",
425
+ report_error_consent: "Vous devez autoriser la sauvegarde du fichier pour soumettre un signalement.",
426
  report_error_fields: "Veuillez remplir tous les champs obligatoires.",
427
  report_error_generic: "Échec de l'envoi du signalement. Veuillez réessayer.",
428
  },
 
707
  }
708
 
709
  /* --- Report modal --- */
710
+ // Reasons that should show the contextual detail input
711
+ const REASONS_WITH_DETAILS = ["evidence", "known_source", "professional", "forensic", "reverse_search"];
712
+
713
+ function updateReasonUI(selectedValue) {
714
+ const otherWrap = $("reason-other-wrap");
715
+ const detailsWrap = $("reason-details-wrap");
716
+
717
+ // "Other" free-text
718
+ if (selectedValue === "other") {
719
+ otherWrap.classList.remove("hidden");
720
+ } else {
721
+ otherWrap.classList.add("hidden");
722
+ }
723
+
724
+ // Contextual detail input
725
+ if (REASONS_WITH_DETAILS.includes(selectedValue)) {
726
+ detailsWrap.classList.remove("hidden");
727
+ const T = t();
728
+ const labelKey = "report_details_label_" + selectedValue;
729
+ const label = T[labelKey] || T.report_details_label;
730
+ $("reason-details-label").textContent = label;
731
+ } else {
732
+ detailsWrap.classList.add("hidden");
733
+ }
734
+ }
735
+
736
  function openReportModal() {
737
  // Reset the form
738
  $("report-form").reset();
739
  $("reason-other-wrap").classList.add("hidden");
740
+ $("reason-details-wrap").classList.add("hidden");
741
  $("report-error").classList.add("hidden");
742
  $("report-success").classList.add("hidden");
743
  $("report-submit").disabled = false;
 
793
  formData.append("is_real", isRealRadio.value);
794
  formData.append("reason", reasonValue);
795
  formData.append("reason_other", reasonOther);
796
+ formData.append("reason_details", $("reason-details-input").value.trim());
797
  formData.append("comment", $("report-comment").value.trim());
798
  formData.append("p_fake", state.result ? state.result.p_fake : 0);
799
  formData.append("consent", "true");
 
876
  $("report-backdrop").addEventListener("click", closeReportModal);
877
  $("report-form").addEventListener("submit", submitReport);
878
 
879
+ // Toggle reason sub-fields (other text input + contextual details)
880
  document.querySelectorAll('input[name="reason"]').forEach((radio) => {
881
  radio.addEventListener("change", () => {
882
+ if (radio.checked) updateReasonUI(radio.value);
 
 
 
 
 
883
  });
884
  });
885