@willwade/aac-processors 0.0.5 → 0.0.6
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/cli/index.js +2 -2
- package/dist/core/treeStructure.d.ts +9 -1
- package/dist/core/treeStructure.js +5 -1
- package/dist/processors/applePanelsProcessor.js +67 -41
- package/dist/processors/astericsGridProcessor.js +120 -88
- package/dist/processors/excelProcessor.d.ts +3 -3
- package/dist/processors/excelProcessor.js +48 -77
- package/dist/processors/gridset/wordlistHelpers.js +3 -2
- package/dist/processors/gridsetProcessor.js +97 -105
- package/dist/processors/obfProcessor.d.ts +2 -0
- package/dist/processors/obfProcessor.js +129 -52
- package/dist/processors/opmlProcessor.js +13 -3
- package/dist/processors/snapProcessor.js +18 -10
- package/dist/processors/touchchatProcessor.js +45 -22
- package/dist/types/aac.d.ts +4 -0
- package/package.json +1 -1
|
@@ -9,12 +9,15 @@ const treeStructure_1 = require("../core/treeStructure");
|
|
|
9
9
|
// Removed unused import: FileProcessor
|
|
10
10
|
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
11
11
|
const fs_1 = __importDefault(require("fs"));
|
|
12
|
+
// Removed unused import: path
|
|
13
|
+
const OBF_FORMAT_VERSION = 'open-board-0.1';
|
|
12
14
|
class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
13
15
|
constructor(options) {
|
|
14
16
|
super(options);
|
|
15
17
|
}
|
|
16
18
|
processBoard(boardData, _boardPath) {
|
|
17
|
-
const
|
|
19
|
+
const sourceButtons = boardData.buttons || [];
|
|
20
|
+
const buttons = sourceButtons.map((btn) => {
|
|
18
21
|
const semanticAction = btn.load_board
|
|
19
22
|
? {
|
|
20
23
|
category: treeStructure_1.AACSemanticCategory.NAVIGATION,
|
|
@@ -46,33 +49,62 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
46
49
|
targetPageId: btn.load_board?.path,
|
|
47
50
|
});
|
|
48
51
|
});
|
|
52
|
+
const buttonMap = new Map(buttons.map((btn) => [btn.id, btn]));
|
|
49
53
|
const page = new treeStructure_1.AACPage({
|
|
50
54
|
id: String(boardData?.id || ''),
|
|
51
55
|
name: String(boardData?.name || ''),
|
|
52
56
|
grid: [],
|
|
53
57
|
buttons,
|
|
54
58
|
parentId: null,
|
|
59
|
+
locale: boardData.locale,
|
|
60
|
+
descriptionHtml: boardData.description_html,
|
|
61
|
+
images: boardData.images,
|
|
62
|
+
sounds: boardData.sounds,
|
|
55
63
|
});
|
|
56
64
|
// Process grid layout if available
|
|
57
65
|
if (boardData.grid) {
|
|
58
|
-
const rows = boardData.grid.rows
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
const rows = typeof boardData.grid.rows === 'number'
|
|
67
|
+
? boardData.grid.rows
|
|
68
|
+
: boardData.grid.order?.length || 0;
|
|
69
|
+
const cols = typeof boardData.grid.columns === 'number'
|
|
70
|
+
? boardData.grid.columns
|
|
71
|
+
: boardData.grid.order
|
|
72
|
+
? boardData.grid.order.reduce((max, row) => Math.max(max, Array.isArray(row) ? row.length : 0), 0)
|
|
73
|
+
: 0;
|
|
74
|
+
if (rows > 0 && cols > 0) {
|
|
75
|
+
const grid = Array.from({ length: rows }, () => Array.from({ length: cols }, () => null));
|
|
76
|
+
if (Array.isArray(boardData.grid.order) && boardData.grid.order.length) {
|
|
77
|
+
boardData.grid.order.forEach((orderRow, rowIndex) => {
|
|
78
|
+
if (!Array.isArray(orderRow))
|
|
79
|
+
return;
|
|
80
|
+
orderRow.forEach((cellId, colIndex) => {
|
|
81
|
+
if (cellId === null || cellId === undefined)
|
|
82
|
+
return;
|
|
83
|
+
if (rowIndex >= rows || colIndex >= cols)
|
|
84
|
+
return;
|
|
85
|
+
const aacBtn = buttonMap.get(String(cellId));
|
|
86
|
+
if (aacBtn) {
|
|
87
|
+
grid[rowIndex][colIndex] = aacBtn;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
for (const btn of sourceButtons) {
|
|
94
|
+
if (typeof btn.box_id === 'number') {
|
|
95
|
+
const row = Math.floor(btn.box_id / cols);
|
|
96
|
+
const col = btn.box_id % cols;
|
|
97
|
+
if (row < rows && col < cols) {
|
|
98
|
+
const aacBtn = buttonMap.get(String(btn.id));
|
|
99
|
+
if (aacBtn) {
|
|
100
|
+
grid[row][col] = aacBtn;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
71
103
|
}
|
|
72
104
|
}
|
|
73
105
|
}
|
|
106
|
+
page.grid = grid;
|
|
74
107
|
}
|
|
75
|
-
page.grid = grid;
|
|
76
108
|
}
|
|
77
109
|
return page;
|
|
78
110
|
}
|
|
@@ -123,9 +155,8 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
123
155
|
}
|
|
124
156
|
// If input is a string path and ends with .obf, treat as JSON
|
|
125
157
|
if (typeof filePathOrBuffer === 'string' && filePathOrBuffer.endsWith('.obf')) {
|
|
126
|
-
const fs = require('fs');
|
|
127
158
|
try {
|
|
128
|
-
const content =
|
|
159
|
+
const content = fs_1.default.readFileSync(filePathOrBuffer, 'utf8');
|
|
129
160
|
const boardData = tryParseObfJson(content);
|
|
130
161
|
if (boardData) {
|
|
131
162
|
console.log('[OBF] Detected .obf file, parsed as JSON');
|
|
@@ -186,6 +217,73 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
186
217
|
});
|
|
187
218
|
return tree;
|
|
188
219
|
}
|
|
220
|
+
buildGridMetadata(page) {
|
|
221
|
+
const buttonPositions = new Map();
|
|
222
|
+
const totalRows = Array.isArray(page.grid) ? page.grid.length : 0;
|
|
223
|
+
const totalColumns = totalRows > 0
|
|
224
|
+
? page.grid.reduce((max, row) => Math.max(max, Array.isArray(row) ? row.length : 0), 0)
|
|
225
|
+
: 0;
|
|
226
|
+
if (totalRows === 0 || totalColumns === 0) {
|
|
227
|
+
if (!page.buttons.length) {
|
|
228
|
+
return { rows: 0, columns: 0, order: [], buttonPositions };
|
|
229
|
+
}
|
|
230
|
+
const fallbackRow = page.buttons.map((button, index) => {
|
|
231
|
+
const id = String(button.id ?? '');
|
|
232
|
+
buttonPositions.set(id, index);
|
|
233
|
+
return id;
|
|
234
|
+
});
|
|
235
|
+
return { rows: 1, columns: fallbackRow.length, order: [fallbackRow], buttonPositions };
|
|
236
|
+
}
|
|
237
|
+
const order = [];
|
|
238
|
+
for (let rowIndex = 0; rowIndex < totalRows; rowIndex++) {
|
|
239
|
+
const sourceRow = page.grid[rowIndex] || [];
|
|
240
|
+
const orderRow = [];
|
|
241
|
+
for (let colIndex = 0; colIndex < totalColumns; colIndex++) {
|
|
242
|
+
const cell = sourceRow[colIndex] || null;
|
|
243
|
+
if (cell) {
|
|
244
|
+
const id = String(cell.id ?? '');
|
|
245
|
+
orderRow.push(id);
|
|
246
|
+
buttonPositions.set(id, rowIndex * totalColumns + colIndex);
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
orderRow.push(null);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
order.push(orderRow);
|
|
253
|
+
}
|
|
254
|
+
return { rows: totalRows, columns: totalColumns, order, buttonPositions };
|
|
255
|
+
}
|
|
256
|
+
createObfBoardFromPage(page, fallbackName) {
|
|
257
|
+
const { rows, columns, order, buttonPositions } = this.buildGridMetadata(page);
|
|
258
|
+
const boardName = page.name || fallbackName;
|
|
259
|
+
return {
|
|
260
|
+
format: OBF_FORMAT_VERSION,
|
|
261
|
+
id: page.id,
|
|
262
|
+
locale: page.locale || 'en',
|
|
263
|
+
name: boardName,
|
|
264
|
+
description_html: page.descriptionHtml || boardName,
|
|
265
|
+
grid: {
|
|
266
|
+
rows,
|
|
267
|
+
columns,
|
|
268
|
+
order,
|
|
269
|
+
},
|
|
270
|
+
buttons: page.buttons.map((button) => ({
|
|
271
|
+
id: button.id,
|
|
272
|
+
label: button.label,
|
|
273
|
+
vocalization: button.message || button.label,
|
|
274
|
+
load_board: button.semanticAction?.intent === treeStructure_1.AACSemanticIntent.NAVIGATE_TO && button.targetPageId
|
|
275
|
+
? {
|
|
276
|
+
path: button.targetPageId,
|
|
277
|
+
}
|
|
278
|
+
: undefined,
|
|
279
|
+
background_color: button.style?.backgroundColor,
|
|
280
|
+
border_color: button.style?.borderColor,
|
|
281
|
+
box_id: buttonPositions.get(String(button.id ?? '')),
|
|
282
|
+
})),
|
|
283
|
+
images: Array.isArray(page.images) ? page.images : [],
|
|
284
|
+
sounds: Array.isArray(page.sounds) ? page.sounds : [],
|
|
285
|
+
};
|
|
286
|
+
}
|
|
189
287
|
processTexts(filePathOrBuffer, translations, outputPath) {
|
|
190
288
|
// Load the tree, apply translations, and save to new file
|
|
191
289
|
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
@@ -193,15 +291,24 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
193
291
|
Object.values(tree.pages).forEach((page) => {
|
|
194
292
|
// Translate page names
|
|
195
293
|
if (page.name && translations.has(page.name)) {
|
|
196
|
-
|
|
294
|
+
const translatedName = translations.get(page.name);
|
|
295
|
+
if (translatedName !== undefined) {
|
|
296
|
+
page.name = translatedName;
|
|
297
|
+
}
|
|
197
298
|
}
|
|
198
299
|
// Translate button labels and messages
|
|
199
300
|
page.buttons.forEach((button) => {
|
|
200
301
|
if (button.label && translations.has(button.label)) {
|
|
201
|
-
|
|
302
|
+
const translatedLabel = translations.get(button.label);
|
|
303
|
+
if (translatedLabel !== undefined) {
|
|
304
|
+
button.label = translatedLabel;
|
|
305
|
+
}
|
|
202
306
|
}
|
|
203
307
|
if (button.message && translations.has(button.message)) {
|
|
204
|
-
|
|
308
|
+
const translatedMessage = translations.get(button.message);
|
|
309
|
+
if (translatedMessage !== undefined) {
|
|
310
|
+
button.message = translatedMessage;
|
|
311
|
+
}
|
|
205
312
|
}
|
|
206
313
|
});
|
|
207
314
|
});
|
|
@@ -216,44 +323,14 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
216
323
|
if (!rootPage) {
|
|
217
324
|
throw new Error('No pages to save');
|
|
218
325
|
}
|
|
219
|
-
const obfBoard =
|
|
220
|
-
id: rootPage.id,
|
|
221
|
-
name: rootPage.name || 'Exported Board',
|
|
222
|
-
buttons: rootPage.buttons.map((button) => ({
|
|
223
|
-
id: button.id,
|
|
224
|
-
label: button.label,
|
|
225
|
-
vocalization: button.message || button.label,
|
|
226
|
-
load_board: button.semanticAction?.intent === treeStructure_1.AACSemanticIntent.NAVIGATE_TO && button.targetPageId
|
|
227
|
-
? {
|
|
228
|
-
path: button.targetPageId,
|
|
229
|
-
}
|
|
230
|
-
: undefined,
|
|
231
|
-
background_color: button.style?.backgroundColor,
|
|
232
|
-
border_color: button.style?.borderColor,
|
|
233
|
-
})),
|
|
234
|
-
};
|
|
326
|
+
const obfBoard = this.createObfBoardFromPage(rootPage, 'Exported Board');
|
|
235
327
|
fs_1.default.writeFileSync(outputPath, JSON.stringify(obfBoard, null, 2));
|
|
236
328
|
}
|
|
237
329
|
else {
|
|
238
330
|
// Save as OBZ (zip with multiple OBF files)
|
|
239
331
|
const zip = new adm_zip_1.default();
|
|
240
332
|
Object.values(tree.pages).forEach((page) => {
|
|
241
|
-
const obfBoard =
|
|
242
|
-
id: page.id,
|
|
243
|
-
name: page.name || 'Board',
|
|
244
|
-
buttons: page.buttons.map((button) => ({
|
|
245
|
-
id: button.id,
|
|
246
|
-
label: button.label,
|
|
247
|
-
vocalization: button.message || button.label,
|
|
248
|
-
load_board: button.semanticAction?.intent === treeStructure_1.AACSemanticIntent.NAVIGATE_TO && button.targetPageId
|
|
249
|
-
? {
|
|
250
|
-
path: button.targetPageId,
|
|
251
|
-
}
|
|
252
|
-
: undefined,
|
|
253
|
-
background_color: button.style?.backgroundColor,
|
|
254
|
-
border_color: button.style?.borderColor,
|
|
255
|
-
})),
|
|
256
|
-
};
|
|
333
|
+
const obfBoard = this.createObfBoardFromPage(page, 'Board');
|
|
257
334
|
const obfContent = JSON.stringify(obfBoard, null, 2);
|
|
258
335
|
zip.addFile(`${page.id}.obf`, Buffer.from(obfContent, 'utf8'));
|
|
259
336
|
});
|
|
@@ -173,7 +173,18 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
173
173
|
.filter((b) => b.semanticAction?.intent === treeStructure_1.AACSemanticIntent.NAVIGATE_TO &&
|
|
174
174
|
!!b.targetPageId &&
|
|
175
175
|
!!tree.pages[b.targetPageId])
|
|
176
|
-
.map((b) =>
|
|
176
|
+
.map((b) => {
|
|
177
|
+
const targetId = b.targetPageId;
|
|
178
|
+
if (!targetId) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
const targetPage = tree.pages[targetId];
|
|
182
|
+
if (!targetPage) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
return buildOutline(targetPage, new Set(visited));
|
|
186
|
+
})
|
|
187
|
+
.filter((childOutline) => childOutline !== null);
|
|
177
188
|
if (childOutlines.length)
|
|
178
189
|
outline.outline = childOutlines;
|
|
179
190
|
return outline;
|
|
@@ -206,8 +217,7 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
206
217
|
},
|
|
207
218
|
};
|
|
208
219
|
// Convert to XML
|
|
209
|
-
const
|
|
210
|
-
const builder = new XMLBuilder({
|
|
220
|
+
const builder = new fast_xml_parser_1.XMLBuilder({
|
|
211
221
|
ignoreAttributes: false,
|
|
212
222
|
format: true,
|
|
213
223
|
indentBy: ' ',
|
|
@@ -185,7 +185,6 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
185
185
|
}
|
|
186
186
|
// Create semantic action for Snap button
|
|
187
187
|
let semanticAction;
|
|
188
|
-
let legacyAction = null;
|
|
189
188
|
if (targetPageUniqueId) {
|
|
190
189
|
semanticAction = {
|
|
191
190
|
category: treeStructure_1.AACSemanticCategory.NAVIGATION,
|
|
@@ -202,10 +201,6 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
202
201
|
targetPageId: targetPageUniqueId,
|
|
203
202
|
},
|
|
204
203
|
};
|
|
205
|
-
legacyAction = {
|
|
206
|
-
type: 'NAVIGATE',
|
|
207
|
-
targetPageId: targetPageUniqueId,
|
|
208
|
-
};
|
|
209
204
|
}
|
|
210
205
|
else {
|
|
211
206
|
semanticAction = {
|
|
@@ -283,15 +278,16 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
283
278
|
return tree;
|
|
284
279
|
}
|
|
285
280
|
catch (error) {
|
|
281
|
+
const fileIdentifier = typeof filePathOrBuffer === 'string' ? filePathOrBuffer : '[buffer input]';
|
|
286
282
|
// Provide more specific error messages
|
|
287
283
|
if (error.code === 'SQLITE_NOTADB') {
|
|
288
284
|
throw new Error(`Invalid SQLite database file: ${typeof filePathOrBuffer === 'string' ? filePathOrBuffer : 'buffer'}`);
|
|
289
285
|
}
|
|
290
286
|
else if (error.code === 'ENOENT') {
|
|
291
|
-
throw new Error(`File not found: ${
|
|
287
|
+
throw new Error(`File not found: ${fileIdentifier}`);
|
|
292
288
|
}
|
|
293
289
|
else if (error.code === 'EACCES') {
|
|
294
|
-
throw new Error(`Permission denied accessing file: ${
|
|
290
|
+
throw new Error(`Permission denied accessing file: ${fileIdentifier}`);
|
|
295
291
|
}
|
|
296
292
|
else {
|
|
297
293
|
throw new Error(`Failed to load Snap file: ${error.message}`);
|
|
@@ -320,15 +316,24 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
320
316
|
Object.values(tree.pages).forEach((page) => {
|
|
321
317
|
// Translate page names
|
|
322
318
|
if (page.name && translations.has(page.name)) {
|
|
323
|
-
|
|
319
|
+
const translatedName = translations.get(page.name);
|
|
320
|
+
if (translatedName !== undefined) {
|
|
321
|
+
page.name = translatedName;
|
|
322
|
+
}
|
|
324
323
|
}
|
|
325
324
|
// Translate button labels and messages
|
|
326
325
|
page.buttons.forEach((button) => {
|
|
327
326
|
if (button.label && translations.has(button.label)) {
|
|
328
|
-
|
|
327
|
+
const translatedLabel = translations.get(button.label);
|
|
328
|
+
if (translatedLabel !== undefined) {
|
|
329
|
+
button.label = translatedLabel;
|
|
330
|
+
}
|
|
329
331
|
}
|
|
330
332
|
if (button.message && translations.has(button.message)) {
|
|
331
|
-
|
|
333
|
+
const translatedMessage = translations.get(button.message);
|
|
334
|
+
if (translatedMessage !== undefined) {
|
|
335
|
+
button.message = translatedMessage;
|
|
336
|
+
}
|
|
332
337
|
}
|
|
333
338
|
});
|
|
334
339
|
});
|
|
@@ -419,6 +424,9 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
419
424
|
// Second pass: create buttons with proper page references
|
|
420
425
|
Object.values(tree.pages).forEach((page) => {
|
|
421
426
|
const numericPageId = pageIdMap.get(page.id);
|
|
427
|
+
if (numericPageId === undefined) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
422
430
|
page.buttons.forEach((button, index) => {
|
|
423
431
|
// Find button position in grid layout
|
|
424
432
|
let gridPosition = `${index % 4},${Math.floor(index / 4)}`; // Default fallback
|
|
@@ -12,6 +12,9 @@ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
|
12
12
|
const path_1 = __importDefault(require("path"));
|
|
13
13
|
const fs_1 = __importDefault(require("fs"));
|
|
14
14
|
const os_1 = __importDefault(require("os"));
|
|
15
|
+
const toNumberOrUndefined = (value) => typeof value === 'number' ? value : undefined;
|
|
16
|
+
const toStringOrUndefined = (value) => typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
17
|
+
const toBooleanOrUndefined = (value) => typeof value === 'number' ? value !== 0 : undefined;
|
|
15
18
|
function intToHex(colorInt) {
|
|
16
19
|
if (colorInt === null || typeof colorInt === 'undefined') {
|
|
17
20
|
return undefined;
|
|
@@ -84,7 +87,9 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
84
87
|
const buttonStyles = new Map();
|
|
85
88
|
const pageStyles = new Map();
|
|
86
89
|
try {
|
|
87
|
-
const buttonStyleRows = db
|
|
90
|
+
const buttonStyleRows = db
|
|
91
|
+
.prepare('SELECT * FROM button_styles')
|
|
92
|
+
.all();
|
|
88
93
|
buttonStyleRows.forEach((style) => {
|
|
89
94
|
buttonStyles.set(style.id, style);
|
|
90
95
|
});
|
|
@@ -162,15 +167,15 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
162
167
|
style: {
|
|
163
168
|
backgroundColor: intToHex(style?.body_color),
|
|
164
169
|
borderColor: intToHex(style?.border_color),
|
|
165
|
-
borderWidth: style?.border_width,
|
|
170
|
+
borderWidth: toNumberOrUndefined(style?.border_width),
|
|
166
171
|
fontColor: intToHex(style?.font_color),
|
|
167
|
-
fontSize: style?.font_height,
|
|
168
|
-
fontFamily: style?.font_name,
|
|
169
|
-
fontWeight: style?.font_bold ? 'bold' :
|
|
170
|
-
fontStyle: style?.font_italic ? 'italic' :
|
|
171
|
-
textUnderline: style?.font_underline,
|
|
172
|
-
transparent: style?.transparent,
|
|
173
|
-
labelOnTop: style?.label_on_top,
|
|
172
|
+
fontSize: toNumberOrUndefined(style?.font_height),
|
|
173
|
+
fontFamily: toStringOrUndefined(style?.font_name),
|
|
174
|
+
fontWeight: style?.font_bold ? 'bold' : undefined,
|
|
175
|
+
fontStyle: style?.font_italic ? 'italic' : undefined,
|
|
176
|
+
textUnderline: toBooleanOrUndefined(style?.font_underline),
|
|
177
|
+
transparent: toBooleanOrUndefined(style?.transparent),
|
|
178
|
+
labelOnTop: toBooleanOrUndefined(style?.label_on_top),
|
|
174
179
|
},
|
|
175
180
|
});
|
|
176
181
|
buttonBoxes.get(cell.box_id)?.push({
|
|
@@ -276,15 +281,15 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
276
281
|
style: {
|
|
277
282
|
backgroundColor: intToHex(style?.body_color),
|
|
278
283
|
borderColor: intToHex(style?.border_color),
|
|
279
|
-
borderWidth: style?.border_width,
|
|
284
|
+
borderWidth: toNumberOrUndefined(style?.border_width),
|
|
280
285
|
fontColor: intToHex(style?.font_color),
|
|
281
|
-
fontSize: style?.font_height,
|
|
282
|
-
fontFamily: style?.font_name,
|
|
283
|
-
fontWeight: style?.font_bold ? 'bold' :
|
|
284
|
-
fontStyle: style?.font_italic ? 'italic' :
|
|
285
|
-
textUnderline: style?.font_underline,
|
|
286
|
-
transparent: style?.transparent,
|
|
287
|
-
labelOnTop: style?.label_on_top,
|
|
286
|
+
fontSize: toNumberOrUndefined(style?.font_height),
|
|
287
|
+
fontFamily: toStringOrUndefined(style?.font_name),
|
|
288
|
+
fontWeight: style?.font_bold ? 'bold' : undefined,
|
|
289
|
+
fontStyle: style?.font_italic ? 'italic' : undefined,
|
|
290
|
+
textUnderline: toBooleanOrUndefined(style?.font_underline),
|
|
291
|
+
transparent: toBooleanOrUndefined(style?.transparent),
|
|
292
|
+
labelOnTop: toBooleanOrUndefined(style?.label_on_top),
|
|
288
293
|
},
|
|
289
294
|
});
|
|
290
295
|
// Find the page that references this resource
|
|
@@ -380,15 +385,24 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
380
385
|
Object.values(tree.pages).forEach((page) => {
|
|
381
386
|
// Translate page names
|
|
382
387
|
if (page.name && translations.has(page.name)) {
|
|
383
|
-
|
|
388
|
+
const translatedName = translations.get(page.name);
|
|
389
|
+
if (translatedName !== undefined) {
|
|
390
|
+
page.name = translatedName;
|
|
391
|
+
}
|
|
384
392
|
}
|
|
385
393
|
// Translate button labels and messages
|
|
386
394
|
page.buttons.forEach((button) => {
|
|
387
395
|
if (button.label && translations.has(button.label)) {
|
|
388
|
-
|
|
396
|
+
const translatedLabel = translations.get(button.label);
|
|
397
|
+
if (translatedLabel !== undefined) {
|
|
398
|
+
button.label = translatedLabel;
|
|
399
|
+
}
|
|
389
400
|
}
|
|
390
401
|
if (button.message && translations.has(button.message)) {
|
|
391
|
-
|
|
402
|
+
const translatedMessage = translations.get(button.message);
|
|
403
|
+
if (translatedMessage !== undefined) {
|
|
404
|
+
button.message = translatedMessage;
|
|
405
|
+
}
|
|
392
406
|
}
|
|
393
407
|
});
|
|
394
408
|
});
|
|
@@ -560,7 +574,10 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
560
574
|
insertPageStyle.run(pageStyleId, hexToInt(page.style.backgroundColor), page.style.backgroundColor ? 1 : 0);
|
|
561
575
|
}
|
|
562
576
|
else {
|
|
563
|
-
|
|
577
|
+
const existingPageStyleId = pageStyleMap.get(styleKey);
|
|
578
|
+
if (typeof existingPageStyleId === 'number') {
|
|
579
|
+
pageStyleId = existingPageStyleId;
|
|
580
|
+
}
|
|
564
581
|
}
|
|
565
582
|
}
|
|
566
583
|
// Insert resource for page name
|
|
@@ -577,6 +594,9 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
577
594
|
// Second pass: create buttons and their relationships
|
|
578
595
|
Object.values(tree.pages).forEach((page) => {
|
|
579
596
|
const numericPageId = pageIdMap.get(page.id);
|
|
597
|
+
if (numericPageId === undefined) {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
580
600
|
if (page.buttons.length > 0) {
|
|
581
601
|
// Calculate grid dimensions from page.grid or use fallback
|
|
582
602
|
let gridWidth = 4; // Default fallback
|
|
@@ -631,7 +651,10 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
631
651
|
insertButtonStyle.run(buttonStyleId, button.style.labelOnTop ? 1 : 0, button.style.transparent ? 1 : 0, hexToInt(button.style.fontColor), hexToInt(button.style.backgroundColor), hexToInt(button.style.borderColor), button.style.borderWidth, button.style.fontFamily, button.style.fontWeight === 'bold' ? 1 : 0, button.style.textUnderline ? 1 : 0, button.style.fontStyle === 'italic' ? 1 : 0, button.style.fontSize);
|
|
632
652
|
}
|
|
633
653
|
else {
|
|
634
|
-
|
|
654
|
+
const existingButtonStyleId = buttonStyleMap.get(styleKey);
|
|
655
|
+
if (typeof existingButtonStyleId === 'number') {
|
|
656
|
+
buttonStyleId = existingButtonStyleId;
|
|
657
|
+
}
|
|
635
658
|
}
|
|
636
659
|
}
|
|
637
660
|
if (!insertedButtonIds.has(numericButtonId)) {
|
package/dist/types/aac.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@willwade/aac-processors",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "A comprehensive TypeScript library for processing AAC (Augmentative and Alternative Communication) file formats with translation support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|