@willwade/aac-processors 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/processors/gridset/password.js +12 -6
- package/dist/browser/processors/gridset/symbols.js +62 -19
- package/dist/browser/processors/gridsetProcessor.js +10 -3
- package/dist/browser/processors/obfProcessor.js +6 -4
- package/dist/browser/validation/gridsetValidator.js +3 -2
- package/dist/processors/gridset/password.js +12 -9
- package/dist/processors/gridset/symbols.js +63 -46
- package/dist/processors/gridsetProcessor.js +10 -3
- package/dist/processors/obfProcessor.js +6 -4
- package/dist/validation/gridsetValidator.js +3 -2
- package/docs/PAGESET_GETTING_STARTED.md +185 -0
- package/examples/vitedemo/QUICKSTART.md +1 -0
- package/examples/vitedemo/index.html +160 -5
- package/examples/vitedemo/src/main.ts +367 -15
- package/examples/vitedemo/vite.config.ts +5 -1
- package/package.json +5 -3
|
@@ -74,9 +74,9 @@ import {
|
|
|
74
74
|
GridsetProcessor,
|
|
75
75
|
ApplePanelsProcessor,
|
|
76
76
|
AstericsGridProcessor,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
AACTree,
|
|
78
|
+
AACPage,
|
|
79
|
+
AACButton
|
|
80
80
|
} from 'aac-processors';
|
|
81
81
|
|
|
82
82
|
// UI Elements
|
|
@@ -93,11 +93,38 @@ const results = document.getElementById('results') as HTMLElement;
|
|
|
93
93
|
const logPanel = document.getElementById('logPanel') as HTMLElement;
|
|
94
94
|
const testResults = document.getElementById('testResults') as HTMLElement;
|
|
95
95
|
const testList = document.getElementById('testList') as HTMLElement;
|
|
96
|
+
const tabButtons = document.querySelectorAll('.tab-btn') as NodeListOf<HTMLButtonElement>;
|
|
97
|
+
const inspectTab = document.getElementById('inspectTab') as HTMLElement;
|
|
98
|
+
const pagesetTab = document.getElementById('pagesetTab') as HTMLElement;
|
|
99
|
+
const templateSelect = document.getElementById('templateSelect') as HTMLSelectElement;
|
|
100
|
+
const formatSelect = document.getElementById('formatSelect') as HTMLSelectElement;
|
|
101
|
+
const createPagesetBtn = document.getElementById('createPagesetBtn') as HTMLButtonElement;
|
|
102
|
+
const previewPagesetBtn = document.getElementById('previewPagesetBtn') as HTMLButtonElement;
|
|
103
|
+
const convertToObfBtn = document.getElementById('convertToObfBtn') as HTMLButtonElement;
|
|
104
|
+
const convertToObzBtn = document.getElementById('convertToObzBtn') as HTMLButtonElement;
|
|
105
|
+
const conversionStatus = document.getElementById('conversionStatus') as HTMLElement;
|
|
106
|
+
const pagesetOutput = document.getElementById('pagesetOutput') as HTMLElement;
|
|
96
107
|
|
|
97
108
|
// State
|
|
98
109
|
let currentFile: File | null = null;
|
|
99
110
|
let currentProcessor: any = null;
|
|
100
111
|
let currentTree: AACTree | null = null;
|
|
112
|
+
let currentSourceLabel = 'pageset';
|
|
113
|
+
|
|
114
|
+
// Tabs
|
|
115
|
+
function setActiveTab(tabId: string) {
|
|
116
|
+
tabButtons.forEach((btn) => {
|
|
117
|
+
btn.classList.toggle('active', btn.dataset.tab === tabId);
|
|
118
|
+
});
|
|
119
|
+
inspectTab.classList.toggle('active', tabId === 'inspectTab');
|
|
120
|
+
pagesetTab.classList.toggle('active', tabId === 'pagesetTab');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
tabButtons.forEach((btn) => {
|
|
124
|
+
btn.addEventListener('click', () => {
|
|
125
|
+
setActiveTab(btn.dataset.tab || 'inspectTab');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
101
128
|
|
|
102
129
|
// Logging
|
|
103
130
|
function log(message: string, type: 'info' | 'success' | 'error' | 'warn' = 'info') {
|
|
@@ -109,6 +136,52 @@ function log(message: string, type: 'info' | 'success' | 'error' | 'warn' = 'inf
|
|
|
109
136
|
console.log(`[${type.toUpperCase()}]`, message);
|
|
110
137
|
}
|
|
111
138
|
|
|
139
|
+
function setConversionStatus(message: string, state: 'success' | 'warn' | 'info' = 'info') {
|
|
140
|
+
conversionStatus.textContent = message;
|
|
141
|
+
conversionStatus.classList.remove('success', 'warn');
|
|
142
|
+
if (state !== 'info') {
|
|
143
|
+
conversionStatus.classList.add(state);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function updateConvertButtons() {
|
|
148
|
+
const hasTree = !!currentTree;
|
|
149
|
+
convertToObfBtn.disabled = !hasTree;
|
|
150
|
+
convertToObzBtn.disabled = !hasTree;
|
|
151
|
+
if (!hasTree) {
|
|
152
|
+
setConversionStatus('No pageset loaded yet.', 'info');
|
|
153
|
+
} else {
|
|
154
|
+
setConversionStatus(`Ready to export: ${currentSourceLabel}`, 'success');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function updateStatsForTree(tree: AACTree, textCount?: number, loadTimeMs?: number) {
|
|
159
|
+
const pageCount = Object.keys(tree.pages).length;
|
|
160
|
+
const buttonCount = Object.values(tree.pages).reduce(
|
|
161
|
+
(sum: number, page: AACPage) => sum + page.buttons.length,
|
|
162
|
+
0
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
document.getElementById('pageCount')!.textContent = pageCount.toString();
|
|
166
|
+
document.getElementById('buttonCount')!.textContent = buttonCount.toString();
|
|
167
|
+
document.getElementById('textCount')!.textContent = (textCount ?? 0).toString();
|
|
168
|
+
document.getElementById('loadTime')!.textContent =
|
|
169
|
+
loadTimeMs !== undefined ? `${loadTimeMs.toFixed(0)}ms` : '—';
|
|
170
|
+
stats.style.display = 'grid';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function collectTextCount(tree: AACTree): number {
|
|
174
|
+
const texts = new Set<string>();
|
|
175
|
+
Object.values(tree.pages).forEach((page) => {
|
|
176
|
+
if (page.name) texts.add(page.name);
|
|
177
|
+
page.buttons.forEach((button) => {
|
|
178
|
+
if (button.label) texts.add(button.label);
|
|
179
|
+
if (button.message) texts.add(button.message);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
return texts.size;
|
|
183
|
+
}
|
|
184
|
+
|
|
112
185
|
// Get file extension
|
|
113
186
|
function getFileExtension(filename: string): string {
|
|
114
187
|
const match = filename.toLowerCase().match(/\.\w+$/);
|
|
@@ -146,6 +219,7 @@ function handleFile(file: File) {
|
|
|
146
219
|
fileDetails.textContent = `${file.name} • ${formatFileSize(file.size)}`;
|
|
147
220
|
fileInfo.style.display = 'block';
|
|
148
221
|
processBtn.disabled = false;
|
|
222
|
+
currentSourceLabel = file.name;
|
|
149
223
|
|
|
150
224
|
log(`Using processor: ${currentProcessor.constructor.name}`, 'success');
|
|
151
225
|
} catch (error) {
|
|
@@ -213,22 +287,13 @@ processBtn.addEventListener('click', async () => {
|
|
|
213
287
|
log(`Extracted ${texts.length} texts`, 'success');
|
|
214
288
|
|
|
215
289
|
// Update stats
|
|
216
|
-
|
|
217
|
-
const buttonCount = Object.values(currentTree.pages).reduce(
|
|
218
|
-
(sum: number, page: AACPage) => sum + page.buttons.length,
|
|
219
|
-
0
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
document.getElementById('pageCount')!.textContent = pageCount.toString();
|
|
223
|
-
document.getElementById('buttonCount')!.textContent = buttonCount.toString();
|
|
224
|
-
document.getElementById('textCount')!.textContent = texts.length.toString();
|
|
225
|
-
document.getElementById('loadTime')!.textContent = `${loadTime.toFixed(0)}ms`;
|
|
226
|
-
stats.style.display = 'grid';
|
|
290
|
+
updateStatsForTree(currentTree, texts.length, loadTime);
|
|
227
291
|
|
|
228
292
|
// Display results
|
|
229
293
|
displayResults(currentTree);
|
|
294
|
+
updateConvertButtons();
|
|
230
295
|
|
|
231
|
-
log(`✅ Successfully processed ${
|
|
296
|
+
log(`✅ Successfully processed ${Object.keys(currentTree.pages).length} pages`, 'success');
|
|
232
297
|
} catch (error) {
|
|
233
298
|
const errorMsg = (error as Error).message;
|
|
234
299
|
log(`❌ Error: ${errorMsg}`, 'error');
|
|
@@ -343,12 +408,299 @@ clearBtn.addEventListener('click', () => {
|
|
|
343
408
|
currentFile = null;
|
|
344
409
|
currentProcessor = null;
|
|
345
410
|
currentTree = null;
|
|
411
|
+
currentSourceLabel = 'pageset';
|
|
346
412
|
fileInput.value = '';
|
|
347
413
|
fileInfo.style.display = 'none';
|
|
348
414
|
stats.style.display = 'none';
|
|
349
415
|
results.innerHTML = '<p style="color: #999; text-align: center; padding: 40px;">Load a file to see its contents here</p>';
|
|
350
416
|
testResults.style.display = 'none';
|
|
351
417
|
logPanel.innerHTML = '<div class="log-entry log-info">Cleared. Ready to process files...</div>';
|
|
418
|
+
pagesetOutput.textContent = 'Generate or convert a pageset to preview the output JSON.';
|
|
419
|
+
updateConvertButtons();
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
function sanitizeFilename(name: string): string {
|
|
423
|
+
return name
|
|
424
|
+
.toLowerCase()
|
|
425
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
426
|
+
.replace(/(^-|-$)/g, '') || 'pageset';
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function buildSampleTree(template: string): AACTree {
|
|
430
|
+
const tree = new AACTree();
|
|
431
|
+
tree.metadata = {
|
|
432
|
+
name: template === 'home' ? 'Home & Core Demo' : 'Starter Demo',
|
|
433
|
+
description: 'Generated in the AAC Processors browser demo',
|
|
434
|
+
locale: 'en',
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
if (template === 'home') {
|
|
438
|
+
const hello = new AACButton({ id: 'hello', label: 'Hello', message: 'Hello', action: { type: 'SPEAK' } });
|
|
439
|
+
const want = new AACButton({ id: 'want', label: 'I want', message: 'I want', action: { type: 'SPEAK' } });
|
|
440
|
+
const help = new AACButton({ id: 'help', label: 'Help', message: 'Help', action: { type: 'SPEAK' } });
|
|
441
|
+
const more = new AACButton({
|
|
442
|
+
id: 'more',
|
|
443
|
+
label: 'More',
|
|
444
|
+
targetPageId: 'core',
|
|
445
|
+
action: { type: 'NAVIGATE', targetPageId: 'core' },
|
|
446
|
+
});
|
|
447
|
+
const yes = new AACButton({ id: 'yes', label: 'Yes', message: 'Yes', action: { type: 'SPEAK' } });
|
|
448
|
+
const no = new AACButton({ id: 'no', label: 'No', message: 'No', action: { type: 'SPEAK' } });
|
|
449
|
+
const stop = new AACButton({ id: 'stop', label: 'Stop', message: 'Stop', action: { type: 'SPEAK' } });
|
|
450
|
+
const go = new AACButton({ id: 'go', label: 'Go', message: 'Go', action: { type: 'SPEAK' } });
|
|
451
|
+
const food = new AACButton({
|
|
452
|
+
id: 'food',
|
|
453
|
+
label: 'Food',
|
|
454
|
+
targetPageId: 'food',
|
|
455
|
+
action: { type: 'NAVIGATE', targetPageId: 'food' },
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
const homePage = new AACPage({
|
|
459
|
+
id: 'home',
|
|
460
|
+
name: 'Home',
|
|
461
|
+
buttons: [hello, want, help, more, yes, no, stop, go, food],
|
|
462
|
+
grid: [
|
|
463
|
+
[hello, want, help],
|
|
464
|
+
[more, yes, no],
|
|
465
|
+
[stop, go, food],
|
|
466
|
+
],
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
const hungry = new AACButton({ id: 'hungry', label: 'Hungry', message: 'I am hungry', action: { type: 'SPEAK' } });
|
|
470
|
+
const drink = new AACButton({ id: 'drink', label: 'Drink', message: 'I want a drink', action: { type: 'SPEAK' } });
|
|
471
|
+
const snack = new AACButton({ id: 'snack', label: 'Snack', message: 'Snack', action: { type: 'SPEAK' } });
|
|
472
|
+
const backFood = new AACButton({
|
|
473
|
+
id: 'back-food',
|
|
474
|
+
label: 'Back',
|
|
475
|
+
targetPageId: 'home',
|
|
476
|
+
action: { type: 'NAVIGATE', targetPageId: 'home' },
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
const foodPage = new AACPage({
|
|
480
|
+
id: 'food',
|
|
481
|
+
name: 'Food',
|
|
482
|
+
buttons: [hungry, drink, snack, backFood],
|
|
483
|
+
grid: [
|
|
484
|
+
[hungry, drink],
|
|
485
|
+
[snack, backFood],
|
|
486
|
+
],
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
const coreYes = new AACButton({ id: 'core-yes', label: 'Yes', message: 'Yes', action: { type: 'SPEAK' } });
|
|
490
|
+
const coreNo = new AACButton({ id: 'core-no', label: 'No', message: 'No', action: { type: 'SPEAK' } });
|
|
491
|
+
const coreStop = new AACButton({ id: 'core-stop', label: 'Stop', message: 'Stop', action: { type: 'SPEAK' } });
|
|
492
|
+
const coreGo = new AACButton({ id: 'core-go', label: 'Go', message: 'Go', action: { type: 'SPEAK' } });
|
|
493
|
+
const backCore = new AACButton({
|
|
494
|
+
id: 'back-core',
|
|
495
|
+
label: 'Back',
|
|
496
|
+
targetPageId: 'home',
|
|
497
|
+
action: { type: 'NAVIGATE', targetPageId: 'home' },
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
const corePage = new AACPage({
|
|
501
|
+
id: 'core',
|
|
502
|
+
name: 'Core Words',
|
|
503
|
+
buttons: [coreYes, coreNo, coreStop, coreGo, backCore],
|
|
504
|
+
grid: [
|
|
505
|
+
[coreYes, coreNo],
|
|
506
|
+
[coreStop, coreGo],
|
|
507
|
+
[backCore, null],
|
|
508
|
+
],
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
tree.addPage(homePage);
|
|
512
|
+
tree.addPage(corePage);
|
|
513
|
+
tree.addPage(foodPage);
|
|
514
|
+
tree.rootId = 'home';
|
|
515
|
+
return tree;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const hello = new AACButton({ id: 'hello', label: 'Hello', message: 'Hello', action: { type: 'SPEAK' } });
|
|
519
|
+
const thanks = new AACButton({ id: 'thanks', label: 'Thanks', message: 'Thank you', action: { type: 'SPEAK' } });
|
|
520
|
+
const yes = new AACButton({ id: 'yes', label: 'Yes', message: 'Yes', action: { type: 'SPEAK' } });
|
|
521
|
+
const more = new AACButton({
|
|
522
|
+
id: 'more',
|
|
523
|
+
label: 'Feelings',
|
|
524
|
+
targetPageId: 'feelings',
|
|
525
|
+
action: { type: 'NAVIGATE', targetPageId: 'feelings' },
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
const homePage = new AACPage({
|
|
529
|
+
id: 'home',
|
|
530
|
+
name: 'Starter',
|
|
531
|
+
buttons: [hello, thanks, yes, more],
|
|
532
|
+
grid: [
|
|
533
|
+
[hello, thanks],
|
|
534
|
+
[yes, more],
|
|
535
|
+
],
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
const happy = new AACButton({ id: 'happy', label: 'Happy', message: 'I feel happy', action: { type: 'SPEAK' } });
|
|
539
|
+
const sad = new AACButton({ id: 'sad', label: 'Sad', message: 'I feel sad', action: { type: 'SPEAK' } });
|
|
540
|
+
const back = new AACButton({
|
|
541
|
+
id: 'back',
|
|
542
|
+
label: 'Back',
|
|
543
|
+
targetPageId: 'home',
|
|
544
|
+
action: { type: 'NAVIGATE', targetPageId: 'home' },
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
const feelingsPage = new AACPage({
|
|
548
|
+
id: 'feelings',
|
|
549
|
+
name: 'Feelings',
|
|
550
|
+
buttons: [happy, sad, back],
|
|
551
|
+
grid: [
|
|
552
|
+
[happy, sad],
|
|
553
|
+
[back, null],
|
|
554
|
+
],
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
tree.addPage(homePage);
|
|
558
|
+
tree.addPage(feelingsPage);
|
|
559
|
+
tree.rootId = 'home';
|
|
560
|
+
return tree;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function buildFallbackObfBoard(page: AACPage, metadata?: AACTree['metadata']) {
|
|
564
|
+
const rows = page.grid.length || 1;
|
|
565
|
+
const columns = page.grid.reduce((max, row) => Math.max(max, row.length), 0) || page.buttons.length;
|
|
566
|
+
const order: (string | null)[][] = [];
|
|
567
|
+
const positions = new Map<string, number>();
|
|
568
|
+
|
|
569
|
+
if (page.grid.length) {
|
|
570
|
+
page.grid.forEach((row, rowIndex) => {
|
|
571
|
+
const orderRow: (string | null)[] = [];
|
|
572
|
+
for (let colIndex = 0; colIndex < columns; colIndex++) {
|
|
573
|
+
const cell = row[colIndex] || null;
|
|
574
|
+
if (cell) {
|
|
575
|
+
const id = String(cell.id ?? '');
|
|
576
|
+
orderRow.push(id);
|
|
577
|
+
positions.set(id, rowIndex * columns + colIndex);
|
|
578
|
+
} else {
|
|
579
|
+
orderRow.push(null);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
order.push(orderRow);
|
|
583
|
+
});
|
|
584
|
+
} else {
|
|
585
|
+
const fallbackRow = page.buttons.map((button, index) => {
|
|
586
|
+
const id = String(button.id ?? '');
|
|
587
|
+
positions.set(id, index);
|
|
588
|
+
return id;
|
|
589
|
+
});
|
|
590
|
+
order.push(fallbackRow);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
return {
|
|
594
|
+
format: 'open-board-0.1',
|
|
595
|
+
id: page.id,
|
|
596
|
+
locale: metadata?.locale || page.locale || 'en',
|
|
597
|
+
name: page.name || metadata?.name || 'Board',
|
|
598
|
+
description_html: page.descriptionHtml || metadata?.description || '',
|
|
599
|
+
grid: { rows, columns, order },
|
|
600
|
+
buttons: page.buttons.map((button) => ({
|
|
601
|
+
id: button.id,
|
|
602
|
+
label: button.label,
|
|
603
|
+
vocalization: button.message || button.label,
|
|
604
|
+
load_board: button.targetPageId ? { path: button.targetPageId } : undefined,
|
|
605
|
+
box_id: positions.get(String(button.id ?? '')),
|
|
606
|
+
background_color: button.style?.backgroundColor,
|
|
607
|
+
border_color: button.style?.borderColor,
|
|
608
|
+
})),
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
async function buildObfExport(tree: AACTree, format: 'obf' | 'obz') {
|
|
613
|
+
const obfProcessor = new ObfProcessor();
|
|
614
|
+
const obfInternal = obfProcessor as ObfProcessor & {
|
|
615
|
+
createObfBoardFromPage?: (page: AACPage, fallbackName: string, metadata?: AACTree['metadata']) => any;
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
const boards = Object.values(tree.pages).map((page) => ({
|
|
619
|
+
pageId: page.id,
|
|
620
|
+
board: obfInternal.createObfBoardFromPage
|
|
621
|
+
? obfInternal.createObfBoardFromPage(page, 'Board', tree.metadata)
|
|
622
|
+
: buildFallbackObfBoard(page, tree.metadata),
|
|
623
|
+
}));
|
|
624
|
+
|
|
625
|
+
if (format === 'obf') {
|
|
626
|
+
const rootPage = tree.rootId ? tree.getPage(tree.rootId) : Object.values(tree.pages)[0];
|
|
627
|
+
const board =
|
|
628
|
+
boards.find((entry) => entry.pageId === rootPage?.id)?.board ?? boards[0]?.board ?? {};
|
|
629
|
+
const json = JSON.stringify(board, null, 2);
|
|
630
|
+
return { filename: `${sanitizeFilename(tree.metadata?.name || 'pageset')}.obf`, data: json };
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const module = await import('jszip');
|
|
634
|
+
const JSZip = module.default || module;
|
|
635
|
+
const zip = new JSZip();
|
|
636
|
+
boards.forEach((entry) => {
|
|
637
|
+
zip.file(`${entry.pageId}.obf`, JSON.stringify(entry.board, null, 2));
|
|
638
|
+
});
|
|
639
|
+
const zipData = await zip.generateAsync({ type: 'uint8array' });
|
|
640
|
+
return { filename: `${sanitizeFilename(tree.metadata?.name || 'pageset')}.obz`, data: zipData };
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function triggerDownload(data: Uint8Array | string, filename: string, mime: string) {
|
|
644
|
+
const blob = new Blob([data], { type: mime });
|
|
645
|
+
const url = URL.createObjectURL(blob);
|
|
646
|
+
const a = document.createElement('a');
|
|
647
|
+
a.href = url;
|
|
648
|
+
a.download = filename;
|
|
649
|
+
a.click();
|
|
650
|
+
URL.revokeObjectURL(url);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
createPagesetBtn.addEventListener('click', async () => {
|
|
654
|
+
const template = templateSelect.value;
|
|
655
|
+
const format = formatSelect.value === 'obz' ? 'obz' : 'obf';
|
|
656
|
+
const tree = buildSampleTree(template);
|
|
657
|
+
currentTree = tree;
|
|
658
|
+
currentSourceLabel = `${tree.metadata?.name || 'sample pageset'}`;
|
|
659
|
+
updateConvertButtons();
|
|
660
|
+
|
|
661
|
+
const exportData = await buildObfExport(tree, format);
|
|
662
|
+
const isObf = typeof exportData.data === 'string';
|
|
663
|
+
triggerDownload(
|
|
664
|
+
exportData.data,
|
|
665
|
+
exportData.filename,
|
|
666
|
+
isObf ? 'application/json' : 'application/zip'
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
pagesetOutput.textContent = isObf
|
|
670
|
+
? exportData.data
|
|
671
|
+
: `Generated OBZ with ${Object.keys(tree.pages).length} boards.`;
|
|
672
|
+
|
|
673
|
+
log(`Created sample pageset and exported ${exportData.filename}`, 'success');
|
|
674
|
+
setConversionStatus(`Exported ${exportData.filename}`, 'success');
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
previewPagesetBtn.addEventListener('click', () => {
|
|
678
|
+
const tree = buildSampleTree(templateSelect.value);
|
|
679
|
+
currentTree = tree;
|
|
680
|
+
currentSourceLabel = `${tree.metadata?.name || 'sample pageset'}`;
|
|
681
|
+
displayResults(tree);
|
|
682
|
+
updateStatsForTree(tree, collectTextCount(tree));
|
|
683
|
+
updateConvertButtons();
|
|
684
|
+
setActiveTab('inspectTab');
|
|
685
|
+
log('Previewing sample pageset in viewer', 'info');
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
convertToObfBtn.addEventListener('click', async () => {
|
|
689
|
+
if (!currentTree) return;
|
|
690
|
+
const exportData = await buildObfExport(currentTree, 'obf');
|
|
691
|
+
triggerDownload(exportData.data, exportData.filename, 'application/json');
|
|
692
|
+
pagesetOutput.textContent = exportData.data as string;
|
|
693
|
+
log(`Converted ${currentSourceLabel} to ${exportData.filename}`, 'success');
|
|
694
|
+
setConversionStatus(`Exported ${exportData.filename}`, 'success');
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
convertToObzBtn.addEventListener('click', async () => {
|
|
698
|
+
if (!currentTree) return;
|
|
699
|
+
const exportData = await buildObfExport(currentTree, 'obz');
|
|
700
|
+
triggerDownload(exportData.data, exportData.filename, 'application/zip');
|
|
701
|
+
pagesetOutput.textContent = `Generated OBZ with ${Object.keys(currentTree.pages).length} boards.`;
|
|
702
|
+
log(`Converted ${currentSourceLabel} to ${exportData.filename}`, 'success');
|
|
703
|
+
setConversionStatus(`Exported ${exportData.filename}`, 'success');
|
|
352
704
|
});
|
|
353
705
|
|
|
354
706
|
// Run compatibility tests
|
|
@@ -4,7 +4,11 @@ import path from 'path';
|
|
|
4
4
|
export default defineConfig({
|
|
5
5
|
resolve: {
|
|
6
6
|
alias: {
|
|
7
|
-
'aac-processors': path.resolve(__dirname, '../../src/index.browser.ts')
|
|
7
|
+
'aac-processors': path.resolve(__dirname, '../../src/index.browser.ts'),
|
|
8
|
+
stream: path.resolve(__dirname, 'node_modules/stream-browserify'),
|
|
9
|
+
events: path.resolve(__dirname, 'node_modules/events'),
|
|
10
|
+
timers: path.resolve(__dirname, 'node_modules/timers-browserify'),
|
|
11
|
+
util: path.resolve(__dirname, 'node_modules/util')
|
|
8
12
|
}
|
|
9
13
|
},
|
|
10
14
|
optimizeDeps: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@willwade/aac-processors",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "A comprehensive TypeScript library for processing AAC (Augmentative and Alternative Communication) file formats with translation support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"browser": "dist/browser/index.browser.js",
|
|
@@ -93,6 +93,8 @@
|
|
|
93
93
|
"lint:fix": "eslint \"src/**/*.{js,ts}\" \"test/**/*.{js,ts}\" --fix",
|
|
94
94
|
"format": "prettier --write \"src/**/*.{js,ts}\" \"test/**/*.{js,ts}\" \"*.{js,ts,json,md}\"",
|
|
95
95
|
"format:check": "prettier --check \"src/**/*.{js,ts}\" \"test/**/*.{js,ts}\" \"*.{js,ts,json,md}\"",
|
|
96
|
+
"smoke:browser": "node scripts/smoke-browser-bundle.js",
|
|
97
|
+
"verify:browser-build": "node scripts/verify-browser-build.js",
|
|
96
98
|
"test": "npm run build && jest",
|
|
97
99
|
"test:watch": "npm run build && jest --watch",
|
|
98
100
|
"test:coverage": "npm run build && jest --coverage",
|
|
@@ -101,8 +103,8 @@
|
|
|
101
103
|
"docs": "typedoc",
|
|
102
104
|
"coverage:report": "node scripts/coverage-analysis.js",
|
|
103
105
|
"type-check": "tsc --noEmit",
|
|
104
|
-
"prepublishOnly": "npm run build",
|
|
105
|
-
"prepack": "npm run build"
|
|
106
|
+
"prepublishOnly": "npm run build:all && npm run verify:browser-build",
|
|
107
|
+
"prepack": "npm run build:all && npm run verify:browser-build"
|
|
106
108
|
},
|
|
107
109
|
"keywords": [
|
|
108
110
|
"aac",
|