Spaces:
Running
Running
Update index.html
Browse files- index.html +99 -61
index.html
CHANGED
|
@@ -4038,6 +4038,41 @@
|
|
| 4038 |
display: none;
|
| 4039 |
}
|
| 4040 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4041 |
</style>
|
| 4042 |
</head>
|
| 4043 |
|
|
@@ -4068,7 +4103,8 @@
|
|
| 4068 |
<p class="hero-subtitle">Chat with Nexa AI to get answers, generate stunning images, and boost your productivity. Your personal AI assistant for work and creativity.</p>
|
| 4069 |
<div class="cta-container">
|
| 4070 |
<button onclick="enterApp()" class="btn-primary landing-cta">Start Chatting — Free Forever</button>
|
| 4071 |
-
<
|
|
|
|
| 4072 |
</div>
|
| 4073 |
<div class="trust-badges">
|
| 4074 |
<div class="trust-badge"><i data-lucide="shield-check"></i> No credit card required</div>
|
|
@@ -4298,7 +4334,8 @@
|
|
| 4298 |
<span>Deep Research</span>
|
| 4299 |
</div>
|
| 4300 |
<label class="switch">
|
| 4301 |
-
<
|
|
|
|
| 4302 |
<span class="slider"></span>
|
| 4303 |
</label>
|
| 4304 |
</div>
|
|
@@ -4530,12 +4567,13 @@
|
|
| 4530 |
<button class="action-btn mic-btn" id="micBtn" onclick="toggleSpeechRecognition()" title="Voice Input">
|
| 4531 |
<i data-lucide="mic"></i>
|
| 4532 |
</button>
|
| 4533 |
-
<
|
|
|
|
| 4534 |
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 4535 |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18" />
|
| 4536 |
</svg>
|
| 4537 |
</button>
|
| 4538 |
-
<button class="stop-button" id="stopBtn"
|
| 4539 |
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 4540 |
<rect x="6" y="6" width="12" height="12" rx="2" fill="currentColor" />
|
| 4541 |
</svg>
|
|
@@ -4814,6 +4852,7 @@
|
|
| 4814 |
};
|
| 4815 |
|
| 4816 |
// Critical Global Functions (Must be defined first)
|
|
|
|
| 4817 |
function enterApp() {
|
| 4818 |
try {
|
| 4819 |
const landing = document.getElementById('landingPage');
|
|
@@ -4822,11 +4861,13 @@
|
|
| 4822 |
|
| 4823 |
if (!currentUser) {
|
| 4824 |
if (landing) {
|
|
|
|
|
|
|
| 4825 |
landing.classList.add('hidden');
|
| 4826 |
-
setTimeout(() => { if (landing) landing.style.display = 'none'; }, 800);
|
| 4827 |
}
|
| 4828 |
if (authOvl) {
|
| 4829 |
authOvl.style.display = 'flex';
|
|
|
|
| 4830 |
authOvl.classList.add('show');
|
| 4831 |
}
|
| 4832 |
if (window.lightningTimeout) {
|
|
@@ -4854,6 +4895,7 @@
|
|
| 4854 |
}
|
| 4855 |
}
|
| 4856 |
|
|
|
|
| 4857 |
function scrollToSection(id) {
|
| 4858 |
try {
|
| 4859 |
const el = document.getElementById(id);
|
|
@@ -5011,15 +5053,9 @@
|
|
| 5011 |
if (modelDropdown) modelDropdown.classList.remove('open');
|
| 5012 |
});
|
| 5013 |
|
|
|
|
| 5014 |
function toggleDeepResearch(checked) {
|
| 5015 |
-
|
| 5016 |
-
if (checked !== undefined) {
|
| 5017 |
-
isDeepResearch = checked;
|
| 5018 |
-
} else {
|
| 5019 |
-
isDeepResearch = !isDeepResearch;
|
| 5020 |
-
toggle.checked = isDeepResearch;
|
| 5021 |
-
}
|
| 5022 |
-
|
| 5023 |
localStorage.setItem('nexa_ai_deep_research', isDeepResearch);
|
| 5024 |
|
| 5025 |
// Update UI indicator
|
|
@@ -5028,8 +5064,6 @@
|
|
| 5028 |
if (isDeepResearch) indicator.classList.add('visible');
|
| 5029 |
else indicator.classList.remove('visible');
|
| 5030 |
}
|
| 5031 |
-
|
| 5032 |
-
console.log(`Deep Research Mode: ${isDeepResearch}`);
|
| 5033 |
}
|
| 5034 |
|
| 5035 |
function initAiModes() {
|
|
@@ -5461,21 +5495,31 @@
|
|
| 5461 |
}
|
| 5462 |
|
| 5463 |
function stopAI() {
|
| 5464 |
-
|
| 5465 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5466 |
abortController = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5467 |
}
|
| 5468 |
-
isTyping = false;
|
| 5469 |
-
isGeneratingImage = false;
|
| 5470 |
-
isSearching = false;
|
| 5471 |
-
isAnalyzingFile = false;
|
| 5472 |
-
|
| 5473 |
-
// UI Cleanup
|
| 5474 |
-
const thinking = document.getElementById('thinkingMessage');
|
| 5475 |
-
if (thinking) thinking.remove();
|
| 5476 |
-
|
| 5477 |
-
if (stopBtn) stopBtn.classList.remove('active');
|
| 5478 |
-
if (sendBtn) sendBtn.style.display = 'flex';
|
| 5479 |
}
|
| 5480 |
|
| 5481 |
function handleImageRemix(encodedQuery) {
|
|
@@ -5519,7 +5563,7 @@
|
|
| 5519 |
}
|
| 5520 |
|
| 5521 |
function setupEventListeners() {
|
| 5522 |
-
//
|
| 5523 |
document.addEventListener('click', (e) => {
|
| 5524 |
const target = e.target;
|
| 5525 |
|
|
@@ -5528,29 +5572,19 @@
|
|
| 5528 |
// 1. Landing Navigation Links (Features, Pricing, etc.)
|
| 5529 |
const navLink = target.closest('.nav-link[data-section]');
|
| 5530 |
if (navLink) {
|
| 5531 |
-
const sectionId = navLink.getAttribute('data-section');
|
| 5532 |
-
scrollToSection(sectionId);
|
| 5533 |
e.preventDefault();
|
|
|
|
| 5534 |
return;
|
| 5535 |
}
|
| 5536 |
|
| 5537 |
// 2. Landing CTA Buttons ("Try Nexa AI", "Start Chatting")
|
| 5538 |
if (target.closest('.landing-cta')) {
|
| 5539 |
-
enterApp();
|
| 5540 |
-
e.preventDefault();
|
| 5541 |
-
return;
|
| 5542 |
-
}
|
| 5543 |
-
|
| 5544 |
-
// 3. Landing "View Demo" Button
|
| 5545 |
-
const demoBtn = target.closest('.btn-secondary[data-section]');
|
| 5546 |
-
if (demoBtn) {
|
| 5547 |
-
const sectionId = demoBtn.getAttribute('data-section');
|
| 5548 |
-
scrollToSection(sectionId);
|
| 5549 |
e.preventDefault();
|
|
|
|
| 5550 |
return;
|
| 5551 |
}
|
| 5552 |
|
| 5553 |
-
//
|
| 5554 |
const faqQuestion = target.closest('.faq-question');
|
| 5555 |
if (faqQuestion) {
|
| 5556 |
const item = faqQuestion.parentElement;
|
|
@@ -5574,23 +5608,12 @@
|
|
| 5574 |
return;
|
| 5575 |
}
|
| 5576 |
|
| 5577 |
-
// 6. Tool Actions (
|
| 5578 |
const toolBtn = target.closest('.tool-btn');
|
| 5579 |
if (toolBtn) {
|
| 5580 |
-
|
| 5581 |
-
|
| 5582 |
-
if (
|
| 5583 |
-
const text = toolBtn.textContent.toLowerCase();
|
| 5584 |
-
if (text.includes('image')) action = 'image';
|
| 5585 |
-
else if (text.includes('search')) action = 'search';
|
| 5586 |
-
else if (text.includes('video')) action = 'video';
|
| 5587 |
-
else if (text.includes('upload')) action = 'upload';
|
| 5588 |
-
else if (text.includes('voice')) action = 'voice';
|
| 5589 |
-
}
|
| 5590 |
-
if (action) {
|
| 5591 |
-
e.preventDefault();
|
| 5592 |
-
handleToolAction(action);
|
| 5593 |
-
}
|
| 5594 |
return;
|
| 5595 |
}
|
| 5596 |
|
|
@@ -5605,14 +5628,15 @@
|
|
| 5605 |
return;
|
| 5606 |
}
|
| 5607 |
|
| 5608 |
-
//
|
| 5609 |
if (target.closest('.account-btn')) {
|
| 5610 |
toggleAccountDropdown();
|
| 5611 |
return;
|
| 5612 |
}
|
| 5613 |
|
| 5614 |
-
//
|
| 5615 |
if (!target.closest('.mode-dropdown') && !target.closest('.account-btn') && !target.closest('.top-right-menu')) {
|
|
|
|
| 5616 |
document.querySelectorAll('.mode-dropdown, .account-dropdown').forEach(d => d.classList.remove('open', 'show'));
|
| 5617 |
}
|
| 5618 |
|
|
@@ -7061,6 +7085,8 @@
|
|
| 7061 |
}
|
| 7062 |
|
| 7063 |
function startNewChat() {
|
|
|
|
|
|
|
| 7064 |
currentConversationId = null;
|
| 7065 |
const messageInput = document.getElementById('messageInput');
|
| 7066 |
if (messageInput) messageInput.placeholder = "Message Nexa AI...";
|
|
@@ -7427,8 +7453,12 @@
|
|
| 7427 |
}
|
| 7428 |
|
| 7429 |
// Account dropdown
|
|
|
|
| 7430 |
function toggleAccountDropdown() {
|
| 7431 |
-
if (accountDropdown)
|
|
|
|
|
|
|
|
|
|
| 7432 |
}
|
| 7433 |
|
| 7434 |
// Hidden file input
|
|
@@ -7733,10 +7763,13 @@
|
|
| 7733 |
if (e.target === document.getElementById('imgModalOverlay')) closeImageGenModal();
|
| 7734 |
}
|
| 7735 |
|
|
|
|
| 7736 |
async function generateImage() {
|
|
|
|
| 7737 |
const prompt = document.getElementById('imgPromptInput').value.trim();
|
| 7738 |
if (!prompt) return;
|
| 7739 |
|
|
|
|
| 7740 |
const genBtn = document.getElementById('imgGenerateBtn');
|
| 7741 |
const skeleton = document.getElementById('imgSkeleton');
|
| 7742 |
const resultArea = document.getElementById('imgResultArea');
|
|
@@ -7764,6 +7797,7 @@
|
|
| 7764 |
resultArea.classList.add('visible');
|
| 7765 |
statusEl.textContent = '✓ Done!';
|
| 7766 |
genBtn.disabled = false;
|
|
|
|
| 7767 |
};
|
| 7768 |
img.onerror = () => {
|
| 7769 |
throw new Error("Failed to load image from Pollinations AI");
|
|
@@ -7774,6 +7808,7 @@
|
|
| 7774 |
skeleton.classList.remove('visible');
|
| 7775 |
statusEl.textContent = '✗ Error: ' + err.message;
|
| 7776 |
genBtn.disabled = false;
|
|
|
|
| 7777 |
}
|
| 7778 |
}
|
| 7779 |
|
|
@@ -7903,7 +7938,10 @@
|
|
| 7903 |
}
|
| 7904 |
|
| 7905 |
function handleSettingsOverlayClick(e) {
|
| 7906 |
-
|
|
|
|
|
|
|
|
|
|
| 7907 |
}
|
| 7908 |
|
| 7909 |
let isAutoVoiceEnabled = localStorage.getItem('nexa_auto_voice') !== 'false';
|
|
|
|
| 4038 |
display: none;
|
| 4039 |
}
|
| 4040 |
}
|
| 4041 |
+
/* FIXED: Added visual states for disabled buttons */
|
| 4042 |
+
.send-button:disabled {
|
| 4043 |
+
opacity: 0.3 !important;
|
| 4044 |
+
cursor: not-allowed !important;
|
| 4045 |
+
transform: none !important;
|
| 4046 |
+
}
|
| 4047 |
+
|
| 4048 |
+
.btn-primary:disabled {
|
| 4049 |
+
opacity: 0.6 !important;
|
| 4050 |
+
cursor: wait !important;
|
| 4051 |
+
}
|
| 4052 |
+
|
| 4053 |
+
/* FIXED: Ensure auth overlay is always on top */
|
| 4054 |
+
#authOverlay {
|
| 4055 |
+
z-index: 9999 !important;
|
| 4056 |
+
}
|
| 4057 |
+
|
| 4058 |
+
/* FIXED: Send/Stop button positioning */
|
| 4059 |
+
.input-actions {
|
| 4060 |
+
position: relative;
|
| 4061 |
+
display: flex;
|
| 4062 |
+
align-items: center;
|
| 4063 |
+
gap: 8px;
|
| 4064 |
+
}
|
| 4065 |
+
|
| 4066 |
+
.stop-button {
|
| 4067 |
+
display: none;
|
| 4068 |
+
background: var(--bg-secondary);
|
| 4069 |
+
border: 1px solid var(--border-color);
|
| 4070 |
+
color: var(--text-primary);
|
| 4071 |
+
}
|
| 4072 |
+
|
| 4073 |
+
.stop-button.active {
|
| 4074 |
+
display: flex;
|
| 4075 |
+
}
|
| 4076 |
</style>
|
| 4077 |
</head>
|
| 4078 |
|
|
|
|
| 4103 |
<p class="hero-subtitle">Chat with Nexa AI to get answers, generate stunning images, and boost your productivity. Your personal AI assistant for work and creativity.</p>
|
| 4104 |
<div class="cta-container">
|
| 4105 |
<button onclick="enterApp()" class="btn-primary landing-cta">Start Chatting — Free Forever</button>
|
| 4106 |
+
<!-- FIXED: Replaced data-section with direct onclick for reliability -->
|
| 4107 |
+
<button class="btn-secondary" onclick="scrollToSection('featuresSection')">View Demo</button>
|
| 4108 |
</div>
|
| 4109 |
<div class="trust-badges">
|
| 4110 |
<div class="trust-badge"><i data-lucide="shield-check"></i> No credit card required</div>
|
|
|
|
| 4334 |
<span>Deep Research</span>
|
| 4335 |
</div>
|
| 4336 |
<label class="switch">
|
| 4337 |
+
<!-- FIXED: Added explicit parameter to toggleDeepResearch -->
|
| 4338 |
+
<input type="checkbox" id="deepResearchToggle" onchange="toggleDeepResearch(this.checked)">
|
| 4339 |
<span class="slider"></span>
|
| 4340 |
</label>
|
| 4341 |
</div>
|
|
|
|
| 4567 |
<button class="action-btn mic-btn" id="micBtn" onclick="toggleSpeechRecognition()" title="Voice Input">
|
| 4568 |
<i data-lucide="mic"></i>
|
| 4569 |
</button>
|
| 4570 |
+
<!-- FIXED: Removed inline onclick to prevent double-firing and used consistent SVG icons -->
|
| 4571 |
+
<button class="send-button" id="sendBtn" title="Send message (Enter)">
|
| 4572 |
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 4573 |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18" />
|
| 4574 |
</svg>
|
| 4575 |
</button>
|
| 4576 |
+
<button class="stop-button" id="stopBtn" title="Stop generating (Esc)">
|
| 4577 |
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 4578 |
<rect x="6" y="6" width="12" height="12" rx="2" fill="currentColor" />
|
| 4579 |
</svg>
|
|
|
|
| 4852 |
};
|
| 4853 |
|
| 4854 |
// Critical Global Functions (Must be defined first)
|
| 4855 |
+
<!-- FIXED: Optimized enterApp to handle unauthenticated state properly -->
|
| 4856 |
function enterApp() {
|
| 4857 |
try {
|
| 4858 |
const landing = document.getElementById('landingPage');
|
|
|
|
| 4861 |
|
| 4862 |
if (!currentUser) {
|
| 4863 |
if (landing) {
|
| 4864 |
+
// Immediately hide landing page
|
| 4865 |
+
landing.style.display = 'none';
|
| 4866 |
landing.classList.add('hidden');
|
|
|
|
| 4867 |
}
|
| 4868 |
if (authOvl) {
|
| 4869 |
authOvl.style.display = 'flex';
|
| 4870 |
+
authOvl.style.zIndex = '9999'; // Ensure auth is above everything
|
| 4871 |
authOvl.classList.add('show');
|
| 4872 |
}
|
| 4873 |
if (window.lightningTimeout) {
|
|
|
|
| 4895 |
}
|
| 4896 |
}
|
| 4897 |
|
| 4898 |
+
<!-- FIXED: Improved scrollToSection with null-safety and smooth scrolling -->
|
| 4899 |
function scrollToSection(id) {
|
| 4900 |
try {
|
| 4901 |
const el = document.getElementById(id);
|
|
|
|
| 5053 |
if (modelDropdown) modelDropdown.classList.remove('open');
|
| 5054 |
});
|
| 5055 |
|
| 5056 |
+
<!-- FIXED: Removed inversion logic and added explicit parameter -->
|
| 5057 |
function toggleDeepResearch(checked) {
|
| 5058 |
+
isDeepResearch = !!checked;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5059 |
localStorage.setItem('nexa_ai_deep_research', isDeepResearch);
|
| 5060 |
|
| 5061 |
// Update UI indicator
|
|
|
|
| 5064 |
if (isDeepResearch) indicator.classList.add('visible');
|
| 5065 |
else indicator.classList.remove('visible');
|
| 5066 |
}
|
|
|
|
|
|
|
| 5067 |
}
|
| 5068 |
|
| 5069 |
function initAiModes() {
|
|
|
|
| 5495 |
}
|
| 5496 |
|
| 5497 |
function stopAI() {
|
| 5498 |
+
try {
|
| 5499 |
+
if (abortController) {
|
| 5500 |
+
abortController.abort();
|
| 5501 |
+
}
|
| 5502 |
+
} catch (e) {
|
| 5503 |
+
console.error("Stop AI error:", e);
|
| 5504 |
+
} finally {
|
| 5505 |
+
// FIXED: Ensure all states are reset in finally block
|
| 5506 |
abortController = null;
|
| 5507 |
+
isTyping = false;
|
| 5508 |
+
isGeneratingImage = false;
|
| 5509 |
+
isSearching = false;
|
| 5510 |
+
isAnalyzingFile = false;
|
| 5511 |
+
|
| 5512 |
+
// UI Cleanup
|
| 5513 |
+
const thinking = document.getElementById('thinkingMessage');
|
| 5514 |
+
if (thinking) thinking.remove();
|
| 5515 |
+
|
| 5516 |
+
if (stopBtn) stopBtn.classList.remove('active');
|
| 5517 |
+
if (sendBtn) {
|
| 5518 |
+
sendBtn.style.display = 'flex';
|
| 5519 |
+
sendBtn.disabled = false;
|
| 5520 |
+
}
|
| 5521 |
+
updateSendButtonState();
|
| 5522 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5523 |
}
|
| 5524 |
|
| 5525 |
function handleImageRemix(encodedQuery) {
|
|
|
|
| 5563 |
}
|
| 5564 |
|
| 5565 |
function setupEventListeners() {
|
| 5566 |
+
// FIXED: Restructured with early returns to prevent fall-through bugs
|
| 5567 |
document.addEventListener('click', (e) => {
|
| 5568 |
const target = e.target;
|
| 5569 |
|
|
|
|
| 5572 |
// 1. Landing Navigation Links (Features, Pricing, etc.)
|
| 5573 |
const navLink = target.closest('.nav-link[data-section]');
|
| 5574 |
if (navLink) {
|
|
|
|
|
|
|
| 5575 |
e.preventDefault();
|
| 5576 |
+
scrollToSection(navLink.getAttribute('data-section'));
|
| 5577 |
return;
|
| 5578 |
}
|
| 5579 |
|
| 5580 |
// 2. Landing CTA Buttons ("Try Nexa AI", "Start Chatting")
|
| 5581 |
if (target.closest('.landing-cta')) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5582 |
e.preventDefault();
|
| 5583 |
+
enterApp();
|
| 5584 |
return;
|
| 5585 |
}
|
| 5586 |
|
| 5587 |
+
// 3. FAQ Items
|
| 5588 |
const faqQuestion = target.closest('.faq-question');
|
| 5589 |
if (faqQuestion) {
|
| 5590 |
const item = faqQuestion.parentElement;
|
|
|
|
| 5608 |
return;
|
| 5609 |
}
|
| 5610 |
|
| 5611 |
+
// 6. Tool Actions (Using data-action instead of regex parsing)
|
| 5612 |
const toolBtn = target.closest('.tool-btn');
|
| 5613 |
if (toolBtn) {
|
| 5614 |
+
e.preventDefault();
|
| 5615 |
+
const action = toolBtn.dataset.action;
|
| 5616 |
+
if (action) handleToolAction(action);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5617 |
return;
|
| 5618 |
}
|
| 5619 |
|
|
|
|
| 5628 |
return;
|
| 5629 |
}
|
| 5630 |
|
| 5631 |
+
// 8. Account Dropdown Toggle
|
| 5632 |
if (target.closest('.account-btn')) {
|
| 5633 |
toggleAccountDropdown();
|
| 5634 |
return;
|
| 5635 |
}
|
| 5636 |
|
| 5637 |
+
// 9. Close dropdowns when clicking outside
|
| 5638 |
if (!target.closest('.mode-dropdown') && !target.closest('.account-btn') && !target.closest('.top-right-menu')) {
|
| 5639 |
+
isDropdownOpen = false;
|
| 5640 |
document.querySelectorAll('.mode-dropdown, .account-dropdown').forEach(d => d.classList.remove('open', 'show'));
|
| 5641 |
}
|
| 5642 |
|
|
|
|
| 7085 |
}
|
| 7086 |
|
| 7087 |
function startNewChat() {
|
| 7088 |
+
// FIXED: Close sidebar on mobile when starting a new chat
|
| 7089 |
+
closeSidebarOnMobile();
|
| 7090 |
currentConversationId = null;
|
| 7091 |
const messageInput = document.getElementById('messageInput');
|
| 7092 |
if (messageInput) messageInput.placeholder = "Message Nexa AI...";
|
|
|
|
| 7453 |
}
|
| 7454 |
|
| 7455 |
// Account dropdown
|
| 7456 |
+
let isDropdownOpen = false; // FIXED: Track dropdown state globally
|
| 7457 |
function toggleAccountDropdown() {
|
| 7458 |
+
if (accountDropdown) {
|
| 7459 |
+
isDropdownOpen = !isDropdownOpen;
|
| 7460 |
+
accountDropdown.classList.toggle('show', isDropdownOpen);
|
| 7461 |
+
}
|
| 7462 |
}
|
| 7463 |
|
| 7464 |
// Hidden file input
|
|
|
|
| 7763 |
if (e.target === document.getElementById('imgModalOverlay')) closeImageGenModal();
|
| 7764 |
}
|
| 7765 |
|
| 7766 |
+
let isGeneratingImageModal = false; // FIXED: Prevent double-clicks in modal
|
| 7767 |
async function generateImage() {
|
| 7768 |
+
if (isGeneratingImageModal) return;
|
| 7769 |
const prompt = document.getElementById('imgPromptInput').value.trim();
|
| 7770 |
if (!prompt) return;
|
| 7771 |
|
| 7772 |
+
isGeneratingImageModal = true;
|
| 7773 |
const genBtn = document.getElementById('imgGenerateBtn');
|
| 7774 |
const skeleton = document.getElementById('imgSkeleton');
|
| 7775 |
const resultArea = document.getElementById('imgResultArea');
|
|
|
|
| 7797 |
resultArea.classList.add('visible');
|
| 7798 |
statusEl.textContent = '✓ Done!';
|
| 7799 |
genBtn.disabled = false;
|
| 7800 |
+
isGeneratingImageModal = false;
|
| 7801 |
};
|
| 7802 |
img.onerror = () => {
|
| 7803 |
throw new Error("Failed to load image from Pollinations AI");
|
|
|
|
| 7808 |
skeleton.classList.remove('visible');
|
| 7809 |
statusEl.textContent = '✗ Error: ' + err.message;
|
| 7810 |
genBtn.disabled = false;
|
| 7811 |
+
isGeneratingImageModal = false;
|
| 7812 |
}
|
| 7813 |
}
|
| 7814 |
|
|
|
|
| 7938 |
}
|
| 7939 |
|
| 7940 |
function handleSettingsOverlayClick(e) {
|
| 7941 |
+
// FIXED: Improved click-outside detection for settings modal
|
| 7942 |
+
if (e.target.closest('.settings-modal') === null) {
|
| 7943 |
+
closeSettings();
|
| 7944 |
+
}
|
| 7945 |
}
|
| 7946 |
|
| 7947 |
let isAutoVoiceEnabled = localStorage.getItem('nexa_auto_voice') !== 'false';
|