@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 CHANGED
@@ -63,9 +63,9 @@ const tree = await processor.loadIntoTree(gridsetUint8Array);
63
63
  - Asterics Grid
64
64
  - Excel export
65
65
 
66
- ## Translation Workflow
66
+ ## Wrangle strings workflow
67
67
 
68
- All processors implement `processTexts()` for translation use cases.
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 (!this.zipFile || !images) {
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
- // Try to get the image file from the ZIP
74
- // Images are typically stored in an 'images' folder or root
75
- const possiblePaths = [
76
- imageData.path, // Explicit path if provided
77
- `images/${imageData.filename || imageId}`, // Standard images folder
78
- imageData.id, // Just the ID
79
- ].filter(Boolean);
80
- for (const imagePath of possiblePaths) {
81
- try {
82
- const buffer = await this.zipFile.readFile(imagePath);
83
- if (buffer) {
84
- const contentType = imageData.content_type ||
85
- this.getMimeTypeFromFilename(imagePath);
86
- const dataUrl = `data:${contentType};base64,${encodeBase64(buffer)}`;
87
- this.imageCache.set(imageId, dataUrl);
88
- return dataUrl;
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.endsWith('.obf') ? page.id : `${page.id}.obf`;
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 (!this.zipFile || !images) {
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
- // Try to get the image file from the ZIP
77
- // Images are typically stored in an 'images' folder or root
78
- const possiblePaths = [
79
- imageData.path, // Explicit path if provided
80
- `images/${imageData.filename || imageId}`, // Standard images folder
81
- imageData.id, // Just the ID
82
- ].filter(Boolean);
83
- for (const imagePath of possiblePaths) {
84
- try {
85
- const buffer = await this.zipFile.readFile(imagePath);
86
- if (buffer) {
87
- const contentType = imageData.content_type ||
88
- this.getMimeTypeFromFilename(imagePath);
89
- const dataUrl = `data:${contentType};base64,${(0, io_1.encodeBase64)(buffer)}`;
90
- this.imageCache.set(imageId, dataUrl);
91
- return dataUrl;
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.endsWith('.obf') ? page.id : `${page.id}.obf`;
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);
@@ -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.0",
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",