@willwade/aac-processors 0.2.0 → 0.2.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/README.md +3 -2
- package/dist/browser/processors/obfProcessor.js +45 -23
- package/dist/processors/obfProcessor.js +45 -23
- package/docs/BROWSER_USAGE.md +0 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,9 +63,9 @@ const tree = await processor.loadIntoTree(gridsetUint8Array);
|
|
|
63
63
|
- Asterics Grid
|
|
64
64
|
- Excel export
|
|
65
65
|
|
|
66
|
-
##
|
|
66
|
+
## Wrangle strings workflow
|
|
67
67
|
|
|
68
|
-
All processors implement `processTexts()`
|
|
68
|
+
All processors implement `processTexts()` to get all strings eg
|
|
69
69
|
|
|
70
70
|
```ts
|
|
71
71
|
import { DotProcessor } from '@willwade/aac-processors';
|
|
@@ -80,6 +80,7 @@ const translations = new Map([
|
|
|
80
80
|
|
|
81
81
|
await processor.processTexts('board.dot', translations, 'board-es.dot');
|
|
82
82
|
```
|
|
83
|
+
NB: Please use [https://aactools.co.uk](https://aactools.co.uk) for a far more comphrensive translation logic - where we do far far more than this...
|
|
83
84
|
|
|
84
85
|
## Documentation
|
|
85
86
|
|
|
@@ -62,35 +62,42 @@ class ObfProcessor extends BaseProcessor {
|
|
|
62
62
|
if (this.imageCache.has(imageId)) {
|
|
63
63
|
return this.imageCache.get(imageId) ?? null;
|
|
64
64
|
}
|
|
65
|
-
if (!
|
|
65
|
+
if (!images)
|
|
66
66
|
return null;
|
|
67
|
-
}
|
|
68
67
|
// Find the image metadata
|
|
69
68
|
const imageData = images.find((img) => img.id === imageId);
|
|
70
69
|
if (!imageData) {
|
|
71
70
|
return null;
|
|
72
71
|
}
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
72
|
+
// If image has data property, use that
|
|
73
|
+
if (imageData.data) {
|
|
74
|
+
const dataUrl = imageData.data;
|
|
75
|
+
this.imageCache.set(imageId, dataUrl);
|
|
76
|
+
return dataUrl;
|
|
77
|
+
}
|
|
78
|
+
if (this.zipFile) {
|
|
79
|
+
// Try to get the image file from the ZIP
|
|
80
|
+
// Images are typically stored in an 'images' folder or root
|
|
81
|
+
const possiblePaths = [
|
|
82
|
+
imageData.path, // Explicit path if provided
|
|
83
|
+
`images/${imageData.filename || imageId}`, // Standard images folder
|
|
84
|
+
imageData.id, // Just the ID
|
|
85
|
+
].filter(Boolean);
|
|
86
|
+
for (const imagePath of possiblePaths) {
|
|
87
|
+
try {
|
|
88
|
+
const buffer = await this.zipFile.readFile(imagePath);
|
|
89
|
+
if (buffer) {
|
|
90
|
+
const contentType = imageData.content_type ||
|
|
91
|
+
this.getMimeTypeFromFilename(imagePath);
|
|
92
|
+
const dataUrl = `data:${contentType};base64,${encodeBase64(buffer)}`;
|
|
93
|
+
this.imageCache.set(imageId, dataUrl);
|
|
94
|
+
return dataUrl;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
// Continue to next path
|
|
99
|
+
continue;
|
|
89
100
|
}
|
|
90
|
-
}
|
|
91
|
-
catch (err) {
|
|
92
|
-
// Continue to next path
|
|
93
|
-
continue;
|
|
94
101
|
}
|
|
95
102
|
}
|
|
96
103
|
// If image has a URL, use that as fallback
|
|
@@ -528,6 +535,7 @@ class ObfProcessor extends BaseProcessor {
|
|
|
528
535
|
border_color: button.style?.borderColor,
|
|
529
536
|
box_id: buttonPositions.get(String(button.id ?? '')),
|
|
530
537
|
image_id: imageId,
|
|
538
|
+
hidden: button.visibility === 'Hidden' || false,
|
|
531
539
|
};
|
|
532
540
|
}),
|
|
533
541
|
images: Array.isArray(page.images) ? page.images : [],
|
|
@@ -579,15 +587,29 @@ class ObfProcessor extends BaseProcessor {
|
|
|
579
587
|
await writeTextToPath(outputPath, JSON.stringify(obfBoard, null, 2));
|
|
580
588
|
}
|
|
581
589
|
else {
|
|
590
|
+
const getPageFilename = (id) => (id.endsWith('.obf') ? id : `${id}.obf`);
|
|
582
591
|
const files = Object.values(tree.pages).map((page) => {
|
|
583
592
|
const obfBoard = this.createObfBoardFromPage(page, 'Board', tree.metadata);
|
|
584
593
|
const obfContent = JSON.stringify(obfBoard, null, 2);
|
|
585
|
-
const name = page.id
|
|
594
|
+
const name = getPageFilename(page.id);
|
|
586
595
|
return {
|
|
587
596
|
name,
|
|
588
597
|
data: new TextEncoder().encode(obfContent),
|
|
589
598
|
};
|
|
590
599
|
});
|
|
600
|
+
const manifest = {
|
|
601
|
+
format: OBF_FORMAT_VERSION,
|
|
602
|
+
root: tree.metadata.defaultHomePageId,
|
|
603
|
+
paths: {
|
|
604
|
+
boards: Object.fromEntries(Object.entries(tree.pages).map(([id, page]) => [id, getPageFilename(page.id)])),
|
|
605
|
+
images: {}, //TODO Add support for saving images as files
|
|
606
|
+
sounds: {}, //TODO Add support for saving sounds as files
|
|
607
|
+
},
|
|
608
|
+
};
|
|
609
|
+
files.push({
|
|
610
|
+
name: 'manifest.json',
|
|
611
|
+
data: new TextEncoder().encode(JSON.stringify(manifest)),
|
|
612
|
+
});
|
|
591
613
|
const fileExists = await pathExists(outputPath);
|
|
592
614
|
this.zipFile = await this.options.zipAdapter(fileExists ? outputPath : undefined, this.options.fileAdapter);
|
|
593
615
|
const zipData = await this.zipFile.writeFiles(files);
|
|
@@ -65,35 +65,42 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
65
65
|
if (this.imageCache.has(imageId)) {
|
|
66
66
|
return this.imageCache.get(imageId) ?? null;
|
|
67
67
|
}
|
|
68
|
-
if (!
|
|
68
|
+
if (!images)
|
|
69
69
|
return null;
|
|
70
|
-
}
|
|
71
70
|
// Find the image metadata
|
|
72
71
|
const imageData = images.find((img) => img.id === imageId);
|
|
73
72
|
if (!imageData) {
|
|
74
73
|
return null;
|
|
75
74
|
}
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
75
|
+
// If image has data property, use that
|
|
76
|
+
if (imageData.data) {
|
|
77
|
+
const dataUrl = imageData.data;
|
|
78
|
+
this.imageCache.set(imageId, dataUrl);
|
|
79
|
+
return dataUrl;
|
|
80
|
+
}
|
|
81
|
+
if (this.zipFile) {
|
|
82
|
+
// Try to get the image file from the ZIP
|
|
83
|
+
// Images are typically stored in an 'images' folder or root
|
|
84
|
+
const possiblePaths = [
|
|
85
|
+
imageData.path, // Explicit path if provided
|
|
86
|
+
`images/${imageData.filename || imageId}`, // Standard images folder
|
|
87
|
+
imageData.id, // Just the ID
|
|
88
|
+
].filter(Boolean);
|
|
89
|
+
for (const imagePath of possiblePaths) {
|
|
90
|
+
try {
|
|
91
|
+
const buffer = await this.zipFile.readFile(imagePath);
|
|
92
|
+
if (buffer) {
|
|
93
|
+
const contentType = imageData.content_type ||
|
|
94
|
+
this.getMimeTypeFromFilename(imagePath);
|
|
95
|
+
const dataUrl = `data:${contentType};base64,${(0, io_1.encodeBase64)(buffer)}`;
|
|
96
|
+
this.imageCache.set(imageId, dataUrl);
|
|
97
|
+
return dataUrl;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
// Continue to next path
|
|
102
|
+
continue;
|
|
92
103
|
}
|
|
93
|
-
}
|
|
94
|
-
catch (err) {
|
|
95
|
-
// Continue to next path
|
|
96
|
-
continue;
|
|
97
104
|
}
|
|
98
105
|
}
|
|
99
106
|
// If image has a URL, use that as fallback
|
|
@@ -531,6 +538,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
531
538
|
border_color: button.style?.borderColor,
|
|
532
539
|
box_id: buttonPositions.get(String(button.id ?? '')),
|
|
533
540
|
image_id: imageId,
|
|
541
|
+
hidden: button.visibility === 'Hidden' || false,
|
|
534
542
|
};
|
|
535
543
|
}),
|
|
536
544
|
images: Array.isArray(page.images) ? page.images : [],
|
|
@@ -582,15 +590,29 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
582
590
|
await writeTextToPath(outputPath, JSON.stringify(obfBoard, null, 2));
|
|
583
591
|
}
|
|
584
592
|
else {
|
|
593
|
+
const getPageFilename = (id) => (id.endsWith('.obf') ? id : `${id}.obf`);
|
|
585
594
|
const files = Object.values(tree.pages).map((page) => {
|
|
586
595
|
const obfBoard = this.createObfBoardFromPage(page, 'Board', tree.metadata);
|
|
587
596
|
const obfContent = JSON.stringify(obfBoard, null, 2);
|
|
588
|
-
const name = page.id
|
|
597
|
+
const name = getPageFilename(page.id);
|
|
589
598
|
return {
|
|
590
599
|
name,
|
|
591
600
|
data: new TextEncoder().encode(obfContent),
|
|
592
601
|
};
|
|
593
602
|
});
|
|
603
|
+
const manifest = {
|
|
604
|
+
format: OBF_FORMAT_VERSION,
|
|
605
|
+
root: tree.metadata.defaultHomePageId,
|
|
606
|
+
paths: {
|
|
607
|
+
boards: Object.fromEntries(Object.entries(tree.pages).map(([id, page]) => [id, getPageFilename(page.id)])),
|
|
608
|
+
images: {}, //TODO Add support for saving images as files
|
|
609
|
+
sounds: {}, //TODO Add support for saving sounds as files
|
|
610
|
+
},
|
|
611
|
+
};
|
|
612
|
+
files.push({
|
|
613
|
+
name: 'manifest.json',
|
|
614
|
+
data: new TextEncoder().encode(JSON.stringify(manifest)),
|
|
615
|
+
});
|
|
594
616
|
const fileExists = await pathExists(outputPath);
|
|
595
617
|
this.zipFile = await this.options.zipAdapter(fileExists ? outputPath : undefined, this.options.fileAdapter);
|
|
596
618
|
const zipData = await this.zipFile.writeFiles(files);
|
package/docs/BROWSER_USAGE.md
CHANGED
|
@@ -212,46 +212,6 @@ The most common browser use case is loading files from an `<input type="file">`
|
|
|
212
212
|
</script>
|
|
213
213
|
```
|
|
214
214
|
|
|
215
|
-
### Processing Translations
|
|
216
|
-
|
|
217
|
-
```html
|
|
218
|
-
<script type="module">
|
|
219
|
-
import { getProcessor } from 'aac-processors';
|
|
220
|
-
|
|
221
|
-
async function translateFile(file, translations) {
|
|
222
|
-
const extension = '.' + file.name.split('.').pop();
|
|
223
|
-
const processor = getProcessor(extension);
|
|
224
|
-
const arrayBuffer = await file.arrayBuffer();
|
|
225
|
-
|
|
226
|
-
// Process texts with translations
|
|
227
|
-
const translatedBuffer = await processor.processTexts(
|
|
228
|
-
arrayBuffer,
|
|
229
|
-
translations,
|
|
230
|
-
null // Don't save to file, return buffer
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
// Create download link
|
|
234
|
-
const blob = new Blob([translatedBuffer], { type: 'application/octet-stream' });
|
|
235
|
-
const url = URL.createObjectURL(blob);
|
|
236
|
-
const a = document.createElement('a');
|
|
237
|
-
a.href = url;
|
|
238
|
-
a.download = 'translated-' + file.name;
|
|
239
|
-
a.click();
|
|
240
|
-
URL.revokeObjectURL(url);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Usage
|
|
244
|
-
const translations = new Map([
|
|
245
|
-
['Hello', 'Hola'],
|
|
246
|
-
['Goodbye', 'Adiós'],
|
|
247
|
-
['Yes', 'Sí'],
|
|
248
|
-
['No', 'No']
|
|
249
|
-
]);
|
|
250
|
-
|
|
251
|
-
translateFile(myFile, translations);
|
|
252
|
-
</script>
|
|
253
|
-
```
|
|
254
|
-
|
|
255
215
|
## Supported File Types
|
|
256
216
|
|
|
257
217
|
### Browser-Compatible Processors
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@willwade/aac-processors",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.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",
|