LLM integration with all the tabs

This commit is contained in:
2026-05-05 08:51:34 -04:00
parent de34ee9067
commit 695a3a510e
11 changed files with 1233 additions and 8 deletions

117
lib/modal.js Normal file
View File

@@ -0,0 +1,117 @@
// 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 = '<span class="llm-loading">Thinking...</span>';
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 = '<span class="llm-error">Error: ' + escapeHTML(msg) + '</span>';
}
}
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, '<pre class="llm-code-block"><code>$1</code></pre>');
// Inline code
text = text.replace(/`([^`]+)`/g, '<code class="llm-inline-code">$1</code>');
// Bold
text = text.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
// Line breaks
text = text.replace(/\n/g, '<br>');
return text;
}
return {
open: open,
update: update,
done: done,
error: error,
close: close
};
})();