// Global state const appState = { currentModel: 'yolov11n', inputModality: 'image', inputSource: 'file', isLoading: false, results: [] }; // Initialize the application function initApp() { console.log('MedVision Model Viewer initialized'); // Load default model (YOLOv11n) loadModel('yolov11n'); // Set up event listeners setupEventListeners(); } // Load a model into the application function loadModel(modelName) { appState.isLoading = true; document.getElementById('loading-overlay').classList.remove('hidden'); console.log(`Loading model: ${modelName}`); // Simulate model loading setTimeout(() => { appState.currentModel = modelName; appState.isLoading = false; document.getElementById('loading-overlay').classList.add('hidden'); updateUI(); }, 1500); } // Run inference with the current model function runInference(inputData) { if (appState.isLoading) return; appState.isLoading = true; document.getElementById('loading-overlay').classList.remove('hidden'); console.log(`Running inference with ${appState.currentModel} on ${appState.inputModality} input`); // Simulate inference setTimeout(() => { // Mock results for demo purposes appState.results = generateMockResults(); appState.isLoading = false; document.getElementById('loading-overlay').classList.add('hidden'); updateUI(); renderResults(); }, 2000); } // Generate mock results for demo function generateMockResults() { const classes = ['tumor', 'organ', 'lesion', 'vessel', 'bone']; const results = []; for (let i = 0; i < 5; i++) { results.push({ class: classes[Math.floor(Math.random() * classes.length)], confidence: (Math.random() * 0.9 + 0.1).toFixed(2), bbox: { x: Math.floor(Math.random() * 100), y: Math.floor(Math.random() * 100), width: Math.floor(Math.random() * 30 + 10), height: Math.floor(Math.random() * 30 + 10) }, color: `#${Math.floor(Math.random()*16777215).toString(16)}` }); } return results; } // Update the UI based on current state function updateUI() { // Update model indicator document.querySelectorAll('[data-model-indicator]').forEach(el => { el.textContent = appState.currentModel; }); } // Render results to the results table function renderResults() { const tbody = document.querySelector('tbody'); if (appState.results.length === 0) { tbody.innerHTML = ` No results yet `; return; } tbody.innerHTML = appState.results.map(result => ` ${result.class} ${result.confidence} [${result.bbox.x}, ${result.bbox.y}, ${result.bbox.width}, ${result.bbox.height}] `).join(''); } // File upload handling document.getElementById('media-upload').addEventListener('change', (e) => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (event) => { const outputContainer = document.getElementById('output-container'); if (file.type.startsWith('image/')) { outputContainer.innerHTML = `Uploaded image`; } else if (file.type.startsWith('video/')) { outputContainer.innerHTML = ` `; } }; reader.readAsDataURL(file); } }); // Make file upload area clickable const fileUploadArea = document.querySelector('#file-upload-container .border-dashed'); fileUploadArea.addEventListener('click', () => { document.getElementById('media-upload').click(); }); // Model upload handling const modelUpload = document.getElementById('model-upload'); if (modelUpload) { modelUpload.addEventListener('change', (e) => { const file = e.target.files[0]; if (file) { console.log('Model file selected:', file.name); // Here you would typically validate and process the model file alert(`Model file selected: ${file.name}`); } }); // Make model upload area clickable const modelUploadArea = document.querySelector('#custom-model-upload .border-dashed'); if (modelUploadArea) { modelUploadArea.addEventListener('click', () => { modelUpload.click(); }); } // Handle model URL input const modelUrlInput = document.querySelector('#custom-model-upload input[type="text"]'); if (modelUrlInput) { modelUrlInput.addEventListener('change', (e) => { const url = e.target.value.trim(); if (url) { console.log('Model URL entered:', url); // Here you would typically validate and load the model from URL alert(`Model URL entered: ${url}`); } }); } } // Drag and drop for file upload const uploadArea = document.getElementById('file-upload-container'); uploadArea.addEventListener('dragover', (e) => { e.preventDefault(); uploadArea.classList.add('border-primary-500', 'bg-amber-50'); }); uploadArea.addEventListener('dragleave', () => { uploadArea.classList.remove('border-primary-500', 'bg-amber-50'); }); uploadArea.addEventListener('drop', (e) => { e.preventDefault(); uploadArea.classList.remove('border-primary-500', 'bg-amber-50'); const file = e.dataTransfer.files[0]; if (file) { document.getElementById('media-upload').files = e.dataTransfer.files; const event = new Event('change'); document.getElementById('media-upload').dispatchEvent(event); } }); // Drag and drop for model upload const modelUploadArea = document.querySelector('#custom-model-upload'); if (modelUploadArea) { modelUploadArea.addEventListener('dragover', (e) => { e.preventDefault(); const dashedBorder = modelUploadArea.querySelector('.border-dashed'); dashedBorder.classList.add('border-primary-500', 'bg-amber-50'); }); modelUploadArea.addEventListener('dragleave', (e) => { const dashedBorder = modelUploadArea.querySelector('.border-dashed'); dashedBorder.classList.remove('border-primary-500', 'bg-amber-50'); }); modelUploadArea.addEventListener('drop', (e) => { e.preventDefault(); const dashedBorder = modelUploadArea.querySelector('.border-dashed'); dashedBorder.classList.remove('border-primary-500', 'bg-amber-50'); const file = e.dataTransfer.files[0]; if (file) { document.getElementById('model-upload').files = e.dataTransfer.files; const event = new Event('change'); document.getElementById('model-upload').dispatchEvent(event); } }); } } // Initialize the app when the DOM is loaded document.addEventListener('DOMContentLoaded', initApp);