Update static/js/script.js
Browse files- static/js/script.js +82 -154
static/js/script.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
// InkBoard JavaScript
|
2 |
|
3 |
class InkBoard {
|
4 |
constructor() {
|
@@ -10,30 +10,28 @@ class InkBoard {
|
|
10 |
init() {
|
11 |
this.setupEventListeners();
|
12 |
this.loadGallery();
|
13 |
-
this.showSection('create');
|
14 |
}
|
15 |
|
16 |
setupEventListeners() {
|
17 |
-
|
18 |
-
document.getElementById('scene-form').addEventListener('submit', (e) => {
|
19 |
e.preventDefault();
|
20 |
this.generateContent();
|
21 |
});
|
22 |
|
23 |
-
|
24 |
-
document.getElementById('save-journal').addEventListener('click', () => {
|
25 |
this.saveJournal();
|
26 |
});
|
27 |
|
28 |
-
|
29 |
-
document.getElementById('journalModal').addEventListener('hidden.bs.modal', () => {
|
30 |
this.currentCreationId = null;
|
31 |
document.getElementById('journal-text').value = '';
|
32 |
});
|
33 |
}
|
34 |
|
35 |
async generateContent() {
|
36 |
-
const
|
|
|
37 |
|
38 |
if (!sceneIdea) {
|
39 |
this.showAlert('Please enter a scene idea', 'danger');
|
@@ -50,12 +48,9 @@ class InkBoard {
|
|
50 |
body: JSON.stringify({ scene_idea: sceneIdea })
|
51 |
});
|
52 |
|
53 |
-
const contentType = response.headers.get(
|
54 |
-
if (!contentType
|
55 |
-
|
56 |
-
console.error("Non-JSON response:", text);
|
57 |
-
this.showAlert("Server error or you're not logged in", "danger");
|
58 |
-
return;
|
59 |
}
|
60 |
|
61 |
const data = await response.json();
|
@@ -63,15 +58,15 @@ class InkBoard {
|
|
63 |
if (data.success) {
|
64 |
this.displayResult(data);
|
65 |
this.loadGallery();
|
66 |
-
|
67 |
-
this.showAlert('Story and image generated
|
68 |
} else {
|
69 |
this.showAlert(data.error || 'Failed to generate content', 'danger');
|
70 |
}
|
71 |
|
72 |
} catch (error) {
|
73 |
console.error('Error generating content:', error);
|
74 |
-
this.showAlert('Network error. Please try again.', 'danger');
|
75 |
} finally {
|
76 |
this.showLoading(false);
|
77 |
this.setButtonLoading(false);
|
@@ -83,70 +78,53 @@ class InkBoard {
|
|
83 |
|
84 |
const imageSection = data.image_url ? `
|
85 |
<div class="col-lg-6 mb-4">
|
86 |
-
<
|
87 |
-
<img src="${data.image_url}" alt="Generated Scene" class="img-fluid rounded shadow">
|
88 |
-
</div>
|
89 |
</div>` : '';
|
90 |
|
91 |
const storyColClass = data.image_url ? 'col-lg-6' : 'col-12';
|
92 |
-
|
93 |
const downloadButton = data.image_url ? `
|
94 |
<button class="btn btn-outline-success btn-sm" onclick="inkBoard.downloadImage('${data.image_url}')">
|
95 |
-
<i class="fas fa-download me-1"></i>
|
96 |
-
Download Image
|
97 |
</button>` : '';
|
98 |
|
99 |
-
|
100 |
-
<div class="
|
101 |
-
<
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
<div class="story-container">
|
111 |
-
<h5 class="mb-3">
|
112 |
-
<i class="fas fa-book-open me-2"></i>
|
113 |
-
Your Story
|
114 |
-
</h5>
|
115 |
-
<p class="story-text">${data.story}</p>
|
116 |
-
<div class="mt-3">
|
117 |
-
<button class="btn btn-outline-primary btn-sm me-2" onclick="inkBoard.openJournal('${data.creation_id}')">
|
118 |
-
<i class="fas fa-journal-whills me-1"></i>
|
119 |
-
Add Journal Entry
|
120 |
-
</button>
|
121 |
-
${downloadButton}
|
122 |
-
</div>
|
123 |
-
</div>
|
124 |
-
</div>
|
125 |
-
</div>
|
126 |
</div>
|
127 |
</div>
|
128 |
</div>
|
129 |
`;
|
130 |
-
|
131 |
-
resultsSection.innerHTML = resultHTML;
|
132 |
resultsSection.scrollIntoView({ behavior: 'smooth' });
|
133 |
}
|
134 |
|
135 |
async loadGallery() {
|
136 |
try {
|
137 |
const response = await fetch('/get_creations');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
138 |
const data = await response.json();
|
139 |
const galleryGrid = document.getElementById('gallery-grid');
|
140 |
|
141 |
-
if (data.creations
|
142 |
-
|
143 |
-
galleryGrid.innerHTML = galleryHTML;
|
144 |
} else {
|
145 |
galleryGrid.innerHTML = `
|
146 |
-
<div class="
|
147 |
-
<
|
148 |
-
<
|
149 |
-
<p class="text-muted">Start by describing a scene above!</p>
|
150 |
</div>`;
|
151 |
}
|
152 |
} catch (error) {
|
@@ -156,122 +134,72 @@ class InkBoard {
|
|
156 |
|
157 |
createGalleryItem(creation) {
|
158 |
const journalEntry = creation.journal_entry ? `
|
159 |
-
<div class="
|
160 |
-
<small class="text-muted">
|
161 |
-
<i class="fas fa-journal-whills me-1"></i>
|
162 |
-
Journal Entry:
|
163 |
-
</small>
|
164 |
-
<p class="mb-0 mt-1">${creation.journal_entry}</p>
|
165 |
-
</div>` : '';
|
166 |
-
|
167 |
-
const imageSection = creation.image_url ?
|
168 |
-
`<img src="${creation.image_url}" alt="Scene: ${creation.scene_idea}" loading="lazy">` : '';
|
169 |
-
|
170 |
-
const downloadButton = creation.image_url ?
|
171 |
-
`<button class="btn btn-outline-success btn-sm" onclick="inkBoard.downloadImage('${creation.image_url}')">
|
172 |
-
<i class="fas fa-download me-1"></i>
|
173 |
-
Download
|
174 |
-
</button>` : '';
|
175 |
|
176 |
return `
|
177 |
-
<div class="gallery-item">
|
178 |
-
|
179 |
-
<
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
${creation.story}
|
186 |
-
</div>
|
187 |
-
${journalEntry}
|
188 |
-
<div class="gallery-item-actions">
|
189 |
-
<button class="btn btn-outline-primary btn-sm" onclick="inkBoard.openJournal('${creation.id}', '${creation.journal_entry || ''}')">
|
190 |
-
<i class="fas fa-journal-whills me-1"></i>
|
191 |
-
${creation.journal_entry ? 'Edit' : 'Add'} Journal
|
192 |
-
</button>
|
193 |
-
${downloadButton}
|
194 |
-
</div>
|
195 |
-
</div>
|
196 |
</div>`;
|
197 |
}
|
198 |
|
199 |
-
openJournal(
|
200 |
-
this.currentCreationId =
|
201 |
-
document.getElementById('journal-text').value =
|
202 |
-
|
203 |
const modal = new bootstrap.Modal(document.getElementById('journalModal'));
|
204 |
modal.show();
|
205 |
}
|
206 |
|
207 |
async saveJournal() {
|
208 |
-
if (!this.currentCreationId)
|
209 |
-
this.showAlert('No creation selected', 'danger');
|
210 |
-
return;
|
211 |
-
}
|
212 |
|
213 |
-
const
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
})
|
223 |
-
});
|
224 |
-
|
225 |
-
const data = await res.json();
|
226 |
-
|
227 |
-
if (data.success) {
|
228 |
-
this.showAlert('Journal saved successfully!', 'success');
|
229 |
-
this.loadGallery();
|
230 |
-
const modal = bootstrap.Modal.getInstance(document.getElementById('journalModal'));
|
231 |
-
modal.hide();
|
232 |
-
} else {
|
233 |
-
this.showAlert('Failed to save journal.', 'danger');
|
234 |
-
}
|
235 |
-
} catch (error) {
|
236 |
-
console.error('Error saving journal:', error);
|
237 |
-
this.showAlert('Network error.', 'danger');
|
238 |
-
}
|
239 |
}
|
240 |
|
241 |
-
|
242 |
-
const
|
243 |
-
|
244 |
-
a.download = 'inkboard-image.png';
|
245 |
-
document.body.appendChild(a);
|
246 |
-
a.click();
|
247 |
-
document.body.removeChild(a);
|
248 |
}
|
249 |
|
250 |
-
|
251 |
-
const
|
252 |
-
|
253 |
-
<div class="alert alert-${type} alert-dismissible fade show" role="alert">
|
254 |
-
${message}
|
255 |
-
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
256 |
-
</div>`;
|
257 |
}
|
258 |
|
259 |
-
|
260 |
-
const
|
261 |
-
|
|
|
|
|
|
|
|
|
262 |
}
|
263 |
|
264 |
-
|
265 |
-
|
266 |
-
button.disabled = loading;
|
267 |
-
button.innerHTML = loading ? `<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Generating...` : `Generate`;
|
268 |
}
|
269 |
|
270 |
-
|
271 |
-
|
272 |
-
|
|
|
|
|
|
|
|
|
273 |
}
|
274 |
}
|
275 |
|
276 |
-
// Initialize
|
277 |
const inkBoard = new InkBoard();
|
|
|
1 |
+
// --- InkBoard JavaScript (Fixed) ---
|
2 |
|
3 |
class InkBoard {
|
4 |
constructor() {
|
|
|
10 |
init() {
|
11 |
this.setupEventListeners();
|
12 |
this.loadGallery();
|
13 |
+
this.showSection('create'); // Default
|
14 |
}
|
15 |
|
16 |
setupEventListeners() {
|
17 |
+
document.getElementById('scene-form')?.addEventListener('submit', (e) => {
|
|
|
18 |
e.preventDefault();
|
19 |
this.generateContent();
|
20 |
});
|
21 |
|
22 |
+
document.getElementById('save-journal')?.addEventListener('click', () => {
|
|
|
23 |
this.saveJournal();
|
24 |
});
|
25 |
|
26 |
+
document.getElementById('journalModal')?.addEventListener('hidden.bs.modal', () => {
|
|
|
27 |
this.currentCreationId = null;
|
28 |
document.getElementById('journal-text').value = '';
|
29 |
});
|
30 |
}
|
31 |
|
32 |
async generateContent() {
|
33 |
+
const sceneIdeaInput = document.getElementById('scene-idea');
|
34 |
+
const sceneIdea = sceneIdeaInput?.value.trim();
|
35 |
|
36 |
if (!sceneIdea) {
|
37 |
this.showAlert('Please enter a scene idea', 'danger');
|
|
|
48 |
body: JSON.stringify({ scene_idea: sceneIdea })
|
49 |
});
|
50 |
|
51 |
+
const contentType = response.headers.get('content-type') || '';
|
52 |
+
if (!contentType.includes('application/json')) {
|
53 |
+
throw new Error("Server didn't return JSON – are you logged in?");
|
|
|
|
|
|
|
54 |
}
|
55 |
|
56 |
const data = await response.json();
|
|
|
58 |
if (data.success) {
|
59 |
this.displayResult(data);
|
60 |
this.loadGallery();
|
61 |
+
sceneIdeaInput.value = '';
|
62 |
+
this.showAlert('✅ Story and image generated!', 'success');
|
63 |
} else {
|
64 |
this.showAlert(data.error || 'Failed to generate content', 'danger');
|
65 |
}
|
66 |
|
67 |
} catch (error) {
|
68 |
console.error('Error generating content:', error);
|
69 |
+
this.showAlert('Network or server error. Please try again.', 'danger');
|
70 |
} finally {
|
71 |
this.showLoading(false);
|
72 |
this.setButtonLoading(false);
|
|
|
78 |
|
79 |
const imageSection = data.image_url ? `
|
80 |
<div class="col-lg-6 mb-4">
|
81 |
+
<img src="${data.image_url}" alt="Generated Scene" class="img-fluid rounded shadow">
|
|
|
|
|
82 |
</div>` : '';
|
83 |
|
84 |
const storyColClass = data.image_url ? 'col-lg-6' : 'col-12';
|
|
|
85 |
const downloadButton = data.image_url ? `
|
86 |
<button class="btn btn-outline-success btn-sm" onclick="inkBoard.downloadImage('${data.image_url}')">
|
87 |
+
<i class="fas fa-download me-1"></i> Download Image
|
|
|
88 |
</button>` : '';
|
89 |
|
90 |
+
resultsSection.innerHTML = `
|
91 |
+
<div class="card shadow border-0 p-4">
|
92 |
+
<h4 class="mb-4">Your Creation</h4>
|
93 |
+
<div class="row">
|
94 |
+
${imageSection}
|
95 |
+
<div class="${storyColClass}">
|
96 |
+
<p>${data.story}</p>
|
97 |
+
<button class="btn btn-outline-primary btn-sm me-2" onclick="inkBoard.openJournal('${data.creation_id}')">
|
98 |
+
<i class="fas fa-journal-whills me-1"></i> Add Journal
|
99 |
+
</button>
|
100 |
+
${downloadButton}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
</div>
|
102 |
</div>
|
103 |
</div>
|
104 |
`;
|
|
|
|
|
105 |
resultsSection.scrollIntoView({ behavior: 'smooth' });
|
106 |
}
|
107 |
|
108 |
async loadGallery() {
|
109 |
try {
|
110 |
const response = await fetch('/get_creations');
|
111 |
+
|
112 |
+
const contentType = response.headers.get('content-type') || '';
|
113 |
+
if (!contentType.includes('application/json')) {
|
114 |
+
console.warn('Gallery load skipped — likely not logged in.');
|
115 |
+
return;
|
116 |
+
}
|
117 |
+
|
118 |
const data = await response.json();
|
119 |
const galleryGrid = document.getElementById('gallery-grid');
|
120 |
|
121 |
+
if (data.creations.length > 0) {
|
122 |
+
galleryGrid.innerHTML = data.creations.map(this.createGalleryItem).join('');
|
|
|
123 |
} else {
|
124 |
galleryGrid.innerHTML = `
|
125 |
+
<div class="text-center py-5 text-muted">
|
126 |
+
<h5>No creations yet</h5>
|
127 |
+
<p>Start by describing a scene above!</p>
|
|
|
128 |
</div>`;
|
129 |
}
|
130 |
} catch (error) {
|
|
|
134 |
|
135 |
createGalleryItem(creation) {
|
136 |
const journalEntry = creation.journal_entry ? `
|
137 |
+
<div class="mt-2"><strong>Journal:</strong> ${creation.journal_entry}</div>` : '';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
138 |
|
139 |
return `
|
140 |
+
<div class="gallery-item card shadow-sm p-3 mb-3">
|
141 |
+
<h6>${creation.scene_idea}</h6>
|
142 |
+
<p>${creation.story}</p>
|
143 |
+
${creation.image_url ? `<img src="${creation.image_url}" class="img-fluid">` : ''}
|
144 |
+
${journalEntry}
|
145 |
+
<button class="btn btn-sm btn-outline-primary mt-2" onclick="inkBoard.openJournal('${creation.id}', '${creation.journal_entry || ''}')">
|
146 |
+
${creation.journal_entry ? 'Edit' : 'Add'} Journal
|
147 |
+
</button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
</div>`;
|
149 |
}
|
150 |
|
151 |
+
openJournal(id, text = '') {
|
152 |
+
this.currentCreationId = id;
|
153 |
+
document.getElementById('journal-text').value = text;
|
|
|
154 |
const modal = new bootstrap.Modal(document.getElementById('journalModal'));
|
155 |
modal.show();
|
156 |
}
|
157 |
|
158 |
async saveJournal() {
|
159 |
+
if (!this.currentCreationId) return;
|
|
|
|
|
|
|
160 |
|
161 |
+
const text = document.getElementById('journal-text').value.trim();
|
162 |
+
await fetch('/save_journal', {
|
163 |
+
method: 'POST',
|
164 |
+
headers: { 'Content-Type': 'application/json' },
|
165 |
+
body: JSON.stringify({ creation_id: this.currentCreationId, journal_entry: text })
|
166 |
+
});
|
167 |
+
this.loadGallery();
|
168 |
+
const modal = bootstrap.Modal.getInstance(document.getElementById('journalModal'));
|
169 |
+
modal.hide();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
}
|
171 |
|
172 |
+
showLoading(state) {
|
173 |
+
const spinner = document.getElementById('loading-spinner');
|
174 |
+
if (spinner) spinner.style.display = state ? 'block' : 'none';
|
|
|
|
|
|
|
|
|
175 |
}
|
176 |
|
177 |
+
setButtonLoading(state) {
|
178 |
+
const btn = document.querySelector('#scene-form button[type="submit"]');
|
179 |
+
if (btn) btn.disabled = state;
|
|
|
|
|
|
|
|
|
180 |
}
|
181 |
|
182 |
+
showAlert(msg, type = 'info') {
|
183 |
+
const alertBox = document.getElementById('alert-box');
|
184 |
+
if (!alertBox) return;
|
185 |
+
alertBox.className = `alert alert-${type}`;
|
186 |
+
alertBox.innerText = msg;
|
187 |
+
alertBox.style.display = 'block';
|
188 |
+
setTimeout(() => alertBox.style.display = 'none', 5000);
|
189 |
}
|
190 |
|
191 |
+
showSection(name) {
|
192 |
+
this.currentSection = name;
|
|
|
|
|
193 |
}
|
194 |
|
195 |
+
downloadImage(url) {
|
196 |
+
const link = document.createElement('a');
|
197 |
+
link.href = url;
|
198 |
+
link.download = 'inkboard_image.png';
|
199 |
+
document.body.appendChild(link);
|
200 |
+
link.click();
|
201 |
+
document.body.removeChild(link);
|
202 |
}
|
203 |
}
|
204 |
|
|
|
205 |
const inkBoard = new InkBoard();
|