samu's picture
1st
7c7ef49
// 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();