nonzeroexit commited on
Commit
f8ea596
·
verified ·
1 Parent(s): 17b7fc2

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +112 -82
index.html CHANGED
@@ -690,8 +690,8 @@
690
  <div class="button-group-wrapper">
691
  <div id="processing-time-info">Estimated processing time: around 30 seconds.</div>
692
  <div class="button-group">
693
- <button id="clear-btn">Clear Input</button> <!-- NO onclick attribute here, handled in JS -->
694
- <button id="predict-btn">Submit</button> <!-- NO onclick attribute here, handled in JS -->
695
  </div>
696
  </div>
697
 
@@ -860,7 +860,7 @@
860
  </li>
861
  <li><strong>Solvent Accessibility (SolventAccessibilityD3001):</strong>
862
  <ul>
863
- <li><strong>Description:</strong> Reflects how exposed the residues are to the solvent (water).</li>
864
  <li><strong>Interpretation:</b> Solvent-accessible residues positively contribute to AMP prediction. This might indicate that exposed residues facilitate interaction with bacterial membranes or the surrounding environment.</li>
865
  </ul>
866
  </li>
@@ -992,23 +992,21 @@
992
  </address>
993
  </footer>
994
 
995
- <!-- IMPORTANT: The script tag must be type="module" to use import statements. -->
996
  <script type="module">
997
  import { Client } from "https://esm.sh/@gradio/client";
998
 
999
  const GRADIO_HF_SPACE_RAW_URL = "https://huggingface.co/spaces/nonzeroexit/AMP-Classifier";
1000
  const GRADIO_PREDICTION_TARGET_ID = "nonzeroexit/AMP-Classifier";
1001
-
1002
- // ACTION REQUIRED: ENSURE THIS SPACE IS PUBLIC ON HUGGING FACE.
1003
- // If it's private, you WILL get a 401 Unauthorized error.
1004
- const GRADIO_EMAIL_TARGET_ID = "nonzeroexit/amp-email-sender"; // Verify this Space ID is correct and PUBLIC!
1005
 
1006
  // --- DOM Elements ---
1007
  const sequenceInput = document.getElementById('sequence');
1008
  const charCount = document.getElementById('char-count');
1009
  const errorDiv = document.getElementById('error');
1010
  const predictBtn = document.getElementById('predict-btn');
1011
- const clearBtn = document.getElementById('clear-btn'); // Get clear button reference
1012
  const fileInput = document.getElementById('file-input');
1013
  const downloadLink = document.getElementById('download-link');
1014
  const ampClassificationOutput = document.getElementById('amp-classification-output');
@@ -1021,11 +1019,14 @@
1021
  const emailErrorDiv = document.getElementById('email-error');
1022
  const emailStatusDiv = document.getElementById('email-status');
1023
 
1024
- let clientInstancePrediction; // Dedicated client for the prediction space
1025
- let clientInstanceEmail; // Dedicated client for the email sending space
1026
  let debounceTimer;
1027
  let countUpIntervalId;
1028
 
 
 
 
1029
  // --- Utility Functions for Error Display (Defined at top of module scope) ---
1030
  function showError(message) {
1031
  if(errorDiv) {
@@ -1058,26 +1059,27 @@
1058
  clientInstancePrediction = await Client.connect(GRADIO_PREDICTION_TARGET_ID);
1059
  console.log("Gradio client for prediction initialized successfully directly to space ID:", GRADIO_PREDICTION_TARGET_ID);
1060
 
1061
- // Attempt to connect to the email sending space
1062
- // This is the point where the 401 Unauthorized error will occur if the space is private.
1063
- clientInstanceEmail = await Client.connect(GRADIO_EMAIL_TARGET_ID);
1064
- console.log("Gradio client for email sender initialized successfully directly to space ID:", GRADIO_EMAIL_TARGET_ID);
1065
-
1066
- // Initial check for button state after clients are ready
 
 
1067
  updatePredictButtonState();
1068
 
1069
  } catch (error) {
1070
- console.error("Failed to initialize Gradio client for prediction or email:", error);
1071
- // Use the locally available showError function
1072
  showError(`Failed to connect to a Hugging Face Space. This might be due to incorrect Space IDs, private spaces, or network issues. Error details: ${error.message}`);
1073
- // If either client fails to initialize, disable the predict button.
1074
  if (predictBtn) predictBtn.disabled = true;
1075
  }
1076
  }
1077
 
1078
  // --- Initial Setup on Window Load ---
1079
  window.onload = function() {
1080
- initClient(); // Call initClient when window loads
1081
 
1082
  if (document.getElementById('info-icon')) {
1083
  document.getElementById('info-icon').addEventListener('mouseenter', showTooltip);
@@ -1085,23 +1087,22 @@
1085
  }
1086
  if (fileInput) fileInput.addEventListener('change', handleFileSelect);
1087
 
1088
- // Add event listeners for input fields to update button state
1089
  if (sequenceInput) {
1090
  sequenceInput.addEventListener('input', updateAllInputsAndButtonState);
1091
  sequenceInput.addEventListener('paste', handleSequencePaste);
1092
  sequenceInput.addEventListener('keypress', handleSequenceKeyPress);
1093
  }
 
1094
  if (userEmailInput) {
1095
  userEmailInput.addEventListener('input', updateAllInputsAndButtonState);
1096
  }
1097
 
1098
- // --- ATTACH EVENT LISTENERS TO BUTTONS ---
1099
- // This is the fix for the ReferenceError when using type="module".
1100
  if (clearBtn) clearBtn.addEventListener('click', clearInput);
1101
  if (predictBtn) predictBtn.addEventListener('click', classifySequenceDebounced);
1102
 
1103
 
1104
- clearInput(); // Clear everything and set initial disabled state
1105
  };
1106
 
1107
  // --- Tooltip Functions ---
@@ -1120,14 +1121,19 @@
1120
  if(sequenceInput) sequenceInput.value = '';
1121
  if(fileInput) fileInput.value = '';
1122
  if(charCount) charCount.textContent = '0';
1123
- clearGeneralError(); // Clears general sequence error
1124
 
1125
  // Clear email specific fields
1126
  if(userEmailInput) userEmailInput.value = '';
1127
  if(emailErrorDiv) emailErrorDiv.style.display = 'none';
1128
  if(emailStatusDiv) {
1129
- emailStatusDiv.textContent = '';
1130
- emailStatusDiv.style.color = 'var(--text-color)'; // Reset color
 
 
 
 
 
1131
  }
1132
 
1133
  if(downloadLink) {
@@ -1155,7 +1161,7 @@
1155
  countUpIntervalId = null;
1156
  }
1157
 
1158
- updatePredictButtonState(); // Ensure button is disabled after clearing
1159
  if(predictBtn) predictBtn.textContent = 'Submit';
1160
  }
1161
 
@@ -1182,7 +1188,9 @@
1182
  }
1183
 
1184
  clearGeneralError();
1185
- if(emailErrorDiv) emailErrorDiv.style.display = 'none'; // Clear email validation error
 
 
1186
  const reader = new FileReader();
1187
  reader.onload = function(e) {
1188
  try {
@@ -1193,7 +1201,7 @@
1193
  sequenceInput.dataset.source = 'file';
1194
  }
1195
  updateCharacterCount();
1196
- updateAllInputsAndButtonState(); // Re-evaluate all states
1197
  } else {
1198
  showError("No valid sequence found in FASTA file.");
1199
  if(fileInput) fileInput.value = '';
@@ -1262,12 +1270,13 @@
1262
  }
1263
 
1264
  function validateEmail(email) {
 
1265
  if (!email) {
1266
- return { isValid: false, message: "Email address is required to send the report." };
1267
  }
1268
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1269
  if (!emailRegex.test(email)) {
1270
- return { isValid: false, message: "Please enter a valid email address." };
1271
  }
1272
  return { isValid: true, message: "" };
1273
  }
@@ -1287,11 +1296,12 @@
1287
  const emailValidation = validateEmail(userEmailInput ? userEmailInput.value : '');
1288
 
1289
  if (!sequenceValidation.isValid) {
1290
- showError(sequenceValidation.message); // Use showError here
1291
  } else {
1292
  clearGeneralError();
1293
  }
1294
 
 
1295
  if (!emailValidation.isValid && userEmailInput && userEmailInput.value.length > 0) {
1296
  if (emailErrorDiv) {
1297
  emailErrorDiv.textContent = emailValidation.message;
@@ -1300,29 +1310,33 @@
1300
  } else {
1301
  if (emailErrorDiv) emailErrorDiv.style.display = 'none';
1302
  }
 
 
 
 
 
 
 
 
 
 
 
1303
 
1304
  if(micCheckboxes) micCheckboxes.forEach(checkbox => {
1305
  checkbox.disabled = !sequenceValidation.isValid;
1306
  if (!sequenceValidation.isValid) checkbox.checked = false;
1307
  });
1308
 
1309
- updatePredictButtonState(sequenceValidation.isValid, emailValidation.isValid);
1310
  }
1311
 
1312
- function updatePredictButtonState(sequenceIsValid = false, emailIsValid = false) {
1313
  if (predictBtn) {
1314
- const currentSequenceValue = sequenceInput ? sequenceInput.value.trim() : '';
1315
- const currentEmailValue = userEmailInput ? userEmailInput.value.trim() : '';
1316
-
1317
- const seqState = validateSequence(currentSequenceValue).isValid;
1318
- const emailState = validateEmail(currentEmailValue).isValid;
1319
-
1320
- // Button is disabled if either seq or email is invalid, OR if Gradio clients aren't ready
1321
- predictBtn.disabled = !(seqState && emailState) || !clientInstancePrediction || !clientInstanceEmail;
1322
  }
1323
  }
1324
 
1325
-
1326
  // --- Sequence Input Event Handlers ---
1327
  function handleSequencePaste(e) {
1328
  e.preventDefault();
@@ -1362,25 +1376,18 @@
1362
  }
1363
 
1364
  async function classifySequence() {
1365
- // IMPORTANT: Ensure BOTH prediction and email clients are ready before proceeding
1366
  if (!clientInstancePrediction) {
1367
  showError("Classifier service is not ready. Please wait a moment and try again.");
1368
  console.error("Gradio client instance for prediction is not initialized when classifySequence called.");
1369
- updatePredictButtonState(); // Re-check button state if an issue is found
1370
- return;
1371
- }
1372
- if (!clientInstanceEmail) {
1373
- showError("Email service is not ready. Please wait a moment and try again.");
1374
- console.error("Gradio client instance for email is not initialized when classifySequence called.");
1375
- updatePredictButtonState(); // Re-check button state if an issue is found
1376
  return;
1377
  }
1378
 
1379
  const currentSequence = sequenceInput ? sequenceInput.value.trim().toUpperCase() : "";
1380
- const userEmail = userEmailInput ? userEmailInput.value.trim() : "";
1381
 
1382
  const sequenceValidation = validateSequence(currentSequence);
1383
- const emailValidation = validateEmail(userEmail);
1384
 
1385
  if (!sequenceValidation.isValid) {
1386
  showError(sequenceValidation.message);
@@ -1389,21 +1396,28 @@
1389
  } else {
1390
  clearGeneralError();
1391
  }
 
 
 
 
 
 
 
 
 
 
 
 
1392
 
1393
- if (!emailValidation.isValid) {
1394
- if (emailErrorDiv) {
1395
- emailErrorDiv.textContent = emailValidation.message;
1396
- emailErrorDiv.style.display = 'block';
1397
- }
1398
- updatePredictButtonState();
1399
- return;
1400
- } else {
1401
- if (emailErrorDiv) emailErrorDiv.style.display = 'none';
1402
- }
1403
 
1404
  if(emailStatusDiv) {
1405
- emailStatusDiv.textContent = '';
1406
- emailStatusDiv.style.color = 'var(--text-color)';
 
 
 
 
 
1407
  }
1408
 
1409
  if (predictBtn) predictBtn.disabled = true;
@@ -1497,7 +1511,7 @@
1497
  } catch (pdfGenerationError) {
1498
  console.error("Error during PDF report content generation:", pdfGenerationError);
1499
  showError(`PDF report generation failed: ${pdfGenerationError.message}`);
1500
- emailStatusDiv.textContent = 'PDF generation failed, cannot send email.';
1501
  emailStatusDiv.style.color = 'var(--clear-button-bg)';
1502
  pdfDoc = null; // Ensure pdfDoc is null on error
1503
  }
@@ -1520,9 +1534,16 @@
1520
  additionalDetailsOutput.appendChild(downloadLink);
1521
  }
1522
 
1523
- // --- Call Hugging Face Gradio backend to send email ---
1524
- // This relies on clientInstanceEmail being successfully initialized
1525
- await sendEmailWithReport(userEmail, pdfBlob, pdfFileName, currentSequence, ampLabelFromPrediction, ampConfidenceFromPrediction);
 
 
 
 
 
 
 
1526
 
1527
  } else {
1528
  additionalDetailsOutput.innerHTML = '<p class="info-message" style="color: var(--warning-color);">Could not generate PDF report for download.</p>';
@@ -1541,7 +1562,7 @@
1541
  }
1542
 
1543
  } catch (error) {
1544
- console.error("Error during prediction or processing (direct attempt):", error);
1545
  showError(`Prediction failed: ${error.message || "Unknown error."} Please check the Hugging Face Gradio Space for any issues or temporary downtime.`);
1546
  if(ampClassificationOutput) ampClassificationOutput.innerHTML = '<p style="text-align: center; color: var(--clear-button-bg);">Error in Prediction</p>';
1547
  if(additionalDetailsOutput) {
@@ -1551,19 +1572,31 @@
1551
  downloadLink.style.display = 'none';
1552
  }
1553
  }
1554
- emailStatusDiv.textContent = `Email not sent due to prediction error: ${error.message}`;
 
 
 
 
1555
  emailStatusDiv.style.color = 'var(--clear-button-bg)';
1556
  } finally {
1557
  if (countUpIntervalId) { clearInterval(countUpIntervalId); countUpIntervalId = null; }
1558
  if (predictBtn) { predictBtn.textContent = 'Submit';}
1559
- updatePredictButtonState();
1560
  }
1561
  }
1562
 
1563
- // --- Send Email Function (Frontend to Gradio Backend) ---
1564
  async function sendEmailWithReport(email, pdfBlob, fileName, sequence, prediction, confidence) {
1565
  if (!emailStatusDiv) return;
1566
 
 
 
 
 
 
 
 
 
1567
  emailStatusDiv.textContent = 'Sending email...';
1568
  emailStatusDiv.style.color = 'var(--info-color)'; // Blue for processing
1569
 
@@ -1577,14 +1610,11 @@
1577
  if (!clientInstanceEmail) {
1578
  emailStatusDiv.textContent = 'Email service is not ready. Please try again.';
1579
  emailStatusDiv.style.color = 'var(--clear-button-bg)';
1580
- console.error("Gradio email client instance is not initialized.");
1581
  return;
1582
  }
1583
 
1584
  try {
1585
- // IMPORTANT: "/send_email_report" must match the name of the Python function
1586
- // in your Gradio `app.py` that you wrapped with `gr.Interface`.
1587
- // e.g., gr.Interface(fn=send_email_report, ...) -> "/send_email_report"
1588
  const emailResult = await clientInstanceEmail.predict(
1589
  "/send_email_report",
1590
  [
@@ -1661,7 +1691,7 @@
1661
  async function generatePDFReportContent(sequenceForPdf, predictionForPdf, confidenceForPdf, micResultsForPdf, limeFeaturesForPdf) {
1662
  if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined' || typeof window.jspdf.jsPDF !== 'function') {
1663
  console.error("jsPDF library (window.jspdf.jsPDF) is not loaded or not correctly exposed as a function.");
1664
- showError("PDF generation library not loaded. Cannot create report."); // Using showError
1665
  return null;
1666
  }
1667
  const jsPDF = window.jspdf.jsPDF;
@@ -1671,12 +1701,12 @@
1671
  pdf = new jsPDF();
1672
  if (!pdf || !pdf.internal || !pdf.internal.pageSize || pdf.internal.pageSize.width === undefined || pdf.internal.pageSize.width === null) {
1673
  console.error("Critical error: jsPDF instance or its internal properties are invalid.");
1674
- showError("PDF generation failed: Invalid PDF object."); // Using showError
1675
  return null;
1676
  }
1677
  } catch (e) {
1678
  console.error("Failed to instantiate jsPDF object:", e);
1679
- showError(`PDF generation failed: Could not initialize PDF library. Error: ${e.message}`); // Using showError
1680
  return null;
1681
  }
1682
 
 
690
  <div class="button-group-wrapper">
691
  <div id="processing-time-info">Estimated processing time: around 30 seconds.</div>
692
  <div class="button-group">
693
+ <button id="clear-btn">Clear Input</button> <!-- NO onclick attribute here -->
694
+ <button id="predict-btn">Submit</button> <!-- NO onclick attribute here -->
695
  </div>
696
  </div>
697
 
 
860
  </li>
861
  <li><strong>Solvent Accessibility (SolventAccessibilityD3001):</strong>
862
  <ul>
863
+ <li><strong>Description:</b> Reflects how exposed the residues are to the solvent (water).</li>
864
  <li><strong>Interpretation:</b> Solvent-accessible residues positively contribute to AMP prediction. This might indicate that exposed residues facilitate interaction with bacterial membranes or the surrounding environment.</li>
865
  </ul>
866
  </li>
 
992
  </address>
993
  </footer>
994
 
 
995
  <script type="module">
996
  import { Client } from "https://esm.sh/@gradio/client";
997
 
998
  const GRADIO_HF_SPACE_RAW_URL = "https://huggingface.co/spaces/nonzeroexit/AMP-Classifier";
999
  const GRADIO_PREDICTION_TARGET_ID = "nonzeroexit/AMP-Classifier";
1000
+ // This Space ID is technically still used in initClient but won't be connected
1001
+ // for email sending functionality due to being disabled.
1002
+ const GRADIO_EMAIL_TARGET_ID = "nonzeroexit/amp-email-sender"; // Email backend reference (disabled)
 
1003
 
1004
  // --- DOM Elements ---
1005
  const sequenceInput = document.getElementById('sequence');
1006
  const charCount = document.getElementById('char-count');
1007
  const errorDiv = document.getElementById('error');
1008
  const predictBtn = document.getElementById('predict-btn');
1009
+ const clearBtn = document.getElementById('clear-btn');
1010
  const fileInput = document.getElementById('file-input');
1011
  const downloadLink = document.getElementById('download-link');
1012
  const ampClassificationOutput = document.getElementById('amp-classification-output');
 
1019
  const emailErrorDiv = document.getElementById('email-error');
1020
  const emailStatusDiv = document.getElementById('email-status');
1021
 
1022
+ let clientInstancePrediction; // Only prediction client will be connected
1023
+ let clientInstanceEmail = null; // Email client will NOT be connected (explicitly null)
1024
  let debounceTimer;
1025
  let countUpIntervalId;
1026
 
1027
+ // Flag to control email functionality globally
1028
+ const IS_EMAIL_SENDING_ENABLED = false; // <<< SET TO false TO DISABLE EMAIL SENDING
1029
+
1030
  // --- Utility Functions for Error Display (Defined at top of module scope) ---
1031
  function showError(message) {
1032
  if(errorDiv) {
 
1059
  clientInstancePrediction = await Client.connect(GRADIO_PREDICTION_TARGET_ID);
1060
  console.log("Gradio client for prediction initialized successfully directly to space ID:", GRADIO_PREDICTION_TARGET_ID);
1061
 
1062
+ // ONLY CONNECT EMAIL CLIENT IF FEATURE IS ENABLED
1063
+ if (IS_EMAIL_SENDING_ENABLED) {
1064
+ clientInstanceEmail = await Client.connect(GRADIO_EMAIL_TARGET_ID);
1065
+ console.log("Gradio client for email sender initialized successfully directly to space ID:", GRADIO_EMAIL_TARGET_ID);
1066
+ } else {
1067
+ console.log("Email sending functionality is disabled, skipping email client connection.");
1068
+ }
1069
+
1070
  updatePredictButtonState();
1071
 
1072
  } catch (error) {
1073
+ console.error("Failed to initialize Gradio client for prediction (or email if enabled):", error);
 
1074
  showError(`Failed to connect to a Hugging Face Space. This might be due to incorrect Space IDs, private spaces, or network issues. Error details: ${error.message}`);
1075
+ // If prediction client fails, or if email is enabled and its client fails, disable the button.
1076
  if (predictBtn) predictBtn.disabled = true;
1077
  }
1078
  }
1079
 
1080
  // --- Initial Setup on Window Load ---
1081
  window.onload = function() {
1082
+ initClient();
1083
 
1084
  if (document.getElementById('info-icon')) {
1085
  document.getElementById('info-icon').addEventListener('mouseenter', showTooltip);
 
1087
  }
1088
  if (fileInput) fileInput.addEventListener('change', handleFileSelect);
1089
 
 
1090
  if (sequenceInput) {
1091
  sequenceInput.addEventListener('input', updateAllInputsAndButtonState);
1092
  sequenceInput.addEventListener('paste', handleSequencePaste);
1093
  sequenceInput.addEventListener('keypress', handleSequenceKeyPress);
1094
  }
1095
+ // Email input listeners still active for user input and local visual validation
1096
  if (userEmailInput) {
1097
  userEmailInput.addEventListener('input', updateAllInputsAndButtonState);
1098
  }
1099
 
1100
+ // Attach event listeners to buttons
 
1101
  if (clearBtn) clearBtn.addEventListener('click', clearInput);
1102
  if (predictBtn) predictBtn.addEventListener('click', classifySequenceDebounced);
1103
 
1104
 
1105
+ clearInput();
1106
  };
1107
 
1108
  // --- Tooltip Functions ---
 
1121
  if(sequenceInput) sequenceInput.value = '';
1122
  if(fileInput) fileInput.value = '';
1123
  if(charCount) charCount.textContent = '0';
1124
+ clearGeneralError();
1125
 
1126
  // Clear email specific fields
1127
  if(userEmailInput) userEmailInput.value = '';
1128
  if(emailErrorDiv) emailErrorDiv.style.display = 'none';
1129
  if(emailStatusDiv) {
1130
+ if (IS_EMAIL_SENDING_ENABLED) {
1131
+ emailStatusDiv.textContent = '';
1132
+ emailStatusDiv.style.color = 'var(--text-color)';
1133
+ } else {
1134
+ emailStatusDiv.textContent = 'Email sending is currently disabled by the tool.';
1135
+ emailStatusDiv.style.color = 'var(--info-color)'; // Neutral info color
1136
+ }
1137
  }
1138
 
1139
  if(downloadLink) {
 
1161
  countUpIntervalId = null;
1162
  }
1163
 
1164
+ updatePredictButtonState();
1165
  if(predictBtn) predictBtn.textContent = 'Submit';
1166
  }
1167
 
 
1188
  }
1189
 
1190
  clearGeneralError();
1191
+ // Clear email validation error, though it won't block submission
1192
+ if(emailErrorDiv) emailErrorDiv.style.display = 'none';
1193
+
1194
  const reader = new FileReader();
1195
  reader.onload = function(e) {
1196
  try {
 
1201
  sequenceInput.dataset.source = 'file';
1202
  }
1203
  updateCharacterCount();
1204
+ updateAllInputsAndButtonState();
1205
  } else {
1206
  showError("No valid sequence found in FASTA file.");
1207
  if(fileInput) fileInput.value = '';
 
1270
  }
1271
 
1272
  function validateEmail(email) {
1273
+ // Email validation is now only for local display, not blocking submission
1274
  if (!email) {
1275
+ return { isValid: false, message: "Email address is empty." };
1276
  }
1277
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1278
  if (!emailRegex.test(email)) {
1279
+ return { isValid: false, message: "Please enter a valid email format." };
1280
  }
1281
  return { isValid: true, message: "" };
1282
  }
 
1296
  const emailValidation = validateEmail(userEmailInput ? userEmailInput.value : '');
1297
 
1298
  if (!sequenceValidation.isValid) {
1299
+ showError(sequenceValidation.message);
1300
  } else {
1301
  clearGeneralError();
1302
  }
1303
 
1304
+ // Display email format errors locally, but don't clear prediction error message
1305
  if (!emailValidation.isValid && userEmailInput && userEmailInput.value.length > 0) {
1306
  if (emailErrorDiv) {
1307
  emailErrorDiv.textContent = emailValidation.message;
 
1310
  } else {
1311
  if (emailErrorDiv) emailErrorDiv.style.display = 'none';
1312
  }
1313
+ // If email sending is disabled, provide a default status message.
1314
+ if (emailStatusDiv) {
1315
+ if (!IS_EMAIL_SENDING_ENABLED) {
1316
+ emailStatusDiv.textContent = 'Email sending is currently disabled by the tool.';
1317
+ emailStatusDiv.style.color = 'var(--info-color)';
1318
+ } else if (userEmailInput && userEmailInput.value.trim() === '') {
1319
+ // Clear previous status if user input cleared.
1320
+ emailStatusDiv.textContent = '';
1321
+ emailStatusDiv.style.color = 'var(--text-color)';
1322
+ }
1323
+ }
1324
 
1325
  if(micCheckboxes) micCheckboxes.forEach(checkbox => {
1326
  checkbox.disabled = !sequenceValidation.isValid;
1327
  if (!sequenceValidation.isValid) checkbox.checked = false;
1328
  });
1329
 
1330
+ updatePredictButtonState(sequenceValidation.isValid); // Pass only sequence validity
1331
  }
1332
 
1333
+ function updatePredictButtonState(sequenceIsValid = false) {
1334
  if (predictBtn) {
1335
+ // Button enabled solely on valid sequence input AND successful prediction client connection
1336
+ predictBtn.disabled = !sequenceIsValid || !clientInstancePrediction;
 
 
 
 
 
 
1337
  }
1338
  }
1339
 
 
1340
  // --- Sequence Input Event Handlers ---
1341
  function handleSequencePaste(e) {
1342
  e.preventDefault();
 
1376
  }
1377
 
1378
  async function classifySequence() {
 
1379
  if (!clientInstancePrediction) {
1380
  showError("Classifier service is not ready. Please wait a moment and try again.");
1381
  console.error("Gradio client instance for prediction is not initialized when classifySequence called.");
1382
+ updatePredictButtonState();
 
 
 
 
 
 
1383
  return;
1384
  }
1385
 
1386
  const currentSequence = sequenceInput ? sequenceInput.value.trim().toUpperCase() : "";
1387
+ const userEmail = userEmailInput ? userEmailInput.value.trim() : ""; // Still get email, but won't be used for sending
1388
 
1389
  const sequenceValidation = validateSequence(currentSequence);
1390
+ // const emailValidation = validateEmail(userEmail); // Email validation is now only for local display/info
1391
 
1392
  if (!sequenceValidation.isValid) {
1393
  showError(sequenceValidation.message);
 
1396
  } else {
1397
  clearGeneralError();
1398
  }
1399
+
1400
+ // Remove email validation check that blocked submission
1401
+ // if (!emailValidation.isValid && IS_EMAIL_SENDING_ENABLED) {
1402
+ // if (emailErrorDiv) {
1403
+ // emailErrorDiv.textContent = emailValidation.message;
1404
+ // emailErrorDiv.style.display = 'block';
1405
+ // }
1406
+ // updatePredictButtonState();
1407
+ // return;
1408
+ // } else {
1409
+ // if (emailErrorDiv) emailErrorDiv.style.display = 'none';
1410
+ // }
1411
 
 
 
 
 
 
 
 
 
 
 
1412
 
1413
  if(emailStatusDiv) {
1414
+ if (IS_EMAIL_SENDING_ENABLED) {
1415
+ emailStatusDiv.textContent = '';
1416
+ emailStatusDiv.style.color = 'var(--text-color)';
1417
+ } else {
1418
+ emailStatusDiv.textContent = 'Email sending is currently disabled by the tool.';
1419
+ emailStatusDiv.style.color = 'var(--info-color)';
1420
+ }
1421
  }
1422
 
1423
  if (predictBtn) predictBtn.disabled = true;
 
1511
  } catch (pdfGenerationError) {
1512
  console.error("Error during PDF report content generation:", pdfGenerationError);
1513
  showError(`PDF report generation failed: ${pdfGenerationError.message}`);
1514
+ emailStatusDiv.textContent = 'PDF generation failed, email not sent (functionality disabled).';
1515
  emailStatusDiv.style.color = 'var(--clear-button-bg)';
1516
  pdfDoc = null; // Ensure pdfDoc is null on error
1517
  }
 
1534
  additionalDetailsOutput.appendChild(downloadLink);
1535
  }
1536
 
1537
+ // --- Email logic based on IS_EMAIL_SENDING_ENABLED flag ---
1538
+ if (IS_EMAIL_SENDING_ENABLED) {
1539
+ await sendEmailWithReport(userEmail, pdfBlob, pdfFileName, currentSequence, ampLabelFromPrediction, ampConfidenceFromPrediction);
1540
+ } else {
1541
+ console.log("Email sending is disabled. Not attempting to send.");
1542
+ if(emailStatusDiv) {
1543
+ emailStatusDiv.textContent = 'Email report generation and sending are currently disabled by the tool.';
1544
+ emailStatusDiv.style.color = 'var(--info-color)';
1545
+ }
1546
+ }
1547
 
1548
  } else {
1549
  additionalDetailsOutput.innerHTML = '<p class="info-message" style="color: var(--warning-color);">Could not generate PDF report for download.</p>';
 
1562
  }
1563
 
1564
  } catch (error) {
1565
+ console.error("Error during prediction or processing:", error);
1566
  showError(`Prediction failed: ${error.message || "Unknown error."} Please check the Hugging Face Gradio Space for any issues or temporary downtime.`);
1567
  if(ampClassificationOutput) ampClassificationOutput.innerHTML = '<p style="text-align: center; color: var(--clear-button-bg);">Error in Prediction</p>';
1568
  if(additionalDetailsOutput) {
 
1572
  downloadLink.style.display = 'none';
1573
  }
1574
  }
1575
+ if (IS_EMAIL_SENDING_ENABLED) {
1576
+ emailStatusDiv.textContent = `Email not sent due to prediction error: ${error.message}`;
1577
+ } else {
1578
+ emailStatusDiv.textContent = `Email sending is disabled. Prediction failed: ${error.message}`;
1579
+ }
1580
  emailStatusDiv.style.color = 'var(--clear-button-bg)';
1581
  } finally {
1582
  if (countUpIntervalId) { clearInterval(countUpIntervalId); countUpIntervalId = null; }
1583
  if (predictBtn) { predictBtn.textContent = 'Submit';}
1584
+ updatePredictButtonState(); // Ensure button is re-enabled/disabled based on final prediction input state
1585
  }
1586
  }
1587
 
1588
+ // --- Dummy Send Email Function (when disabled) / Original logic (when enabled) ---
1589
  async function sendEmailWithReport(email, pdfBlob, fileName, sequence, prediction, confidence) {
1590
  if (!emailStatusDiv) return;
1591
 
1592
+ if (!IS_EMAIL_SENDING_ENABLED) {
1593
+ console.log("Email sending is currently disabled. Not proceeding with backend call.");
1594
+ emailStatusDiv.textContent = 'Email report was NOT sent: Functionality is disabled.';
1595
+ emailStatusDiv.style.color = 'var(--info-color)';
1596
+ return;
1597
+ }
1598
+
1599
+ // --- Original email sending logic (ONLY runs if IS_EMAIL_SENDING_ENABLED is true) ---
1600
  emailStatusDiv.textContent = 'Sending email...';
1601
  emailStatusDiv.style.color = 'var(--info-color)'; // Blue for processing
1602
 
 
1610
  if (!clientInstanceEmail) {
1611
  emailStatusDiv.textContent = 'Email service is not ready. Please try again.';
1612
  emailStatusDiv.style.color = 'var(--clear-button-bg)';
1613
+ console.error("Gradio email client instance is not initialized, even though IS_EMAIL_SENDING_ENABLED is true. Check initClient for errors.");
1614
  return;
1615
  }
1616
 
1617
  try {
 
 
 
1618
  const emailResult = await clientInstanceEmail.predict(
1619
  "/send_email_report",
1620
  [
 
1691
  async function generatePDFReportContent(sequenceForPdf, predictionForPdf, confidenceForPdf, micResultsForPdf, limeFeaturesForPdf) {
1692
  if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined' || typeof window.jspdf.jsPDF !== 'function') {
1693
  console.error("jsPDF library (window.jspdf.jsPDF) is not loaded or not correctly exposed as a function.");
1694
+ showError("PDF generation library not loaded. Cannot create report.");
1695
  return null;
1696
  }
1697
  const jsPDF = window.jspdf.jsPDF;
 
1701
  pdf = new jsPDF();
1702
  if (!pdf || !pdf.internal || !pdf.internal.pageSize || pdf.internal.pageSize.width === undefined || pdf.internal.pageSize.width === null) {
1703
  console.error("Critical error: jsPDF instance or its internal properties are invalid.");
1704
+ showError("PDF generation failed: Invalid PDF object.");
1705
  return null;
1706
  }
1707
  } catch (e) {
1708
  console.error("Failed to instantiate jsPDF object:", e);
1709
+ showError(`PDF generation failed: Could not initialize PDF library. Error: ${e.message}`);
1710
  return null;
1711
  }
1712