Skip to main content
Glama

Semiconductor Component RAG Search

script.js10.8 kB
// Frontend JavaScript - Connects to Node.js backend which proxies to FastAPI const API_BASE = '/api'; // DOM Elements const statusBadge = document.getElementById('statusBadge'); const statusText = document.getElementById('statusText'); const uploadArea = document.getElementById('uploadArea'); const fileInput = document.getElementById('fileInput'); const uploadProgress = document.getElementById('uploadProgress'); const progressFill = document.getElementById('progressFill'); const uploadStatus = document.getElementById('uploadStatus'); const uploadResult = document.getElementById('uploadResult'); const questionInput = document.getElementById('questionInput'); const askButton = document.getElementById('askButton'); const answerSection = document.getElementById('answerSection'); const answerContent = document.getElementById('answerContent'); const contextItems = document.getElementById('contextItems'); const copyButton = document.getElementById('copyButton'); const quickButtons = document.querySelectorAll('.quick-btn'); const docCount = document.getElementById('docCount'); const collectionName = document.getElementById('collectionName'); const collectionStatus = document.getElementById('collectionStatus'); const refreshInfoBtn = document.getElementById('refreshInfoBtn'); const loadingOverlay = document.getElementById('loadingOverlay'); const toastContainer = document.getElementById('toastContainer'); // Initialize document.addEventListener('DOMContentLoaded', () => { checkHealth(); loadCollectionInfo(); setupEventListeners(); }); // Setup Event Listeners function setupEventListeners() { // Upload area uploadArea.addEventListener('click', () => fileInput.click()); uploadArea.addEventListener('dragover', handleDragOver); uploadArea.addEventListener('dragleave', handleDragLeave); uploadArea.addEventListener('drop', handleDrop); fileInput.addEventListener('change', handleFileSelect); // Question input questionInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { askQuestion(); } }); askButton.addEventListener('click', askQuestion); // Quick question buttons quickButtons.forEach(btn => { btn.addEventListener('click', () => { const question = btn.getAttribute('data-question'); questionInput.value = question; askQuestion(); }); }); // Copy button copyButton.addEventListener('click', copyAnswer); // Refresh info refreshInfoBtn.addEventListener('click', loadCollectionInfo); } // Check Health async function checkHealth() { try { const response = await fetch(`${API_BASE}/health`); const data = await response.json(); if (response.ok) { statusBadge.classList.add('connected'); statusText.textContent = 'Connected'; } else { statusBadge.classList.remove('connected'); statusText.textContent = 'Disconnected'; } } catch (error) { statusBadge.classList.remove('connected'); statusText.textContent = 'Disconnected'; showToast('Cannot connect to backend. Please check if the backend is running.', 'error'); } } // File Upload Handlers function handleDragOver(e) { e.preventDefault(); uploadArea.classList.add('dragover'); } function handleDragLeave(e) { e.preventDefault(); uploadArea.classList.remove('dragover'); } function handleDrop(e) { e.preventDefault(); uploadArea.classList.remove('dragover'); const files = e.dataTransfer.files; if (files.length > 0) { handleFileUpload(files[0]); } } function handleFileSelect(e) { const file = e.target.files[0]; if (file) { handleFileUpload(file); } } async function handleFileUpload(file) { if (!file.name.match(/\.(xlsx|xls)$/i)) { showToast('Please upload an Excel file (.xlsx or .xls)', 'error'); return; } const formData = new FormData(); formData.append('file', file); uploadProgress.style.display = 'block'; uploadResult.style.display = 'none'; uploadStatus.textContent = 'Uploading...'; progressFill.style.width = '30%'; try { const response = await fetch(`${API_BASE}/upload`, { method: 'POST', body: formData }); progressFill.style.width = '100%'; if (response.ok) { const data = await response.json(); uploadResult.className = 'upload-result success'; uploadResult.innerHTML = ` <i class="fas fa-check-circle"></i> <strong>Success!</strong> ${data.message || 'File uploaded successfully'} <br><small>Processed ${data.chunks_processed || 0} chunks</small> `; uploadResult.style.display = 'block'; uploadStatus.textContent = 'Upload complete!'; showToast('File uploaded successfully!', 'success'); // Reload collection info setTimeout(() => { loadCollectionInfo(); uploadProgress.style.display = 'none'; }, 2000); } else { const errorData = await response.json(); uploadResult.className = 'upload-result error'; uploadResult.innerHTML = ` <i class="fas fa-exclamation-circle"></i> <strong>Error:</strong> ${errorData.message || errorData.error || 'Upload failed'} `; uploadResult.style.display = 'block'; uploadStatus.textContent = 'Upload failed'; showToast('Upload failed. Please try again.', 'error'); uploadProgress.style.display = 'none'; } } catch (error) { uploadResult.className = 'upload-result error'; uploadResult.innerHTML = ` <i class="fas fa-exclamation-circle"></i> <strong>Error:</strong> ${error.message} `; uploadResult.style.display = 'block'; uploadStatus.textContent = 'Upload failed'; showToast('Upload failed. Please check your connection.', 'error'); uploadProgress.style.display = 'none'; } } // Ask Question async function askQuestion() { const question = questionInput.value.trim(); if (!question) { showToast('Please enter a question', 'error'); return; } askButton.disabled = true; askButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Asking...'; answerSection.style.display = 'none'; showLoading(); try { const response = await fetch(`${API_BASE}/ask`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ question: question, n_results: 5 }) }); if (response.ok) { const data = await response.json(); // Display answer answerContent.innerHTML = formatAnswer(data.answer); // Display context if (data.context && data.context.length > 0) { contextItems.innerHTML = data.context.map((ctx, index) => ` <div class="context-item"> <strong>Context ${index + 1}:</strong> <p>${escapeHtml(ctx.substring(0, 500))}${ctx.length > 500 ? '...' : ''}</p> </div> `).join(''); } else { contextItems.innerHTML = '<p class="context-item">No context retrieved.</p>'; } answerSection.style.display = 'block'; answerSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); showToast('Answer retrieved successfully!', 'success'); } else { const errorData = await response.json(); answerContent.innerHTML = `<p style="color: var(--error-color);">Error: ${errorData.message || errorData.error || 'Failed to get answer'}</p>`; answerSection.style.display = 'block'; showToast('Failed to get answer. Please try again.', 'error'); } } catch (error) { answerContent.innerHTML = `<p style="color: var(--error-color);">Error: ${error.message}</p>`; answerSection.style.display = 'block'; showToast('Connection error. Please check your connection.', 'error'); } finally { askButton.disabled = false; askButton.innerHTML = '<i class="fas fa-paper-plane"></i> Ask'; hideLoading(); } } // Format Answer function formatAnswer(answer) { // Split by newlines and format const lines = answer.split('\n').filter(line => line.trim()); return lines.map(line => { line = line.trim(); if (line.startsWith('**') || line.match(/^[A-Z][a-z]+:/)) { return `<p><strong>${escapeHtml(line)}</strong></p>`; } return `<p>${escapeHtml(line)}</p>`; }).join(''); } // Copy Answer function copyAnswer() { const text = answerContent.innerText; navigator.clipboard.writeText(text).then(() => { copyButton.innerHTML = '<i class="fas fa-check"></i>'; showToast('Answer copied to clipboard!', 'success'); setTimeout(() => { copyButton.innerHTML = '<i class="fas fa-copy"></i>'; }, 2000); }); } // Load Collection Info async function loadCollectionInfo() { try { const response = await fetch(`${API_BASE}/info`); if (response.ok) { const data = await response.json(); docCount.textContent = data.document_count || 0; collectionName.textContent = data.collection_name || 'N/A'; collectionStatus.textContent = data.status || 'Unknown'; } } catch (error) { console.error('Failed to load collection info:', error); } } // Utility Functions function showLoading() { loadingOverlay.style.display = 'flex'; } function hideLoading() { loadingOverlay.style.display = 'none'; } function showToast(message, type = 'info') { const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.innerHTML = ` <i class="fas fa-${type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-circle' : 'info-circle'}"></i> <span>${message}</span> `; toastContainer.appendChild(toast); setTimeout(() => { toast.style.animation = 'slideIn 0.3s ease reverse'; setTimeout(() => { toast.remove(); }, 300); }, 3000); } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/chakradharkalle03-arch/MCP2'

If you have feedback or need assistance with the MCP directory API, please join our Discord server