// 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 = `
`;
} 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);