Update static/js/script.js
Browse files- 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');
|
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 |
-
|
49 |
-
|
50 |
-
|
51 |
-
})
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
.
|
57 |
-
|
58 |
-
|
59 |
-
|
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();
|
72 |
-
document.getElementById('scene-idea').value = '';
|
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(
|
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 |
-
|
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
|
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
|
246 |
|
247 |
if (data.success) {
|
248 |
-
this.showAlert('Journal
|
249 |
-
this.loadGallery();
|
250 |
-
|
251 |
-
// Close modal
|
252 |
const modal = bootstrap.Modal.getInstance(document.getElementById('journalModal'));
|
253 |
modal.hide();
|
254 |
} else {
|
255 |
-
this.showAlert(
|
256 |
}
|
257 |
-
|
258 |
} catch (error) {
|
259 |
console.error('Error saving journal:', error);
|
260 |
-
this.showAlert('Network error.
|
261 |
}
|
262 |
}
|
263 |
|
264 |
-
downloadImage(
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
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 |
-
|
280 |
-
const
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
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 |
-
|
310 |
-
|
311 |
-
|
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 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
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 |
-
|
440 |
-
|
441 |
-
|
442 |
-
// You could add highlighting logic here
|
443 |
}
|
444 |
}
|
445 |
|
446 |
-
// Initialize
|
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();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|