@willwade/aac-processors 0.1.21 → 0.2.0
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/applePanelsProcessor.js +24 -24
- package/dist/browser/processors/astericsGridProcessor.js +22 -24
- package/dist/browser/processors/dotProcessor.js +6 -10
- package/dist/browser/processors/gridset/helpers.js +33 -30
- package/dist/browser/processors/gridset/symbolExtractor.js +2 -2
- package/dist/browser/processors/gridset/symbolSearch.js +22 -22
- package/dist/browser/processors/gridset/symbols.js +14 -14
- package/dist/browser/processors/gridsetProcessor.js +7 -7
- package/dist/browser/processors/obfProcessor.js +24 -25
- package/dist/browser/processors/opmlProcessor.js +6 -10
- package/dist/browser/processors/snap/helpers.js +34 -30
- package/dist/browser/processors/snapProcessor.js +28 -28
- package/dist/browser/processors/touchchatProcessor.js +24 -25
- package/dist/browser/utilities/analytics/history.js +24 -18
- package/dist/browser/utilities/analytics/metrics/comparison.js +16 -16
- package/dist/browser/utilities/analytics/metrics/vocabulary.js +2 -2
- package/dist/browser/utilities/analytics/reference/browser.js +16 -16
- package/dist/browser/utilities/analytics/reference/index.js +25 -24
- package/dist/browser/utils/io.js +29 -25
- package/dist/browser/utils/sqlite.js +5 -5
- package/dist/browser/utils/zip.js +2 -4
- package/dist/browser/validation/gridsetValidator.js +2 -2
- package/dist/browser/validation/obfValidator.js +2 -2
- package/dist/browser/validation/snapValidator.js +3 -3
- package/dist/browser/validation/touchChatValidator.js +3 -3
- package/dist/cli/index.js +19 -16
- package/dist/core/baseProcessor.d.ts +1 -1
- package/dist/processors/applePanelsProcessor.js +24 -24
- package/dist/processors/astericsGridProcessor.d.ts +4 -4
- package/dist/processors/astericsGridProcessor.js +22 -24
- package/dist/processors/dotProcessor.js +6 -10
- package/dist/processors/excelProcessor.d.ts +3 -3
- package/dist/processors/excelProcessor.js +10 -13
- package/dist/processors/gridset/helpers.d.ts +9 -9
- package/dist/processors/gridset/helpers.js +33 -30
- package/dist/processors/gridset/symbolExtractor.d.ts +1 -1
- package/dist/processors/gridset/symbolExtractor.js +2 -2
- package/dist/processors/gridset/symbolSearch.d.ts +10 -10
- package/dist/processors/gridset/symbolSearch.js +22 -22
- package/dist/processors/gridset/symbols.d.ts +3 -3
- package/dist/processors/gridset/symbols.js +14 -14
- package/dist/processors/gridsetProcessor.d.ts +2 -2
- package/dist/processors/gridsetProcessor.js +7 -7
- package/dist/processors/obfProcessor.d.ts +2 -2
- package/dist/processors/obfProcessor.js +24 -25
- package/dist/processors/obfsetProcessor.js +1 -2
- package/dist/processors/opmlProcessor.js +6 -10
- package/dist/processors/snap/helpers.d.ts +8 -8
- package/dist/processors/snap/helpers.js +34 -30
- package/dist/processors/snapProcessor.d.ts +2 -2
- package/dist/processors/snapProcessor.js +28 -28
- package/dist/processors/touchchatProcessor.d.ts +2 -2
- package/dist/processors/touchchatProcessor.js +24 -25
- package/dist/types/aac.d.ts +2 -2
- package/dist/utilities/analytics/history.d.ts +8 -8
- package/dist/utilities/analytics/history.js +24 -18
- package/dist/utilities/analytics/index.d.ts +1 -1
- package/dist/utilities/analytics/index.js +3 -2
- package/dist/utilities/analytics/metrics/comparison.d.ts +1 -1
- package/dist/utilities/analytics/metrics/comparison.js +16 -16
- package/dist/utilities/analytics/metrics/vocabulary.d.ts +1 -1
- package/dist/utilities/analytics/metrics/vocabulary.js +2 -2
- package/dist/utilities/analytics/reference/browser.d.ts +9 -9
- package/dist/utilities/analytics/reference/browser.js +16 -16
- package/dist/utilities/analytics/reference/index.d.ts +21 -21
- package/dist/utilities/analytics/reference/index.js +25 -24
- package/dist/utilities/symbolTools.d.ts +5 -5
- package/dist/utilities/symbolTools.js +10 -8
- package/dist/utils/io.d.ts +11 -11
- package/dist/utils/io.js +29 -25
- package/dist/utils/sqlite.d.ts +1 -1
- package/dist/utils/sqlite.js +5 -5
- package/dist/utils/zip.js +2 -4
- package/dist/validation/applePanelsValidator.js +7 -6
- package/dist/validation/astericsValidator.js +2 -2
- package/dist/validation/dotValidator.js +2 -2
- package/dist/validation/excelValidator.js +2 -2
- package/dist/validation/gridsetValidator.js +2 -2
- package/dist/validation/index.js +2 -2
- package/dist/validation/obfValidator.js +2 -2
- package/dist/validation/obfsetValidator.js +2 -2
- package/dist/validation/opmlValidator.js +2 -2
- package/dist/validation/snapValidator.js +3 -3
- package/dist/validation/touchChatValidator.js +3 -3
- package/package.json +1 -1
|
@@ -65,7 +65,6 @@ class SnapProcessor extends BaseProcessor {
|
|
|
65
65
|
}
|
|
66
66
|
async loadIntoTree(filePathOrBuffer) {
|
|
67
67
|
const { writeBinaryToPath, removePath, mkTempDir, basename, join } = this.options.fileAdapter;
|
|
68
|
-
await Promise.resolve();
|
|
69
68
|
const tree = new AACTree();
|
|
70
69
|
let dbResult = null;
|
|
71
70
|
let cleanupTempZip = null;
|
|
@@ -76,22 +75,22 @@ class SnapProcessor extends BaseProcessor {
|
|
|
76
75
|
const fileName = basename(filePathOrBuffer).toLowerCase();
|
|
77
76
|
if (fileName.endsWith('.sub.zip') || filePathOrBuffer.endsWith('.sub')) {
|
|
78
77
|
// Extract .sub.zip to find the embedded .sps file
|
|
79
|
-
const tempDir = mkTempDir('snap-sub-');
|
|
78
|
+
const tempDir = await mkTempDir('snap-sub-');
|
|
80
79
|
const zip = await this.options.zipAdapter(filePathOrBuffer);
|
|
81
80
|
// Find the .sps file in the archive
|
|
82
81
|
const files = zip.listFiles();
|
|
83
82
|
const spsFile = files.find((f) => f.endsWith('.sps'));
|
|
84
83
|
if (!spsFile) {
|
|
85
|
-
removePath(tempDir, { recursive: true, force: true });
|
|
84
|
+
await removePath(tempDir, { recursive: true, force: true });
|
|
86
85
|
throw new Error('No .sps file found in .sub.zip archive');
|
|
87
86
|
}
|
|
88
87
|
// Extract the .sps file
|
|
89
88
|
const spsData = await zip.readFile(spsFile);
|
|
90
89
|
const extractedSpsPath = join(tempDir, basename(spsFile));
|
|
91
|
-
writeBinaryToPath(extractedSpsPath, Buffer.from(spsData));
|
|
90
|
+
await writeBinaryToPath(extractedSpsPath, Buffer.from(spsData));
|
|
92
91
|
inputFile = extractedSpsPath;
|
|
93
|
-
cleanupTempZip = () => {
|
|
94
|
-
removePath(tempDir, { recursive: true, force: true });
|
|
92
|
+
cleanupTempZip = async () => {
|
|
93
|
+
await removePath(tempDir, { recursive: true, force: true });
|
|
95
94
|
};
|
|
96
95
|
}
|
|
97
96
|
}
|
|
@@ -683,7 +682,7 @@ class SnapProcessor extends BaseProcessor {
|
|
|
683
682
|
}
|
|
684
683
|
finally {
|
|
685
684
|
if (dbResult?.cleanup) {
|
|
686
|
-
dbResult.cleanup();
|
|
685
|
+
await dbResult.cleanup();
|
|
687
686
|
}
|
|
688
687
|
else if (dbResult?.db) {
|
|
689
688
|
dbResult.db.close();
|
|
@@ -691,7 +690,7 @@ class SnapProcessor extends BaseProcessor {
|
|
|
691
690
|
// Clean up temporary extracted .sps file from .sub.zip
|
|
692
691
|
if (cleanupTempZip) {
|
|
693
692
|
try {
|
|
694
|
-
cleanupTempZip();
|
|
693
|
+
await cleanupTempZip();
|
|
695
694
|
}
|
|
696
695
|
catch (e) {
|
|
697
696
|
console.warn('[SnapProcessor] Failed to clean up temporary .sps file:', e);
|
|
@@ -707,13 +706,14 @@ class SnapProcessor extends BaseProcessor {
|
|
|
707
706
|
if (typeof filePathOrBuffer === 'string') {
|
|
708
707
|
const inputPath = filePathOrBuffer;
|
|
709
708
|
const outputDir = dirname(outputPath);
|
|
710
|
-
|
|
711
|
-
|
|
709
|
+
const dirExists = await pathExists(outputDir);
|
|
710
|
+
if (!dirExists) {
|
|
711
|
+
await mkDir(outputDir, { recursive: true });
|
|
712
712
|
}
|
|
713
|
-
if (pathExists(outputPath)) {
|
|
714
|
-
removePath(outputPath);
|
|
713
|
+
if (await pathExists(outputPath)) {
|
|
714
|
+
await removePath(outputPath);
|
|
715
715
|
}
|
|
716
|
-
writeBinaryToPath(outputPath, readBinaryFromInput(inputPath));
|
|
716
|
+
await writeBinaryToPath(outputPath, await readBinaryFromInput(inputPath));
|
|
717
717
|
const Database = requireBetterSqlite3();
|
|
718
718
|
const db = new Database(outputPath, { readonly: false });
|
|
719
719
|
try {
|
|
@@ -775,7 +775,7 @@ class SnapProcessor extends BaseProcessor {
|
|
|
775
775
|
finally {
|
|
776
776
|
db.close();
|
|
777
777
|
}
|
|
778
|
-
return readBinaryFromInput(outputPath);
|
|
778
|
+
return await readBinaryFromInput(outputPath);
|
|
779
779
|
}
|
|
780
780
|
// Fallback for buffer inputs: rebuild from tree (may drop Snap assets)
|
|
781
781
|
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
@@ -802,20 +802,20 @@ class SnapProcessor extends BaseProcessor {
|
|
|
802
802
|
});
|
|
803
803
|
});
|
|
804
804
|
await this.saveFromTree(tree, outputPath);
|
|
805
|
-
return readBinaryFromInput(outputPath);
|
|
805
|
+
return await readBinaryFromInput(outputPath);
|
|
806
806
|
}
|
|
807
807
|
async saveFromTree(tree, outputPath) {
|
|
808
808
|
const { pathExists, mkDir, removePath, dirname } = this.options.fileAdapter;
|
|
809
809
|
if (!isNodeRuntime()) {
|
|
810
810
|
throw new Error('saveFromTree is only supported in Node.js environments for Snap files.');
|
|
811
811
|
}
|
|
812
|
-
await Promise.resolve();
|
|
813
812
|
const outputDir = dirname(outputPath);
|
|
814
|
-
|
|
815
|
-
|
|
813
|
+
const dirExists = await pathExists(outputDir);
|
|
814
|
+
if (!dirExists) {
|
|
815
|
+
await mkDir(outputDir, { recursive: true });
|
|
816
816
|
}
|
|
817
|
-
if (pathExists(outputPath)) {
|
|
818
|
-
removePath(outputPath);
|
|
817
|
+
if (await pathExists(outputPath)) {
|
|
818
|
+
await removePath(outputPath);
|
|
819
819
|
}
|
|
820
820
|
// Create a new SQLite database for Snap format
|
|
821
821
|
const Database = requireBetterSqlite3();
|
|
@@ -1050,7 +1050,6 @@ class SnapProcessor extends BaseProcessor {
|
|
|
1050
1050
|
* Add audio recording to a button in the database
|
|
1051
1051
|
*/
|
|
1052
1052
|
async addAudioToButton(dbPath, buttonId, audioData, metadata) {
|
|
1053
|
-
await Promise.resolve();
|
|
1054
1053
|
if (!isNodeRuntime()) {
|
|
1055
1054
|
throw new Error('addAudioToButton is only supported in Node.js environments.');
|
|
1056
1055
|
}
|
|
@@ -1088,7 +1087,7 @@ class SnapProcessor extends BaseProcessor {
|
|
|
1088
1087
|
const updateButton = db.prepare('UPDATE Button SET MessageRecordingId = ?, UseMessageRecording = 1, SerializedMessageSoundMetadata = ? WHERE Id = ?');
|
|
1089
1088
|
const metadataJson = metadata ? JSON.stringify({ FileName: metadata }) : null;
|
|
1090
1089
|
updateButton.run(audioId, metadataJson, buttonId);
|
|
1091
|
-
return audioId;
|
|
1090
|
+
return Promise.resolve(audioId);
|
|
1092
1091
|
}
|
|
1093
1092
|
finally {
|
|
1094
1093
|
db.close();
|
|
@@ -1103,7 +1102,7 @@ class SnapProcessor extends BaseProcessor {
|
|
|
1103
1102
|
throw new Error('createAudioEnhancedPageset is only supported in Node.js environments.');
|
|
1104
1103
|
}
|
|
1105
1104
|
// Copy the source database to target
|
|
1106
|
-
writeBinaryToPath(targetDbPath, readBinaryFromInput(sourceDbPath));
|
|
1105
|
+
await writeBinaryToPath(targetDbPath, await readBinaryFromInput(sourceDbPath));
|
|
1107
1106
|
// Add audio recordings to the copy
|
|
1108
1107
|
for (const [buttonId, audioInfo] of audioMappings.entries()) {
|
|
1109
1108
|
await this.addAudioToButton(targetDbPath, buttonId, audioInfo.audioData, audioInfo.metadata);
|
|
@@ -1171,16 +1170,16 @@ class SnapProcessor extends BaseProcessor {
|
|
|
1171
1170
|
* Get available PageLayouts for a Snap file
|
|
1172
1171
|
* Useful for UI components that want to let users select layout size
|
|
1173
1172
|
* @param filePath - Path to the Snap file
|
|
1174
|
-
* @returns
|
|
1173
|
+
* @returns Promise resolving to available PageLayouts with their dimensions
|
|
1175
1174
|
*/
|
|
1176
|
-
getAvailablePageLayouts(filePath) {
|
|
1175
|
+
async getAvailablePageLayouts(filePath) {
|
|
1177
1176
|
const { writeBinaryToPath, removePath, pathExists, join } = this.options.fileAdapter;
|
|
1178
1177
|
if (!isNodeRuntime()) {
|
|
1179
1178
|
throw new Error('getAvailablePageLayouts is only supported in Node.js environments.');
|
|
1180
1179
|
}
|
|
1181
1180
|
const dbPath = typeof filePath === 'string' ? filePath : join(process.cwd(), 'temp.spb');
|
|
1182
1181
|
if (Buffer.isBuffer(filePath)) {
|
|
1183
|
-
writeBinaryToPath(dbPath, filePath);
|
|
1182
|
+
await writeBinaryToPath(dbPath, filePath);
|
|
1184
1183
|
}
|
|
1185
1184
|
let db = null;
|
|
1186
1185
|
try {
|
|
@@ -1231,9 +1230,10 @@ class SnapProcessor extends BaseProcessor {
|
|
|
1231
1230
|
db.close();
|
|
1232
1231
|
}
|
|
1233
1232
|
// Clean up temporary file if created from buffer
|
|
1234
|
-
|
|
1233
|
+
const exists = await pathExists(dbPath);
|
|
1234
|
+
if (Buffer.isBuffer(filePath) && exists) {
|
|
1235
1235
|
try {
|
|
1236
|
-
removePath(dbPath);
|
|
1236
|
+
await removePath(dbPath);
|
|
1237
1237
|
}
|
|
1238
1238
|
catch (e) {
|
|
1239
1239
|
console.warn('Failed to clean up temporary file:', e);
|
|
@@ -56,7 +56,6 @@ class TouchChatProcessor extends BaseProcessor {
|
|
|
56
56
|
}
|
|
57
57
|
async loadIntoTree(filePathOrBuffer) {
|
|
58
58
|
const { readBinaryFromInput } = this.options.fileAdapter;
|
|
59
|
-
await Promise.resolve();
|
|
60
59
|
// Unzip .ce file, extract the .c4v SQLite DB, and parse pages/buttons
|
|
61
60
|
let db = null;
|
|
62
61
|
let cleanup;
|
|
@@ -64,7 +63,7 @@ class TouchChatProcessor extends BaseProcessor {
|
|
|
64
63
|
// Store source file path or buffer
|
|
65
64
|
this.sourceFile = filePathOrBuffer;
|
|
66
65
|
// Step 1: Unzip
|
|
67
|
-
const zipInput = readBinaryFromInput(filePathOrBuffer);
|
|
66
|
+
const zipInput = await readBinaryFromInput(filePathOrBuffer);
|
|
68
67
|
const zip = await this.options.zipAdapter(zipInput);
|
|
69
68
|
const vocabEntry = zip.listFiles().find((name) => name.endsWith('.c4v'));
|
|
70
69
|
if (!vocabEntry) {
|
|
@@ -469,7 +468,7 @@ class TouchChatProcessor extends BaseProcessor {
|
|
|
469
468
|
finally {
|
|
470
469
|
// Clean up
|
|
471
470
|
if (cleanup) {
|
|
472
|
-
cleanup();
|
|
471
|
+
await cleanup();
|
|
473
472
|
}
|
|
474
473
|
else if (db) {
|
|
475
474
|
db.close();
|
|
@@ -492,11 +491,12 @@ class TouchChatProcessor extends BaseProcessor {
|
|
|
492
491
|
if (typeof filePathOrBuffer === 'string') {
|
|
493
492
|
const inputPath = filePathOrBuffer;
|
|
494
493
|
const outputDir = dirname(outputPath);
|
|
495
|
-
|
|
496
|
-
|
|
494
|
+
const dirExists = await pathExists(outputDir);
|
|
495
|
+
if (!dirExists) {
|
|
496
|
+
await mkDir(outputDir, { recursive: true });
|
|
497
497
|
}
|
|
498
|
-
if (pathExists(outputPath)) {
|
|
499
|
-
removePath(outputPath);
|
|
498
|
+
if (await pathExists(outputPath)) {
|
|
499
|
+
await removePath(outputPath);
|
|
500
500
|
}
|
|
501
501
|
const zip = await this.options.zipAdapter(inputPath);
|
|
502
502
|
const entries = getZipEntriesFromAdapter(zip);
|
|
@@ -504,10 +504,10 @@ class TouchChatProcessor extends BaseProcessor {
|
|
|
504
504
|
if (!vocabEntry) {
|
|
505
505
|
throw new Error('No .c4v vocab DB found in TouchChat export');
|
|
506
506
|
}
|
|
507
|
-
const tempDir = mkTempDir('touchchat-translate-');
|
|
507
|
+
const tempDir = await mkTempDir('touchchat-translate-');
|
|
508
508
|
const dbPath = join(tempDir, 'vocab.c4v');
|
|
509
509
|
try {
|
|
510
|
-
writeBinaryToPath(dbPath, await vocabEntry.getData());
|
|
510
|
+
await writeBinaryToPath(dbPath, await vocabEntry.getData());
|
|
511
511
|
const Database = requireBetterSqlite3();
|
|
512
512
|
const db = new Database(dbPath, { readonly: false });
|
|
513
513
|
try {
|
|
@@ -574,20 +574,20 @@ class TouchChatProcessor extends BaseProcessor {
|
|
|
574
574
|
}
|
|
575
575
|
files.push({
|
|
576
576
|
name: vocabEntry.entryName,
|
|
577
|
-
data: readBinaryFromInput(dbPath),
|
|
577
|
+
data: await readBinaryFromInput(dbPath),
|
|
578
578
|
});
|
|
579
579
|
const zipData = await outputZip.writeFiles(files);
|
|
580
|
-
writeBinaryToPath(outputPath, zipData);
|
|
580
|
+
await writeBinaryToPath(outputPath, zipData);
|
|
581
581
|
}
|
|
582
582
|
finally {
|
|
583
583
|
try {
|
|
584
|
-
removePath(tempDir, { recursive: true, force: true });
|
|
584
|
+
await removePath(tempDir, { recursive: true, force: true });
|
|
585
585
|
}
|
|
586
586
|
catch {
|
|
587
587
|
// Best-effort cleanup
|
|
588
588
|
}
|
|
589
589
|
}
|
|
590
|
-
return readBinaryFromInput(outputPath);
|
|
590
|
+
return await readBinaryFromInput(outputPath);
|
|
591
591
|
}
|
|
592
592
|
// Fallback for buffer inputs: rebuild from tree (may drop TouchChat metadata)
|
|
593
593
|
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
@@ -614,16 +614,15 @@ class TouchChatProcessor extends BaseProcessor {
|
|
|
614
614
|
});
|
|
615
615
|
});
|
|
616
616
|
await this.saveFromTree(tree, outputPath);
|
|
617
|
-
return readBinaryFromInput(outputPath);
|
|
617
|
+
return await readBinaryFromInput(outputPath);
|
|
618
618
|
}
|
|
619
619
|
async saveFromTree(tree, outputPath) {
|
|
620
620
|
const { writeBinaryToPath, mkTempDir, readBinaryFromInput, pathExists, removePath, join } = this.options.fileAdapter;
|
|
621
|
-
await Promise.resolve();
|
|
622
621
|
if (!isNodeRuntime()) {
|
|
623
622
|
throw new Error('saveFromTree is only supported in Node.js environments for TouchChat files.');
|
|
624
623
|
}
|
|
625
624
|
// Create a TouchChat database that matches the expected schema for loading
|
|
626
|
-
const tmpDir = mkTempDir('touchchat-export-');
|
|
625
|
+
const tmpDir = await mkTempDir('touchchat-export-');
|
|
627
626
|
const dbPath = join(tmpDir, 'vocab.c4v');
|
|
628
627
|
try {
|
|
629
628
|
const Database = requireBetterSqlite3();
|
|
@@ -918,19 +917,19 @@ class TouchChatProcessor extends BaseProcessor {
|
|
|
918
917
|
db.close();
|
|
919
918
|
// Create zip file with the database
|
|
920
919
|
const zip = await this.options.zipAdapter();
|
|
921
|
-
const data = readBinaryFromInput(dbPath);
|
|
920
|
+
const data = await readBinaryFromInput(dbPath);
|
|
922
921
|
const zipData = await zip.writeFiles([
|
|
923
922
|
{
|
|
924
923
|
name: 'vocab.c4v',
|
|
925
924
|
data,
|
|
926
925
|
},
|
|
927
926
|
]);
|
|
928
|
-
writeBinaryToPath(outputPath, zipData);
|
|
927
|
+
await writeBinaryToPath(outputPath, zipData);
|
|
929
928
|
}
|
|
930
929
|
finally {
|
|
931
930
|
// Clean up
|
|
932
|
-
if (pathExists(tmpDir)) {
|
|
933
|
-
removePath(tmpDir, { recursive: true, force: true });
|
|
931
|
+
if (await pathExists(tmpDir)) {
|
|
932
|
+
await removePath(tmpDir, { recursive: true, force: true });
|
|
934
933
|
}
|
|
935
934
|
}
|
|
936
935
|
}
|
|
@@ -1013,7 +1012,7 @@ class TouchChatProcessor extends BaseProcessor {
|
|
|
1013
1012
|
const outputPath = filePath.replace(/\.ce$/, '_translated.ce');
|
|
1014
1013
|
// Use existing processTexts method
|
|
1015
1014
|
await this.processTexts(filePath, translations, outputPath);
|
|
1016
|
-
return
|
|
1015
|
+
return outputPath;
|
|
1017
1016
|
}
|
|
1018
1017
|
catch (error) {
|
|
1019
1018
|
return Promise.reject(new Error(`Failed to generate translated download: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
@@ -1025,7 +1024,7 @@ class TouchChatProcessor extends BaseProcessor {
|
|
|
1025
1024
|
* @returns Promise with validation result
|
|
1026
1025
|
*/
|
|
1027
1026
|
async validate(filePath) {
|
|
1028
|
-
return TouchChatValidator.validateFile(filePath, this.options.fileAdapter);
|
|
1027
|
+
return await TouchChatValidator.validateFile(filePath, this.options.fileAdapter);
|
|
1029
1028
|
}
|
|
1030
1029
|
/**
|
|
1031
1030
|
* Extract symbol information from a TouchChat file for LLM-based translation.
|
|
@@ -1034,7 +1033,7 @@ class TouchChatProcessor extends BaseProcessor {
|
|
|
1034
1033
|
* This method uses shared translation utilities that work across all AAC formats.
|
|
1035
1034
|
*
|
|
1036
1035
|
* @param filePathOrBuffer - Path to TouchChat .ce file or buffer
|
|
1037
|
-
* @returns
|
|
1036
|
+
* @returns Promise resolving to symbol information for LLM processing
|
|
1038
1037
|
*/
|
|
1039
1038
|
async extractSymbolsForLLM(filePathOrBuffer) {
|
|
1040
1039
|
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
@@ -1064,7 +1063,7 @@ class TouchChatProcessor extends BaseProcessor {
|
|
|
1064
1063
|
* @param llmTranslations - Array of LLM translations with symbol info
|
|
1065
1064
|
* @param outputPath - Where to save the translated TouchChat file
|
|
1066
1065
|
* @param options - Translation options (e.g., allowPartial for testing)
|
|
1067
|
-
* @returns
|
|
1066
|
+
* @returns Promise resolving to a buffer of the translated TouchChat file
|
|
1068
1067
|
*/
|
|
1069
1068
|
async processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
|
|
1070
1069
|
const { readBinaryFromInput } = this.options.fileAdapter;
|
|
@@ -1109,7 +1108,7 @@ class TouchChatProcessor extends BaseProcessor {
|
|
|
1109
1108
|
});
|
|
1110
1109
|
// Save and return
|
|
1111
1110
|
await this.saveFromTree(tree, outputPath);
|
|
1112
|
-
return readBinaryFromInput(outputPath);
|
|
1111
|
+
return await readBinaryFromInput(outputPath);
|
|
1113
1112
|
}
|
|
1114
1113
|
}
|
|
1115
1114
|
export { TouchChatProcessor };
|
|
@@ -60,8 +60,9 @@ export function exportHistoryToBaton(entries, options) {
|
|
|
60
60
|
/**
|
|
61
61
|
* Read Grid 3 phrase history from a history.sqlite database and tag entries with their source.
|
|
62
62
|
*/
|
|
63
|
-
export function readGrid3History(historyDbPath) {
|
|
64
|
-
|
|
63
|
+
export async function readGrid3History(historyDbPath) {
|
|
64
|
+
const history = await readGrid3HistoryImpl(historyDbPath);
|
|
65
|
+
return history.map((e) => ({
|
|
65
66
|
...e,
|
|
66
67
|
source: 'Grid',
|
|
67
68
|
}));
|
|
@@ -69,8 +70,9 @@ export function readGrid3History(historyDbPath) {
|
|
|
69
70
|
/**
|
|
70
71
|
* Read Grid 3 history for a specific user/language combination.
|
|
71
72
|
*/
|
|
72
|
-
export function readGrid3HistoryForUser(userName, langCode) {
|
|
73
|
-
|
|
73
|
+
export async function readGrid3HistoryForUser(userName, langCode) {
|
|
74
|
+
const history = await readGrid3HistoryForUserImpl(userName, langCode);
|
|
75
|
+
return history.map((e) => ({
|
|
74
76
|
...e,
|
|
75
77
|
source: 'Grid',
|
|
76
78
|
}));
|
|
@@ -78,39 +80,43 @@ export function readGrid3HistoryForUser(userName, langCode) {
|
|
|
78
80
|
/**
|
|
79
81
|
* Read every available Grid 3 history database on the machine.
|
|
80
82
|
*/
|
|
81
|
-
export function readAllGrid3History() {
|
|
82
|
-
|
|
83
|
+
export async function readAllGrid3History() {
|
|
84
|
+
const history = await readAllGrid3HistoryImpl();
|
|
85
|
+
return history.map((e) => ({ ...e, source: 'Grid' }));
|
|
83
86
|
}
|
|
84
87
|
/**
|
|
85
88
|
* Read Snap button usage from a pageset database and tag entries with source.
|
|
86
89
|
*/
|
|
87
|
-
export function readSnapUsage(pagesetPath) {
|
|
88
|
-
|
|
90
|
+
export async function readSnapUsage(pagesetPath) {
|
|
91
|
+
const usage = await readSnapUsageImpl(pagesetPath);
|
|
92
|
+
return usage.map((e) => ({ ...e, source: 'Snap' }));
|
|
89
93
|
}
|
|
90
94
|
/**
|
|
91
95
|
* Read Snap usage for a specific user across all discovered pagesets.
|
|
92
96
|
*/
|
|
93
|
-
export function readSnapUsageForUser(userId, packageNamePattern = 'TobiiDynavox') {
|
|
94
|
-
|
|
97
|
+
export async function readSnapUsageForUser(userId, packageNamePattern = 'TobiiDynavox') {
|
|
98
|
+
const usage = await readSnapUsageForUserImpl(userId, packageNamePattern);
|
|
99
|
+
return usage.map((e) => ({
|
|
95
100
|
...e,
|
|
96
101
|
source: 'Snap',
|
|
97
102
|
}));
|
|
98
103
|
}
|
|
99
|
-
export function listSnapUsers() {
|
|
100
|
-
return findSnapUsers();
|
|
104
|
+
export async function listSnapUsers() {
|
|
105
|
+
return await findSnapUsers();
|
|
101
106
|
}
|
|
102
107
|
/**
|
|
103
108
|
* List Grid 3 users on the current machine.
|
|
104
109
|
*/
|
|
105
|
-
export function listGrid3Users() {
|
|
106
|
-
return findGrid3Users();
|
|
110
|
+
export async function listGrid3Users() {
|
|
111
|
+
return await findGrid3Users();
|
|
107
112
|
}
|
|
108
113
|
/**
|
|
109
114
|
* Convenience helper to gather all available history across Grid 3 and Snap.
|
|
110
115
|
* Returns an empty array if no history files are present.
|
|
111
116
|
*/
|
|
112
|
-
export function collectUnifiedHistory() {
|
|
113
|
-
const gridHistory = readAllGrid3History();
|
|
114
|
-
const
|
|
115
|
-
|
|
117
|
+
export async function collectUnifiedHistory() {
|
|
118
|
+
const gridHistory = await readAllGrid3History();
|
|
119
|
+
const users = await findSnapUsers();
|
|
120
|
+
const snapHistory = await Promise.all(users.map(async (u) => await readSnapUsageForUser(u.userId)));
|
|
121
|
+
return [...gridHistory, ...snapHistory.flat()];
|
|
116
122
|
}
|
|
@@ -23,7 +23,7 @@ export class ComparisonAnalyzer {
|
|
|
23
23
|
/**
|
|
24
24
|
* Compare two board sets
|
|
25
25
|
*/
|
|
26
|
-
compare(targetResult, compareResult, options) {
|
|
26
|
+
async compare(targetResult, compareResult, options) {
|
|
27
27
|
// Create base result from target
|
|
28
28
|
const baseResult = { ...targetResult };
|
|
29
29
|
// Create word maps with normalized keys
|
|
@@ -77,7 +77,7 @@ export class ComparisonAnalyzer {
|
|
|
77
77
|
};
|
|
78
78
|
});
|
|
79
79
|
// Calculate CARE components
|
|
80
|
-
const careComponents = this.calculateCareComponents(targetResult, compareResult, overlappingWords, options);
|
|
80
|
+
const careComponents = await this.calculateCareComponents(targetResult, compareResult, overlappingWords, options);
|
|
81
81
|
// Analyze high/low effort words
|
|
82
82
|
const highEffortWords = [];
|
|
83
83
|
const lowEffortWords = [];
|
|
@@ -110,7 +110,7 @@ export class ComparisonAnalyzer {
|
|
|
110
110
|
// Sentence analysis
|
|
111
111
|
let sentences = [];
|
|
112
112
|
if (options?.includeSentences) {
|
|
113
|
-
const testSentences = this.referenceLoader.loadSentences();
|
|
113
|
+
const testSentences = await this.referenceLoader.loadSentences();
|
|
114
114
|
const targetSentences = this.sentenceAnalyzer.analyzeSentences(targetResult, testSentences);
|
|
115
115
|
const compareSentences = this.sentenceAnalyzer.analyzeSentences(compareResult, testSentences);
|
|
116
116
|
sentences = targetSentences.map((ts, idx) => ({
|
|
@@ -123,7 +123,7 @@ export class ComparisonAnalyzer {
|
|
|
123
123
|
}));
|
|
124
124
|
}
|
|
125
125
|
// Core vocabulary analysis
|
|
126
|
-
const coreLists = this.referenceLoader.loadCoreLists();
|
|
126
|
+
const coreLists = await this.referenceLoader.loadCoreLists();
|
|
127
127
|
const cores = {};
|
|
128
128
|
coreLists.forEach((list) => {
|
|
129
129
|
let targetTotal = 0;
|
|
@@ -171,8 +171,8 @@ export class ComparisonAnalyzer {
|
|
|
171
171
|
}
|
|
172
172
|
});
|
|
173
173
|
// Fringe vocabulary analysis
|
|
174
|
-
const fringeWords = this.analyzeFringe(targetWords, compareWords);
|
|
175
|
-
const commonFringeWords = this.analyzeCommonFringe(targetWords, compareWords);
|
|
174
|
+
const fringeWords = await this.analyzeFringe(targetWords, compareWords);
|
|
175
|
+
const commonFringeWords = await this.analyzeCommonFringe(targetWords, compareWords);
|
|
176
176
|
return {
|
|
177
177
|
...baseResult,
|
|
178
178
|
buttons: enrichedButtons,
|
|
@@ -214,9 +214,9 @@ export class ComparisonAnalyzer {
|
|
|
214
214
|
/**
|
|
215
215
|
* Calculate CARE component scores
|
|
216
216
|
*/
|
|
217
|
-
calculateCareComponents(targetResult, compareResult, _overlappingWords, options) {
|
|
217
|
+
async calculateCareComponents(targetResult, compareResult, _overlappingWords, options) {
|
|
218
218
|
// Load common words with baseline efforts (matching Ruby line 527-534)
|
|
219
|
-
const commonWordsData = this.referenceLoader.loadCommonWords();
|
|
219
|
+
const commonWordsData = await this.referenceLoader.loadCommonWords();
|
|
220
220
|
const commonWords = new Map();
|
|
221
221
|
commonWordsData.words.forEach((word) => {
|
|
222
222
|
commonWords.set(word.toLowerCase(), commonWordsData.efforts[word] || 0);
|
|
@@ -268,10 +268,10 @@ export class ComparisonAnalyzer {
|
|
|
268
268
|
}
|
|
269
269
|
});
|
|
270
270
|
// Load reference data
|
|
271
|
-
const coreLists = this.referenceLoader.loadCoreLists();
|
|
272
|
-
const fringe = this.referenceLoader.loadFringe();
|
|
273
|
-
const commonFringe = this.referenceLoader.loadCommonFringe();
|
|
274
|
-
const sentences = this.referenceLoader.loadSentences();
|
|
271
|
+
const coreLists = await this.referenceLoader.loadCoreLists();
|
|
272
|
+
const fringe = await this.referenceLoader.loadFringe();
|
|
273
|
+
const commonFringe = await this.referenceLoader.loadCommonFringe();
|
|
274
|
+
const sentences = await this.referenceLoader.loadSentences();
|
|
275
275
|
// Calculate core coverage and effort (matching Ruby lines 609-647)
|
|
276
276
|
let coreCount = 0;
|
|
277
277
|
let compCoreCount = 0;
|
|
@@ -423,8 +423,8 @@ export class ComparisonAnalyzer {
|
|
|
423
423
|
/**
|
|
424
424
|
* Analyze fringe vocabulary
|
|
425
425
|
*/
|
|
426
|
-
analyzeFringe(targetWords, compareWords) {
|
|
427
|
-
const fringe = this.referenceLoader.loadFringe();
|
|
426
|
+
async analyzeFringe(targetWords, compareWords) {
|
|
427
|
+
const fringe = await this.referenceLoader.loadFringe();
|
|
428
428
|
const result = [];
|
|
429
429
|
fringe.forEach((word) => {
|
|
430
430
|
const key = this.normalize(word);
|
|
@@ -444,8 +444,8 @@ export class ComparisonAnalyzer {
|
|
|
444
444
|
/**
|
|
445
445
|
* Analyze common fringe vocabulary
|
|
446
446
|
*/
|
|
447
|
-
analyzeCommonFringe(targetWords, compareWords) {
|
|
448
|
-
const fringe = this.referenceLoader.loadFringe();
|
|
447
|
+
async analyzeCommonFringe(targetWords, compareWords) {
|
|
448
|
+
const fringe = await this.referenceLoader.loadFringe();
|
|
449
449
|
const result = [];
|
|
450
450
|
fringe.forEach((word) => {
|
|
451
451
|
const key = this.normalize(word);
|
|
@@ -13,12 +13,12 @@ export class VocabularyAnalyzer {
|
|
|
13
13
|
/**
|
|
14
14
|
* Analyze vocabulary coverage against core lists
|
|
15
15
|
*/
|
|
16
|
-
analyze(metrics, options) {
|
|
16
|
+
async analyze(metrics, options) {
|
|
17
17
|
// const locale = options?.locale || metrics.locale || 'en';
|
|
18
18
|
const highEffortThreshold = options?.highEffortThreshold || 5.0;
|
|
19
19
|
const lowEffortThreshold = options?.lowEffortThreshold || 2.0;
|
|
20
20
|
// Load reference data
|
|
21
|
-
const coreLists = this.referenceLoader.loadCoreLists();
|
|
21
|
+
const coreLists = await this.referenceLoader.loadCoreLists();
|
|
22
22
|
// Create word to effort map (using lowercase keys for matching)
|
|
23
23
|
const wordEffortMap = new Map();
|
|
24
24
|
metrics.buttons.forEach((btn) => {
|
|
@@ -5,34 +5,34 @@ export class InMemoryReferenceLoader {
|
|
|
5
5
|
constructor(data) {
|
|
6
6
|
this.data = data;
|
|
7
7
|
}
|
|
8
|
-
loadCoreLists() {
|
|
9
|
-
return this.data.coreLists;
|
|
8
|
+
async loadCoreLists() {
|
|
9
|
+
return Promise.resolve(this.data.coreLists);
|
|
10
10
|
}
|
|
11
|
-
loadCommonWords() {
|
|
12
|
-
return this.data.commonWords;
|
|
11
|
+
async loadCommonWords() {
|
|
12
|
+
return Promise.resolve(this.data.commonWords);
|
|
13
13
|
}
|
|
14
|
-
loadSynonyms() {
|
|
15
|
-
return this.data.synonyms;
|
|
14
|
+
async loadSynonyms() {
|
|
15
|
+
return Promise.resolve(this.data.synonyms);
|
|
16
16
|
}
|
|
17
|
-
loadSentences() {
|
|
18
|
-
return this.data.sentences;
|
|
17
|
+
async loadSentences() {
|
|
18
|
+
return Promise.resolve(this.data.sentences);
|
|
19
19
|
}
|
|
20
|
-
loadFringe() {
|
|
21
|
-
return this.data.fringe;
|
|
20
|
+
async loadFringe() {
|
|
21
|
+
return Promise.resolve(this.data.fringe);
|
|
22
22
|
}
|
|
23
|
-
loadBaseWords() {
|
|
24
|
-
return this.data.baseWords;
|
|
23
|
+
async loadBaseWords() {
|
|
24
|
+
return Promise.resolve(this.data.baseWords);
|
|
25
25
|
}
|
|
26
|
-
loadCommonFringe() {
|
|
26
|
+
async loadCommonFringe() {
|
|
27
27
|
const commonWords = new Set(this.data.commonWords.words.map((w) => w.toLowerCase()));
|
|
28
28
|
const coreWords = new Set();
|
|
29
29
|
this.data.coreLists.forEach((list) => {
|
|
30
30
|
list.words.forEach((word) => coreWords.add(word.toLowerCase()));
|
|
31
31
|
});
|
|
32
|
-
return Array.from(commonWords).filter((word) => !coreWords.has(word));
|
|
32
|
+
return Promise.resolve(Array.from(commonWords).filter((word) => !coreWords.has(word)));
|
|
33
33
|
}
|
|
34
|
-
loadAll() {
|
|
35
|
-
return this.data;
|
|
34
|
+
async loadAll() {
|
|
35
|
+
return Promise.resolve(this.data);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
export async function loadReferenceDataFromUrl(baseUrl, locale = 'en') {
|