// Shared modal for LLM explanations var LLMModal = (function() { var modalEl = null; var contentEl = null; var isOpen = false; function createModal() { if (modalEl) return; modalEl = document.createElement('div'); modalEl.className = 'llm-modal-backdrop'; modalEl.style.cssText = 'display:none; position:fixed; top:0; left:0; width:100%; height:100%; z-index:9999; background:rgba(0,0,0,0.5); justify-content:center; align-items:center; padding:2rem;'; var container = document.createElement('div'); container.className = 'llm-modal-container'; container.style.cssText = 'background:var(--white); border-radius:20px; max-width:700px; width:100%; max-height:80vh; display:flex; flex-direction:column; box-shadow:0 20px 60px rgba(0,0,0,0.3); overflow:hidden;'; var header = document.createElement('div'); header.className = 'llm-modal-header'; header.style.cssText = 'display:flex; justify-content:space-between; align-items:center; padding:1.2rem 1.5rem; border-bottom:2px solid var(--pink-200);'; var title = document.createElement('h3'); title.className = 'llm-modal-title'; title.style.cssText = 'color:var(--pink-600); font-size:1.1rem; margin:0;'; var closeBtn = document.createElement('button'); closeBtn.className = 'llm-modal-close'; closeBtn.innerHTML = '✕'; closeBtn.style.cssText = 'background:none; border:none; color:var(--pink-500); font-size:1.5rem; cursor:pointer; padding:0.2rem 0.5rem; line-height:1; transition:color 0.2s;'; closeBtn.addEventListener('mouseenter', function() { this.style.color = 'var(--pink-700)'; }); closeBtn.addEventListener('mouseleave', function() { this.style.color = 'var(--pink-500)'; }); closeBtn.addEventListener('click', close); header.appendChild(title); header.appendChild(closeBtn); var body = document.createElement('div'); body.className = 'llm-modal-body'; body.id = 'llm-modal-content'; body.style.cssText = 'padding:1.5rem; overflow-y:auto; flex:1; font-size:0.95rem; line-height:1.7; color:var(--pink-900); white-space:pre-wrap;'; container.appendChild(header); container.appendChild(body); modalEl.appendChild(container); document.body.appendChild(modalEl); modalEl.addEventListener('click', function(e) { if (e.target === modalEl) close(); }); document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && isOpen) close(); }); modalEl.style.display = 'flex'; } function open(titleText) { if (!modalEl) createModal(); var title = modalEl.querySelector('.llm-modal-title'); title.textContent = titleText; contentEl = document.getElementById('llm-modal-content'); contentEl.innerHTML = 'Thinking...'; modalEl.style.display = 'flex'; isOpen = true; } function update(text) { if (contentEl) { contentEl.innerHTML = formatMarkdown(text); } } function done() { // nothing to do, modal stays open } function error(msg) { if (contentEl) { contentEl.innerHTML = 'Error: ' + escapeHTML(msg) + ''; } } function close() { if (modalEl) { modalEl.style.display = 'none'; isOpen = false; } } function escapeHTML(str) { var div = document.createElement('div'); div.textContent = str; return div.innerHTML; } function formatMarkdown(text) { // Escape HTML first text = escapeHTML(text); // Code blocks (must be before inline code) text = text.replace(/```([\s\S]*?)```/g, '
$1
'); // Inline code text = text.replace(/`([^`]+)`/g, '$1'); // Bold text = text.replace(/\*\*([^*]+)\*\*/g, '$1'); // Line breaks text = text.replace(/\n/g, '
'); return text; } return { open: open, update: update, done: done, error: error, close: close }; })();