@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
|
@@ -86,15 +86,14 @@ class ApplePanelsProcessor extends BaseProcessor {
|
|
|
86
86
|
}
|
|
87
87
|
async loadIntoTree(filePathOrBuffer) {
|
|
88
88
|
const { readBinaryFromInput, readTextFromInput, pathExists, getFileSize, join } = this.options.fileAdapter;
|
|
89
|
-
await Promise.resolve();
|
|
90
89
|
const filename = typeof filePathOrBuffer === 'string' ? getBasename(filePathOrBuffer) : 'upload.plist';
|
|
91
90
|
let buffer;
|
|
92
91
|
try {
|
|
93
92
|
if (typeof filePathOrBuffer === 'string') {
|
|
94
93
|
if (filePathOrBuffer.endsWith('.ascconfig')) {
|
|
95
94
|
const panelDefsPath = join(filePathOrBuffer, 'Contents', 'Resources', 'PanelDefinitions.plist');
|
|
96
|
-
if (pathExists(panelDefsPath)) {
|
|
97
|
-
buffer = readBinaryFromInput(panelDefsPath);
|
|
95
|
+
if (await pathExists(panelDefsPath)) {
|
|
96
|
+
buffer = await readBinaryFromInput(panelDefsPath);
|
|
98
97
|
}
|
|
99
98
|
else {
|
|
100
99
|
const validation = buildValidationResultFromMessage({
|
|
@@ -109,13 +108,13 @@ class ApplePanelsProcessor extends BaseProcessor {
|
|
|
109
108
|
}
|
|
110
109
|
}
|
|
111
110
|
else {
|
|
112
|
-
buffer = readBinaryFromInput(filePathOrBuffer);
|
|
111
|
+
buffer = await readBinaryFromInput(filePathOrBuffer);
|
|
113
112
|
}
|
|
114
113
|
}
|
|
115
114
|
else {
|
|
116
|
-
buffer = readBinaryFromInput(filePathOrBuffer);
|
|
115
|
+
buffer = await readBinaryFromInput(filePathOrBuffer);
|
|
117
116
|
}
|
|
118
|
-
const content = readTextFromInput(buffer);
|
|
117
|
+
const content = await readTextFromInput(buffer);
|
|
119
118
|
const parsedData = plist.parse(content);
|
|
120
119
|
let panelsData = [];
|
|
121
120
|
if (Array.isArray(parsedData.panels)) {
|
|
@@ -244,10 +243,12 @@ class ApplePanelsProcessor extends BaseProcessor {
|
|
|
244
243
|
const validation = buildValidationResultFromMessage({
|
|
245
244
|
filename,
|
|
246
245
|
filesize: typeof filePathOrBuffer === 'string'
|
|
247
|
-
? (() => {
|
|
248
|
-
return
|
|
246
|
+
? await (async () => {
|
|
247
|
+
return (await pathExists(filePathOrBuffer))
|
|
248
|
+
? await getFileSize(filePathOrBuffer)
|
|
249
|
+
: 0;
|
|
249
250
|
})()
|
|
250
|
-
: readBinaryFromInput(filePathOrBuffer).byteLength,
|
|
251
|
+
: (await readBinaryFromInput(filePathOrBuffer)).byteLength,
|
|
251
252
|
format: 'applepanels',
|
|
252
253
|
message: err?.message || 'Failed to parse Apple Panels file',
|
|
253
254
|
type: 'parse',
|
|
@@ -307,15 +308,14 @@ class ApplePanelsProcessor extends BaseProcessor {
|
|
|
307
308
|
// Save the translated tree to the requested location and return its content
|
|
308
309
|
await this.saveFromTree(tree, outputPath);
|
|
309
310
|
if (outputPath.endsWith('.plist')) {
|
|
310
|
-
return readBinaryFromInput(outputPath);
|
|
311
|
+
return await readBinaryFromInput(outputPath);
|
|
311
312
|
}
|
|
312
313
|
const configPath = outputPath.endsWith('.ascconfig') ? outputPath : `${outputPath}.ascconfig`;
|
|
313
314
|
const panelDefsPath = join(configPath, 'Contents', 'Resources', 'PanelDefinitions.plist');
|
|
314
|
-
return readBinaryFromInput(panelDefsPath);
|
|
315
|
+
return await readBinaryFromInput(panelDefsPath);
|
|
315
316
|
}
|
|
316
317
|
async saveFromTree(tree, outputPath) {
|
|
317
318
|
const { writeTextToPath, pathExists, mkDir, join, dirname } = this.options.fileAdapter;
|
|
318
|
-
await Promise.resolve();
|
|
319
319
|
// Support two output modes:
|
|
320
320
|
// 1) Single-file .plist (PanelDefinitions.plist content written directly)
|
|
321
321
|
// 2) Apple Panels bundle folder (*.ascconfig) with Contents/Resources structure
|
|
@@ -328,12 +328,12 @@ class ApplePanelsProcessor extends BaseProcessor {
|
|
|
328
328
|
configPath = outputPath.endsWith('.ascconfig') ? outputPath : `${outputPath}.ascconfig`;
|
|
329
329
|
contentsPath = join(configPath, 'Contents');
|
|
330
330
|
resourcesPath = join(contentsPath, 'Resources');
|
|
331
|
-
if (!pathExists(configPath))
|
|
332
|
-
mkDir(configPath, { recursive: true });
|
|
333
|
-
if (!pathExists(contentsPath))
|
|
334
|
-
mkDir(contentsPath, { recursive: true });
|
|
335
|
-
if (!pathExists(resourcesPath))
|
|
336
|
-
mkDir(resourcesPath, { recursive: true });
|
|
331
|
+
if (!(await pathExists(configPath)))
|
|
332
|
+
await mkDir(configPath, { recursive: true });
|
|
333
|
+
if (!(await pathExists(contentsPath)))
|
|
334
|
+
await mkDir(contentsPath, { recursive: true });
|
|
335
|
+
if (!(await pathExists(resourcesPath)))
|
|
336
|
+
await mkDir(resourcesPath, { recursive: true });
|
|
337
337
|
// Create Info.plist (bundle mode only)
|
|
338
338
|
const infoPlist = {
|
|
339
339
|
ASCConfigurationDisplayName: tree.metadata?.name || 'AAC Processors Export',
|
|
@@ -349,10 +349,10 @@ class ApplePanelsProcessor extends BaseProcessor {
|
|
|
349
349
|
`Generated by AAC Processors${tree.metadata?.author ? ` - Author: ${tree.metadata.author}` : ''}`,
|
|
350
350
|
};
|
|
351
351
|
const infoPlistContent = plist.build(infoPlist);
|
|
352
|
-
writeTextToPath(join(contentsPath, 'Info.plist'), infoPlistContent);
|
|
352
|
+
await writeTextToPath(join(contentsPath, 'Info.plist'), infoPlistContent);
|
|
353
353
|
// Create AssetIndex.plist (empty)
|
|
354
354
|
const assetIndexContent = plist.build({});
|
|
355
|
-
writeTextToPath(join(resourcesPath, 'AssetIndex.plist'), assetIndexContent);
|
|
355
|
+
await writeTextToPath(join(resourcesPath, 'AssetIndex.plist'), assetIndexContent);
|
|
356
356
|
}
|
|
357
357
|
// Build PanelDefinitions content from tree
|
|
358
358
|
const panelsDict = {};
|
|
@@ -479,13 +479,13 @@ class ApplePanelsProcessor extends BaseProcessor {
|
|
|
479
479
|
if (isSinglePlist) {
|
|
480
480
|
// Write single PanelDefinitions.plist file directly
|
|
481
481
|
const dir = dirname(outputPath);
|
|
482
|
-
if (!pathExists(dir))
|
|
483
|
-
mkDir(dir, { recursive: true });
|
|
484
|
-
writeTextToPath(outputPath, panelDefsContent);
|
|
482
|
+
if (!(await pathExists(dir)))
|
|
483
|
+
await mkDir(dir, { recursive: true });
|
|
484
|
+
await writeTextToPath(outputPath, panelDefsContent);
|
|
485
485
|
}
|
|
486
486
|
else {
|
|
487
487
|
// Write into bundle structure
|
|
488
|
-
writeTextToPath(join(resourcesPath, 'PanelDefinitions.plist'), panelDefsContent);
|
|
488
|
+
await writeTextToPath(join(resourcesPath, 'PanelDefinitions.plist'), panelDefsContent);
|
|
489
489
|
}
|
|
490
490
|
}
|
|
491
491
|
createApplePanelsAction(button) {
|
|
@@ -562,7 +562,7 @@ class AstericsGridProcessor extends BaseProcessor {
|
|
|
562
562
|
});
|
|
563
563
|
}
|
|
564
564
|
// Also extract texts from the raw file for comprehensive coverage
|
|
565
|
-
const rawTexts = this.extractRawTexts(filePathOrBuffer);
|
|
565
|
+
const rawTexts = await this.extractRawTexts(filePathOrBuffer);
|
|
566
566
|
rawTexts.forEach((text) => {
|
|
567
567
|
if (text && !texts.includes(text)) {
|
|
568
568
|
texts.push(text);
|
|
@@ -570,9 +570,9 @@ class AstericsGridProcessor extends BaseProcessor {
|
|
|
570
570
|
});
|
|
571
571
|
return texts;
|
|
572
572
|
}
|
|
573
|
-
extractRawTexts(filePathOrBuffer) {
|
|
573
|
+
async extractRawTexts(filePathOrBuffer) {
|
|
574
574
|
const { readTextFromInput } = this.options.fileAdapter;
|
|
575
|
-
let content = readTextFromInput(filePathOrBuffer);
|
|
575
|
+
let content = await readTextFromInput(filePathOrBuffer);
|
|
576
576
|
// Remove BOM if present
|
|
577
577
|
if (content.charCodeAt(0) === 0xfeff) {
|
|
578
578
|
content = content.slice(1);
|
|
@@ -653,12 +653,11 @@ class AstericsGridProcessor extends BaseProcessor {
|
|
|
653
653
|
}
|
|
654
654
|
async loadIntoTree(filePathOrBuffer) {
|
|
655
655
|
const { readBinaryFromInput, readTextFromInput } = this.options.fileAdapter;
|
|
656
|
-
await Promise.resolve();
|
|
657
656
|
const tree = new AACTree();
|
|
658
657
|
const filename = typeof filePathOrBuffer === 'string' ? getBasename(filePathOrBuffer) : 'upload.grd';
|
|
659
|
-
const buffer = readBinaryFromInput(filePathOrBuffer);
|
|
658
|
+
const buffer = await readBinaryFromInput(filePathOrBuffer);
|
|
660
659
|
try {
|
|
661
|
-
let content = readTextFromInput(buffer);
|
|
660
|
+
let content = await readTextFromInput(buffer);
|
|
662
661
|
// Remove BOM if present
|
|
663
662
|
if (content.charCodeAt(0) === 0xfeff) {
|
|
664
663
|
content = content.slice(1);
|
|
@@ -1052,8 +1051,7 @@ class AstericsGridProcessor extends BaseProcessor {
|
|
|
1052
1051
|
}
|
|
1053
1052
|
async processTexts(filePathOrBuffer, translations, outputPath) {
|
|
1054
1053
|
const { readTextFromInput, readBinaryFromInput, writeTextToPath } = this.options.fileAdapter;
|
|
1055
|
-
await
|
|
1056
|
-
let content = readTextFromInput(filePathOrBuffer);
|
|
1054
|
+
let content = await readTextFromInput(filePathOrBuffer);
|
|
1057
1055
|
// Remove BOM if present
|
|
1058
1056
|
if (content.charCodeAt(0) === 0xfeff) {
|
|
1059
1057
|
content = content.slice(1);
|
|
@@ -1062,8 +1060,8 @@ class AstericsGridProcessor extends BaseProcessor {
|
|
|
1062
1060
|
// Apply translations directly to the JSON structure for comprehensive coverage
|
|
1063
1061
|
this.applyTranslationsToGridFile(grdFile, translations);
|
|
1064
1062
|
// Write the translated file
|
|
1065
|
-
writeTextToPath(outputPath, JSON.stringify(grdFile, null, 2));
|
|
1066
|
-
return readBinaryFromInput(outputPath);
|
|
1063
|
+
await writeTextToPath(outputPath, JSON.stringify(grdFile, null, 2));
|
|
1064
|
+
return await readBinaryFromInput(outputPath);
|
|
1067
1065
|
}
|
|
1068
1066
|
applyTranslationsToGridFile(grdFile, translations) {
|
|
1069
1067
|
grdFile.grids.forEach((grid) => {
|
|
@@ -1176,7 +1174,6 @@ class AstericsGridProcessor extends BaseProcessor {
|
|
|
1176
1174
|
}
|
|
1177
1175
|
async saveFromTree(tree, outputPath) {
|
|
1178
1176
|
const { writeTextToPath } = this.options.fileAdapter;
|
|
1179
|
-
await Promise.resolve();
|
|
1180
1177
|
// Use default Asterics Grid styling instead of taking from first page
|
|
1181
1178
|
// This prevents issues where the first page has unusual colors (like purple)
|
|
1182
1179
|
const defaultPageStyle = {
|
|
@@ -1379,14 +1376,14 @@ class AstericsGridProcessor extends BaseProcessor {
|
|
|
1379
1376
|
},
|
|
1380
1377
|
},
|
|
1381
1378
|
};
|
|
1382
|
-
writeTextToPath(outputPath, JSON.stringify(grdFile, null, 2));
|
|
1379
|
+
await writeTextToPath(outputPath, JSON.stringify(grdFile, null, 2));
|
|
1383
1380
|
}
|
|
1384
1381
|
/**
|
|
1385
1382
|
* Add audio recording to a specific grid element
|
|
1386
1383
|
*/
|
|
1387
|
-
addAudioToElement(filePath, elementId, audioData, metadata) {
|
|
1384
|
+
async addAudioToElement(filePath, elementId, audioData, metadata) {
|
|
1388
1385
|
const { readTextFromInput, writeTextToPath } = this.options.fileAdapter;
|
|
1389
|
-
let content = readTextFromInput(filePath);
|
|
1386
|
+
let content = await readTextFromInput(filePath);
|
|
1390
1387
|
// Remove BOM if present
|
|
1391
1388
|
if (content.charCodeAt(0) === 0xfeff) {
|
|
1392
1389
|
content = content.slice(1);
|
|
@@ -1429,32 +1426,33 @@ class AstericsGridProcessor extends BaseProcessor {
|
|
|
1429
1426
|
throw new Error(`Element with ID ${elementId} not found`);
|
|
1430
1427
|
}
|
|
1431
1428
|
// Write back to file
|
|
1432
|
-
writeTextToPath(filePath, JSON.stringify(grdFile, null, 2));
|
|
1429
|
+
await writeTextToPath(filePath, JSON.stringify(grdFile, null, 2));
|
|
1433
1430
|
}
|
|
1434
1431
|
/**
|
|
1435
1432
|
* Create a copy of the grid file with audio recordings added
|
|
1436
1433
|
*/
|
|
1437
|
-
createAudioEnhancedGridFile(sourceFilePath, targetFilePath, audioMappings) {
|
|
1434
|
+
async createAudioEnhancedGridFile(sourceFilePath, targetFilePath, audioMappings) {
|
|
1438
1435
|
const { writeBinaryToPath, readBinaryFromInput } = this.options.fileAdapter;
|
|
1439
1436
|
// Copy the source file to target
|
|
1440
|
-
writeBinaryToPath(targetFilePath, readBinaryFromInput(sourceFilePath));
|
|
1437
|
+
await writeBinaryToPath(targetFilePath, await readBinaryFromInput(sourceFilePath));
|
|
1441
1438
|
// Add audio recordings to the copy
|
|
1442
|
-
audioMappings.
|
|
1439
|
+
await Promise.all(Object.entries(audioMappings).map(async (item) => {
|
|
1440
|
+
const [elementId, audioInfo] = item;
|
|
1443
1441
|
try {
|
|
1444
|
-
this.addAudioToElement(targetFilePath, elementId, audioInfo.audioData, audioInfo.metadata);
|
|
1442
|
+
await this.addAudioToElement(targetFilePath, elementId, audioInfo.audioData, audioInfo.metadata);
|
|
1445
1443
|
}
|
|
1446
1444
|
catch (error) {
|
|
1447
1445
|
// Failed to add audio to element - continue with others
|
|
1448
1446
|
console.warn(`Failed to add audio to element ${elementId}:`, error);
|
|
1449
1447
|
}
|
|
1450
|
-
});
|
|
1448
|
+
}));
|
|
1451
1449
|
}
|
|
1452
1450
|
/**
|
|
1453
1451
|
* Extract all element IDs from the grid file for audio mapping
|
|
1454
1452
|
*/
|
|
1455
|
-
getElementIds(filePathOrBuffer) {
|
|
1453
|
+
async getElementIds(filePathOrBuffer) {
|
|
1456
1454
|
const { readTextFromInput } = this.options.fileAdapter;
|
|
1457
|
-
let content = readTextFromInput(filePathOrBuffer);
|
|
1455
|
+
let content = await readTextFromInput(filePathOrBuffer);
|
|
1458
1456
|
// Remove BOM if present
|
|
1459
1457
|
if (content.charCodeAt(0) === 0xfeff) {
|
|
1460
1458
|
content = content.slice(1);
|
|
@@ -1476,9 +1474,9 @@ class AstericsGridProcessor extends BaseProcessor {
|
|
|
1476
1474
|
/**
|
|
1477
1475
|
* Check if an element has audio recording
|
|
1478
1476
|
*/
|
|
1479
|
-
hasAudioRecording(filePathOrBuffer, elementId) {
|
|
1477
|
+
async hasAudioRecording(filePathOrBuffer, elementId) {
|
|
1480
1478
|
const { readTextFromInput } = this.options.fileAdapter;
|
|
1481
|
-
let content = readTextFromInput(filePathOrBuffer);
|
|
1479
|
+
let content = await readTextFromInput(filePathOrBuffer);
|
|
1482
1480
|
// Remove BOM if present
|
|
1483
1481
|
if (content.charCodeAt(0) === 0xfeff) {
|
|
1484
1482
|
content = content.slice(1);
|
|
@@ -49,8 +49,7 @@ class DotProcessor extends BaseProcessor {
|
|
|
49
49
|
}
|
|
50
50
|
async extractTexts(filePathOrBuffer) {
|
|
51
51
|
const { readTextFromInput } = this.options.fileAdapter;
|
|
52
|
-
await
|
|
53
|
-
const content = readTextFromInput(filePathOrBuffer);
|
|
52
|
+
const content = await readTextFromInput(filePathOrBuffer);
|
|
54
53
|
const { nodes, edges } = this.parseDotFile(content);
|
|
55
54
|
const texts = [];
|
|
56
55
|
// Collect node labels
|
|
@@ -67,12 +66,11 @@ class DotProcessor extends BaseProcessor {
|
|
|
67
66
|
}
|
|
68
67
|
async loadIntoTree(filePathOrBuffer) {
|
|
69
68
|
const { readBinaryFromInput, readTextFromInput } = this.options.fileAdapter;
|
|
70
|
-
await Promise.resolve();
|
|
71
69
|
const filename = typeof filePathOrBuffer === 'string' ? getBasename(filePathOrBuffer) : 'upload.dot';
|
|
72
|
-
const buffer = readBinaryFromInput(filePathOrBuffer);
|
|
70
|
+
const buffer = await readBinaryFromInput(filePathOrBuffer);
|
|
73
71
|
const filesize = buffer.byteLength;
|
|
74
72
|
try {
|
|
75
|
-
const content = readTextFromInput(buffer);
|
|
73
|
+
const content = await readTextFromInput(buffer);
|
|
76
74
|
if (!content || content.trim().length === 0) {
|
|
77
75
|
const validation = buildValidationResultFromMessage({
|
|
78
76
|
filename,
|
|
@@ -157,8 +155,7 @@ class DotProcessor extends BaseProcessor {
|
|
|
157
155
|
}
|
|
158
156
|
async processTexts(filePathOrBuffer, translations, outputPath) {
|
|
159
157
|
const { readTextFromInput, writeBinaryToPath } = this.options.fileAdapter;
|
|
160
|
-
await
|
|
161
|
-
const content = readTextFromInput(filePathOrBuffer);
|
|
158
|
+
const content = await readTextFromInput(filePathOrBuffer);
|
|
162
159
|
let translatedContent = content;
|
|
163
160
|
translations.forEach((translation, text) => {
|
|
164
161
|
if (typeof text === 'string' && typeof translation === 'string') {
|
|
@@ -169,12 +166,11 @@ class DotProcessor extends BaseProcessor {
|
|
|
169
166
|
}
|
|
170
167
|
});
|
|
171
168
|
const resultBuffer = encodeText(translatedContent || '');
|
|
172
|
-
writeBinaryToPath(outputPath, resultBuffer);
|
|
169
|
+
await writeBinaryToPath(outputPath, resultBuffer);
|
|
173
170
|
return resultBuffer;
|
|
174
171
|
}
|
|
175
172
|
async saveFromTree(tree, _outputPath) {
|
|
176
173
|
const { writeTextToPath } = this.options.fileAdapter;
|
|
177
|
-
await Promise.resolve();
|
|
178
174
|
let dotContent = `digraph "${tree.metadata?.name || 'AACBoard'}" {\n`;
|
|
179
175
|
// Helper to escape DOT string
|
|
180
176
|
const escapeDotString = (str) => {
|
|
@@ -204,7 +200,7 @@ class DotProcessor extends BaseProcessor {
|
|
|
204
200
|
});
|
|
205
201
|
}
|
|
206
202
|
dotContent += '}\n';
|
|
207
|
-
writeTextToPath(_outputPath, dotContent);
|
|
203
|
+
await writeTextToPath(_outputPath, dotContent);
|
|
208
204
|
}
|
|
209
205
|
/**
|
|
210
206
|
* Extract strings with metadata for aac-tools-platform compatibility
|
|
@@ -170,7 +170,7 @@ export function getCommonDocumentsPath() {
|
|
|
170
170
|
* C:\Users\Public\Documents\Smartbox\Grid 3\Users\{UserName}\Grid Sets\
|
|
171
171
|
* @returns Array of Grid3 user path information
|
|
172
172
|
*/
|
|
173
|
-
export function findGrid3UserPaths(fileAdapter = defaultFileAdapter) {
|
|
173
|
+
export async function findGrid3UserPaths(fileAdapter = defaultFileAdapter) {
|
|
174
174
|
const { pathExists, listDir, isDirectory } = fileAdapter;
|
|
175
175
|
const results = [];
|
|
176
176
|
// Only works on Windows
|
|
@@ -182,26 +182,26 @@ export function findGrid3UserPaths(fileAdapter = defaultFileAdapter) {
|
|
|
182
182
|
// Use Windows path joining so tests that mock a Windows platform stay consistent even on POSIX runners
|
|
183
183
|
const grid3BasePath = joinWin32(commonDocs, 'Smartbox', 'Grid 3', 'Users');
|
|
184
184
|
// Check if Grid3 Users directory exists
|
|
185
|
-
if (!pathExists(grid3BasePath)) {
|
|
185
|
+
if (!(await pathExists(grid3BasePath))) {
|
|
186
186
|
return results;
|
|
187
187
|
}
|
|
188
188
|
// Enumerate users
|
|
189
|
-
const users = listDir(grid3BasePath);
|
|
189
|
+
const users = await listDir(grid3BasePath);
|
|
190
190
|
for (const userDir of users) {
|
|
191
|
-
if (!isDirectory(userDir))
|
|
191
|
+
if (!(await isDirectory(userDir)))
|
|
192
192
|
continue;
|
|
193
193
|
const userName = userDir;
|
|
194
194
|
const userPath = joinWin32(grid3BasePath, userName);
|
|
195
195
|
// Enumerate language codes
|
|
196
|
-
const langDirs = listDir(userPath);
|
|
196
|
+
const langDirs = await listDir(userPath);
|
|
197
197
|
for (const langDir of langDirs) {
|
|
198
|
-
if (!isDirectory(langDir))
|
|
198
|
+
if (!(await isDirectory(langDir)))
|
|
199
199
|
continue;
|
|
200
200
|
const langCode = langDir;
|
|
201
201
|
const basePath = joinWin32(userPath, langCode);
|
|
202
202
|
const historyDbPath = joinWin32(basePath, 'Phrases', 'history.sqlite');
|
|
203
203
|
// Only include if history database exists
|
|
204
|
-
if (pathExists(historyDbPath)) {
|
|
204
|
+
if (await pathExists(historyDbPath)) {
|
|
205
205
|
results.push({
|
|
206
206
|
userName,
|
|
207
207
|
langCode,
|
|
@@ -222,21 +222,22 @@ export function findGrid3UserPaths(fileAdapter = defaultFileAdapter) {
|
|
|
222
222
|
* Convenience method that returns just the database file paths
|
|
223
223
|
* @returns Array of paths to history.sqlite files
|
|
224
224
|
*/
|
|
225
|
-
export function findGrid3HistoryDatabases(fileAdapter) {
|
|
226
|
-
|
|
225
|
+
export async function findGrid3HistoryDatabases(fileAdapter) {
|
|
226
|
+
const userPaths = await findGrid3UserPaths(fileAdapter);
|
|
227
|
+
return userPaths.map((userPath) => userPath.historyDbPath);
|
|
227
228
|
}
|
|
228
229
|
/**
|
|
229
230
|
* Get Grid 3 users (alias of findGrid3UserPaths for clarity)
|
|
230
231
|
*/
|
|
231
|
-
export function findGrid3Users() {
|
|
232
|
-
return findGrid3UserPaths();
|
|
232
|
+
export async function findGrid3Users() {
|
|
233
|
+
return await findGrid3UserPaths();
|
|
233
234
|
}
|
|
234
235
|
/**
|
|
235
236
|
* Find Grid 3 gridset/vocabulary files for each user
|
|
236
237
|
* @param userName Optional user filter; matches case-insensitively
|
|
237
238
|
* @returns Array of user/gridset path pairs
|
|
238
239
|
*/
|
|
239
|
-
export function findGrid3Vocabularies(userName, fileAdapter = defaultFileAdapter) {
|
|
240
|
+
export async function findGrid3Vocabularies(userName, fileAdapter = defaultFileAdapter) {
|
|
240
241
|
const { pathExists, listDir, isDirectory } = fileAdapter;
|
|
241
242
|
const results = [];
|
|
242
243
|
if (process.platform !== 'win32') {
|
|
@@ -244,23 +245,23 @@ export function findGrid3Vocabularies(userName, fileAdapter = defaultFileAdapter
|
|
|
244
245
|
}
|
|
245
246
|
const commonDocs = getCommonDocumentsPath();
|
|
246
247
|
const grid3BasePath = joinWin32(commonDocs, 'Smartbox', 'Grid 3', 'Users');
|
|
247
|
-
if (!pathExists(grid3BasePath)) {
|
|
248
|
+
if (!(await pathExists(grid3BasePath))) {
|
|
248
249
|
return results;
|
|
249
250
|
}
|
|
250
251
|
const normalizedUser = userName?.toLowerCase();
|
|
251
|
-
const users = listDir(grid3BasePath);
|
|
252
|
+
const users = await listDir(grid3BasePath);
|
|
252
253
|
for (const userDir of users) {
|
|
253
|
-
if (!isDirectory(userDir))
|
|
254
|
+
if (!(await isDirectory(userDir)))
|
|
254
255
|
continue;
|
|
255
256
|
if (normalizedUser && userDir.toLowerCase() !== normalizedUser)
|
|
256
257
|
continue;
|
|
257
258
|
const userRoot = joinWin32(grid3BasePath, userDir);
|
|
258
259
|
const gridSetsDir = joinWin32(userRoot, 'Grid Sets');
|
|
259
|
-
if (!pathExists(gridSetsDir))
|
|
260
|
+
if (!(await pathExists(gridSetsDir)))
|
|
260
261
|
continue;
|
|
261
|
-
const entries = listDir(gridSetsDir);
|
|
262
|
+
const entries = await listDir(gridSetsDir);
|
|
262
263
|
for (const entry of entries) {
|
|
263
|
-
if (!pathExists(entry) || isDirectory(entry))
|
|
264
|
+
if (!(await pathExists(entry)) || (await isDirectory(entry)))
|
|
264
265
|
continue;
|
|
265
266
|
const ext = extname(entry).toLowerCase();
|
|
266
267
|
if (ext === '.gridset' || ext === '.gridsetx' || ext === '.grd' || ext === '.grdl') {
|
|
@@ -279,19 +280,20 @@ export function findGrid3Vocabularies(userName, fileAdapter = defaultFileAdapter
|
|
|
279
280
|
* @param langCode Optional language code filter (case-insensitive)
|
|
280
281
|
* @returns Path to history.sqlite or null if not found
|
|
281
282
|
*/
|
|
282
|
-
export function findGrid3UserHistory(userName, langCode, fileAdapter) {
|
|
283
|
+
export async function findGrid3UserHistory(userName, langCode, fileAdapter) {
|
|
283
284
|
if (!userName)
|
|
284
285
|
return null;
|
|
285
286
|
const normalizedUser = userName.toLowerCase();
|
|
286
287
|
const normalizedLang = langCode?.toLowerCase();
|
|
287
|
-
const
|
|
288
|
+
const userPaths = await findGrid3UserPaths(fileAdapter);
|
|
289
|
+
const match = userPaths.find((u) => u.userName.toLowerCase() === normalizedUser &&
|
|
288
290
|
(!normalizedLang || u.langCode.toLowerCase() === normalizedLang));
|
|
289
291
|
return match?.historyDbPath ?? null;
|
|
290
292
|
}
|
|
291
293
|
/**
|
|
292
294
|
* Check whether Grid 3 appears to be installed (Windows only)
|
|
293
295
|
*/
|
|
294
|
-
export function isGrid3Installed(fileAdapter = defaultFileAdapter) {
|
|
296
|
+
export async function isGrid3Installed(fileAdapter = defaultFileAdapter) {
|
|
295
297
|
const { pathExists } = fileAdapter;
|
|
296
298
|
if (process.platform !== 'win32')
|
|
297
299
|
return false;
|
|
@@ -299,7 +301,7 @@ export function isGrid3Installed(fileAdapter = defaultFileAdapter) {
|
|
|
299
301
|
if (!commonDocs)
|
|
300
302
|
return false;
|
|
301
303
|
const grid3BasePath = joinWin32(commonDocs, 'Smartbox', 'Grid 3', 'Users');
|
|
302
|
-
return pathExists(grid3BasePath);
|
|
304
|
+
return await pathExists(grid3BasePath);
|
|
303
305
|
}
|
|
304
306
|
function parseGrid3ContentXml(xmlContent) {
|
|
305
307
|
const regex = /<r>(?:<!\[CDATA\[)?(.*?)(?:\]\]>)?<\/r>/gis;
|
|
@@ -318,9 +320,9 @@ function parseGrid3ContentXml(xmlContent) {
|
|
|
318
320
|
* @param historyDbPath Absolute path to the history database
|
|
319
321
|
* @returns Parsed history entries grouped by phrase
|
|
320
322
|
*/
|
|
321
|
-
export function readGrid3History(historyDbPath, fileAdapter = defaultFileAdapter) {
|
|
323
|
+
export async function readGrid3History(historyDbPath, fileAdapter = defaultFileAdapter) {
|
|
322
324
|
const { pathExists } = fileAdapter;
|
|
323
|
-
if (!pathExists(historyDbPath))
|
|
325
|
+
if (!(await pathExists(historyDbPath)))
|
|
324
326
|
return [];
|
|
325
327
|
const Database = requireBetterSqlite3();
|
|
326
328
|
const db = new Database(historyDbPath, { readonly: true });
|
|
@@ -380,17 +382,18 @@ export function readGrid3History(historyDbPath, fileAdapter = defaultFileAdapter
|
|
|
380
382
|
* @param langCode Optional language code to narrow selection (case-insensitive)
|
|
381
383
|
* @returns History entries for that user/language, or empty array if none
|
|
382
384
|
*/
|
|
383
|
-
export function readGrid3HistoryForUser(userName, langCode) {
|
|
384
|
-
const dbPath = findGrid3UserHistory(userName, langCode);
|
|
385
|
+
export async function readGrid3HistoryForUser(userName, langCode) {
|
|
386
|
+
const dbPath = await findGrid3UserHistory(userName, langCode);
|
|
385
387
|
if (!dbPath)
|
|
386
388
|
return [];
|
|
387
|
-
return readGrid3History(dbPath);
|
|
389
|
+
return await readGrid3History(dbPath);
|
|
388
390
|
}
|
|
389
391
|
/**
|
|
390
392
|
* Load all available Grid 3 histories on the machine.
|
|
391
393
|
* @returns Combined history entries from every discovered history.sqlite
|
|
392
394
|
*/
|
|
393
|
-
export function readAllGrid3History() {
|
|
394
|
-
const paths = findGrid3HistoryDatabases();
|
|
395
|
-
|
|
395
|
+
export async function readAllGrid3History() {
|
|
396
|
+
const paths = await findGrid3HistoryDatabases();
|
|
397
|
+
const history = await Promise.all(paths.map(async (p) => await readGrid3History(p)));
|
|
398
|
+
return history.flat();
|
|
396
399
|
}
|
|
@@ -277,13 +277,13 @@ function parseSymbolReferenceSafe(reference) {
|
|
|
277
277
|
/**
|
|
278
278
|
* Export symbol references to CSV for manual extraction
|
|
279
279
|
*/
|
|
280
|
-
export function exportSymbolReferencesToCsv(report, outputPath, fileAdapter = defaultFileAdapter) {
|
|
280
|
+
export async function exportSymbolReferencesToCsv(report, outputPath, fileAdapter = defaultFileAdapter) {
|
|
281
281
|
const { writeTextToPath } = fileAdapter;
|
|
282
282
|
const lines = ['Reference,Library,Path,Attribution,License'];
|
|
283
283
|
for (const symbol of report.missingSymbols) {
|
|
284
284
|
lines.push(`"${symbol.reference}","${symbol.library}","${symbol.path}","${symbol.attribution || ''}","${symbol.license || ''}"`);
|
|
285
285
|
}
|
|
286
|
-
writeTextToPath(outputPath, lines.join('\n'));
|
|
286
|
+
await writeTextToPath(outputPath, lines.join('\n'));
|
|
287
287
|
}
|
|
288
288
|
export function createSymbolManifest(tree, gridsetName) {
|
|
289
289
|
const manifest = {
|
|
@@ -14,9 +14,9 @@ import { defaultFileAdapter } from '../../utils/io';
|
|
|
14
14
|
* @param pixFilePath - Path to .pix file
|
|
15
15
|
* @returns Search index
|
|
16
16
|
*/
|
|
17
|
-
export function parsePixFile(pixFilePath, fileAdapter = defaultFileAdapter) {
|
|
17
|
+
export async function parsePixFile(pixFilePath, fileAdapter = defaultFileAdapter) {
|
|
18
18
|
const { readTextFromInput, basename } = fileAdapter;
|
|
19
|
-
const content = readTextFromInput(pixFilePath);
|
|
19
|
+
const content = await readTextFromInput(pixFilePath);
|
|
20
20
|
const library = basename(pixFilePath, '.pix');
|
|
21
21
|
const searchTerms = new Map();
|
|
22
22
|
const filenames = new Map();
|
|
@@ -43,18 +43,18 @@ export function parsePixFile(pixFilePath, fileAdapter = defaultFileAdapter) {
|
|
|
43
43
|
* @param options - Search options
|
|
44
44
|
* @returns Map of library name to search index
|
|
45
45
|
*/
|
|
46
|
-
export function loadSearchIndexes(options = {}, fileAdapter = defaultFileAdapter) {
|
|
46
|
+
export async function loadSearchIndexes(options = {}, fileAdapter = defaultFileAdapter) {
|
|
47
47
|
const { listDir, pathExists, join, basename } = fileAdapter;
|
|
48
48
|
const { grid3Path, locale = 'en-GB', libraries: specifiedLibs } = options;
|
|
49
49
|
if (!grid3Path) {
|
|
50
50
|
throw new Error('grid3Path is required for symbol search');
|
|
51
51
|
}
|
|
52
52
|
const searchIndexesDir = join(grid3Path, 'Locale', locale, 'symbolsearch');
|
|
53
|
-
if (!pathExists(searchIndexesDir)) {
|
|
53
|
+
if (!(await pathExists(searchIndexesDir))) {
|
|
54
54
|
throw new Error(`Symbol search directory not found: ${searchIndexesDir}`);
|
|
55
55
|
}
|
|
56
56
|
const indexes = new Map();
|
|
57
|
-
const files = listDir(searchIndexesDir);
|
|
57
|
+
const files = await listDir(searchIndexesDir);
|
|
58
58
|
for (const file of files) {
|
|
59
59
|
if (!file.endsWith('.pix')) {
|
|
60
60
|
continue;
|
|
@@ -68,7 +68,7 @@ export function loadSearchIndexes(options = {}, fileAdapter = defaultFileAdapter
|
|
|
68
68
|
}
|
|
69
69
|
try {
|
|
70
70
|
const pixFilePath = join(searchIndexesDir, file);
|
|
71
|
-
const index = parsePixFile(pixFilePath);
|
|
71
|
+
const index = await parsePixFile(pixFilePath);
|
|
72
72
|
indexes.set(libraryName, index);
|
|
73
73
|
}
|
|
74
74
|
catch (error) {
|
|
@@ -83,8 +83,8 @@ export function loadSearchIndexes(options = {}, fileAdapter = defaultFileAdapter
|
|
|
83
83
|
* @param options - Search options
|
|
84
84
|
* @returns Array of search results
|
|
85
85
|
*/
|
|
86
|
-
export function searchSymbols(searchTerm, options = {}) {
|
|
87
|
-
const indexes = loadSearchIndexes(options);
|
|
86
|
+
export async function searchSymbols(searchTerm, options = {}) {
|
|
87
|
+
const indexes = await loadSearchIndexes(options);
|
|
88
88
|
const results = [];
|
|
89
89
|
const lowerSearchTerm = searchTerm.toLowerCase().trim();
|
|
90
90
|
const limit = options.limit || 100;
|
|
@@ -137,8 +137,8 @@ export function searchSymbols(searchTerm, options = {}) {
|
|
|
137
137
|
* @param options - Search options
|
|
138
138
|
* @returns Symbol filename or undefined
|
|
139
139
|
*/
|
|
140
|
-
export function getSymbolFilename(searchTerm, library, options = {}) {
|
|
141
|
-
const indexes = loadSearchIndexes({
|
|
140
|
+
export async function getSymbolFilename(searchTerm, library, options = {}) {
|
|
141
|
+
const indexes = await loadSearchIndexes({
|
|
142
142
|
...options,
|
|
143
143
|
libraries: [library],
|
|
144
144
|
});
|
|
@@ -155,8 +155,8 @@ export function getSymbolFilename(searchTerm, library, options = {}) {
|
|
|
155
155
|
* @param options - Search options
|
|
156
156
|
* @returns Display name or undefined
|
|
157
157
|
*/
|
|
158
|
-
export function getSymbolDisplayName(symbolFilename, library, options = {}) {
|
|
159
|
-
const indexes = loadSearchIndexes({
|
|
158
|
+
export async function getSymbolDisplayName(symbolFilename, library, options = {}) {
|
|
159
|
+
const indexes = await loadSearchIndexes({
|
|
160
160
|
...options,
|
|
161
161
|
libraries: [library],
|
|
162
162
|
});
|
|
@@ -172,8 +172,8 @@ export function getSymbolDisplayName(symbolFilename, library, options = {}) {
|
|
|
172
172
|
* @param options - Search options
|
|
173
173
|
* @returns Array of search terms
|
|
174
174
|
*/
|
|
175
|
-
export function getAllSearchTerms(library, options = {}) {
|
|
176
|
-
const indexes = loadSearchIndexes({
|
|
175
|
+
export async function getAllSearchTerms(library, options = {}) {
|
|
176
|
+
const indexes = await loadSearchIndexes({
|
|
177
177
|
...options,
|
|
178
178
|
libraries: [library],
|
|
179
179
|
});
|
|
@@ -189,8 +189,8 @@ export function getAllSearchTerms(library, options = {}) {
|
|
|
189
189
|
* @param options - Search options
|
|
190
190
|
* @returns Array of suggested terms
|
|
191
191
|
*/
|
|
192
|
-
export function getSearchSuggestions(partialTerm, options = {}) {
|
|
193
|
-
const indexes = loadSearchIndexes(options);
|
|
192
|
+
export async function getSearchSuggestions(partialTerm, options = {}) {
|
|
193
|
+
const indexes = await loadSearchIndexes(options);
|
|
194
194
|
const suggestions = new Set();
|
|
195
195
|
const lowerPartial = partialTerm.toLowerCase().trim();
|
|
196
196
|
for (const index of indexes.values()) {
|
|
@@ -208,8 +208,8 @@ export function getSearchSuggestions(partialTerm, options = {}) {
|
|
|
208
208
|
* @param options - Search options
|
|
209
209
|
* @returns Array of full symbol references
|
|
210
210
|
*/
|
|
211
|
-
export function searchSymbolsWithReferences(searchTerm, options = {}) {
|
|
212
|
-
const results = searchSymbols(searchTerm, options);
|
|
211
|
+
export async function searchSymbolsWithReferences(searchTerm, options = {}) {
|
|
212
|
+
const results = await searchSymbols(searchTerm, options);
|
|
213
213
|
return results.map((r) => `[${r.library}]${r.symbolFilename}`);
|
|
214
214
|
}
|
|
215
215
|
/**
|
|
@@ -217,8 +217,8 @@ export function searchSymbolsWithReferences(searchTerm, options = {}) {
|
|
|
217
217
|
* @param options - Search options
|
|
218
218
|
* @returns Map of library name to symbol count
|
|
219
219
|
*/
|
|
220
|
-
export function countLibrarySymbols(options = {}) {
|
|
221
|
-
const indexes = loadSearchIndexes(options);
|
|
220
|
+
export async function countLibrarySymbols(options = {}) {
|
|
221
|
+
const indexes = await loadSearchIndexes(options);
|
|
222
222
|
const counts = new Map();
|
|
223
223
|
for (const [libraryName, index] of indexes.entries()) {
|
|
224
224
|
counts.set(libraryName, index.searchTerms.size);
|
|
@@ -230,8 +230,8 @@ export function countLibrarySymbols(options = {}) {
|
|
|
230
230
|
* @param options - Search options
|
|
231
231
|
* @returns Statistics about available symbols
|
|
232
232
|
*/
|
|
233
|
-
export function getSymbolSearchStats(options = {}) {
|
|
234
|
-
const indexes = loadSearchIndexes(options);
|
|
233
|
+
export async function getSymbolSearchStats(options = {}) {
|
|
234
|
+
const indexes = await loadSearchIndexes(options);
|
|
235
235
|
const stats = {
|
|
236
236
|
totalLibraries: indexes.size,
|
|
237
237
|
totalSymbols: 0,
|