aminskjen commited on
Commit
7017b8d
·
verified ·
1 Parent(s): e8e460c

Update static/js/script.js

Browse files
Files changed (1) hide show
  1. static/js/script.js +217 -87
static/js/script.js CHANGED
@@ -1,5 +1,3 @@
1
- // --- InkBoard JavaScript (Fixed) ---
2
-
3
  class InkBoard {
4
  constructor() {
5
  this.currentCreationId = null;
@@ -10,29 +8,28 @@ class InkBoard {
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');
38
  return;
@@ -48,25 +45,20 @@ class InkBoard {
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();
57
 
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,53 +70,62 @@ class InkBoard {
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,72 +135,201 @@ class InkBoard {
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();
 
 
 
1
  class InkBoard {
2
  constructor() {
3
  this.currentCreationId = null;
 
8
  init() {
9
  this.setupEventListeners();
10
  this.loadGallery();
11
+ this.showSection('create');
12
  }
13
 
14
  setupEventListeners() {
15
+ document.getElementById('scene-form').addEventListener('submit', (e) => {
16
  e.preventDefault();
17
  this.generateContent();
18
  });
19
 
20
+ document.getElementById('save-journal').addEventListener('click', () => {
21
  this.saveJournal();
22
  });
23
 
24
+ document.getElementById('journalModal').addEventListener('hidden.bs.modal', () => {
25
  this.currentCreationId = null;
26
  document.getElementById('journal-text').value = '';
27
  });
28
  }
29
 
30
  async generateContent() {
31
+ const sceneIdea = document.getElementById('scene-idea').value.trim();
32
+
 
33
  if (!sceneIdea) {
34
  this.showAlert('Please enter a scene idea', 'danger');
35
  return;
 
45
  body: JSON.stringify({ scene_idea: sceneIdea })
46
  });
47
 
 
 
 
 
 
48
  const data = await response.json();
49
 
50
  if (data.success) {
51
  this.displayResult(data);
52
  this.loadGallery();
53
+ document.getElementById('scene-idea').value = '';
54
+ this.showAlert('Story and image generated successfully!', 'success');
55
  } else {
56
  this.showAlert(data.error || 'Failed to generate content', 'danger');
57
  }
58
 
59
  } catch (error) {
60
  console.error('Error generating content:', error);
61
+ this.showAlert('Network error. Please try again.', 'danger');
62
  } finally {
63
  this.showLoading(false);
64
  this.setButtonLoading(false);
 
70
 
71
  const imageSection = data.image_url ? `
72
  <div class="col-lg-6 mb-4">
73
+ <div class="image-container">
74
+ <img src="${data.image_url}" alt="Generated Scene" class="img-fluid rounded shadow">
75
+ </div>
76
  </div>` : '';
77
 
78
  const storyColClass = data.image_url ? 'col-lg-6' : 'col-12';
79
+
80
  const downloadButton = data.image_url ? `
81
  <button class="btn btn-outline-success btn-sm" onclick="inkBoard.downloadImage('${data.image_url}')">
82
  <i class="fas fa-download me-1"></i> Download Image
83
  </button>` : '';
84
 
85
+ const resultHTML = `
86
+ <div class="col-12 mb-5">
87
+ <div class="card shadow-lg border-0">
88
+ <div class="card-body p-4">
89
+ <h4 class="card-title text-center mb-4"><i class="fas fa-sparkles me-2"></i>Your Creation</h4>
90
+ <div class="row">
91
+ ${imageSection}
92
+ <div class="${storyColClass}">
93
+ <div class="story-container">
94
+ <h5 class="mb-3"><i class="fas fa-book-open me-2"></i>Your Story</h5>
95
+ <p class="story-text">${data.story}</p>
96
+ <div class="mt-3">
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 Entry
99
+ </button>
100
+ ${downloadButton}
101
+ </div>
102
+ </div>
103
+ </div>
104
+ </div>
105
  </div>
106
  </div>
107
+ </div>`;
108
+
109
+ resultsSection.innerHTML = resultHTML;
110
  resultsSection.scrollIntoView({ behavior: 'smooth' });
111
  }
112
 
113
  async loadGallery() {
114
  try {
115
  const response = await fetch('/get_creations');
 
 
 
 
 
 
 
116
  const data = await response.json();
117
+
118
  const galleryGrid = document.getElementById('gallery-grid');
119
 
120
+ if (data.creations && data.creations.length > 0) {
121
+ const galleryHTML = data.creations.map(creation => this.createGalleryItem(creation)).join('');
122
+ galleryGrid.innerHTML = galleryHTML;
123
  } else {
124
  galleryGrid.innerHTML = `
125
+ <div class="col-12 text-center py-5">
126
+ <i class="fas fa-palette fa-3x text-muted mb-3"></i>
127
+ <h5 class="text-muted">No creations yet</h5>
128
+ <p class="text-muted">Start by describing a scene above!</p>
129
  </div>`;
130
  }
131
  } catch (error) {
 
135
 
136
  createGalleryItem(creation) {
137
  const journalEntry = creation.journal_entry ? `
138
+ <div class="journal-entry">
139
+ <small class="text-muted">
140
+ <i class="fas fa-journal-whills me-1"></i> Journal Entry:
141
+ </small>
142
+ <p class="mb-0 mt-1">${creation.journal_entry}</p>
143
+ </div>` : '';
144
+
145
+ const imageSection = creation.image_url ? `
146
+ <img src="${creation.image_url}" alt="Scene: ${creation.scene_idea}" loading="lazy">` : '';
147
+
148
+ const downloadButton = creation.image_url ? `
149
+ <button class="btn btn-outline-success btn-sm" onclick="inkBoard.downloadImage('${creation.image_url}')">
150
+ <i class="fas fa-download me-1"></i> Download
151
+ </button>` : '';
152
 
153
  return `
154
+ <div class="gallery-item">
155
+ ${imageSection}
156
+ <div class="gallery-item-content">
157
+ <div class="gallery-item-scene">
158
+ <i class="fas fa-quote-left me-1"></i> ${creation.scene_idea}
159
+ </div>
160
+ <div class="gallery-item-story">${creation.story}</div>
161
+ ${journalEntry}
162
+ <div class="gallery-item-actions">
163
+ <button class="btn btn-outline-primary btn-sm" onclick="inkBoard.openJournal('${creation.id}', '${creation.journal_entry || ''}')">
164
+ <i class="fas fa-journal-whills me-1"></i> ${creation.journal_entry ? 'Edit' : 'Add'} Journal
165
+ </button>
166
+ ${downloadButton}
167
+ </div>
168
+ </div>
169
  </div>`;
170
  }
171
 
172
+ openJournal(creationId, existingText = '') {
173
+ this.currentCreationId = creationId;
174
+ document.getElementById('journal-text').value = existingText;
175
+
176
  const modal = new bootstrap.Modal(document.getElementById('journalModal'));
177
  modal.show();
178
  }
179
 
180
  async saveJournal() {
181
+ if (!this.currentCreationId) {
182
+ this.showAlert('No creation selected', 'danger');
183
+ return;
184
+ }
185
 
186
+ const journalText = document.getElementById('journal-text').value.trim();
 
 
 
 
 
 
 
 
 
187
 
188
+ try {
189
+ const response = await fetch('/save_journal', {
190
+ method: 'POST',
191
+ headers: { 'Content-Type': 'application/json' },
192
+ body: JSON.stringify({
193
+ creation_id: this.currentCreationId,
194
+ journal_entry: journalText
195
+ })
196
+ });
197
 
198
+ const data = await response.json();
 
 
 
199
 
200
+ if (data.success) {
201
+ this.showAlert('Journal entry saved!', 'success');
202
+ this.loadGallery();
 
 
 
 
 
203
 
204
+ const modal = bootstrap.Modal.getInstance(document.getElementById('journalModal'));
205
+ modal.hide();
206
+ } else {
207
+ this.showAlert(data.error || 'Failed to save journal', 'danger');
208
+ }
209
+ } catch (error) {
210
+ console.error('Error saving journal:', error);
211
+ this.showAlert('Network error. Please try again.', 'danger');
212
+ }
213
  }
214
 
215
+ downloadImage(imageUrl) {
216
  const link = document.createElement('a');
217
+ link.href = imageUrl;
218
+ link.download = `inkboard-creation-${Date.now()}.png`;
219
+ link.target = '_blank';
220
  document.body.appendChild(link);
221
  link.click();
222
  document.body.removeChild(link);
223
+ this.showAlert('Image download started!', 'success');
224
+ }
225
+
226
+ showLoading(show) {
227
+ const loadingSection = document.getElementById('loading-section');
228
+ const resultsSection = document.getElementById('results-section');
229
+
230
+ if (show) {
231
+ loadingSection.classList.remove('d-none');
232
+ resultsSection.innerHTML = '';
233
+ } else {
234
+ loadingSection.classList.add('d-none');
235
+ }
236
+ }
237
+
238
+ setButtonLoading(loading) {
239
+ const btn = document.getElementById('generate-btn');
240
+ const btnText = btn.querySelector('.btn-text');
241
+ const spinner = btn.querySelector('.spinner-border');
242
+
243
+ if (loading) {
244
+ btn.classList.add('loading');
245
+ btn.disabled = true;
246
+ btnText.classList.add('d-none');
247
+ spinner.classList.remove('d-none');
248
+ } else {
249
+ btn.classList.remove('loading');
250
+ btn.disabled = false;
251
+ btnText.classList.remove('d-none');
252
+ spinner.classList.add('d-none');
253
+ }
254
+ }
255
+
256
+ showAlert(message, type) {
257
+ const existingAlert = document.querySelector('.alert');
258
+ if (existingAlert) existingAlert.remove();
259
+
260
+ const alert = document.createElement('div');
261
+ alert.className = `alert alert-${type} alert-dismissible fade show`;
262
+ alert.innerHTML = `${message}
263
+ <button type="button" class="btn-close" data-bs-dismiss="alert"></button>`;
264
+
265
+ const main = document.querySelector('main');
266
+ main.insertBefore(alert, main.firstChild);
267
+
268
+ setTimeout(() => {
269
+ if (alert.parentNode) alert.remove();
270
+ }, 5000);
271
+ }
272
+
273
+ showSection(sectionName) {
274
+ document.querySelectorAll('.dashboard-section').forEach(section => {
275
+ section.classList.add('d-none');
276
+ });
277
+
278
+ document.querySelectorAll('.btn-nav').forEach(btn => {
279
+ btn.classList.remove('active');
280
+ });
281
+
282
+ const targetSection = document.getElementById(`${sectionName}-section`);
283
+ if (targetSection) targetSection.classList.remove('d-none');
284
+
285
+ const buttonSelectors = {
286
+ 'create': 'Create Story',
287
+ 'gallery': 'Gallery',
288
+ 'journal': 'Journal'
289
+ };
290
+
291
+ document.querySelectorAll('.btn-nav').forEach(btn => {
292
+ if (btn.textContent.trim().includes(buttonSelectors[sectionName])) {
293
+ btn.classList.add('active');
294
+ }
295
+ });
296
+
297
+ this.currentSection = sectionName;
298
+
299
+ if (sectionName === 'gallery') {
300
+ this.loadGallery();
301
+ } else if (sectionName === 'journal') {
302
+ this.loadJournalEntries();
303
+ }
304
+ }
305
+
306
+ async loadJournalEntries() {
307
+ try {
308
+ const response = await fetch('/get_creations');
309
+ const data = await response.json();
310
+
311
+ const journalContainer = document.getElementById('journal-entries');
312
+ const entriesWithJournal = data.creations?.filter(creation => creation.journal_entry) || [];
313
+
314
+ if (entriesWithJournal.length > 0) {
315
+ const journalHTML = entriesWithJournal.map(creation => `
316
+ <div class="journal-entry-card mb-3">
317
+ <h5>${creation.scene_idea}</h5>
318
+ <p>${creation.journal_entry}</p>
319
+ </div>`).join('');
320
+ journalContainer.innerHTML = journalHTML;
321
+ } else {
322
+ journalContainer.innerHTML = `
323
+ <div class="col-12 text-center py-5">
324
+ <i class="fas fa-journal-whills fa-3x text-muted mb-3"></i>
325
+ <h5 class="text-muted">No journal entries yet</h5>
326
+ </div>`;
327
+ }
328
+ } catch (error) {
329
+ console.error('Error loading journal entries:', error);
330
+ }
331
  }
332
  }
333
 
334
+ // Initialize
335
  const inkBoard = new InkBoard();