@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.
@@ -0,0 +1,185 @@
1
+ # AAC Pageset Quickstart (Node + Browser)
2
+
3
+ This guide shows two simple ways to generate or convert AAC pagesets using `aac-processors`:
4
+
5
+ - Node.js: full conversion between formats (read + write)
6
+ - Browser: generate or export to OBF/OBZ in-memory (downloadable)
7
+
8
+ If you need lossless conversion in the browser, use a Node/worker service for the save step (file I/O is required for most formats).
9
+
10
+ ## Node.js: Convert and Generate Pagesets
11
+
12
+ ### Install
13
+
14
+ ```bash
15
+ npm install aac-processors
16
+ ```
17
+
18
+ ### Convert a Gridset to OBF
19
+
20
+ ```ts
21
+ import { getProcessor, ObfProcessor } from 'aac-processors';
22
+
23
+ async function convertGridsetToObf() {
24
+ const sourcePath = './input/example.gridset';
25
+ const targetPath = './output/example.obf';
26
+
27
+ const sourceProcessor = getProcessor(sourcePath); // GridsetProcessor
28
+ const tree = await sourceProcessor.loadIntoTree(sourcePath);
29
+
30
+ const obf = new ObfProcessor();
31
+ await obf.saveFromTree(tree, targetPath);
32
+
33
+ console.log('Saved:', targetPath);
34
+ }
35
+
36
+ convertGridsetToObf().catch(console.error);
37
+ ```
38
+
39
+ ### Generate a Simple Pageset and Save as OBZ
40
+
41
+ ```ts
42
+ import { AACTree, AACPage, AACButton, ObfProcessor } from 'aac-processors';
43
+
44
+ async function generateObz() {
45
+ const tree = new AACTree();
46
+ tree.metadata = { name: 'Starter Demo', locale: 'en' };
47
+
48
+ const hello = new AACButton({ id: 'hello', label: 'Hello', message: 'Hello' });
49
+ const thanks = new AACButton({ id: 'thanks', label: 'Thanks', message: 'Thank you' });
50
+
51
+ const home = new AACPage({
52
+ id: 'home',
53
+ name: 'Home',
54
+ buttons: [hello, thanks],
55
+ grid: [[hello, thanks]],
56
+ });
57
+
58
+ tree.addPage(home);
59
+ tree.rootId = 'home';
60
+
61
+ const obf = new ObfProcessor();
62
+ await obf.saveFromTree(tree, './output/starter.obz');
63
+
64
+ console.log('Saved: ./output/starter.obz');
65
+ }
66
+
67
+ generateObz().catch(console.error);
68
+ ```
69
+
70
+ ## Browser: Generate or Convert to OBF/OBZ
71
+
72
+ In the browser you can still parse files and build an `AACTree`, but most processors cannot write to disk (no `fs`).
73
+ The example below generates an `AACTree`, converts it to OBF JSON in-memory, and downloads it.
74
+
75
+ If you need full conversions in a browser app, do the save step in Node (server or worker).
76
+
77
+ ### Generate a Pageset and Download as OBF
78
+
79
+ ```ts
80
+ import { AACTree, AACPage, AACButton, ObfProcessor } from 'aac-processors';
81
+
82
+ function downloadBlob(data: BlobPart, filename: string, type: string) {
83
+ const blob = new Blob([data], { type });
84
+ const url = URL.createObjectURL(blob);
85
+ const a = document.createElement('a');
86
+ a.href = url;
87
+ a.download = filename;
88
+ a.click();
89
+ URL.revokeObjectURL(url);
90
+ }
91
+
92
+ function buildSampleTree() {
93
+ const tree = new AACTree();
94
+ tree.metadata = { name: 'Browser Demo', locale: 'en' };
95
+
96
+ const hello = new AACButton({ id: 'hello', label: 'Hello', message: 'Hello' });
97
+ const yes = new AACButton({ id: 'yes', label: 'Yes', message: 'Yes' });
98
+
99
+ const home = new AACPage({
100
+ id: 'home',
101
+ name: 'Home',
102
+ buttons: [hello, yes],
103
+ grid: [[hello, yes]],
104
+ });
105
+
106
+ tree.addPage(home);
107
+ tree.rootId = 'home';
108
+ return tree;
109
+ }
110
+
111
+ async function exportObf(tree: AACTree) {
112
+ // This mirrors the browser demo approach: create OBF JSON and download.
113
+ // ObfProcessor.saveFromTree writes to disk, so we build a board in-memory.
114
+ const obf = new ObfProcessor() as ObfProcessor & {
115
+ createObfBoardFromPage?: (page: AACPage, fallbackName: string, metadata?: AACTree['metadata']) => any;
116
+ };
117
+
118
+ const rootPage = tree.rootId ? tree.getPage(tree.rootId) : Object.values(tree.pages)[0];
119
+ const board = obf.createObfBoardFromPage
120
+ ? obf.createObfBoardFromPage(rootPage!, 'Board', tree.metadata)
121
+ : {
122
+ format: 'open-board-0.1',
123
+ id: rootPage?.id ?? 'board',
124
+ name: rootPage?.name ?? 'Board',
125
+ locale: tree.metadata?.locale || 'en',
126
+ grid: { rows: 1, columns: rootPage?.buttons.length ?? 0, order: [] },
127
+ buttons: (rootPage?.buttons || []).map((button) => ({
128
+ id: button.id,
129
+ label: button.label,
130
+ vocalization: button.message || button.label,
131
+ })),
132
+ };
133
+
134
+ const json = JSON.stringify(board, null, 2);
135
+ downloadBlob(json, 'browser-demo.obf', 'application/json');
136
+ }
137
+
138
+ const tree = buildSampleTree();
139
+ exportObf(tree);
140
+ ```
141
+
142
+ ### Convert an Uploaded Pageset to OBZ (Browser)
143
+
144
+ This uses the same idea as the Vite demo: parse any supported file into a tree,
145
+ then export OBF/OBZ in-memory and download.
146
+
147
+ ```ts
148
+ import { getProcessor, ObfProcessor, type AACTree, type AACPage } from 'aac-processors';
149
+ import JSZip from 'jszip';
150
+
151
+ async function convertToObz(file: File) {
152
+ const extension = '.' + file.name.split('.').pop();
153
+ const processor = getProcessor(extension);
154
+ const buffer = await file.arrayBuffer();
155
+ const tree = await processor.loadIntoTree(buffer);
156
+
157
+ const obf = new ObfProcessor() as ObfProcessor & {
158
+ createObfBoardFromPage?: (page: AACPage, fallbackName: string, metadata?: AACTree['metadata']) => any;
159
+ };
160
+
161
+ const zip = new JSZip();
162
+ Object.values(tree.pages).forEach((page) => {
163
+ const board = obf.createObfBoardFromPage
164
+ ? obf.createObfBoardFromPage(page, 'Board', tree.metadata)
165
+ : { format: 'open-board-0.1', id: page.id, name: page.name, grid: { rows: 0, columns: 0, order: [] }, buttons: [] };
166
+ zip.file(`${page.id}.obf`, JSON.stringify(board, null, 2));
167
+ });
168
+
169
+ const data = await zip.generateAsync({ type: 'uint8array' });
170
+ const blob = new Blob([data], { type: 'application/zip' });
171
+ const url = URL.createObjectURL(blob);
172
+ const a = document.createElement('a');
173
+ a.href = url;
174
+ a.download = 'converted.obz';
175
+ a.click();
176
+ URL.revokeObjectURL(url);
177
+ }
178
+ ```
179
+
180
+ ## Tips
181
+
182
+ - Use `loadIntoTree()` to normalize different AAC formats into one structure.
183
+ - In Node, `saveFromTree()` lets you write to OBF, OBZ, Gridset, etc.
184
+ - In the browser, build the output in memory and offer it for download.
185
+ - For full-fidelity conversions in the browser, use a server-side endpoint to save files.
@@ -40,6 +40,7 @@ The `test-files/` folder contains example AAC files you can use:
40
40
  - **Navigation**: Click NAVIGATE buttons to jump between pages
41
41
  - **Stats**: See page/button/text counts and load time
42
42
  - **Logs**: Watch the processing log in real-time
43
+ - **Pageset Lab**: Open the "Create & Convert" tab to generate a sample pageset or convert an upload to OBF/OBZ
43
44
 
44
45
  ## 🛠️ Development
45
46
 
@@ -63,6 +63,39 @@
63
63
  border-bottom: 2px solid #667eea;
64
64
  }
65
65
 
66
+ .tab-header {
67
+ display: flex;
68
+ gap: 8px;
69
+ margin-bottom: 15px;
70
+ }
71
+
72
+ .tab-btn {
73
+ flex: 1;
74
+ border: 1px solid #d6daf5;
75
+ background: #f7f8ff;
76
+ color: #3d4bb8;
77
+ padding: 8px 12px;
78
+ border-radius: 8px;
79
+ font-size: 13px;
80
+ font-weight: 600;
81
+ cursor: pointer;
82
+ transition: all 0.2s;
83
+ }
84
+
85
+ .tab-btn.active {
86
+ background: #667eea;
87
+ color: #fff;
88
+ border-color: #667eea;
89
+ }
90
+
91
+ .tab-content {
92
+ display: none;
93
+ }
94
+
95
+ .tab-content.active {
96
+ display: block;
97
+ }
98
+
66
99
  .upload-area {
67
100
  border: 2px dashed #ccc;
68
101
  border-radius: 8px;
@@ -290,6 +323,83 @@
290
323
  font-size: 13px;
291
324
  }
292
325
 
326
+ .demo-section {
327
+ background: #f8f9ff;
328
+ border-radius: 10px;
329
+ padding: 15px;
330
+ border: 1px solid #e4e7ff;
331
+ margin-bottom: 15px;
332
+ }
333
+
334
+ .section-title {
335
+ font-size: 14px;
336
+ font-weight: 700;
337
+ color: #3942a3;
338
+ margin-bottom: 10px;
339
+ }
340
+
341
+ .field {
342
+ display: flex;
343
+ flex-direction: column;
344
+ gap: 6px;
345
+ margin-bottom: 10px;
346
+ font-size: 12px;
347
+ color: #4d4d4d;
348
+ }
349
+
350
+ .field select,
351
+ .field input {
352
+ border: 1px solid #d7d9f5;
353
+ border-radius: 6px;
354
+ padding: 8px 10px;
355
+ font-size: 13px;
356
+ }
357
+
358
+ .action-row {
359
+ display: flex;
360
+ gap: 10px;
361
+ }
362
+
363
+ .action-row .btn {
364
+ margin-bottom: 0;
365
+ }
366
+
367
+ .hint {
368
+ font-size: 12px;
369
+ color: #667;
370
+ margin-top: 8px;
371
+ }
372
+
373
+ .status-pill {
374
+ margin-top: 10px;
375
+ font-size: 12px;
376
+ padding: 8px 10px;
377
+ border-radius: 8px;
378
+ background: #eef0ff;
379
+ color: #4a4a8a;
380
+ }
381
+
382
+ .status-pill.success {
383
+ background: #e0f4e7;
384
+ color: #256d3b;
385
+ }
386
+
387
+ .status-pill.warn {
388
+ background: #fff4d6;
389
+ color: #7a5a00;
390
+ }
391
+
392
+ .code-preview {
393
+ background: #0f172a;
394
+ color: #d7e3ff;
395
+ padding: 12px;
396
+ border-radius: 8px;
397
+ font-size: 12px;
398
+ max-height: 220px;
399
+ overflow: auto;
400
+ white-space: pre-wrap;
401
+ }
402
+
293
403
  @media (max-width: 1024px) {
294
404
  .main-grid {
295
405
  grid-template-columns: 1fr;
@@ -361,11 +471,56 @@
361
471
 
362
472
  <!-- Right Panel: Results -->
363
473
  <div class="panel results-panel">
364
- <div class="panel-title">📊 File Contents</div>
365
- <div id="results">
366
- <p style="color: #999; text-align: center; padding: 40px;">
367
- Load a file to see its contents here
368
- </p>
474
+ <div class="panel-title">📊 AAC Pageset Lab</div>
475
+ <div class="tab-header">
476
+ <button class="tab-btn active" data-tab="inspectTab">Inspect</button>
477
+ <button class="tab-btn" data-tab="pagesetTab">Create & Convert</button>
478
+ </div>
479
+ <div class="tab-content active" id="inspectTab">
480
+ <div id="results">
481
+ <p style="color: #999; text-align: center; padding: 40px;">
482
+ Load a file to see its contents here
483
+ </p>
484
+ </div>
485
+ </div>
486
+ <div class="tab-content" id="pagesetTab">
487
+ <div class="demo-section">
488
+ <div class="section-title">✨ Create a Sample Pageset</div>
489
+ <div class="field">
490
+ <label for="templateSelect">Template</label>
491
+ <select id="templateSelect">
492
+ <option value="starter">Starter 2x2 + Feelings</option>
493
+ <option value="home">Home & Core 3x3</option>
494
+ </select>
495
+ </div>
496
+ <div class="field">
497
+ <label for="formatSelect">Output format</label>
498
+ <select id="formatSelect">
499
+ <option value="obf">OBF (.obf)</option>
500
+ <option value="obz">OBZ (.obz)</option>
501
+ </select>
502
+ </div>
503
+ <div class="action-row">
504
+ <button class="btn" id="createPagesetBtn">Generate & Download</button>
505
+ <button class="btn btn-secondary" id="previewPagesetBtn">Preview in Viewer</button>
506
+ </div>
507
+ <div class="hint">Creates a demo AACTree, then exports to OBF/OBZ.</div>
508
+ </div>
509
+
510
+ <div class="demo-section">
511
+ <div class="section-title">🔁 Convert Loaded Pageset</div>
512
+ <div class="action-row">
513
+ <button class="btn" id="convertToObfBtn" disabled>Download .obf</button>
514
+ <button class="btn btn-secondary" id="convertToObzBtn" disabled>Download .obz</button>
515
+ </div>
516
+ <div class="hint">Upload and process a file first, then export it in another format.</div>
517
+ <div class="status-pill" id="conversionStatus">No pageset loaded yet.</div>
518
+ </div>
519
+
520
+ <div class="demo-section">
521
+ <div class="section-title">📄 Export Preview</div>
522
+ <pre class="code-preview" id="pagesetOutput">Generate or convert a pageset to preview the output JSON.</pre>
523
+ </div>
369
524
  </div>
370
525
  </div>
371
526
  </div>