Spaces:
Running
Running
Update index.html
Browse files- index.html +39 -9
index.html
CHANGED
|
@@ -713,7 +713,12 @@
|
|
| 713 |
if (level3Slider) level3Slider.disabled = false;
|
| 714 |
|
| 715 |
// Permitir sembrar también en modo anónimo (no se guardará en la nube)
|
| 716 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 717 |
if (isAnonymous) {
|
| 718 |
console.log('Modo anónimo: podrás visualizar neuronas, pero no se guardarán en Firestore.');
|
| 719 |
topicInput.placeholder = 'Ingresa un tema para sembrar (modo anónimo)';
|
|
@@ -794,14 +799,25 @@
|
|
| 794 |
renderer.render(scene, camera);
|
| 795 |
}
|
| 796 |
|
| 797 |
-
// Fetch helper with backoff
|
| 798 |
-
async function
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 799 |
try {
|
| 800 |
-
return await
|
| 801 |
} catch (err) {
|
| 802 |
if (retries > 0) {
|
| 803 |
await new Promise(resolve => setTimeout(resolve, delay));
|
| 804 |
-
return fetchWithBackoff(url, options, retries - 1, delay * 2);
|
| 805 |
} else {
|
| 806 |
throw err;
|
| 807 |
}
|
|
@@ -896,7 +912,7 @@
|
|
| 896 |
method: 'POST',
|
| 897 |
headers: { 'Content-Type': 'application/json' },
|
| 898 |
body: JSON.stringify({ model: modelId, payload })
|
| 899 |
-
});
|
| 900 |
if (proxyResp.ok) {
|
| 901 |
return await proxyResp.json();
|
| 902 |
} else {
|
|
@@ -918,7 +934,7 @@
|
|
| 918 |
method: 'POST',
|
| 919 |
headers: { 'Content-Type': 'application/json' },
|
| 920 |
body: JSON.stringify(payload)
|
| 921 |
-
});
|
| 922 |
if (!directResp.ok) {
|
| 923 |
const errorBody = await directResp.text();
|
| 924 |
console.error('Error en la API de Gemini (fallback):', directResp.status, errorBody);
|
|
@@ -949,7 +965,16 @@
|
|
| 949 |
}
|
| 950 |
|
| 951 |
button.disabled = true;
|
| 952 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 953 |
|
| 954 |
progressBar.style.transition = 'none';
|
| 955 |
progressBar.style.width = '0%';
|
|
@@ -1006,8 +1031,13 @@
|
|
| 1006 |
} catch (error) {
|
| 1007 |
console.error("Error en handleAnalysisAndVisualization:", error);
|
| 1008 |
} finally {
|
|
|
|
| 1009 |
button.disabled = false;
|
| 1010 |
-
button.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1011 |
// Limpiar el input para permitir nueva siembra inmediata
|
| 1012 |
const ti = document.getElementById('topicInput');
|
| 1013 |
if (ti) ti.value = '';
|
|
|
|
| 713 |
if (level3Slider) level3Slider.disabled = false;
|
| 714 |
|
| 715 |
// Permitir sembrar también en modo anónimo (no se guardará en la nube)
|
| 716 |
+
// Evitar múltiples bindings si la escena se reinstala
|
| 717 |
+
if (!visualizeButton.dataset.bound) {
|
| 718 |
+
visualizeButton.dataset.originalHtml = visualizeButton.innerHTML;
|
| 719 |
+
visualizeButton.addEventListener('click', handleAnalysisAndVisualization);
|
| 720 |
+
visualizeButton.dataset.bound = '1';
|
| 721 |
+
}
|
| 722 |
if (isAnonymous) {
|
| 723 |
console.log('Modo anónimo: podrás visualizar neuronas, pero no se guardarán en Firestore.');
|
| 724 |
topicInput.placeholder = 'Ingresa un tema para sembrar (modo anónimo)';
|
|
|
|
| 799 |
renderer.render(scene, camera);
|
| 800 |
}
|
| 801 |
|
| 802 |
+
// Fetch helper with timeout + backoff
|
| 803 |
+
async function fetchWithTimeout(url, options = {}, timeoutMs = 25000) {
|
| 804 |
+
const controller = new AbortController();
|
| 805 |
+
const id = setTimeout(() => controller.abort(), timeoutMs);
|
| 806 |
+
try {
|
| 807 |
+
const resp = await fetch(url, { ...options, signal: controller.signal });
|
| 808 |
+
return resp;
|
| 809 |
+
} finally {
|
| 810 |
+
clearTimeout(id);
|
| 811 |
+
}
|
| 812 |
+
}
|
| 813 |
+
|
| 814 |
+
async function fetchWithBackoff(url, options, retries = 2, delay = 1000, timeoutMs = 25000) {
|
| 815 |
try {
|
| 816 |
+
return await fetchWithTimeout(url, options, timeoutMs);
|
| 817 |
} catch (err) {
|
| 818 |
if (retries > 0) {
|
| 819 |
await new Promise(resolve => setTimeout(resolve, delay));
|
| 820 |
+
return fetchWithBackoff(url, options, retries - 1, delay * 2, timeoutMs);
|
| 821 |
} else {
|
| 822 |
throw err;
|
| 823 |
}
|
|
|
|
| 912 |
method: 'POST',
|
| 913 |
headers: { 'Content-Type': 'application/json' },
|
| 914 |
body: JSON.stringify({ model: modelId, payload })
|
| 915 |
+
}, /*retries*/ 1, /*delay*/ 1000, /*timeoutMs*/ 25000);
|
| 916 |
if (proxyResp.ok) {
|
| 917 |
return await proxyResp.json();
|
| 918 |
} else {
|
|
|
|
| 934 |
method: 'POST',
|
| 935 |
headers: { 'Content-Type': 'application/json' },
|
| 936 |
body: JSON.stringify(payload)
|
| 937 |
+
}, /*retries*/ 2, /*delay*/ 1000, /*timeoutMs*/ 25000);
|
| 938 |
if (!directResp.ok) {
|
| 939 |
const errorBody = await directResp.text();
|
| 940 |
console.error('Error en la API de Gemini (fallback):', directResp.status, errorBody);
|
|
|
|
| 965 |
}
|
| 966 |
|
| 967 |
button.disabled = true;
|
| 968 |
+
// Guardar HTML original si no existe y colocar estado "Analizando..."
|
| 969 |
+
if (!button.dataset.originalHtml) button.dataset.originalHtml = button.innerHTML;
|
| 970 |
+
button.innerHTML = 'Analizando...';
|
| 971 |
+
|
| 972 |
+
// Watchdog: si algo se queda colgado, restaurar UI en 30s
|
| 973 |
+
const watchdog = setTimeout(() => {
|
| 974 |
+
try { button.disabled = false; } catch {}
|
| 975 |
+
try { if (button.dataset.originalHtml) button.innerHTML = button.dataset.originalHtml; } catch {}
|
| 976 |
+
try { progressBarContainer.style.display = 'none'; } catch {}
|
| 977 |
+
}, 30000);
|
| 978 |
|
| 979 |
progressBar.style.transition = 'none';
|
| 980 |
progressBar.style.width = '0%';
|
|
|
|
| 1031 |
} catch (error) {
|
| 1032 |
console.error("Error en handleAnalysisAndVisualization:", error);
|
| 1033 |
} finally {
|
| 1034 |
+
clearTimeout(watchdog);
|
| 1035 |
button.disabled = false;
|
| 1036 |
+
if (button.dataset.originalHtml) {
|
| 1037 |
+
button.innerHTML = button.dataset.originalHtml;
|
| 1038 |
+
} else {
|
| 1039 |
+
button.innerText = 'Sembrar';
|
| 1040 |
+
}
|
| 1041 |
// Limpiar el input para permitir nueva siembra inmediata
|
| 1042 |
const ti = document.getElementById('topicInput');
|
| 1043 |
if (ti) ti.value = '';
|