@willwade/aac-processors 0.1.20 → 0.1.21
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/core/baseProcessor.js +4 -0
- package/dist/browser/processors/applePanelsProcessor.js +24 -31
- package/dist/browser/processors/astericsGridProcessor.js +10 -3
- package/dist/browser/processors/dotProcessor.js +5 -2
- package/dist/browser/processors/gridset/colorUtils.js +354 -0
- package/dist/browser/processors/gridset/helpers.js +49 -45
- package/dist/browser/processors/gridset/index.js +61 -0
- package/dist/browser/processors/gridset/styleHelpers.js +205 -0
- package/dist/browser/processors/gridset/symbolExtractor.js +331 -0
- package/dist/browser/processors/gridset/symbolSearch.js +248 -0
- package/dist/browser/processors/gridset/symbols.js +35 -68
- package/dist/browser/processors/gridsetProcessor.js +32 -41
- package/dist/browser/processors/obfProcessor.js +20 -33
- package/dist/browser/processors/opmlProcessor.js +5 -2
- package/dist/browser/processors/snap/helpers.js +49 -45
- package/dist/browser/processors/snapProcessor.js +39 -42
- package/dist/browser/processors/touchchatProcessor.js +54 -45
- package/dist/browser/utilities/analytics/reference/index.js +27 -19
- package/dist/browser/utils/io.js +67 -14
- package/dist/browser/utils/sqlite.js +6 -8
- package/dist/browser/utils/zip.js +45 -43
- package/dist/browser/validation/baseValidator.js +5 -0
- package/dist/browser/validation/gridsetValidator.js +12 -20
- package/dist/browser/validation/obfValidator.js +5 -4
- package/dist/browser/validation/snapValidator.js +9 -5
- package/dist/browser/validation/touchChatValidator.js +21 -11
- package/dist/cli/index.js +10 -15
- package/dist/core/baseProcessor.d.ts +7 -7
- package/dist/core/baseProcessor.js +4 -0
- package/dist/processors/applePanelsProcessor.js +29 -36
- package/dist/processors/astericsGridProcessor.js +20 -13
- package/dist/processors/dotProcessor.js +10 -7
- package/dist/processors/excelProcessor.js +9 -12
- package/dist/processors/gridset/helpers.d.ts +9 -11
- package/dist/processors/gridset/helpers.js +49 -71
- package/dist/processors/gridset/imageDebug.d.ts +3 -5
- package/dist/processors/gridset/imageDebug.js +4 -4
- package/dist/processors/gridset/password.d.ts +1 -1
- package/dist/processors/gridset/symbolExtractor.d.ts +5 -3
- package/dist/processors/gridset/symbolExtractor.js +15 -38
- package/dist/processors/gridset/symbolSearch.d.ts +3 -2
- package/dist/processors/gridset/symbolSearch.js +12 -34
- package/dist/processors/gridset/symbols.d.ts +8 -6
- package/dist/processors/gridset/symbols.js +34 -67
- package/dist/processors/gridset/wordlistHelpers.d.ts +4 -6
- package/dist/processors/gridset/wordlistHelpers.js +15 -74
- package/dist/processors/gridsetProcessor.js +36 -68
- package/dist/processors/obfProcessor.js +26 -62
- package/dist/processors/obfsetProcessor.js +2 -2
- package/dist/processors/opmlProcessor.js +10 -7
- package/dist/processors/snap/helpers.d.ts +8 -8
- package/dist/processors/snap/helpers.js +50 -72
- package/dist/processors/snapProcessor.js +38 -41
- package/dist/processors/touchchatProcessor.js +54 -45
- package/dist/utilities/analytics/index.d.ts +3 -2
- package/dist/utilities/analytics/index.js +8 -10
- package/dist/utilities/analytics/reference/index.d.ts +5 -3
- package/dist/utilities/analytics/reference/index.js +26 -18
- package/dist/utilities/symbolTools.d.ts +4 -2
- package/dist/utilities/symbolTools.js +16 -15
- package/dist/utils/io.d.ts +24 -6
- package/dist/utils/io.js +64 -14
- package/dist/utils/sqlite.d.ts +2 -0
- package/dist/utils/sqlite.js +6 -8
- package/dist/utils/zip.d.ts +7 -3
- package/dist/utils/zip.js +45 -43
- package/dist/validation/applePanelsValidator.d.ts +2 -1
- package/dist/validation/applePanelsValidator.js +9 -11
- package/dist/validation/astericsValidator.d.ts +2 -1
- package/dist/validation/astericsValidator.js +5 -4
- package/dist/validation/baseValidator.d.ts +2 -2
- package/dist/validation/baseValidator.js +5 -0
- package/dist/validation/dotValidator.d.ts +2 -1
- package/dist/validation/dotValidator.js +5 -4
- package/dist/validation/excelValidator.d.ts +2 -1
- package/dist/validation/excelValidator.js +5 -4
- package/dist/validation/gridsetValidator.d.ts +2 -1
- package/dist/validation/gridsetValidator.js +11 -22
- package/dist/validation/index.d.ts +2 -2
- package/dist/validation/index.js +5 -4
- package/dist/validation/obfValidator.d.ts +2 -1
- package/dist/validation/obfValidator.js +5 -4
- package/dist/validation/obfsetValidator.d.ts +2 -1
- package/dist/validation/obfsetValidator.js +5 -4
- package/dist/validation/opmlValidator.d.ts +2 -1
- package/dist/validation/opmlValidator.js +5 -4
- package/dist/validation/snapValidator.d.ts +2 -1
- package/dist/validation/snapValidator.js +9 -5
- package/dist/validation/touchChatValidator.d.ts +4 -6
- package/dist/validation/touchChatValidator.js +21 -11
- package/dist/validation/validationTypes.d.ts +8 -1
- package/package.json +1 -1
- package/dist/core/fileProcessor.d.ts +0 -7
- package/dist/core/fileProcessor.js +0 -57
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
*
|
|
13
13
|
* This module provides symbol resolution and metadata extraction.
|
|
14
14
|
*/
|
|
15
|
-
import {
|
|
15
|
+
import { defaultFileAdapter } from '../../utils/io';
|
|
16
|
+
import { getZipAdapter } from '../../utils/zip';
|
|
16
17
|
/**
|
|
17
18
|
* Default Grid 3 installation paths by platform
|
|
18
19
|
*/
|
|
@@ -55,38 +56,6 @@ export const SYMBOL_LIBRARIES = {
|
|
|
55
56
|
* Default locale to use
|
|
56
57
|
*/
|
|
57
58
|
export const DEFAULT_LOCALE = 'en-GB';
|
|
58
|
-
function getNodeFs() {
|
|
59
|
-
try {
|
|
60
|
-
return getFs();
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
throw new Error('Symbol library access is not available in this environment.');
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
function getNodePath() {
|
|
67
|
-
try {
|
|
68
|
-
return getPath();
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
throw new Error('Path utilities are not available in this environment.');
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
let cachedAdmZip = null;
|
|
75
|
-
function getAdmZip() {
|
|
76
|
-
if (cachedAdmZip)
|
|
77
|
-
return cachedAdmZip;
|
|
78
|
-
try {
|
|
79
|
-
const nodeRequire = getNodeRequire();
|
|
80
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
81
|
-
const module = nodeRequire('adm-zip');
|
|
82
|
-
const resolved = module.default || module;
|
|
83
|
-
cachedAdmZip = resolved;
|
|
84
|
-
return resolved;
|
|
85
|
-
}
|
|
86
|
-
catch {
|
|
87
|
-
throw new Error('Symbol library access requires AdmZip in this environment.');
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
59
|
/**
|
|
91
60
|
* Parse a symbol reference string
|
|
92
61
|
* @param reference - Symbol reference like "[widgit]/food/apple.png"
|
|
@@ -124,12 +93,12 @@ export function isSymbolReference(reference) {
|
|
|
124
93
|
* Get the default Grid 3 installation path for the current platform
|
|
125
94
|
* @returns Default Grid 3 path or empty string if not found
|
|
126
95
|
*/
|
|
127
|
-
export function getDefaultGrid3Path() {
|
|
96
|
+
export function getDefaultGrid3Path(fileAdapter) {
|
|
97
|
+
const { pathExists } = fileAdapter ?? defaultFileAdapter;
|
|
128
98
|
const platform = (typeof process !== 'undefined' && process.platform ? process.platform : 'unknown');
|
|
129
99
|
const defaultPath = DEFAULT_GRID3_PATHS[platform] || '';
|
|
130
100
|
try {
|
|
131
|
-
|
|
132
|
-
if (defaultPath && fs.existsSync(defaultPath)) {
|
|
101
|
+
if (defaultPath && pathExists(defaultPath)) {
|
|
133
102
|
return defaultPath;
|
|
134
103
|
}
|
|
135
104
|
// Try to find Grid 3 in common locations
|
|
@@ -141,7 +110,7 @@ export function getDefaultGrid3Path() {
|
|
|
141
110
|
'/opt/smartbox/grid3',
|
|
142
111
|
];
|
|
143
112
|
for (const testPath of commonPaths) {
|
|
144
|
-
if (
|
|
113
|
+
if (pathExists(testPath)) {
|
|
145
114
|
return testPath;
|
|
146
115
|
}
|
|
147
116
|
}
|
|
@@ -157,9 +126,9 @@ export function getDefaultGrid3Path() {
|
|
|
157
126
|
* @param grid3Path - Grid 3 installation path
|
|
158
127
|
* @returns Path to Symbol Libraries directory (e.g., "C:\...\Grid 3\Resources\Symbols")
|
|
159
128
|
*/
|
|
160
|
-
export function getSymbolLibrariesDir(grid3Path) {
|
|
161
|
-
const
|
|
162
|
-
return
|
|
129
|
+
export function getSymbolLibrariesDir(grid3Path, fileAdapter = defaultFileAdapter) {
|
|
130
|
+
const { join } = fileAdapter;
|
|
131
|
+
return join(grid3Path, SYMBOLS_SUBDIR);
|
|
163
132
|
}
|
|
164
133
|
/**
|
|
165
134
|
* Get the symbol search indexes directory path for a given locale
|
|
@@ -168,38 +137,37 @@ export function getSymbolLibrariesDir(grid3Path) {
|
|
|
168
137
|
* @param locale - Locale code (e.g., 'en-GB')
|
|
169
138
|
* @returns Path to symbol search indexes directory (e.g., "C:\...\Grid 3\Locale\en-GB\symbolsearch")
|
|
170
139
|
*/
|
|
171
|
-
export function getSymbolSearchIndexesDir(grid3Path, locale = DEFAULT_LOCALE) {
|
|
172
|
-
const
|
|
173
|
-
return
|
|
140
|
+
export function getSymbolSearchIndexesDir(grid3Path, locale = DEFAULT_LOCALE, fileAdapter = defaultFileAdapter) {
|
|
141
|
+
const { join } = fileAdapter;
|
|
142
|
+
return join(grid3Path, SYMBOLSEARCH_SUBDIR, locale, 'symbolsearch');
|
|
174
143
|
}
|
|
175
144
|
/**
|
|
176
145
|
* Get all available symbol libraries in the Grid 3 installation
|
|
177
146
|
* @param options - Resolution options
|
|
178
147
|
* @returns Array of symbol library information
|
|
179
148
|
*/
|
|
180
|
-
export function getAvailableSymbolLibraries(options = {}) {
|
|
149
|
+
export function getAvailableSymbolLibraries(options = {}, fileAdapter) {
|
|
150
|
+
const { pathExists, getFileSize, listDir, join, basename } = fileAdapter ?? defaultFileAdapter;
|
|
181
151
|
const grid3Path = options.grid3Path || options.symbolDir || getDefaultGrid3Path();
|
|
182
152
|
if (!grid3Path) {
|
|
183
153
|
return [];
|
|
184
154
|
}
|
|
185
|
-
const symbolsDir = getSymbolLibrariesDir(grid3Path);
|
|
186
|
-
|
|
187
|
-
if (!fs.existsSync(symbolsDir)) {
|
|
155
|
+
const symbolsDir = getSymbolLibrariesDir(grid3Path, fileAdapter);
|
|
156
|
+
if (!pathExists(symbolsDir)) {
|
|
188
157
|
return [];
|
|
189
158
|
}
|
|
190
159
|
const libraries = [];
|
|
191
|
-
const files =
|
|
160
|
+
const files = listDir(symbolsDir);
|
|
192
161
|
for (const file of files) {
|
|
193
162
|
if (file.endsWith('.symbols')) {
|
|
194
|
-
const
|
|
195
|
-
const
|
|
196
|
-
const
|
|
197
|
-
const libraryName = path.basename(file, '.symbols');
|
|
163
|
+
const fullPath = join(symbolsDir, file);
|
|
164
|
+
const size = getFileSize(fullPath);
|
|
165
|
+
const libraryName = basename(file, '.symbols');
|
|
198
166
|
libraries.push({
|
|
199
167
|
name: libraryName,
|
|
200
168
|
pixFile: fullPath, // Reuse this field for the .symbols file path
|
|
201
169
|
exists: true,
|
|
202
|
-
size
|
|
170
|
+
size,
|
|
203
171
|
locale: 'global', // .symbols files are not locale-specific
|
|
204
172
|
});
|
|
205
173
|
}
|
|
@@ -212,12 +180,13 @@ export function getAvailableSymbolLibraries(options = {}) {
|
|
|
212
180
|
* @param options - Resolution options
|
|
213
181
|
* @returns Symbol library info or undefined if not found
|
|
214
182
|
*/
|
|
215
|
-
export function getSymbolLibraryInfo(libraryName, options = {}) {
|
|
183
|
+
export function getSymbolLibraryInfo(libraryName, options = {}, fileAdapter) {
|
|
184
|
+
const { pathExists, getFileSize, join } = fileAdapter ?? defaultFileAdapter;
|
|
216
185
|
const grid3Path = options.grid3Path || options.symbolDir || getDefaultGrid3Path();
|
|
217
186
|
if (!grid3Path) {
|
|
218
187
|
return undefined;
|
|
219
188
|
}
|
|
220
|
-
const symbolsDir = getSymbolLibrariesDir(grid3Path);
|
|
189
|
+
const symbolsDir = getSymbolLibrariesDir(grid3Path, fileAdapter);
|
|
221
190
|
const normalizedLibName = libraryName.toLowerCase();
|
|
222
191
|
// Try different case variations
|
|
223
192
|
const variations = [
|
|
@@ -226,16 +195,14 @@ export function getSymbolLibraryInfo(libraryName, options = {}) {
|
|
|
226
195
|
libraryName + '.symbols',
|
|
227
196
|
];
|
|
228
197
|
for (const file of variations) {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (fs.existsSync(fullPath)) {
|
|
233
|
-
const stats = fs.statSync(fullPath);
|
|
198
|
+
const fullPath = join(symbolsDir, file);
|
|
199
|
+
if (pathExists(fullPath)) {
|
|
200
|
+
const size = getFileSize(fullPath);
|
|
234
201
|
return {
|
|
235
202
|
name: libraryName,
|
|
236
203
|
pixFile: fullPath,
|
|
237
204
|
exists: true,
|
|
238
|
-
size
|
|
205
|
+
size,
|
|
239
206
|
locale: 'global',
|
|
240
207
|
};
|
|
241
208
|
}
|
|
@@ -248,7 +215,7 @@ export function getSymbolLibraryInfo(libraryName, options = {}) {
|
|
|
248
215
|
* @param options - Resolution options
|
|
249
216
|
* @returns Resolution result with image data if found
|
|
250
217
|
*/
|
|
251
|
-
export function resolveSymbolReference(reference, options = {}) {
|
|
218
|
+
export async function resolveSymbolReference(reference, options = {}, fileAdapter = defaultFileAdapter, zipAdapter) {
|
|
252
219
|
const parsed = parseSymbolReference(reference);
|
|
253
220
|
if (!parsed.isValid) {
|
|
254
221
|
return {
|
|
@@ -275,16 +242,16 @@ export function resolveSymbolReference(reference, options = {}) {
|
|
|
275
242
|
}
|
|
276
243
|
try {
|
|
277
244
|
// .symbols files are ZIP archives
|
|
278
|
-
const
|
|
279
|
-
const zip =
|
|
245
|
+
const zipFile = libraryInfo.pixFile;
|
|
246
|
+
const zip = zipAdapter ? await zipAdapter(zipFile) : await getZipAdapter(zipFile, fileAdapter);
|
|
280
247
|
// The path in the symbol reference becomes the path within the symbols/ folder
|
|
281
248
|
// e.g., [tawasl]/above bw.png becomes symbols/above bw.png
|
|
282
249
|
const symbolPath = `symbols/${parsed.path}`;
|
|
283
|
-
const entry = zip.
|
|
250
|
+
const entry = await zip.readFile(symbolPath);
|
|
284
251
|
if (!entry) {
|
|
285
252
|
// Try without the symbols/ prefix (in case reference already includes it)
|
|
286
253
|
const altPath = parsed.path.startsWith('symbols/') ? parsed.path : `symbols/${parsed.path}`;
|
|
287
|
-
const altEntry = zip.
|
|
254
|
+
const altEntry = await zip.readFile(altPath);
|
|
288
255
|
if (!altEntry) {
|
|
289
256
|
return {
|
|
290
257
|
reference: parsed,
|
|
@@ -295,7 +262,7 @@ export function resolveSymbolReference(reference, options = {}) {
|
|
|
295
262
|
};
|
|
296
263
|
}
|
|
297
264
|
// Found with alternate path
|
|
298
|
-
const data =
|
|
265
|
+
const data = Buffer.from(altEntry);
|
|
299
266
|
return {
|
|
300
267
|
reference: parsed,
|
|
301
268
|
found: true,
|
|
@@ -305,7 +272,7 @@ export function resolveSymbolReference(reference, options = {}) {
|
|
|
305
272
|
};
|
|
306
273
|
}
|
|
307
274
|
// Found the symbol!
|
|
308
|
-
const data =
|
|
275
|
+
const data = Buffer.from(entry);
|
|
309
276
|
return {
|
|
310
277
|
reference: parsed,
|
|
311
278
|
found: true,
|
|
@@ -13,8 +13,7 @@ import { parseSymbolReference } from './gridset/symbols';
|
|
|
13
13
|
import { isSymbolLibraryReference } from './gridset/resolver';
|
|
14
14
|
import { generateCloneId } from '../utilities/analytics/utils/idGenerator';
|
|
15
15
|
import { translateWithSymbols, extractSymbolsFromButton } from './gridset/symbolAlignment';
|
|
16
|
-
import {
|
|
17
|
-
import { openZipFromInput } from '../utils/zip';
|
|
16
|
+
import { decodeText } from '../utils/io';
|
|
18
17
|
class GridsetProcessor extends BaseProcessor {
|
|
19
18
|
constructor(options) {
|
|
20
19
|
super(options);
|
|
@@ -395,19 +394,18 @@ class GridsetProcessor extends BaseProcessor {
|
|
|
395
394
|
return texts;
|
|
396
395
|
}
|
|
397
396
|
async loadIntoTree(filePathOrBuffer) {
|
|
397
|
+
const { readBinaryFromInput } = this.options.fileAdapter;
|
|
398
398
|
const tree = new AACTree();
|
|
399
399
|
let zipResult;
|
|
400
400
|
try {
|
|
401
401
|
const zipInput = readBinaryFromInput(filePathOrBuffer);
|
|
402
|
-
zipResult = this.options.zipAdapter
|
|
403
|
-
? await this.options.zipAdapter(zipInput)
|
|
404
|
-
: await openZipFromInput(zipInput);
|
|
402
|
+
zipResult = await this.options.zipAdapter(zipInput);
|
|
405
403
|
}
|
|
406
404
|
catch (error) {
|
|
407
405
|
throw new Error(`Invalid ZIP file format: ${error.message}`);
|
|
408
406
|
}
|
|
409
407
|
const password = this.getGridsetPassword(filePathOrBuffer);
|
|
410
|
-
const entries = getZipEntriesFromAdapter(zipResult
|
|
408
|
+
const entries = getZipEntriesFromAdapter(zipResult, password);
|
|
411
409
|
const options = {
|
|
412
410
|
ignoreAttributes: false,
|
|
413
411
|
ignoreDeclaration: true,
|
|
@@ -1683,6 +1681,7 @@ class GridsetProcessor extends BaseProcessor {
|
|
|
1683
1681
|
return tree;
|
|
1684
1682
|
}
|
|
1685
1683
|
async processTexts(filePathOrBuffer, translations, outputPath) {
|
|
1684
|
+
const { readBinaryFromInput } = this.options.fileAdapter;
|
|
1686
1685
|
// Load the tree, apply translations, and save to new file
|
|
1687
1686
|
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
1688
1687
|
// Apply translations to all text content
|
|
@@ -1785,6 +1784,7 @@ class GridsetProcessor extends BaseProcessor {
|
|
|
1785
1784
|
* @returns Buffer of the translated gridset
|
|
1786
1785
|
*/
|
|
1787
1786
|
async processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
|
|
1787
|
+
const { readBinaryFromInput } = this.options.fileAdapter;
|
|
1788
1788
|
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
1789
1789
|
// Validate translations using shared utility
|
|
1790
1790
|
const buttonIds = Object.values(tree.pages).flatMap((page) => page.buttons.map((b) => b.id));
|
|
@@ -1826,36 +1826,12 @@ class GridsetProcessor extends BaseProcessor {
|
|
|
1826
1826
|
return readBinaryFromInput(outputPath);
|
|
1827
1827
|
}
|
|
1828
1828
|
async saveFromTree(tree, outputPath) {
|
|
1829
|
-
const
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
let finalizeZip;
|
|
1833
|
-
if (useNodeZip) {
|
|
1834
|
-
const AdmZip = getNodeRequire()('adm-zip');
|
|
1835
|
-
const zip = new AdmZip();
|
|
1836
|
-
addText = (entryPath, content) => {
|
|
1837
|
-
zip.addFile(entryPath, Buffer.from(content, 'utf8'));
|
|
1838
|
-
};
|
|
1839
|
-
addBinary = (entryPath, content) => {
|
|
1840
|
-
zip.addFile(entryPath, Buffer.from(content));
|
|
1841
|
-
};
|
|
1842
|
-
finalizeZip = () => Promise.resolve(zip.toBuffer());
|
|
1843
|
-
}
|
|
1844
|
-
else {
|
|
1845
|
-
const module = await import('jszip');
|
|
1846
|
-
const JSZip = module.default || module;
|
|
1847
|
-
const zip = new JSZip();
|
|
1848
|
-
addText = (entryPath, content) => {
|
|
1849
|
-
zip.file(entryPath, content, { binary: false });
|
|
1850
|
-
};
|
|
1851
|
-
addBinary = (entryPath, content) => {
|
|
1852
|
-
zip.file(entryPath, content);
|
|
1853
|
-
};
|
|
1854
|
-
finalizeZip = async () => zip.generateAsync({ type: 'uint8array' });
|
|
1855
|
-
}
|
|
1829
|
+
const files = [];
|
|
1830
|
+
const { writeBinaryToPath } = this.options.fileAdapter;
|
|
1831
|
+
const zip = await this.options.zipAdapter();
|
|
1856
1832
|
if (Object.keys(tree.pages).length === 0) {
|
|
1857
1833
|
// Create empty zip for empty tree
|
|
1858
|
-
const zipBuffer = await
|
|
1834
|
+
const zipBuffer = await zip.writeFiles([]);
|
|
1859
1835
|
writeBinaryToPath(outputPath, zipBuffer);
|
|
1860
1836
|
return;
|
|
1861
1837
|
}
|
|
@@ -1928,7 +1904,10 @@ class GridsetProcessor extends BaseProcessor {
|
|
|
1928
1904
|
suppressEmptyNode: true,
|
|
1929
1905
|
});
|
|
1930
1906
|
const settingsXmlContent = settingsBuilder.build(settingsData);
|
|
1931
|
-
|
|
1907
|
+
files.push({
|
|
1908
|
+
name: 'Settings0/settings.xml',
|
|
1909
|
+
data: settingsXmlContent,
|
|
1910
|
+
});
|
|
1932
1911
|
// Create Settings0/Styles/style.xml if there are styles
|
|
1933
1912
|
if (uniqueStyles.size > 0) {
|
|
1934
1913
|
const stylesArray = Array.from(uniqueStyles.values()).map(({ id, style }) => {
|
|
@@ -1961,7 +1940,10 @@ class GridsetProcessor extends BaseProcessor {
|
|
|
1961
1940
|
indentBy: ' ',
|
|
1962
1941
|
});
|
|
1963
1942
|
const styleXmlContent = styleBuilder.build(styleData);
|
|
1964
|
-
|
|
1943
|
+
files.push({
|
|
1944
|
+
name: 'Settings0/Styles/styles.xml',
|
|
1945
|
+
data: styleXmlContent,
|
|
1946
|
+
});
|
|
1965
1947
|
}
|
|
1966
1948
|
// Collect grid file paths for FileMap.xml
|
|
1967
1949
|
const gridFilePaths = [];
|
|
@@ -2102,14 +2084,20 @@ class GridsetProcessor extends BaseProcessor {
|
|
|
2102
2084
|
// Add to zip in Grids folder with proper Grid3 naming
|
|
2103
2085
|
const gridPath = `Grids/${page.name || page.id}/grid.xml`;
|
|
2104
2086
|
gridFilePaths.push(gridPath);
|
|
2105
|
-
|
|
2087
|
+
files.push({
|
|
2088
|
+
name: gridPath,
|
|
2089
|
+
data: xmlContent,
|
|
2090
|
+
});
|
|
2106
2091
|
});
|
|
2107
2092
|
// Write image files to ZIP
|
|
2108
2093
|
buttonImages.forEach((imgData) => {
|
|
2109
2094
|
if (imgData.imageData && imgData.imageData.length > 0) {
|
|
2110
2095
|
// Create image path in the grid's directory
|
|
2111
2096
|
const imagePath = `Grids/${imgData.pageName}/${imgData.x}-${imgData.y}-0-text-0.${imgData.ext}`;
|
|
2112
|
-
|
|
2097
|
+
files.push({
|
|
2098
|
+
name: imagePath,
|
|
2099
|
+
data: imgData.imageData,
|
|
2100
|
+
});
|
|
2113
2101
|
}
|
|
2114
2102
|
});
|
|
2115
2103
|
// Create FileMap.xml to map all grid files with their dynamic image files
|
|
@@ -2148,9 +2136,12 @@ class GridsetProcessor extends BaseProcessor {
|
|
|
2148
2136
|
indentBy: ' ',
|
|
2149
2137
|
});
|
|
2150
2138
|
const fileMapXmlContent = fileMapBuilder.build(fileMapData);
|
|
2151
|
-
|
|
2139
|
+
files.push({
|
|
2140
|
+
name: 'FileMap.xml',
|
|
2141
|
+
data: fileMapXmlContent,
|
|
2142
|
+
});
|
|
2152
2143
|
// Write the zip file
|
|
2153
|
-
const zipBuffer = await
|
|
2144
|
+
const zipBuffer = await zip.writeFiles(files);
|
|
2154
2145
|
writeBinaryToPath(outputPath, zipBuffer);
|
|
2155
2146
|
}
|
|
2156
2147
|
// Helper method to calculate column definitions based on page layout
|
|
@@ -2248,7 +2239,7 @@ class GridsetProcessor extends BaseProcessor {
|
|
|
2248
2239
|
* @returns Promise with validation result
|
|
2249
2240
|
*/
|
|
2250
2241
|
async validate(filePath) {
|
|
2251
|
-
return GridsetValidator.validateFile(filePath);
|
|
2242
|
+
return GridsetValidator.validateFile(filePath, this.options.fileAdapter);
|
|
2252
2243
|
}
|
|
2253
2244
|
}
|
|
2254
2245
|
export { GridsetProcessor };
|
|
@@ -2,8 +2,8 @@ import { BaseProcessor, } from '../core/baseProcessor';
|
|
|
2
2
|
import { AACTree, AACPage, AACButton, AACSemanticCategory, AACSemanticIntent, } from '../core/treeStructure';
|
|
3
3
|
import { generateCloneId } from '../utilities/analytics/utils/idGenerator';
|
|
4
4
|
import { extractAllButtonsForTranslation, validateTranslationResults, } from '../utilities/translation/translationProcessor';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { encodeBase64, decodeText } from '../utils/io';
|
|
6
|
+
import { getZipAdapter } from '../utils/zip';
|
|
7
7
|
const OBF_FORMAT_VERSION = 'open-board-0.1';
|
|
8
8
|
/**
|
|
9
9
|
* Map OBF hidden value to AAC standard visibility
|
|
@@ -283,6 +283,7 @@ class ObfProcessor extends BaseProcessor {
|
|
|
283
283
|
return texts;
|
|
284
284
|
}
|
|
285
285
|
async loadIntoTree(filePathOrBuffer) {
|
|
286
|
+
const { readBinaryFromInput, readTextFromInput } = this.options.fileAdapter;
|
|
286
287
|
// Detailed logging for debugging input
|
|
287
288
|
const bufferLength = typeof filePathOrBuffer === 'string'
|
|
288
289
|
? null
|
|
@@ -380,10 +381,7 @@ class ObfProcessor extends BaseProcessor {
|
|
|
380
381
|
return tree;
|
|
381
382
|
}
|
|
382
383
|
try {
|
|
383
|
-
|
|
384
|
-
? await this.options.zipAdapter(filePathOrBuffer)
|
|
385
|
-
: await openZipFromInput(filePathOrBuffer);
|
|
386
|
-
this.zipFile = zipResult.zip;
|
|
384
|
+
this.zipFile = await this.options.zipAdapter(filePathOrBuffer);
|
|
387
385
|
}
|
|
388
386
|
catch (err) {
|
|
389
387
|
console.error('[OBF] Error loading ZIP:', err);
|
|
@@ -539,6 +537,7 @@ class ObfProcessor extends BaseProcessor {
|
|
|
539
537
|
};
|
|
540
538
|
}
|
|
541
539
|
async processTexts(filePathOrBuffer, translations, outputPath) {
|
|
540
|
+
const { readBinaryFromInput } = this.options.fileAdapter;
|
|
542
541
|
// Load the tree, apply translations, and save to new file
|
|
543
542
|
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
544
543
|
// Apply translations to all text content
|
|
@@ -571,6 +570,7 @@ class ObfProcessor extends BaseProcessor {
|
|
|
571
570
|
return readBinaryFromInput(outputPath);
|
|
572
571
|
}
|
|
573
572
|
async saveFromTree(tree, outputPath) {
|
|
573
|
+
const { writeTextToPath, writeBinaryToPath } = this.options.fileAdapter;
|
|
574
574
|
if (outputPath.endsWith('.obf')) {
|
|
575
575
|
// Save as single OBF JSON file
|
|
576
576
|
const rootPage = tree.rootId ? tree.getPage(tree.rootId) : Object.values(tree.pages)[0];
|
|
@@ -581,32 +581,18 @@ class ObfProcessor extends BaseProcessor {
|
|
|
581
581
|
writeTextToPath(outputPath, JSON.stringify(obfBoard, null, 2));
|
|
582
582
|
}
|
|
583
583
|
else {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
const
|
|
587
|
-
const
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
597
|
-
else {
|
|
598
|
-
const module = await import('jszip');
|
|
599
|
-
const JSZip = module.default || module;
|
|
600
|
-
const zip = new JSZip();
|
|
601
|
-
Object.values(tree.pages).forEach((page) => {
|
|
602
|
-
const obfBoard = this.createObfBoardFromPage(page, 'Board', tree.metadata);
|
|
603
|
-
const obfContent = JSON.stringify(obfBoard, null, 2);
|
|
604
|
-
zip.file(`${page.id}.obf`, obfContent);
|
|
605
|
-
});
|
|
606
|
-
const zipBuffer = await zip.generateAsync({ type: 'uint8array' });
|
|
607
|
-
const { writeBinaryToPath } = await import('../utils/io');
|
|
608
|
-
writeBinaryToPath(outputPath, zipBuffer);
|
|
609
|
-
}
|
|
584
|
+
const files = Object.values(tree.pages).map((page) => {
|
|
585
|
+
const obfBoard = this.createObfBoardFromPage(page, 'Board', tree.metadata);
|
|
586
|
+
const obfContent = JSON.stringify(obfBoard, null, 2);
|
|
587
|
+
const name = page.id.endsWith('.obf') ? page.id : `${page.id}.obf`;
|
|
588
|
+
return {
|
|
589
|
+
name,
|
|
590
|
+
data: new TextEncoder().encode(obfContent),
|
|
591
|
+
};
|
|
592
|
+
});
|
|
593
|
+
const zip = await getZipAdapter(undefined, this.options.fileAdapter);
|
|
594
|
+
const zipData = await zip.writeFiles(files);
|
|
595
|
+
writeBinaryToPath(outputPath, zipData);
|
|
610
596
|
}
|
|
611
597
|
}
|
|
612
598
|
/**
|
|
@@ -630,7 +616,7 @@ class ObfProcessor extends BaseProcessor {
|
|
|
630
616
|
*/
|
|
631
617
|
async validate(filePath) {
|
|
632
618
|
const ObfValidator = this.getObfValidator();
|
|
633
|
-
return ObfValidator.validateFile(filePath);
|
|
619
|
+
return ObfValidator.validateFile(filePath, this.options.fileAdapter);
|
|
634
620
|
}
|
|
635
621
|
/**
|
|
636
622
|
* Extract symbol information from an OBF/OBZ file for LLM-based translation.
|
|
@@ -672,6 +658,7 @@ class ObfProcessor extends BaseProcessor {
|
|
|
672
658
|
* @returns Buffer of the translated OBF/OBZ file
|
|
673
659
|
*/
|
|
674
660
|
async processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
|
|
661
|
+
const { readBinaryFromInput } = this.options.fileAdapter;
|
|
675
662
|
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
676
663
|
// Validate translations using shared utility
|
|
677
664
|
const buttonIds = Object.values(tree.pages).flatMap((page) => page.buttons.map((b) => b.id));
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { BaseProcessor, } from '../core/baseProcessor';
|
|
2
2
|
import { AACTree, AACPage, AACButton, AACSemanticIntent } from '../core/treeStructure';
|
|
3
|
-
// Removed unused import: FileProcessor
|
|
4
3
|
import { XMLParser, XMLValidator, XMLBuilder } from 'fast-xml-parser';
|
|
5
4
|
import { ValidationFailureError, buildValidationResultFromMessage, } from '../validation/validationTypes';
|
|
6
|
-
import { getBasename,
|
|
5
|
+
import { getBasename, encodeText } from '../utils/io';
|
|
7
6
|
class OpmlProcessor extends BaseProcessor {
|
|
8
7
|
constructor(options) {
|
|
9
8
|
super(options);
|
|
@@ -50,6 +49,7 @@ class OpmlProcessor extends BaseProcessor {
|
|
|
50
49
|
return { page, childPages };
|
|
51
50
|
}
|
|
52
51
|
async extractTexts(filePathOrBuffer) {
|
|
52
|
+
const { readTextFromInput } = this.options.fileAdapter;
|
|
53
53
|
await Promise.resolve();
|
|
54
54
|
const content = readTextFromInput(filePathOrBuffer);
|
|
55
55
|
const parser = new XMLParser({ ignoreAttributes: false });
|
|
@@ -82,6 +82,7 @@ class OpmlProcessor extends BaseProcessor {
|
|
|
82
82
|
return texts;
|
|
83
83
|
}
|
|
84
84
|
async loadIntoTree(filePathOrBuffer) {
|
|
85
|
+
const { readBinaryFromInput, readTextFromInput } = this.options.fileAdapter;
|
|
85
86
|
await Promise.resolve();
|
|
86
87
|
const filename = typeof filePathOrBuffer === 'string' ? getBasename(filePathOrBuffer) : 'upload.opml';
|
|
87
88
|
const buffer = readBinaryFromInput(filePathOrBuffer);
|
|
@@ -168,6 +169,7 @@ class OpmlProcessor extends BaseProcessor {
|
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
171
|
async processTexts(filePathOrBuffer, translations, outputPath) {
|
|
172
|
+
const { writeBinaryToPath, readTextFromInput } = this.options.fileAdapter;
|
|
171
173
|
await Promise.resolve();
|
|
172
174
|
const content = readTextFromInput(filePathOrBuffer);
|
|
173
175
|
let translatedContent = content;
|
|
@@ -184,6 +186,7 @@ class OpmlProcessor extends BaseProcessor {
|
|
|
184
186
|
return resultBuffer;
|
|
185
187
|
}
|
|
186
188
|
async saveFromTree(tree, outputPath) {
|
|
189
|
+
const { writeTextToPath } = this.options.fileAdapter;
|
|
187
190
|
await Promise.resolve();
|
|
188
191
|
// Helper to recursively build outline nodes with cycle detection
|
|
189
192
|
function buildOutline(page, visited = new Set()) {
|