document.addEventListener('DOMContentLoaded', () => {
// --- Element References ---
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('chatFile');
const uploadForm = document.getElementById('uploadForm');
const settingsBtn = document.getElementById('settingsBtn');
const settingsModal = document.getElementById('settingsModal');
const closeSettings = document.getElementById('closeSettings');
const saveSettingsBtn = document.getElementById('saveSettingsBtn');
const fileList = document.getElementById('fileList');
const trustCenterBtn = document.getElementById('trustCenterBtn');
const trustCenterModal = document.getElementById('trustCenterModal');
const closeTrustCenter = document.getElementById('closeTrustCenter');
const understoodBtn = document.getElementById('understoodBtn');
// --- Tone Selector ---
const toneDescriptions = {
playful: 'Fun, witty insights with personality.',
balanced: 'Clear, helpful insights with context.',
direct: 'Straight facts, no fluff.'
};
const toneSelector = document.getElementById('toneSelector');
const toneInput = document.getElementById('analysisTone');
const toneDesc = document.getElementById('toneDesc');
if (toneSelector) {
toneSelector.querySelectorAll('.tone-btn').forEach(btn => {
btn.addEventListener('click', () => {
toneSelector.querySelectorAll('.tone-btn').forEach(b => {
b.style.background = 'transparent';
b.style.color = 'var(--black)';
b.classList.remove('active');
});
btn.style.background = 'var(--black)';
btn.style.color = 'var(--white)';
btn.classList.add('active');
const tone = btn.dataset.tone;
if (toneInput) toneInput.value = tone;
if (toneDesc) toneDesc.textContent = toneDescriptions[tone] || '';
});
});
}
// --- Custom Select Dropdowns ---
document.querySelectorAll('.custom-select').forEach(wrapper => {
const trigger = wrapper.querySelector('.custom-select-trigger');
const label = wrapper.querySelector('.custom-select-label');
const options = wrapper.querySelectorAll('.custom-select-option');
const targetId = wrapper.dataset.target;
const hiddenSelect = document.getElementById(targetId);
// Toggle open/close
trigger.addEventListener('click', (e) => {
e.preventDefault();
const isOpen = wrapper.classList.contains('open');
// Close all others first
document.querySelectorAll('.custom-select.open').forEach(s => s.classList.remove('open'));
if (!isOpen) wrapper.classList.add('open');
trigger.setAttribute('aria-expanded', !isOpen);
});
// Option click
options.forEach(opt => {
opt.addEventListener('click', () => {
options.forEach(o => o.classList.remove('selected'));
opt.classList.add('selected');
label.textContent = opt.textContent;
if (hiddenSelect) hiddenSelect.value = opt.dataset.value;
wrapper.classList.remove('open');
trigger.setAttribute('aria-expanded', 'false');
});
});
// Keyboard support
trigger.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
trigger.click();
}
});
});
// Close dropdowns on outside click
document.addEventListener('click', (e) => {
if (!e.target.closest('.custom-select')) {
document.querySelectorAll('.custom-select.open').forEach(s => {
s.classList.remove('open');
s.querySelector('.custom-select-trigger')?.setAttribute('aria-expanded', 'false');
});
}
});
// --- API Key Status UI ---
const updateApiKeyUI = () => {
const icon = document.getElementById('apiKeyStatusIcon');
const text = document.getElementById('apiKeyStatusText');
if (!icon || !text) return;
const key = sessionStorage.getItem('_llm_token');
if (key && key.trim() !== "" && key !== btoa("")) {
icon.textContent = '✅';
icon.classList.remove('animate-pulse');
text.textContent = 'API Key Configured';
text.style.color = 'var(--black)';
} else {
icon.textContent = '🔑';
icon.classList.add('animate-pulse');
text.textContent = 'API Key Required';
text.style.color = '';
}
};
updateApiKeyUI();
// --- Modal Helpers ---
const showModal = (modal) => {
if (!modal) return;
modal.classList.add('active');
modal.classList.remove('hidden');
};
const hideModal = (modal, focusEl) => {
if (!modal) return;
modal.classList.remove('active');
setTimeout(() => {
modal.classList.add('hidden');
if (focusEl) focusEl.focus();
}, 250);
};
// Close on Escape / backdrop click
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
if (trustCenterModal && trustCenterModal.classList.contains('active')) hideModal(trustCenterModal, trustCenterBtn);
if (settingsModal && settingsModal.classList.contains('active')) hideModal(settingsModal, settingsBtn);
}
});
[trustCenterModal, settingsModal].forEach(modal => {
if (modal) modal.addEventListener('click', (e) => {
if (e.target === modal) {
if (modal === trustCenterModal) hideModal(trustCenterModal, trustCenterBtn);
if (modal === settingsModal) hideModal(settingsModal, settingsBtn);
}
});
});
// --- Trust Center ---
if (trustCenterBtn && trustCenterModal) {
trustCenterBtn.addEventListener('click', () => showModal(trustCenterModal));
if (closeTrustCenter) closeTrustCenter.addEventListener('click', () => hideModal(trustCenterModal, trustCenterBtn));
if (understoodBtn) understoodBtn.addEventListener('click', () => hideModal(trustCenterModal, trustCenterBtn));
}
// --- Settings Modal ---
if (settingsBtn && settingsModal) {
settingsBtn.addEventListener('click', () => {
showModal(settingsModal);
const provider = document.getElementById('llmProvider');
if (provider) setTimeout(() => provider.focus(), 100);
});
if (closeSettings) closeSettings.addEventListener('click', () => hideModal(settingsModal, settingsBtn));
const apiKeyEl = document.getElementById('apiKey');
const hfUrlEl = document.getElementById('hfUrl');
const llmProviderEl = document.getElementById('llmProvider');
// Enter to save
[apiKeyEl, hfUrlEl].forEach(el => {
if (el) el.addEventListener('keydown', (e) => {
if (e.key === 'Enter') { e.preventDefault(); if (saveSettingsBtn) saveSettingsBtn.click(); }
});
});
// Load saved values
if (apiKeyEl) apiKeyEl.value = sessionStorage.getItem('_llm_token') ? atob(sessionStorage.getItem('_llm_token')) : '';
if (hfUrlEl) hfUrlEl.value = localStorage.getItem('hf_url') || '';
const savedProvider = localStorage.getItem('llm_provider');
if (savedProvider && llmProviderEl) {
llmProviderEl.value = savedProvider;
updateProviderHint(savedProvider);
}
if (llmProviderEl) llmProviderEl.addEventListener('change', (e) => updateProviderHint(e.target.value));
// Save config
if (saveSettingsBtn) {
saveSettingsBtn.addEventListener('click', () => {
const key = apiKeyEl ? apiKeyEl.value.trim() : '';
const hfUrl = hfUrlEl ? hfUrlEl.value.trim() : '';
const provider = llmProviderEl ? llmProviderEl.value : 'openai';
sessionStorage.setItem('_llm_token', btoa(key));
localStorage.setItem('hf_url', hfUrl);
localStorage.setItem('llm_provider', provider);
updateApiKeyUI();
hideModal(settingsModal, settingsBtn);
});
}
}
// --- API Key Visibility Toggle ---
const toggleBtn = document.getElementById('toggleApiKey');
const eyeIcon = document.getElementById('eyeIcon');
const apiKeyInput = document.getElementById('apiKey');
if (toggleBtn && apiKeyInput && eyeIcon) {
toggleBtn.addEventListener('click', () => {
const isPassword = apiKeyInput.type === 'password';
apiKeyInput.type = isPassword ? 'text' : 'password';
toggleBtn.setAttribute('aria-label', isPassword ? 'Hide API Key' : 'Show API Key');
eyeIcon.innerHTML = isPassword
? '
${message}
API Key Required
An AI API Key is needed to generate your relationship insights.