inkboard7 / static /js /script.js
aminskjen's picture
Update static/js/script.js
7017b8d verified
class InkBoard {
constructor() {
this.currentCreationId = null;
this.currentSection = 'create';
this.init();
}
init() {
this.setupEventListeners();
this.loadGallery();
this.showSection('create');
}
setupEventListeners() {
document.getElementById('scene-form').addEventListener('submit', (e) => {
e.preventDefault();
this.generateContent();
});
document.getElementById('save-journal').addEventListener('click', () => {
this.saveJournal();
});
document.getElementById('journalModal').addEventListener('hidden.bs.modal', () => {
this.currentCreationId = null;
document.getElementById('journal-text').value = '';
});
}
async generateContent() {
const sceneIdea = document.getElementById('scene-idea').value.trim();
if (!sceneIdea) {
this.showAlert('Please enter a scene idea', 'danger');
return;
}
try {
this.showLoading(true);
this.setButtonLoading(true);
const response = await fetch('/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ scene_idea: sceneIdea })
});
const data = await response.json();
if (data.success) {
this.displayResult(data);
this.loadGallery();
document.getElementById('scene-idea').value = '';
this.showAlert('Story and image generated successfully!', 'success');
} else {
this.showAlert(data.error || 'Failed to generate content', 'danger');
}
} catch (error) {
console.error('Error generating content:', error);
this.showAlert('Network error. Please try again.', 'danger');
} finally {
this.showLoading(false);
this.setButtonLoading(false);
}
}
displayResult(data) {
const resultsSection = document.getElementById('results-section');
const imageSection = data.image_url ? `
<div class="col-lg-6 mb-4">
<div class="image-container">
<img src="${data.image_url}" alt="Generated Scene" class="img-fluid rounded shadow">
</div>
</div>` : '';
const storyColClass = data.image_url ? 'col-lg-6' : 'col-12';
const downloadButton = data.image_url ? `
<button class="btn btn-outline-success btn-sm" onclick="inkBoard.downloadImage('${data.image_url}')">
<i class="fas fa-download me-1"></i> Download Image
</button>` : '';
const resultHTML = `
<div class="col-12 mb-5">
<div class="card shadow-lg border-0">
<div class="card-body p-4">
<h4 class="card-title text-center mb-4"><i class="fas fa-sparkles me-2"></i>Your Creation</h4>
<div class="row">
${imageSection}
<div class="${storyColClass}">
<div class="story-container">
<h5 class="mb-3"><i class="fas fa-book-open me-2"></i>Your Story</h5>
<p class="story-text">${data.story}</p>
<div class="mt-3">
<button class="btn btn-outline-primary btn-sm me-2" onclick="inkBoard.openJournal('${data.creation_id}')">
<i class="fas fa-journal-whills me-1"></i>Add Journal Entry
</button>
${downloadButton}
</div>
</div>
</div>
</div>
</div>
</div>
</div>`;
resultsSection.innerHTML = resultHTML;
resultsSection.scrollIntoView({ behavior: 'smooth' });
}
async loadGallery() {
try {
const response = await fetch('/get_creations');
const data = await response.json();
const galleryGrid = document.getElementById('gallery-grid');
if (data.creations && data.creations.length > 0) {
const galleryHTML = data.creations.map(creation => this.createGalleryItem(creation)).join('');
galleryGrid.innerHTML = galleryHTML;
} else {
galleryGrid.innerHTML = `
<div class="col-12 text-center py-5">
<i class="fas fa-palette fa-3x text-muted mb-3"></i>
<h5 class="text-muted">No creations yet</h5>
<p class="text-muted">Start by describing a scene above!</p>
</div>`;
}
} catch (error) {
console.error('Error loading gallery:', error);
}
}
createGalleryItem(creation) {
const journalEntry = creation.journal_entry ? `
<div class="journal-entry">
<small class="text-muted">
<i class="fas fa-journal-whills me-1"></i> Journal Entry:
</small>
<p class="mb-0 mt-1">${creation.journal_entry}</p>
</div>` : '';
const imageSection = creation.image_url ? `
<img src="${creation.image_url}" alt="Scene: ${creation.scene_idea}" loading="lazy">` : '';
const downloadButton = creation.image_url ? `
<button class="btn btn-outline-success btn-sm" onclick="inkBoard.downloadImage('${creation.image_url}')">
<i class="fas fa-download me-1"></i> Download
</button>` : '';
return `
<div class="gallery-item">
${imageSection}
<div class="gallery-item-content">
<div class="gallery-item-scene">
<i class="fas fa-quote-left me-1"></i> ${creation.scene_idea}
</div>
<div class="gallery-item-story">${creation.story}</div>
${journalEntry}
<div class="gallery-item-actions">
<button class="btn btn-outline-primary btn-sm" onclick="inkBoard.openJournal('${creation.id}', '${creation.journal_entry || ''}')">
<i class="fas fa-journal-whills me-1"></i> ${creation.journal_entry ? 'Edit' : 'Add'} Journal
</button>
${downloadButton}
</div>
</div>
</div>`;
}
openJournal(creationId, existingText = '') {
this.currentCreationId = creationId;
document.getElementById('journal-text').value = existingText;
const modal = new bootstrap.Modal(document.getElementById('journalModal'));
modal.show();
}
async saveJournal() {
if (!this.currentCreationId) {
this.showAlert('No creation selected', 'danger');
return;
}
const journalText = document.getElementById('journal-text').value.trim();
try {
const response = await fetch('/save_journal', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
creation_id: this.currentCreationId,
journal_entry: journalText
})
});
const data = await response.json();
if (data.success) {
this.showAlert('Journal entry saved!', 'success');
this.loadGallery();
const modal = bootstrap.Modal.getInstance(document.getElementById('journalModal'));
modal.hide();
} else {
this.showAlert(data.error || 'Failed to save journal', 'danger');
}
} catch (error) {
console.error('Error saving journal:', error);
this.showAlert('Network error. Please try again.', 'danger');
}
}
downloadImage(imageUrl) {
const link = document.createElement('a');
link.href = imageUrl;
link.download = `inkboard-creation-${Date.now()}.png`;
link.target = '_blank';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
this.showAlert('Image download started!', 'success');
}
showLoading(show) {
const loadingSection = document.getElementById('loading-section');
const resultsSection = document.getElementById('results-section');
if (show) {
loadingSection.classList.remove('d-none');
resultsSection.innerHTML = '';
} else {
loadingSection.classList.add('d-none');
}
}
setButtonLoading(loading) {
const btn = document.getElementById('generate-btn');
const btnText = btn.querySelector('.btn-text');
const spinner = btn.querySelector('.spinner-border');
if (loading) {
btn.classList.add('loading');
btn.disabled = true;
btnText.classList.add('d-none');
spinner.classList.remove('d-none');
} else {
btn.classList.remove('loading');
btn.disabled = false;
btnText.classList.remove('d-none');
spinner.classList.add('d-none');
}
}
showAlert(message, type) {
const existingAlert = document.querySelector('.alert');
if (existingAlert) existingAlert.remove();
const alert = document.createElement('div');
alert.className = `alert alert-${type} alert-dismissible fade show`;
alert.innerHTML = `${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>`;
const main = document.querySelector('main');
main.insertBefore(alert, main.firstChild);
setTimeout(() => {
if (alert.parentNode) alert.remove();
}, 5000);
}
showSection(sectionName) {
document.querySelectorAll('.dashboard-section').forEach(section => {
section.classList.add('d-none');
});
document.querySelectorAll('.btn-nav').forEach(btn => {
btn.classList.remove('active');
});
const targetSection = document.getElementById(`${sectionName}-section`);
if (targetSection) targetSection.classList.remove('d-none');
const buttonSelectors = {
'create': 'Create Story',
'gallery': 'Gallery',
'journal': 'Journal'
};
document.querySelectorAll('.btn-nav').forEach(btn => {
if (btn.textContent.trim().includes(buttonSelectors[sectionName])) {
btn.classList.add('active');
}
});
this.currentSection = sectionName;
if (sectionName === 'gallery') {
this.loadGallery();
} else if (sectionName === 'journal') {
this.loadJournalEntries();
}
}
async loadJournalEntries() {
try {
const response = await fetch('/get_creations');
const data = await response.json();
const journalContainer = document.getElementById('journal-entries');
const entriesWithJournal = data.creations?.filter(creation => creation.journal_entry) || [];
if (entriesWithJournal.length > 0) {
const journalHTML = entriesWithJournal.map(creation => `
<div class="journal-entry-card mb-3">
<h5>${creation.scene_idea}</h5>
<p>${creation.journal_entry}</p>
</div>`).join('');
journalContainer.innerHTML = journalHTML;
} else {
journalContainer.innerHTML = `
<div class="col-12 text-center py-5">
<i class="fas fa-journal-whills fa-3x text-muted mb-3"></i>
<h5 class="text-muted">No journal entries yet</h5>
</div>`;
}
} catch (error) {
console.error('Error loading journal entries:', error);
}
}
}
// Initialize
const inkBoard = new InkBoard();