@willwade/aac-processors 0.0.29 → 0.1.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/README.md +52 -852
- package/dist/browser/core/baseProcessor.js +241 -0
- package/dist/browser/core/stringCasing.js +179 -0
- package/dist/browser/core/treeStructure.js +255 -0
- package/dist/browser/index.browser.js +73 -0
- package/dist/browser/processors/applePanelsProcessor.js +582 -0
- package/dist/browser/processors/astericsGridProcessor.js +1509 -0
- package/dist/browser/processors/dotProcessor.js +221 -0
- package/dist/browser/processors/gridset/commands.js +962 -0
- package/dist/browser/processors/gridset/crypto.js +53 -0
- package/dist/browser/processors/gridset/password.js +43 -0
- package/dist/browser/processors/gridset/pluginTypes.js +277 -0
- package/dist/browser/processors/gridset/resolver.js +137 -0
- package/dist/browser/processors/gridset/symbolAlignment.js +276 -0
- package/dist/browser/processors/gridset/symbols.js +421 -0
- package/dist/browser/processors/gridsetProcessor.js +2002 -0
- package/dist/browser/processors/obfProcessor.js +705 -0
- package/dist/browser/processors/opmlProcessor.js +274 -0
- package/dist/browser/types/aac.js +38 -0
- package/dist/browser/utilities/analytics/utils/idGenerator.js +89 -0
- package/dist/browser/utilities/translation/translationProcessor.js +200 -0
- package/dist/browser/utils/io.js +95 -0
- package/dist/browser/validation/baseValidator.js +156 -0
- package/dist/browser/validation/gridsetValidator.js +355 -0
- package/dist/browser/validation/obfValidator.js +500 -0
- package/dist/browser/validation/validationTypes.js +46 -0
- package/dist/cli/index.js +5 -5
- package/dist/core/analyze.d.ts +2 -2
- package/dist/core/analyze.js +2 -2
- package/dist/core/baseProcessor.d.ts +5 -4
- package/dist/core/baseProcessor.js +22 -27
- package/dist/core/treeStructure.d.ts +5 -5
- package/dist/core/treeStructure.js +1 -4
- package/dist/index.browser.d.ts +37 -0
- package/dist/index.browser.js +99 -0
- package/dist/index.d.ts +1 -48
- package/dist/index.js +1 -136
- package/dist/index.node.d.ts +48 -0
- package/dist/index.node.js +152 -0
- package/dist/processors/applePanelsProcessor.d.ts +5 -4
- package/dist/processors/applePanelsProcessor.js +58 -62
- package/dist/processors/astericsGridProcessor.d.ts +7 -6
- package/dist/processors/astericsGridProcessor.js +31 -42
- package/dist/processors/dotProcessor.d.ts +5 -4
- package/dist/processors/dotProcessor.js +25 -33
- package/dist/processors/excelProcessor.d.ts +4 -3
- package/dist/processors/excelProcessor.js +6 -3
- package/dist/processors/gridset/crypto.d.ts +18 -0
- package/dist/processors/gridset/crypto.js +57 -0
- package/dist/processors/gridset/helpers.d.ts +1 -1
- package/dist/processors/gridset/helpers.js +18 -8
- package/dist/processors/gridset/password.d.ts +20 -3
- package/dist/processors/gridset/password.js +17 -3
- package/dist/processors/gridset/wordlistHelpers.d.ts +3 -3
- package/dist/processors/gridset/wordlistHelpers.js +21 -20
- package/dist/processors/gridsetProcessor.d.ts +7 -12
- package/dist/processors/gridsetProcessor.js +118 -77
- package/dist/processors/obfProcessor.d.ts +9 -7
- package/dist/processors/obfProcessor.js +131 -56
- package/dist/processors/obfsetProcessor.d.ts +5 -4
- package/dist/processors/obfsetProcessor.js +10 -16
- package/dist/processors/opmlProcessor.d.ts +5 -4
- package/dist/processors/opmlProcessor.js +27 -34
- package/dist/processors/snapProcessor.d.ts +8 -7
- package/dist/processors/snapProcessor.js +15 -12
- package/dist/processors/touchchatProcessor.d.ts +8 -7
- package/dist/processors/touchchatProcessor.js +22 -17
- package/dist/types/aac.d.ts +0 -2
- package/dist/types/aac.js +2 -0
- package/dist/utils/io.d.ts +12 -0
- package/dist/utils/io.js +107 -0
- package/dist/validation/gridsetValidator.js +7 -7
- package/dist/validation/snapValidator.js +28 -35
- package/docs/BROWSER_USAGE.md +618 -0
- package/examples/README.md +77 -0
- package/examples/browser-test-server.js +81 -0
- package/examples/browser-test.html +331 -0
- package/examples/vitedemo/QUICKSTART.md +74 -0
- package/examples/vitedemo/README.md +157 -0
- package/examples/vitedemo/index.html +376 -0
- package/examples/vitedemo/package-lock.json +1221 -0
- package/examples/vitedemo/package.json +18 -0
- package/examples/vitedemo/src/main.ts +519 -0
- package/examples/vitedemo/test-files/example.dot +14 -0
- package/examples/vitedemo/test-files/example.grd +1 -0
- package/examples/vitedemo/test-files/example.gridset +0 -0
- package/examples/vitedemo/test-files/example.obz +0 -0
- package/examples/vitedemo/test-files/example.opml +18 -0
- package/examples/vitedemo/test-files/simple.obf +53 -0
- package/examples/vitedemo/tsconfig.json +24 -0
- package/examples/vitedemo/vite.config.ts +34 -0
- package/package.json +20 -4
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseProcessor, ProcessorOptions, ExtractStringsResult, TranslatedString, SourceString } from '../core/baseProcessor';
|
|
2
2
|
import { AACTree } from '../core/treeStructure';
|
|
3
3
|
import { ValidationResult } from '../validation/validationTypes';
|
|
4
|
+
import { ProcessorInput } from '../utils/io';
|
|
4
5
|
declare class SnapProcessor extends BaseProcessor {
|
|
5
6
|
private symbolResolver;
|
|
6
7
|
private loadAudio;
|
|
@@ -9,21 +10,21 @@ declare class SnapProcessor extends BaseProcessor {
|
|
|
9
10
|
loadAudio?: boolean;
|
|
10
11
|
pageLayoutPreference?: 'largest' | 'smallest' | 'scanning' | number;
|
|
11
12
|
});
|
|
12
|
-
extractTexts(filePathOrBuffer:
|
|
13
|
-
loadIntoTree(filePathOrBuffer:
|
|
14
|
-
processTexts(filePathOrBuffer:
|
|
15
|
-
saveFromTree(tree: AACTree, outputPath: string): void
|
|
13
|
+
extractTexts(filePathOrBuffer: ProcessorInput): Promise<string[]>;
|
|
14
|
+
loadIntoTree(filePathOrBuffer: ProcessorInput): Promise<AACTree>;
|
|
15
|
+
processTexts(filePathOrBuffer: ProcessorInput, translations: Map<string, string>, outputPath: string): Promise<Uint8Array>;
|
|
16
|
+
saveFromTree(tree: AACTree, outputPath: string): Promise<void>;
|
|
16
17
|
/**
|
|
17
18
|
* Add audio recording to a button in the database
|
|
18
19
|
*/
|
|
19
|
-
addAudioToButton(dbPath: string, buttonId: number, audioData:
|
|
20
|
+
addAudioToButton(dbPath: string, buttonId: number, audioData: Uint8Array, metadata?: string): Promise<number>;
|
|
20
21
|
/**
|
|
21
22
|
* Create a copy of the pageset with audio recordings added
|
|
22
23
|
*/
|
|
23
24
|
createAudioEnhancedPageset(sourceDbPath: string, targetDbPath: string, audioMappings: Map<number, {
|
|
24
|
-
audioData:
|
|
25
|
+
audioData: Uint8Array;
|
|
25
26
|
metadata?: string;
|
|
26
|
-
}>): void
|
|
27
|
+
}>): Promise<void>;
|
|
27
28
|
/**
|
|
28
29
|
* Extract buttons from a specific page that need audio recordings
|
|
29
30
|
*/
|
|
@@ -34,8 +34,8 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
34
34
|
this.pageLayoutPreference =
|
|
35
35
|
options.pageLayoutPreference !== undefined ? options.pageLayoutPreference : 'scanning'; // Default to scanning
|
|
36
36
|
}
|
|
37
|
-
extractTexts(filePathOrBuffer) {
|
|
38
|
-
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
37
|
+
async extractTexts(filePathOrBuffer) {
|
|
38
|
+
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
39
39
|
const texts = [];
|
|
40
40
|
for (const pageId in tree.pages) {
|
|
41
41
|
const page = tree.pages[pageId];
|
|
@@ -52,7 +52,8 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
52
52
|
}
|
|
53
53
|
return texts;
|
|
54
54
|
}
|
|
55
|
-
loadIntoTree(filePathOrBuffer) {
|
|
55
|
+
async loadIntoTree(filePathOrBuffer) {
|
|
56
|
+
await Promise.resolve();
|
|
56
57
|
const tree = new treeStructure_1.AACTree();
|
|
57
58
|
const filePath = typeof filePathOrBuffer === 'string'
|
|
58
59
|
? filePathOrBuffer
|
|
@@ -612,9 +613,9 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
612
613
|
}
|
|
613
614
|
}
|
|
614
615
|
}
|
|
615
|
-
processTexts(filePathOrBuffer, translations, outputPath) {
|
|
616
|
+
async processTexts(filePathOrBuffer, translations, outputPath) {
|
|
616
617
|
// Load the tree, apply translations, and save to new file
|
|
617
|
-
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
618
|
+
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
618
619
|
// Apply translations to all text content
|
|
619
620
|
Object.values(tree.pages).forEach((page) => {
|
|
620
621
|
// Translate page names
|
|
@@ -641,10 +642,11 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
641
642
|
});
|
|
642
643
|
});
|
|
643
644
|
// Save the translated tree and return its content
|
|
644
|
-
this.saveFromTree(tree, outputPath);
|
|
645
|
+
await this.saveFromTree(tree, outputPath);
|
|
645
646
|
return fs_1.default.readFileSync(outputPath);
|
|
646
647
|
}
|
|
647
|
-
saveFromTree(tree, outputPath) {
|
|
648
|
+
async saveFromTree(tree, outputPath) {
|
|
649
|
+
await Promise.resolve();
|
|
648
650
|
const outputDir = path_1.default.dirname(outputPath);
|
|
649
651
|
if (!fs_1.default.existsSync(outputDir)) {
|
|
650
652
|
fs_1.default.mkdirSync(outputDir, { recursive: true });
|
|
@@ -840,7 +842,8 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
840
842
|
/**
|
|
841
843
|
* Add audio recording to a button in the database
|
|
842
844
|
*/
|
|
843
|
-
addAudioToButton(dbPath, buttonId, audioData, metadata) {
|
|
845
|
+
async addAudioToButton(dbPath, buttonId, audioData, metadata) {
|
|
846
|
+
await Promise.resolve();
|
|
844
847
|
const db = new better_sqlite3_1.default(dbPath, { fileMustExist: true });
|
|
845
848
|
try {
|
|
846
849
|
// Ensure PageSetData table exists
|
|
@@ -882,13 +885,13 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
882
885
|
/**
|
|
883
886
|
* Create a copy of the pageset with audio recordings added
|
|
884
887
|
*/
|
|
885
|
-
createAudioEnhancedPageset(sourceDbPath, targetDbPath, audioMappings) {
|
|
888
|
+
async createAudioEnhancedPageset(sourceDbPath, targetDbPath, audioMappings) {
|
|
886
889
|
// Copy the source database to target
|
|
887
890
|
fs_1.default.copyFileSync(sourceDbPath, targetDbPath);
|
|
888
891
|
// Add audio recordings to the copy
|
|
889
|
-
audioMappings.
|
|
890
|
-
this.addAudioToButton(targetDbPath, buttonId, audioInfo.audioData, audioInfo.metadata);
|
|
891
|
-
}
|
|
892
|
+
for (const [buttonId, audioInfo] of audioMappings.entries()) {
|
|
893
|
+
await this.addAudioToButton(targetDbPath, buttonId, audioInfo.audioData, audioInfo.metadata);
|
|
894
|
+
}
|
|
892
895
|
}
|
|
893
896
|
/**
|
|
894
897
|
* Extract buttons from a specific page that need audio recordings
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { BaseProcessor, ProcessorOptions, ExtractStringsResult, TranslatedString, SourceString } from '../core/baseProcessor';
|
|
2
2
|
import { AACTree } from '../core/treeStructure';
|
|
3
3
|
import { ValidationResult } from '../validation/validationTypes';
|
|
4
|
+
import { ProcessorInput } from '../utils/io';
|
|
4
5
|
import { type ButtonForTranslation, type LLMLTranslationResult } from '../utilities/translation/translationProcessor';
|
|
5
6
|
declare class TouchChatProcessor extends BaseProcessor {
|
|
6
7
|
private tree;
|
|
7
8
|
private sourceFile;
|
|
8
9
|
constructor(options?: ProcessorOptions);
|
|
9
|
-
extractTexts(filePathOrBuffer:
|
|
10
|
-
loadIntoTree(filePathOrBuffer:
|
|
11
|
-
processTexts(filePathOrBuffer:
|
|
12
|
-
saveFromTree(tree: AACTree, outputPath: string): void
|
|
10
|
+
extractTexts(filePathOrBuffer: ProcessorInput): Promise<string[]>;
|
|
11
|
+
loadIntoTree(filePathOrBuffer: ProcessorInput): Promise<AACTree>;
|
|
12
|
+
processTexts(filePathOrBuffer: ProcessorInput, translations: Map<string, string>, outputPath: string): Promise<Uint8Array>;
|
|
13
|
+
saveFromTree(tree: AACTree, outputPath: string): Promise<void>;
|
|
13
14
|
/**
|
|
14
15
|
* Alias method for aac-tools-platform compatibility
|
|
15
16
|
* Extracts strings with TouchChat-specific metadata required for database storage
|
|
@@ -40,7 +41,7 @@ declare class TouchChatProcessor extends BaseProcessor {
|
|
|
40
41
|
* @param filePathOrBuffer - Path to TouchChat .ce file or buffer
|
|
41
42
|
* @returns Array of symbol information for LLM processing
|
|
42
43
|
*/
|
|
43
|
-
extractSymbolsForLLM(filePathOrBuffer: string | Buffer): ButtonForTranslation[]
|
|
44
|
+
extractSymbolsForLLM(filePathOrBuffer: string | Buffer): Promise<ButtonForTranslation[]>;
|
|
44
45
|
/**
|
|
45
46
|
* Apply LLM translations with symbol information.
|
|
46
47
|
* The LLM should provide translations with symbol attachments in the correct positions.
|
|
@@ -53,8 +54,8 @@ declare class TouchChatProcessor extends BaseProcessor {
|
|
|
53
54
|
* @param options - Translation options (e.g., allowPartial for testing)
|
|
54
55
|
* @returns Buffer of the translated TouchChat file
|
|
55
56
|
*/
|
|
56
|
-
processLLMTranslations(filePathOrBuffer: string |
|
|
57
|
+
processLLMTranslations(filePathOrBuffer: string | Uint8Array, llmTranslations: LLMLTranslationResult[], outputPath: string, options?: {
|
|
57
58
|
allowPartial?: boolean;
|
|
58
|
-
}):
|
|
59
|
+
}): Promise<Uint8Array>;
|
|
59
60
|
}
|
|
60
61
|
export { TouchChatProcessor };
|
|
@@ -14,6 +14,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
14
14
|
const fs_1 = __importDefault(require("fs"));
|
|
15
15
|
const os_1 = __importDefault(require("os"));
|
|
16
16
|
const touchChatValidator_1 = require("../validation/touchChatValidator");
|
|
17
|
+
const io_1 = require("../utils/io");
|
|
17
18
|
const translationProcessor_1 = require("../utilities/translation/translationProcessor");
|
|
18
19
|
const toNumberOrUndefined = (value) => typeof value === 'number' ? value : undefined;
|
|
19
20
|
const toStringOrUndefined = (value) => typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
@@ -42,10 +43,10 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
42
43
|
this.tree = null;
|
|
43
44
|
this.sourceFile = null;
|
|
44
45
|
}
|
|
45
|
-
extractTexts(filePathOrBuffer) {
|
|
46
|
+
async extractTexts(filePathOrBuffer) {
|
|
46
47
|
// Extracts all button labels/texts from TouchChat .ce file
|
|
47
48
|
if (!this.tree && filePathOrBuffer) {
|
|
48
|
-
this.tree = this.loadIntoTree(filePathOrBuffer);
|
|
49
|
+
this.tree = await this.loadIntoTree(filePathOrBuffer);
|
|
49
50
|
}
|
|
50
51
|
if (!this.tree) {
|
|
51
52
|
throw new Error('No tree available - call loadIntoTree first');
|
|
@@ -62,7 +63,8 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
62
63
|
}
|
|
63
64
|
return texts;
|
|
64
65
|
}
|
|
65
|
-
loadIntoTree(filePathOrBuffer) {
|
|
66
|
+
async loadIntoTree(filePathOrBuffer) {
|
|
67
|
+
await Promise.resolve();
|
|
66
68
|
// Unzip .ce file, extract the .c4v SQLite DB, and parse pages/buttons
|
|
67
69
|
let tmpDir = null;
|
|
68
70
|
let db = null;
|
|
@@ -71,7 +73,9 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
71
73
|
this.sourceFile = filePathOrBuffer;
|
|
72
74
|
// Step 1: Unzip
|
|
73
75
|
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'touchchat-'));
|
|
74
|
-
const
|
|
76
|
+
const zipInput = (0, io_1.readBinaryFromInput)(filePathOrBuffer);
|
|
77
|
+
const zipBuffer = Buffer.isBuffer(zipInput) ? zipInput : Buffer.from(zipInput);
|
|
78
|
+
const zip = new adm_zip_1.default(zipBuffer);
|
|
75
79
|
zip.extractAllTo(tmpDir, true);
|
|
76
80
|
// Step 2: Find and open SQLite DB
|
|
77
81
|
const files = fs_1.default.readdirSync(tmpDir);
|
|
@@ -485,9 +489,9 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
485
489
|
}
|
|
486
490
|
}
|
|
487
491
|
}
|
|
488
|
-
processTexts(filePathOrBuffer, translations, outputPath) {
|
|
492
|
+
async processTexts(filePathOrBuffer, translations, outputPath) {
|
|
489
493
|
// Load the tree, apply translations, and save to new file
|
|
490
|
-
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
494
|
+
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
491
495
|
// Apply translations to all text content
|
|
492
496
|
Object.values(tree.pages).forEach((page) => {
|
|
493
497
|
// Translate page names
|
|
@@ -514,10 +518,11 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
514
518
|
});
|
|
515
519
|
});
|
|
516
520
|
// Save the translated tree and return its content
|
|
517
|
-
this.saveFromTree(tree, outputPath);
|
|
521
|
+
await this.saveFromTree(tree, outputPath);
|
|
518
522
|
return fs_1.default.readFileSync(outputPath);
|
|
519
523
|
}
|
|
520
|
-
saveFromTree(tree, outputPath) {
|
|
524
|
+
async saveFromTree(tree, outputPath) {
|
|
525
|
+
await Promise.resolve();
|
|
521
526
|
// Create a TouchChat database that matches the expected schema for loading
|
|
522
527
|
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'touchchat-export-'));
|
|
523
528
|
const dbPath = path_1.default.join(tmpDir, 'vocab.c4v');
|
|
@@ -829,9 +834,9 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
829
834
|
* @param filePath - Path to the TouchChat .ce file
|
|
830
835
|
* @returns Promise with extracted strings and any errors
|
|
831
836
|
*/
|
|
832
|
-
extractStringsWithMetadata(filePath) {
|
|
837
|
+
async extractStringsWithMetadata(filePath) {
|
|
833
838
|
try {
|
|
834
|
-
const tree = this.loadIntoTree(filePath);
|
|
839
|
+
const tree = await this.loadIntoTree(filePath);
|
|
835
840
|
const extractedMap = new Map();
|
|
836
841
|
// Process all pages and buttons with TouchChat-specific logic
|
|
837
842
|
Object.values(tree.pages).forEach((page) => {
|
|
@@ -885,7 +890,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
885
890
|
* @param sourceStrings - Array of source string data with metadata
|
|
886
891
|
* @returns Promise with path to the generated translated file
|
|
887
892
|
*/
|
|
888
|
-
generateTranslatedDownload(filePath, translatedStrings, sourceStrings) {
|
|
893
|
+
async generateTranslatedDownload(filePath, translatedStrings, sourceStrings) {
|
|
889
894
|
try {
|
|
890
895
|
// Build translation map from the provided data
|
|
891
896
|
const translations = new Map();
|
|
@@ -901,7 +906,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
901
906
|
// Generate output path for TouchChat files
|
|
902
907
|
const outputPath = filePath.replace(/\.ce$/, '_translated.ce');
|
|
903
908
|
// Use existing processTexts method
|
|
904
|
-
this.processTexts(filePath, translations, outputPath);
|
|
909
|
+
await this.processTexts(filePath, translations, outputPath);
|
|
905
910
|
return Promise.resolve(outputPath);
|
|
906
911
|
}
|
|
907
912
|
catch (error) {
|
|
@@ -925,8 +930,8 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
925
930
|
* @param filePathOrBuffer - Path to TouchChat .ce file or buffer
|
|
926
931
|
* @returns Array of symbol information for LLM processing
|
|
927
932
|
*/
|
|
928
|
-
extractSymbolsForLLM(filePathOrBuffer) {
|
|
929
|
-
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
933
|
+
async extractSymbolsForLLM(filePathOrBuffer) {
|
|
934
|
+
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
930
935
|
// Collect all buttons from all pages
|
|
931
936
|
const allButtons = [];
|
|
932
937
|
Object.values(tree.pages).forEach((page) => {
|
|
@@ -955,8 +960,8 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
955
960
|
* @param options - Translation options (e.g., allowPartial for testing)
|
|
956
961
|
* @returns Buffer of the translated TouchChat file
|
|
957
962
|
*/
|
|
958
|
-
processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
|
|
959
|
-
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
963
|
+
async processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
|
|
964
|
+
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
960
965
|
// Validate translations using shared utility
|
|
961
966
|
const buttonIds = Object.values(tree.pages).flatMap((page) => page.buttons.map((b) => b.id));
|
|
962
967
|
(0, translationProcessor_1.validateTranslationResults)(llmTranslations, buttonIds, options);
|
|
@@ -993,7 +998,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
993
998
|
});
|
|
994
999
|
});
|
|
995
1000
|
// Save and return
|
|
996
|
-
this.saveFromTree(tree, outputPath);
|
|
1001
|
+
await this.saveFromTree(tree, outputPath);
|
|
997
1002
|
return fs_1.default.readFileSync(outputPath);
|
|
998
1003
|
}
|
|
999
1004
|
}
|
package/dist/types/aac.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { AACSemanticAction } from '../core/treeStructure';
|
|
2
1
|
/**
|
|
3
2
|
* Scanning selection methods for switch access
|
|
4
3
|
* Determines how the scanning advances through items
|
|
@@ -76,7 +75,6 @@ export interface AACButton {
|
|
|
76
75
|
id: string;
|
|
77
76
|
label: string;
|
|
78
77
|
message: string;
|
|
79
|
-
semanticAction?: AACSemanticAction;
|
|
80
78
|
targetPageId?: string;
|
|
81
79
|
style?: AACStyle;
|
|
82
80
|
audioRecording?: {
|
package/dist/types/aac.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// Note: AACSemanticAction is defined in core/treeStructure.ts to avoid circular dependency
|
|
3
|
+
// Import it directly if needed: import { AACSemanticAction } from '../core/treeStructure';
|
|
2
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
5
|
exports.CellScanningOrder = exports.ScanningSelectionMethod = void 0;
|
|
4
6
|
/**
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type ProcessorInput = string | Buffer | ArrayBuffer | Uint8Array;
|
|
2
|
+
export type BinaryOutput = Buffer | Uint8Array;
|
|
3
|
+
export declare function getFs(): typeof import('fs');
|
|
4
|
+
export declare function getPath(): typeof import('path');
|
|
5
|
+
export declare function getBasename(filePath: string): string;
|
|
6
|
+
export declare function decodeText(input: Uint8Array): string;
|
|
7
|
+
export declare function encodeBase64(input: Uint8Array): string;
|
|
8
|
+
export declare function encodeText(text: string): BinaryOutput;
|
|
9
|
+
export declare function readBinaryFromInput(input: ProcessorInput): Uint8Array;
|
|
10
|
+
export declare function readTextFromInput(input: ProcessorInput, encoding?: BufferEncoding): string;
|
|
11
|
+
export declare function writeBinaryToPath(outputPath: string, data: BinaryOutput): void;
|
|
12
|
+
export declare function writeTextToPath(outputPath: string, text: string): void;
|
package/dist/utils/io.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getFs = getFs;
|
|
4
|
+
exports.getPath = getPath;
|
|
5
|
+
exports.getBasename = getBasename;
|
|
6
|
+
exports.decodeText = decodeText;
|
|
7
|
+
exports.encodeBase64 = encodeBase64;
|
|
8
|
+
exports.encodeText = encodeText;
|
|
9
|
+
exports.readBinaryFromInput = readBinaryFromInput;
|
|
10
|
+
exports.readTextFromInput = readTextFromInput;
|
|
11
|
+
exports.writeBinaryToPath = writeBinaryToPath;
|
|
12
|
+
exports.writeTextToPath = writeTextToPath;
|
|
13
|
+
let cachedFs = null;
|
|
14
|
+
let cachedPath = null;
|
|
15
|
+
function getFs() {
|
|
16
|
+
if (!cachedFs) {
|
|
17
|
+
try {
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
19
|
+
cachedFs = require('fs');
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
throw new Error('File system access is not available in this environment.');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (!cachedFs) {
|
|
26
|
+
throw new Error('File system access is not available in this environment.');
|
|
27
|
+
}
|
|
28
|
+
return cachedFs;
|
|
29
|
+
}
|
|
30
|
+
function getPath() {
|
|
31
|
+
if (!cachedPath) {
|
|
32
|
+
try {
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
34
|
+
cachedPath = require('path');
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
throw new Error('Path utilities are not available in this environment.');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (!cachedPath) {
|
|
41
|
+
throw new Error('Path utilities are not available in this environment.');
|
|
42
|
+
}
|
|
43
|
+
return cachedPath;
|
|
44
|
+
}
|
|
45
|
+
function getBasename(filePath) {
|
|
46
|
+
const parts = filePath.split(/[/\\]/);
|
|
47
|
+
return parts[parts.length - 1] || filePath;
|
|
48
|
+
}
|
|
49
|
+
function decodeText(input) {
|
|
50
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(input)) {
|
|
51
|
+
return input.toString('utf8');
|
|
52
|
+
}
|
|
53
|
+
const decoder = new TextDecoder('utf-8');
|
|
54
|
+
return decoder.decode(input);
|
|
55
|
+
}
|
|
56
|
+
function encodeBase64(input) {
|
|
57
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(input)) {
|
|
58
|
+
return input.toString('base64');
|
|
59
|
+
}
|
|
60
|
+
// Browser fallback using btoa
|
|
61
|
+
let binary = '';
|
|
62
|
+
const len = input.byteLength;
|
|
63
|
+
for (let i = 0; i < len; i++) {
|
|
64
|
+
binary += String.fromCharCode(input[i]);
|
|
65
|
+
}
|
|
66
|
+
return btoa(binary);
|
|
67
|
+
}
|
|
68
|
+
function encodeText(text) {
|
|
69
|
+
if (typeof Buffer !== 'undefined') {
|
|
70
|
+
return Buffer.from(text, 'utf8');
|
|
71
|
+
}
|
|
72
|
+
return new TextEncoder().encode(text);
|
|
73
|
+
}
|
|
74
|
+
function readBinaryFromInput(input) {
|
|
75
|
+
if (typeof input === 'string') {
|
|
76
|
+
const fs = getFs();
|
|
77
|
+
return fs.readFileSync(input);
|
|
78
|
+
}
|
|
79
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(input)) {
|
|
80
|
+
return input;
|
|
81
|
+
}
|
|
82
|
+
if (input instanceof ArrayBuffer) {
|
|
83
|
+
return new Uint8Array(input);
|
|
84
|
+
}
|
|
85
|
+
return input;
|
|
86
|
+
}
|
|
87
|
+
function readTextFromInput(input, encoding = 'utf8') {
|
|
88
|
+
if (typeof input === 'string') {
|
|
89
|
+
const fs = getFs();
|
|
90
|
+
return fs.readFileSync(input, encoding);
|
|
91
|
+
}
|
|
92
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(input)) {
|
|
93
|
+
return input.toString(encoding);
|
|
94
|
+
}
|
|
95
|
+
if (input instanceof ArrayBuffer) {
|
|
96
|
+
return decodeText(new Uint8Array(input));
|
|
97
|
+
}
|
|
98
|
+
return decodeText(input);
|
|
99
|
+
}
|
|
100
|
+
function writeBinaryToPath(outputPath, data) {
|
|
101
|
+
const fs = getFs();
|
|
102
|
+
fs.writeFileSync(outputPath, data);
|
|
103
|
+
}
|
|
104
|
+
function writeTextToPath(outputPath, text) {
|
|
105
|
+
const fs = getFs();
|
|
106
|
+
fs.writeFileSync(outputPath, text, 'utf8');
|
|
107
|
+
}
|
|
@@ -33,7 +33,7 @@ exports.GridsetValidator = void 0;
|
|
|
33
33
|
const fs = __importStar(require("fs"));
|
|
34
34
|
const path = __importStar(require("path"));
|
|
35
35
|
const xml2js = __importStar(require("xml2js"));
|
|
36
|
-
const
|
|
36
|
+
const jszip_1 = __importDefault(require("jszip"));
|
|
37
37
|
const baseValidator_1 = require("./baseValidator");
|
|
38
38
|
/**
|
|
39
39
|
* Validator for Grid3/Smartbox Gridset files (.gridset, .gridsetx)
|
|
@@ -140,22 +140,22 @@ class GridsetValidator extends baseValidator_1.BaseValidator {
|
|
|
140
140
|
async validateZipArchive(content, filename, _filesize) {
|
|
141
141
|
let zip;
|
|
142
142
|
try {
|
|
143
|
-
zip =
|
|
143
|
+
zip = await jszip_1.default.loadAsync(Buffer.from(content));
|
|
144
144
|
}
|
|
145
145
|
catch (e) {
|
|
146
146
|
this.err(`Failed to open ZIP archive: ${e.message}`, true);
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
|
-
const entries = zip.
|
|
149
|
+
const entries = Object.values(zip.files).filter((entry) => !entry.dir);
|
|
150
150
|
// Check for gridset.xml (required)
|
|
151
151
|
await this.add_check('gridset_xml_presence', 'gridset.xml presence', async () => {
|
|
152
|
-
const gridsetEntry = entries.find((e) => e.
|
|
152
|
+
const gridsetEntry = entries.find((e) => e.name.toLowerCase() === 'gridset.xml');
|
|
153
153
|
if (!gridsetEntry) {
|
|
154
154
|
this.err('Missing gridset.xml in archive', true);
|
|
155
155
|
}
|
|
156
156
|
else {
|
|
157
157
|
try {
|
|
158
|
-
const gridsetXml = gridsetEntry.
|
|
158
|
+
const gridsetXml = await gridsetEntry.async('string');
|
|
159
159
|
const parser = new xml2js.Parser();
|
|
160
160
|
const xmlObj = await parser.parseStringPromise(gridsetXml);
|
|
161
161
|
const gridset = xmlObj.gridset || xmlObj.Gridset;
|
|
@@ -173,13 +173,13 @@ class GridsetValidator extends baseValidator_1.BaseValidator {
|
|
|
173
173
|
});
|
|
174
174
|
// Check for settings.xml (highly recommended/required for metadata)
|
|
175
175
|
await this.add_check('settings_xml_presence', 'settings.xml presence', async () => {
|
|
176
|
-
const settingsEntry = entries.find((e) => e.
|
|
176
|
+
const settingsEntry = entries.find((e) => e.name.toLowerCase() === 'settings.xml');
|
|
177
177
|
if (!settingsEntry) {
|
|
178
178
|
this.warn('Missing settings.xml in archive (required for full metadata)');
|
|
179
179
|
}
|
|
180
180
|
else {
|
|
181
181
|
try {
|
|
182
|
-
const settingsXml = settingsEntry.
|
|
182
|
+
const settingsXml = await settingsEntry.async('string');
|
|
183
183
|
const parser = new xml2js.Parser();
|
|
184
184
|
const xmlObj = await parser.parseStringPromise(settingsXml);
|
|
185
185
|
const settings = xmlObj.GridSetSettings || xmlObj.gridSetSettings || xmlObj.GridsetSettings;
|
|
@@ -32,7 +32,7 @@ exports.SnapValidator = void 0;
|
|
|
32
32
|
const fs = __importStar(require("fs"));
|
|
33
33
|
const path = __importStar(require("path"));
|
|
34
34
|
const xml2js = __importStar(require("xml2js"));
|
|
35
|
-
const
|
|
35
|
+
const jszip_1 = __importDefault(require("jszip"));
|
|
36
36
|
const baseValidator_1 = require("./baseValidator");
|
|
37
37
|
/**
|
|
38
38
|
* Validator for Snap files (.spb, .sps)
|
|
@@ -62,10 +62,10 @@ class SnapValidator extends baseValidator_1.BaseValidator {
|
|
|
62
62
|
}
|
|
63
63
|
// Try to parse as ZIP and check for Snap structure
|
|
64
64
|
try {
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
return entries.some((
|
|
65
|
+
const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);
|
|
66
|
+
const zip = await jszip_1.default.loadAsync(buffer);
|
|
67
|
+
const entries = Object.values(zip.files).filter((entry) => !entry.dir);
|
|
68
|
+
return entries.some((entry) => entry.name.includes('settings') || entry.name.includes('.xml'));
|
|
69
69
|
}
|
|
70
70
|
catch {
|
|
71
71
|
return false;
|
|
@@ -85,10 +85,9 @@ class SnapValidator extends baseValidator_1.BaseValidator {
|
|
|
85
85
|
let validZip = false;
|
|
86
86
|
await this.add_check('zip', 'valid zip package', async () => {
|
|
87
87
|
try {
|
|
88
|
-
// Ensure content is a Buffer for AdmZip
|
|
89
88
|
const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);
|
|
90
|
-
zip =
|
|
91
|
-
const entries = zip.
|
|
89
|
+
zip = await jszip_1.default.loadAsync(buffer);
|
|
90
|
+
const entries = Object.values(zip.files);
|
|
92
91
|
validZip = entries.length > 0;
|
|
93
92
|
}
|
|
94
93
|
catch (e) {
|
|
@@ -107,8 +106,8 @@ class SnapValidator extends baseValidator_1.BaseValidator {
|
|
|
107
106
|
async validateSnapStructure(zip, _filename) {
|
|
108
107
|
// Check for required files
|
|
109
108
|
await this.add_check('required_files', 'required package files', async () => {
|
|
110
|
-
const entries = zip.
|
|
111
|
-
const entryNames = entries.map((e) => e.
|
|
109
|
+
const entries = Object.values(zip.files);
|
|
110
|
+
const entryNames = entries.map((e) => e.name);
|
|
112
111
|
// Look for common Snap files
|
|
113
112
|
const hasSettings = entryNames.some((n) => n.toLowerCase().includes('settings'));
|
|
114
113
|
const hasXml = entryNames.some((n) => n.toLowerCase().endsWith('.xml'));
|
|
@@ -120,14 +119,12 @@ class SnapValidator extends baseValidator_1.BaseValidator {
|
|
|
120
119
|
}
|
|
121
120
|
});
|
|
122
121
|
// Try to parse and validate the main settings file
|
|
123
|
-
const settingsEntry = zip
|
|
124
|
-
.getEntries()
|
|
125
|
-
.find((e) => e.entryName.toLowerCase().includes('settings'));
|
|
122
|
+
const settingsEntry = Object.values(zip.files).find((entry) => !entry.dir && entry.name.toLowerCase().includes('settings'));
|
|
126
123
|
if (settingsEntry) {
|
|
127
|
-
await this.validateSettingsFile(
|
|
124
|
+
await this.validateSettingsFile(settingsEntry);
|
|
128
125
|
}
|
|
129
126
|
// Check for pages
|
|
130
|
-
const pageEntries = zip.
|
|
127
|
+
const pageEntries = Object.values(zip.files).filter((entry) => !entry.dir && entry.name.toLowerCase().includes('page'));
|
|
131
128
|
await this.add_check('pages', 'pages in package', async () => {
|
|
132
129
|
if (pageEntries.length === 0) {
|
|
133
130
|
this.warn('Snap package should contain at least one page file');
|
|
@@ -136,21 +133,17 @@ class SnapValidator extends baseValidator_1.BaseValidator {
|
|
|
136
133
|
// Validate a sample of pages
|
|
137
134
|
const samplePages = pageEntries.slice(0, 5); // Limit to first 5 pages
|
|
138
135
|
for (let i = 0; i < samplePages.length; i++) {
|
|
139
|
-
await this.validatePageFile(
|
|
136
|
+
await this.validatePageFile(samplePages[i], i);
|
|
140
137
|
}
|
|
141
138
|
// Check for images
|
|
142
|
-
const imageEntries = zip
|
|
143
|
-
.getEntries()
|
|
144
|
-
.filter((e) => e.entryName.toLowerCase().match(/\.(png|jpg|jpeg|gif|bmp)$/i));
|
|
139
|
+
const imageEntries = Object.values(zip.files).filter((entry) => !entry.dir && entry.name.toLowerCase().match(/\.(png|jpg|jpeg|gif|bmp)$/i));
|
|
145
140
|
await this.add_check('images', 'image files', async () => {
|
|
146
141
|
if (imageEntries.length === 0) {
|
|
147
142
|
this.warn('Snap package should contain image files for buttons');
|
|
148
143
|
}
|
|
149
144
|
});
|
|
150
145
|
// Check for audio files
|
|
151
|
-
const audioEntries = zip
|
|
152
|
-
.getEntries()
|
|
153
|
-
.filter((e) => e.entryName.toLowerCase().match(/\.(wav|mp3|m4a|ogg)$/i));
|
|
146
|
+
const audioEntries = Object.values(zip.files).filter((entry) => !entry.dir && entry.name.toLowerCase().match(/\.(wav|mp3|m4a|ogg)$/i));
|
|
154
147
|
await this.add_check('audio', 'audio files', async () => {
|
|
155
148
|
// Audio files are optional, so just warn if missing
|
|
156
149
|
if (audioEntries.length === 0) {
|
|
@@ -159,9 +152,9 @@ class SnapValidator extends baseValidator_1.BaseValidator {
|
|
|
159
152
|
});
|
|
160
153
|
// Check for unexpected files
|
|
161
154
|
await this.add_check('unexpected_files', 'unexpected file types', async () => {
|
|
162
|
-
const entries = zip.
|
|
163
|
-
const unexpectedFiles = entries.filter((
|
|
164
|
-
const name =
|
|
155
|
+
const entries = Object.values(zip.files).filter((entry) => !entry.dir);
|
|
156
|
+
const unexpectedFiles = entries.filter((entry) => {
|
|
157
|
+
const name = entry.name.toLowerCase();
|
|
165
158
|
// Skip common system files and directories
|
|
166
159
|
if (name.startsWith('__macosx') || name.startsWith('.ds_store')) {
|
|
167
160
|
return false;
|
|
@@ -170,7 +163,7 @@ class SnapValidator extends baseValidator_1.BaseValidator {
|
|
|
170
163
|
return !name.match(/\.(xml|png|jpg|jpeg|gif|bmp|wav|mp3|m4a|ogg|json)$/i);
|
|
171
164
|
});
|
|
172
165
|
if (unexpectedFiles.length > 0) {
|
|
173
|
-
const unexpectedNames = unexpectedFiles.map((f) => f.
|
|
166
|
+
const unexpectedNames = unexpectedFiles.map((f) => f.name).slice(0, 5);
|
|
174
167
|
this.warn(`Package contains unexpected file types: ${unexpectedNames.join(', ')}`);
|
|
175
168
|
}
|
|
176
169
|
});
|
|
@@ -178,10 +171,10 @@ class SnapValidator extends baseValidator_1.BaseValidator {
|
|
|
178
171
|
/**
|
|
179
172
|
* Validate the main settings file
|
|
180
173
|
*/
|
|
181
|
-
async validateSettingsFile(
|
|
174
|
+
async validateSettingsFile(entry) {
|
|
182
175
|
await this.add_check('settings_format', 'settings file format', async () => {
|
|
183
176
|
try {
|
|
184
|
-
const content =
|
|
177
|
+
const content = await entry.async('string');
|
|
185
178
|
const parser = new xml2js.Parser();
|
|
186
179
|
const xml = await parser.parseStringPromise(content);
|
|
187
180
|
// Check for expected root element
|
|
@@ -206,30 +199,30 @@ class SnapValidator extends baseValidator_1.BaseValidator {
|
|
|
206
199
|
/**
|
|
207
200
|
* Validate a page file
|
|
208
201
|
*/
|
|
209
|
-
async validatePageFile(
|
|
210
|
-
await this.add_check(`page[${index}]`, `page file ${index}: ${entry.
|
|
202
|
+
async validatePageFile(entry, index) {
|
|
203
|
+
await this.add_check(`page[${index}]`, `page file ${index}: ${entry.name}`, async () => {
|
|
211
204
|
try {
|
|
212
|
-
const content =
|
|
205
|
+
const content = await entry.async('string');
|
|
213
206
|
const parser = new xml2js.Parser();
|
|
214
207
|
const xml = await parser.parseStringPromise(content);
|
|
215
208
|
const page = xml.page || xml.Page;
|
|
216
209
|
if (!page) {
|
|
217
|
-
this.err(`Page file ${entry.
|
|
210
|
+
this.err(`Page file ${entry.name} does not contain a page element`);
|
|
218
211
|
return;
|
|
219
212
|
}
|
|
220
213
|
// Check page attributes
|
|
221
214
|
const pageId = page.$?.id || page.$?.Id;
|
|
222
215
|
if (!pageId) {
|
|
223
|
-
this.warn(`Page ${entry.
|
|
216
|
+
this.warn(`Page ${entry.name} is missing an id attribute`);
|
|
224
217
|
}
|
|
225
218
|
// Check for cells/buttons
|
|
226
219
|
const cells = page.cells || page.Cells || page.button || page.Button;
|
|
227
220
|
if (!cells || (Array.isArray(cells) && cells.length === 0)) {
|
|
228
|
-
this.warn(`Page ${entry.
|
|
221
|
+
this.warn(`Page ${entry.name} has no cells or buttons`);
|
|
229
222
|
}
|
|
230
223
|
}
|
|
231
224
|
catch (e) {
|
|
232
|
-
this.err(`Failed to parse page file ${entry.
|
|
225
|
+
this.err(`Failed to parse page file ${entry.name}: ${e.message}`);
|
|
233
226
|
}
|
|
234
227
|
});
|
|
235
228
|
}
|