aminskjen commited on
Commit
ff7275e
·
verified ·
1 Parent(s): 21aecb6

Update static/js/script.js

Browse files
Files changed (1) hide show
  1. static/js/script.js +64 -268
static/js/script.js CHANGED
@@ -10,7 +10,7 @@ class InkBoard {
10
  init() {
11
  this.setupEventListeners();
12
  this.loadGallery();
13
- this.showSection('create'); // Default to create section
14
  }
15
 
16
  setupEventListeners() {
@@ -34,7 +34,7 @@ class InkBoard {
34
 
35
  async generateContent() {
36
  const sceneIdea = document.getElementById('scene-idea').value.trim();
37
-
38
  if (!sceneIdea) {
39
  this.showAlert('Please enter a scene idea', 'danger');
40
  return;
@@ -45,31 +45,25 @@ class InkBoard {
45
  this.setButtonLoading(true);
46
 
47
  const response = await fetch('/generate', {
48
- method: 'POST',
49
- headers: { 'Content-Type': 'application/json' },
50
- body: JSON.stringify({ scene_idea: userInput })
51
- })
52
- .then(res => {
53
- if (!res.ok) throw new Error("Server error");
54
- return res.json();
55
- })
56
- .then(data => {
57
- if (data.error) throw new Error(data.error);
58
- displayStory(data.story);
59
- displayImage(data.image_url);
60
- })
61
- .catch(err => {
62
- console.error("Fetch error:", err.message);
63
- alert("Oops! " + err.message);
64
- });
65
-
66
-
67
  const data = await response.json();
68
 
69
  if (data.success) {
70
  this.displayResult(data);
71
- this.loadGallery(); // Refresh gallery
72
- document.getElementById('scene-idea').value = ''; // Clear input
73
  this.showAlert('Story and image generated successfully!', 'success');
74
  } else {
75
  this.showAlert(data.error || 'Failed to generate content', 'danger');
@@ -86,27 +80,22 @@ class InkBoard {
86
 
87
  displayResult(data) {
88
  const resultsSection = document.getElementById('results-section');
89
-
90
- // Build image section only if image was generated
91
  const imageSection = data.image_url ? `
92
  <div class="col-lg-6 mb-4">
93
  <div class="image-container">
94
  <img src="${data.image_url}" alt="Generated Scene" class="img-fluid rounded shadow">
95
  </div>
96
- </div>
97
- ` : '';
98
-
99
- // Adjust story column width based on whether image exists
100
  const storyColClass = data.image_url ? 'col-lg-6' : 'col-12';
101
-
102
- // Build download button only if image exists
103
  const downloadButton = data.image_url ? `
104
  <button class="btn btn-outline-success btn-sm" onclick="inkBoard.downloadImage('${data.image_url}')">
105
  <i class="fas fa-download me-1"></i>
106
  Download Image
107
- </button>
108
- ` : '';
109
-
110
  const resultHTML = `
111
  <div class="col-12 mb-5">
112
  <div class="card shadow-lg border-0">
@@ -140,8 +129,6 @@ class InkBoard {
140
  `;
141
 
142
  resultsSection.innerHTML = resultHTML;
143
-
144
- // Smooth scroll to results
145
  resultsSection.scrollIntoView({ behavior: 'smooth' });
146
  }
147
 
@@ -149,11 +136,10 @@ class InkBoard {
149
  try {
150
  const response = await fetch('/get_creations');
151
  const data = await response.json();
152
-
153
  const galleryGrid = document.getElementById('gallery-grid');
154
-
155
  if (data.creations && data.creations.length > 0) {
156
- const galleryHTML = data.creations.map(creation => this.createGalleryItem(creation)).join('');
157
  galleryGrid.innerHTML = galleryHTML;
158
  } else {
159
  galleryGrid.innerHTML = `
@@ -161,8 +147,7 @@ class InkBoard {
161
  <i class="fas fa-palette fa-3x text-muted mb-3"></i>
162
  <h5 class="text-muted">No creations yet</h5>
163
  <p class="text-muted">Start by describing a scene above!</p>
164
- </div>
165
- `;
166
  }
167
  } catch (error) {
168
  console.error('Error loading gallery:', error);
@@ -170,8 +155,8 @@ class InkBoard {
170
  }
171
 
172
  createGalleryItem(creation) {
173
- const journalEntry = creation.journal_entry ?
174
- `<div class="journal-entry">
175
  <small class="text-muted">
176
  <i class="fas fa-journal-whills me-1"></i>
177
  Journal Entry:
@@ -179,11 +164,9 @@ class InkBoard {
179
  <p class="mb-0 mt-1">${creation.journal_entry}</p>
180
  </div>` : '';
181
 
182
- // Only include image section if image exists
183
  const imageSection = creation.image_url ?
184
  `<img src="${creation.image_url}" alt="Scene: ${creation.scene_idea}" loading="lazy">` : '';
185
-
186
- // Only include download button if image exists
187
  const downloadButton = creation.image_url ?
188
  `<button class="btn btn-outline-success btn-sm" onclick="inkBoard.downloadImage('${creation.image_url}')">
189
  <i class="fas fa-download me-1"></i>
@@ -210,14 +193,13 @@ class InkBoard {
210
  ${downloadButton}
211
  </div>
212
  </div>
213
- </div>
214
- `;
215
  }
216
 
217
  openJournal(creationId, existingText = '') {
218
  this.currentCreationId = creationId;
219
  document.getElementById('journal-text').value = existingText;
220
-
221
  const modal = new bootstrap.Modal(document.getElementById('journalModal'));
222
  modal.show();
223
  }
@@ -231,251 +213,65 @@ class InkBoard {
231
  const journalText = document.getElementById('journal-text').value.trim();
232
 
233
  try {
234
- const response = await fetch('/save_journal', {
235
  method: 'POST',
236
- headers: {
237
- 'Content-Type': 'application/json',
238
- },
239
  body: JSON.stringify({
240
  creation_id: this.currentCreationId,
241
  journal_entry: journalText
242
  })
243
  });
244
 
245
- const data = await response.json();
246
 
247
  if (data.success) {
248
- this.showAlert('Journal entry saved!', 'success');
249
- this.loadGallery(); // Refresh gallery
250
-
251
- // Close modal
252
  const modal = bootstrap.Modal.getInstance(document.getElementById('journalModal'));
253
  modal.hide();
254
  } else {
255
- this.showAlert(data.error || 'Failed to save journal', 'danger');
256
  }
257
-
258
  } catch (error) {
259
  console.error('Error saving journal:', error);
260
- this.showAlert('Network error. Please try again.', 'danger');
261
  }
262
  }
263
 
264
- downloadImage(imageUrl) {
265
- // Create a temporary link element to trigger download
266
- const link = document.createElement('a');
267
- link.href = imageUrl;
268
- link.download = `inkboard-creation-${Date.now()}.png`;
269
- link.target = '_blank';
270
-
271
- // Trigger download
272
- document.body.appendChild(link);
273
- link.click();
274
- document.body.removeChild(link);
275
-
276
- this.showAlert('Image download started!', 'success');
277
  }
278
 
279
- showLoading(show) {
280
- const loadingSection = document.getElementById('loading-section');
281
- const resultsSection = document.getElementById('results-section');
282
-
283
- if (show) {
284
- loadingSection.classList.remove('d-none');
285
- resultsSection.innerHTML = ''; // Clear previous results
286
- } else {
287
- loadingSection.classList.add('d-none');
288
- }
289
- }
290
-
291
- setButtonLoading(loading) {
292
- const btn = document.getElementById('generate-btn');
293
- const btnText = btn.querySelector('.btn-text');
294
- const spinner = btn.querySelector('.spinner-border');
295
-
296
- if (loading) {
297
- btn.classList.add('loading');
298
- btn.disabled = true;
299
- btnText.classList.add('d-none');
300
- spinner.classList.remove('d-none');
301
- } else {
302
- btn.classList.remove('loading');
303
- btn.disabled = false;
304
- btnText.classList.remove('d-none');
305
- spinner.classList.add('d-none');
306
- }
307
  }
308
 
309
- showAlert(message, type) {
310
- // Remove existing alerts
311
- const existingAlert = document.querySelector('.alert');
312
- if (existingAlert) {
313
- existingAlert.remove();
314
- }
315
-
316
- // Create new alert
317
- const alert = document.createElement('div');
318
- alert.className = `alert alert-${type} alert-dismissible fade show`;
319
- alert.innerHTML = `
320
- ${message}
321
- <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
322
- `;
323
-
324
- // Insert at the top of the main container
325
- const main = document.querySelector('main');
326
- main.insertBefore(alert, main.firstChild);
327
-
328
- // Auto-dismiss after 5 seconds
329
- setTimeout(() => {
330
- if (alert.parentNode) {
331
- alert.remove();
332
- }
333
- }, 5000);
334
  }
335
 
336
- showSection(sectionName) {
337
- // Hide all sections
338
- document.querySelectorAll('.dashboard-section').forEach(section => {
339
- section.classList.add('d-none');
340
- });
341
-
342
- // Remove active class from all nav buttons
343
- document.querySelectorAll('.btn-nav').forEach(btn => {
344
- btn.classList.remove('active');
345
- });
346
-
347
- // Show selected section
348
- const targetSection = document.getElementById(`${sectionName}-section`);
349
- if (targetSection) {
350
- targetSection.classList.remove('d-none');
351
- }
352
-
353
- // Add active class to the correct button
354
- const buttonSelectors = {
355
- 'create': 'Create Story',
356
- 'gallery': 'Gallery',
357
- 'journal': 'Journal'
358
- };
359
-
360
- document.querySelectorAll('.btn-nav').forEach(btn => {
361
- if (btn.textContent.trim().includes(buttonSelectors[sectionName])) {
362
- btn.classList.add('active');
363
- }
364
- });
365
-
366
- // Update current section
367
- this.currentSection = sectionName;
368
-
369
- // Load content based on section
370
- if (sectionName === 'gallery') {
371
- this.loadGallery();
372
- } else if (sectionName === 'journal') {
373
- this.loadJournalEntries();
374
- }
375
- }
376
-
377
- async loadJournalEntries() {
378
- try {
379
- const response = await fetch('/get_creations');
380
- const data = await response.json();
381
-
382
- const journalContainer = document.getElementById('journal-entries');
383
-
384
- // Filter creations that have journal entries
385
- const entriesWithJournal = data.creations?.filter(creation => creation.journal_entry) || [];
386
-
387
- if (entriesWithJournal.length > 0) {
388
- const journalHTML = entriesWithJournal.map(creation => this.createJournalEntry(creation)).join('');
389
- journalContainer.innerHTML = journalHTML;
390
- } else {
391
- journalContainer.innerHTML = `
392
- <div class="col-12 text-center py-5">
393
- <i class="fas fa-journal-whills fa-3x text-muted mb-3"></i>
394
- <h5 class="text-muted">No journal entries yet</h5>
395
- <p class="text-muted">Create a story and add journal entries to see them here!</p>
396
- </div>
397
- `;
398
- }
399
- } catch (error) {
400
- console.error('Error loading journal entries:', error);
401
- }
402
- }
403
-
404
- createJournalEntry(creation) {
405
- const date = new Date().toLocaleDateString();
406
- return `
407
- <div class="col-lg-6 col-xl-4 mb-4">
408
- <div class="card journal-card">
409
- <div class="card-body">
410
- <div class="journal-meta mb-2">
411
- <small class="text-muted">
412
- <i class="fas fa-calendar me-1"></i>
413
- ${date}
414
- </small>
415
- </div>
416
- <h6 class="card-title">
417
- <i class="fas fa-quote-left me-1"></i>
418
- ${creation.scene_idea}
419
- </h6>
420
- <div class="journal-entry-preview mb-3">
421
- <p class="card-text">${creation.journal_entry}</p>
422
- </div>
423
- <div class="d-flex justify-content-between align-items-center">
424
- <button class="btn btn-sm btn-outline-primary" onclick="inkBoard.openJournal('${creation.id}', '${creation.journal_entry}')">
425
- <i class="fas fa-edit me-1"></i>
426
- Edit
427
- </button>
428
- <button class="btn btn-sm btn-outline-secondary" onclick="inkBoard.viewFullStory('${creation.id}')">
429
- <i class="fas fa-eye me-1"></i>
430
- View Story
431
- </button>
432
- </div>
433
- </div>
434
- </div>
435
- </div>
436
- `;
437
  }
438
 
439
- viewFullStory(creationId) {
440
- // Switch to gallery and highlight the specific creation
441
- this.showSection('gallery');
442
- // You could add highlighting logic here
443
  }
444
  }
445
 
446
- // Initialize the application
447
- const inkBoard = new InkBoard();
448
-
449
- // Additional utility functions
450
- document.addEventListener('DOMContentLoaded', function() {
451
- // Add smooth scrolling for all anchor links
452
- document.querySelectorAll('a[href^="#"]').forEach(anchor => {
453
- anchor.addEventListener('click', function (e) {
454
- e.preventDefault();
455
- document.querySelector(this.getAttribute('href')).scrollIntoView({
456
- behavior: 'smooth'
457
- });
458
- });
459
- });
460
-
461
- // Add loading animation to images
462
- document.addEventListener('load', function(e) {
463
- if (e.target.tagName === 'IMG') {
464
- e.target.style.opacity = '0';
465
- e.target.style.transition = 'opacity 0.3s ease';
466
- setTimeout(() => {
467
- e.target.style.opacity = '1';
468
- }, 100);
469
- }
470
- }, true);
471
- });
472
-
473
- // Handle window resize for masonry grid
474
- window.addEventListener('resize', function() {
475
- // Trigger reflow for masonry grid
476
- const grid = document.getElementById('gallery-grid');
477
- if (grid) {
478
- grid.style.columnCount = '';
479
- grid.offsetHeight; // Force reflow
480
- }
481
- });
 
10
  init() {
11
  this.setupEventListeners();
12
  this.loadGallery();
13
+ this.showSection('create');
14
  }
15
 
16
  setupEventListeners() {
 
34
 
35
  async generateContent() {
36
  const sceneIdea = document.getElementById('scene-idea').value.trim();
37
+
38
  if (!sceneIdea) {
39
  this.showAlert('Please enter a scene idea', 'danger');
40
  return;
 
45
  this.setButtonLoading(true);
46
 
47
  const response = await fetch('/generate', {
48
+ method: 'POST',
49
+ headers: { 'Content-Type': 'application/json' },
50
+ body: JSON.stringify({ scene_idea: sceneIdea })
51
+ });
52
+
53
+ const contentType = response.headers.get("content-type");
54
+ if (!contentType || !contentType.includes("application/json")) {
55
+ const text = await response.text();
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();
62
 
63
  if (data.success) {
64
  this.displayResult(data);
65
+ this.loadGallery();
66
+ document.getElementById('scene-idea').value = '';
67
  this.showAlert('Story and image generated successfully!', 'success');
68
  } else {
69
  this.showAlert(data.error || 'Failed to generate content', 'danger');
 
80
 
81
  displayResult(data) {
82
  const resultsSection = document.getElementById('results-section');
83
+
 
84
  const imageSection = data.image_url ? `
85
  <div class="col-lg-6 mb-4">
86
  <div class="image-container">
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
  const resultHTML = `
100
  <div class="col-12 mb-5">
101
  <div class="card shadow-lg border-0">
 
129
  `;
130
 
131
  resultsSection.innerHTML = resultHTML;
 
 
132
  resultsSection.scrollIntoView({ behavior: 'smooth' });
133
  }
134
 
 
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 && data.creations.length > 0) {
142
+ const galleryHTML = data.creations.map(c => this.createGalleryItem(c)).join('');
143
  galleryGrid.innerHTML = galleryHTML;
144
  } else {
145
  galleryGrid.innerHTML = `
 
147
  <i class="fas fa-palette fa-3x text-muted mb-3"></i>
148
  <h5 class="text-muted">No creations yet</h5>
149
  <p class="text-muted">Start by describing a scene above!</p>
150
+ </div>`;
 
151
  }
152
  } catch (error) {
153
  console.error('Error loading gallery:', error);
 
155
  }
156
 
157
  createGalleryItem(creation) {
158
+ const journalEntry = creation.journal_entry ? `
159
+ <div class="journal-entry">
160
  <small class="text-muted">
161
  <i class="fas fa-journal-whills me-1"></i>
162
  Journal Entry:
 
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>
 
193
  ${downloadButton}
194
  </div>
195
  </div>
196
+ </div>`;
 
197
  }
198
 
199
  openJournal(creationId, existingText = '') {
200
  this.currentCreationId = creationId;
201
  document.getElementById('journal-text').value = existingText;
202
+
203
  const modal = new bootstrap.Modal(document.getElementById('journalModal'));
204
  modal.show();
205
  }
 
213
  const journalText = document.getElementById('journal-text').value.trim();
214
 
215
  try {
216
+ const res = await fetch('/save_journal', {
217
  method: 'POST',
218
+ headers: { 'Content-Type': 'application/json' },
 
 
219
  body: JSON.stringify({
220
  creation_id: this.currentCreationId,
221
  journal_entry: journalText
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
+ downloadImage(url) {
242
+ const a = document.createElement('a');
243
+ a.href = url;
244
+ a.download = 'inkboard-image.png';
245
+ document.body.appendChild(a);
246
+ a.click();
247
+ document.body.removeChild(a);
 
 
 
 
 
 
248
  }
249
 
250
+ showAlert(message, type = 'info') {
251
+ const alertBox = document.getElementById('alert-box');
252
+ alertBox.innerHTML = `
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
+ showLoading(state) {
260
+ const loader = document.getElementById('loading-spinner');
261
+ loader.style.display = state ? 'block' : 'none';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  }
263
 
264
+ setButtonLoading(loading) {
265
+ const button = document.getElementById('generate-btn');
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
+ showSection(section) {
271
+ this.currentSection = section;
272
+ // Add logic here if needed for tab switching
 
273
  }
274
  }
275
 
276
+ // Initialize
277
+ const inkBoard = new InkBoard();