// Get the current hostname (will work both locally and in Docker) const API_BASE = `${window.location.protocol}//${window.location.hostname}:8002`; // Interface switching function setupNavigation() { const navItems = document.querySelectorAll('.nav-item'); navItems.forEach(item => { item.addEventListener('click', () => { const mode = item.dataset.mode; // Update navigation state navItems.forEach(nav => nav.classList.remove('active')); item.classList.add('active'); // Update interface visibility document.querySelectorAll('.interface').forEach(interface => { interface.classList.remove('active'); }); document.getElementById(mode).classList.add('active'); }); }); } // Helper functions function setLoading(button, isLoading) { if (isLoading) { button.classList.add('loading'); button.disabled = true; } else { button.classList.remove('loading'); button.disabled = false; } } function showError(container, message) { const errorDiv = document.createElement('div'); errorDiv.className = 'error-message'; errorDiv.textContent = message || 'An error occurred. Please try again.'; container.innerHTML = ''; container.appendChild(errorDiv); updateResultsPanel(null); // Clear the results panel } function updateResultsPanel(textData) { const panel = document.getElementById('resultsPanel'); const mainContent = document.querySelector('.main-content'); if (!textData) { panel.classList.remove('visible'); mainContent.classList.remove('with-panel'); return; } panel.innerHTML = ''; // Clear previous content // Add title const title = document.createElement('h2'); title.textContent = 'Generated Information'; title.style.marginBottom = '1.5rem'; panel.appendChild(title); if (typeof textData === 'string') { // Handle legacy string data const p = document.createElement('p'); p.textContent = textData; panel.appendChild(p); } else { // Handle structured data Object.entries(textData).forEach(([section, content]) => { const sectionDiv = document.createElement('div'); sectionDiv.className = 'text-section'; const title = document.createElement('h3'); title.className = 'section-title'; title.textContent = section.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1) ).join(' '); const content_p = document.createElement('p'); content_p.textContent = content; sectionDiv.appendChild(title); sectionDiv.appendChild(content_p); panel.appendChild(sectionDiv); }); } panel.classList.add('visible'); mainContent.classList.add('with-panel'); } function createResultElement(item) { if (item.type === 'text') { // Update the panel with text data updateResultsPanel(item.data); return null; // Don't create an element in the main content area } else if (item.type === 'image') { const wrapper = document.createElement('div'); wrapper.className = 'image-wrapper'; const img = document.createElement('img'); img.src = item.data; img.addEventListener('load', () => wrapper.classList.add('loaded')); wrapper.appendChild(img); return wrapper; } return null; } // File input handling function setupFileInput() { const fileInput = document.getElementById('inputImage'); const dropZone = document.querySelector('.file-input-wrapper'); ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { dropZone.addEventListener(eventName, preventDefaults, false); }); function preventDefaults(e) { e.preventDefault(); e.stopPropagation(); } ['dragenter', 'dragover'].forEach(eventName => { dropZone.addEventListener(eventName, () => { dropZone.classList.add('highlight'); }); }); ['dragleave', 'drop'].forEach(eventName => { dropZone.addEventListener(eventName, () => { dropZone.classList.remove('highlight'); }); }); dropZone.addEventListener('drop', (e) => { const dt = e.dataTransfer; const files = dt.files; fileInput.files = files; updateFileLabel(files[0]?.name); }); fileInput.addEventListener('change', (e) => { updateFileLabel(e.target.files[0]?.name); }); } function updateFileLabel(filename) { const label = document.querySelector('label[for="inputImage"] span'); label.textContent = filename || 'Choose an image or drag & drop here'; } // Text to Image Generation document.getElementById('generate').addEventListener('click', async () => { const prompt = document.getElementById('prompt').value.trim(); const category = document.getElementById('category').value; const generateButton = document.getElementById('generate'); const resultsDiv = document.getElementById('results'); if (!prompt) { showError(resultsDiv, 'Please enter a description for the schematic.'); return; } resultsDiv.innerHTML = ''; setLoading(generateButton, true); try { const res = await fetch(`${API_BASE}/generate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt, category }) }); if (!res.ok) { throw new Error(`Server responded with ${res.status}`); } const data = await res.json(); const resultElements = data.results .map(createResultElement) .filter(element => element !== null); if (resultElements.length === 0) { showError(resultsDiv, 'No results generated. Please try again.'); return; } resultElements.forEach(element => resultsDiv.appendChild(element)); } catch (err) { console.error(err); showError(resultsDiv, 'Error generating schematic. Please try again.'); } finally { setLoading(generateButton, false); } }); // Image with Text Generation document.getElementById('generateWithImage').addEventListener('click', async () => { const fileInput = document.getElementById('inputImage'); const prompt = document.getElementById('imagePrompt').value.trim(); const generateButton = document.getElementById('generateWithImage'); const resultsDiv = document.getElementById('resultsImg'); if (!fileInput.files.length) { showError(resultsDiv, 'Please select an image file.'); return; } const file = fileInput.files[0]; if (!file.type.startsWith('image/')) { showError(resultsDiv, 'Please select a valid image file.'); return; } resultsDiv.innerHTML = ''; setLoading(generateButton, true); try { const base64 = await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = e => resolve(e.target.result); reader.onerror = reject; reader.readAsDataURL(file); }); const category = document.getElementById('imageCategory').value; const res = await fetch(`${API_BASE}/generate_with_image`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: prompt || '', // Send empty string if no prompt provided image: base64, category }) }); if (!res.ok) { throw new Error(`Server responded with ${res.status}`); } const data = await res.json(); const resultElements = data.results .map(createResultElement) .filter(element => element !== null); if (resultElements.length === 0) { showError(resultsDiv, 'No results generated. Please try again.'); return; } resultElements.forEach(element => resultsDiv.appendChild(element)); } catch (err) { console.error(err); showError(resultsDiv, 'Error generating modified schematic. Please try again.'); } finally { setLoading(generateButton, false); } }); // Category handling async function fetchCategories() { try { const res = await fetch(`${API_BASE}/categories`); if (!res.ok) throw new Error('Failed to fetch categories'); return await res.json(); } catch (error) { console.error('Error fetching categories:', error); return null; } } function updateCategoryInfo(categoryData, infoDiv) { if (!infoDiv) { console.error('Category info div not found'); return; } if (!categoryData) { infoDiv.innerHTML = ''; infoDiv.classList.remove('visible'); return; } const html = `

${categoryData.name}

${categoryData.description}

Style Guide: ${categoryData.style_guide}

Drawing Conventions:

${categoryData.conventions.map(conv => ` ${conv} `).join('')}

Common Elements:

${categoryData.common_elements.map(elem => ` ${elem} `).join('')}
`; infoDiv.innerHTML = html; infoDiv.classList.add('visible'); } async function setupCategories() { try { const categories = await fetchCategories(); if (!categories) return; // Setup for both category selects [ { selectId: 'category', infoId: 'categoryInfo' }, { selectId: 'imageCategory', infoId: 'imageCategoryInfo' } ].forEach(({ selectId, infoId }) => { const select = document.getElementById(selectId); if (!select) { console.error(`Select element with id ${selectId} not found`); return; } // Clear existing options select.innerHTML = ''; // Add category options Object.entries(categories).forEach(([key, data]) => { const option = document.createElement('option'); option.value = key; option.textContent = data.name; select.appendChild(option); }); // Add change event listener select.addEventListener('change', (e) => { const selectedCategory = e.target.value; const infoElement = document.getElementById(infoId); if (selectedCategory && categories[selectedCategory]) { updateCategoryInfo(categories[selectedCategory], infoElement); } else { updateCategoryInfo(null, infoElement); } }); }); } catch (error) { console.error('Error setting up categories:', error); } } // Initialize functionality setupNavigation(); setupFileInput(); setupCategories();