Spaces:
Sleeping
Sleeping
| // 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 = ` | |
| <h4>${categoryData.name}</h4> | |
| <p>${categoryData.description}</p> | |
| <p><strong>Style Guide:</strong> ${categoryData.style_guide}</p> | |
| <h4>Drawing Conventions:</h4> | |
| <div class="conventions-list"> | |
| ${categoryData.conventions.map(conv => ` | |
| <span class="convention-tag">${conv}</span> | |
| `).join('')} | |
| </div> | |
| <h4>Common Elements:</h4> | |
| <div class="elements-list"> | |
| ${categoryData.common_elements.map(elem => ` | |
| <span class="element-tag">${elem}</span> | |
| `).join('')} | |
| </div> | |
| `; | |
| 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 = '<option value="">Select Engineering Category (Optional)</option>'; | |
| // 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(); | |