@willwade/aac-processors 0.2.3 → 0.2.4

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.
Files changed (47) hide show
  1. package/dist/browser/core/treeStructure.js +3 -1
  2. package/dist/browser/metrics.js +2 -0
  3. package/dist/browser/processors/astericsGridProcessor.js +21 -12
  4. package/dist/browser/processors/gridset/helpers.js +3 -3
  5. package/dist/browser/processors/gridsetProcessor.js +16 -8
  6. package/dist/browser/processors/obfProcessor.js +4 -4
  7. package/dist/browser/processors/snap/helpers.js +3 -3
  8. package/dist/browser/processors/snapProcessor.js +2 -2
  9. package/dist/browser/processors/touchchatProcessor.js +6 -6
  10. package/dist/browser/utilities/analytics/metrics/core.js +213 -8
  11. package/dist/browser/utilities/analytics/metrics/vocabulary.js +13 -1
  12. package/dist/browser/utilities/analytics/morphology/engine.js +910 -0
  13. package/dist/browser/utilities/analytics/morphology/grid3VerbsParser.js +455 -0
  14. package/dist/browser/utilities/analytics/morphology/index.js +3 -0
  15. package/dist/browser/utilities/analytics/morphology/types.js +1 -0
  16. package/dist/browser/utilities/analytics/morphology/wordFormGenerator.js +74 -0
  17. package/dist/core/treeStructure.d.ts +17 -1
  18. package/dist/core/treeStructure.js +3 -1
  19. package/dist/index.node.d.ts +2 -0
  20. package/dist/index.node.js +6 -1
  21. package/dist/metrics.d.ts +3 -0
  22. package/dist/metrics.js +5 -1
  23. package/dist/processors/astericsGridProcessor.js +21 -12
  24. package/dist/processors/excelProcessor.js +5 -1
  25. package/dist/processors/gridset/helpers.js +3 -3
  26. package/dist/processors/gridset/imageDebug.js +2 -2
  27. package/dist/processors/gridsetProcessor.js +16 -8
  28. package/dist/processors/obfProcessor.js +4 -4
  29. package/dist/processors/snap/helpers.js +3 -3
  30. package/dist/processors/snapProcessor.js +2 -2
  31. package/dist/processors/touchchatProcessor.js +6 -6
  32. package/dist/utilities/analytics/metrics/core.d.ts +14 -0
  33. package/dist/utilities/analytics/metrics/core.js +213 -8
  34. package/dist/utilities/analytics/metrics/types.d.ts +18 -3
  35. package/dist/utilities/analytics/metrics/vocabulary.d.ts +3 -0
  36. package/dist/utilities/analytics/metrics/vocabulary.js +13 -1
  37. package/dist/utilities/analytics/morphology/engine.d.ts +30 -0
  38. package/dist/utilities/analytics/morphology/engine.js +914 -0
  39. package/dist/utilities/analytics/morphology/grid3VerbsParser.d.ts +36 -0
  40. package/dist/utilities/analytics/morphology/grid3VerbsParser.js +485 -0
  41. package/dist/utilities/analytics/morphology/index.d.ts +5 -0
  42. package/dist/utilities/analytics/morphology/index.js +9 -0
  43. package/dist/utilities/analytics/morphology/types.d.ts +40 -0
  44. package/dist/utilities/analytics/morphology/types.js +2 -0
  45. package/dist/utilities/analytics/morphology/wordFormGenerator.d.ts +10 -0
  46. package/dist/utilities/analytics/morphology/wordFormGenerator.js +78 -0
  47. package/package.json +12 -11
@@ -613,7 +613,7 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
613
613
  });
614
614
  });
615
615
  }
616
- catch (error) {
616
+ catch (_error) {
617
617
  // If JSON parsing fails, return empty array
618
618
  }
619
619
  return texts;
@@ -1029,7 +1029,7 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
1029
1029
  // Use detected format for filename
1030
1030
  imageName = element.image.id || `image.${imageFormat}`;
1031
1031
  }
1032
- catch (e) {
1032
+ catch (_e) {
1033
1033
  // Invalid base64 data, skip image
1034
1034
  }
1035
1035
  }
@@ -1042,19 +1042,23 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
1042
1042
  audioRecording: audioRecording,
1043
1043
  visibility: mapAstericsVisibility(element.hidden),
1044
1044
  image: imageName, // Store image filename/reference
1045
- parameters: imageData
1046
- ? {
1047
- ...{ imageData: imageData }, // Store actual image data in parameters for conversion
1048
- }
1049
- : undefined,
1050
1045
  style: {
1051
1046
  backgroundColor: finalBackgroundColor,
1052
1047
  borderColor: colorStyles.borderColor || colorConfig?.elementBorderColor || '#CCCCCC',
1053
1048
  borderWidth: colorConfig?.borderWidth || 1,
1054
1049
  fontFamily: colorConfig?.fontFamily || 'Arial',
1055
- fontSize: colorConfig?.fontSizePct ? colorConfig.fontSizePct * 16 : 16, // Default to 16px
1050
+ fontSize: colorConfig?.fontSizePct ? colorConfig.fontSizePct * 16 : 16,
1056
1051
  fontColor: fontColor,
1057
1052
  },
1053
+ wordForms: element.wordForms && element.wordForms.length > 0 ? element.wordForms : undefined,
1054
+ parameters: {
1055
+ ...(imageData ? { imageData: imageData } : {}),
1056
+ ...(element.actions?.some((a) => a.modelName === 'GridActionWordForm')
1057
+ ? {
1058
+ wordFormActions: element.actions.filter((a) => a.modelName === 'GridActionWordForm'),
1059
+ }
1060
+ : {}),
1061
+ },
1058
1062
  });
1059
1063
  }
1060
1064
  async processTexts(filePathOrBuffer, translations, outputPath) {
@@ -1321,6 +1325,11 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
1321
1325
  });
1322
1326
  }
1323
1327
  const locale = tree.metadata?.locale || 'en';
1328
+ if (button.parameters?.wordFormActions &&
1329
+ Array.isArray(button.parameters.wordFormActions)) {
1330
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
1331
+ actions.push(...button.parameters.wordFormActions);
1332
+ }
1324
1333
  return {
1325
1334
  id: button.id,
1326
1335
  modelName: 'GridElement',
@@ -1330,7 +1339,7 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
1330
1339
  x: calculatedX,
1331
1340
  y: calculatedY,
1332
1341
  label: { [locale]: button.label },
1333
- wordForms: [],
1342
+ wordForms: button.wordForms || [],
1334
1343
  image: {
1335
1344
  data: null,
1336
1345
  author: undefined,
@@ -1422,7 +1431,7 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
1422
1431
  audioAction.durationMs = parsedMetadata.durationMs || audioAction.durationMs;
1423
1432
  audioAction.filename = parsedMetadata.filename || audioAction.filename;
1424
1433
  }
1425
- catch (e) {
1434
+ catch (_e) {
1426
1435
  // Use defaults if metadata parsing fails
1427
1436
  }
1428
1437
  }
@@ -1474,7 +1483,7 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
1474
1483
  });
1475
1484
  });
1476
1485
  }
1477
- catch (error) {
1486
+ catch (_error) {
1478
1487
  // If JSON parsing fails, return empty array
1479
1488
  }
1480
1489
  return elementIds;
@@ -1499,7 +1508,7 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
1499
1508
  }
1500
1509
  }
1501
1510
  }
1502
- catch (error) {
1511
+ catch (_error) {
1503
1512
  // If JSON parsing fails, return false
1504
1513
  }
1505
1514
  return false;
@@ -309,7 +309,11 @@ class ExcelProcessor extends baseProcessor_1.BaseProcessor {
309
309
  // Create internal link to another worksheet
310
310
  const sanitizedTargetName = this.sanitizeWorksheetName(targetPageId);
311
311
  cell.value = {
312
- text: cell.value?.toString() || '',
312
+ text: typeof cell.value === 'string'
313
+ ? cell.value
314
+ : typeof cell.value === 'number' || typeof cell.value === 'boolean'
315
+ ? String(cell.value)
316
+ : '',
313
317
  hyperlink: `#'${sanitizedTargetName}'!A1`,
314
318
  };
315
319
  }
@@ -84,7 +84,7 @@ async function openImage(gridsetBuffer, entryPath, password = (0, password_1.res
84
84
  }
85
85
  return data;
86
86
  }
87
- catch (error) {
87
+ catch (_error) {
88
88
  return null;
89
89
  }
90
90
  }
@@ -174,7 +174,7 @@ function getCommonDocumentsPath() {
174
174
  return match[1].trim();
175
175
  }
176
176
  }
177
- catch (error) {
177
+ catch (_error) {
178
178
  // Registry access failed, fall back to default
179
179
  }
180
180
  // Default fallback path
@@ -230,7 +230,7 @@ async function findGrid3UserPaths(fileAdapter = io_1.defaultFileAdapter) {
230
230
  }
231
231
  }
232
232
  }
233
- catch (error) {
233
+ catch (_error) {
234
234
  // Silently fail if directory access fails
235
235
  }
236
236
  return results;
@@ -88,7 +88,7 @@ async function auditGridsetImages(gridsetBuffer, password = (0, password_2.resol
88
88
  }
89
89
  }
90
90
  }
91
- catch (e) {
91
+ catch (_e) {
92
92
  // FileMap parsing failed, continue without it
93
93
  }
94
94
  }
@@ -169,7 +169,7 @@ async function auditGridsetImages(gridsetBuffer, password = (0, password_2.resol
169
169
  }
170
170
  }
171
171
  }
172
- catch (e) {
172
+ catch (_e) {
173
173
  // Skip grids that can't be processed
174
174
  continue;
175
175
  }
@@ -465,7 +465,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
465
465
  }
466
466
  }
467
467
  }
468
- catch (e) {
468
+ catch (_e) {
469
469
  /* ignore: optional FileMap.xml may be missing or malformed */
470
470
  }
471
471
  // First, load styles from Settings0/Styles/styles.xml (Grid3 format)
@@ -536,7 +536,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
536
536
  const normalizedEntry = imageEntry.entryName.replace(/\\/g, '/');
537
537
  imageDataCache.set(normalizedEntry, data);
538
538
  }
539
- catch (err) {
539
+ catch (_err) {
540
540
  // Silently fail - individual image loading failures shouldn't break the entire load
541
541
  }
542
542
  }
@@ -569,7 +569,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
569
569
  }
570
570
  }
571
571
  }
572
- catch (e) {
572
+ catch (_e) {
573
573
  // Skip errors in first pass
574
574
  }
575
575
  }
@@ -584,7 +584,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
584
584
  xmlContent = (0, io_1.decodeText)(buffer);
585
585
  console.log(`[Gridset] Raw XML content (first 200 chars) for ${entry.entryName}:`, xmlContent.substring(0, 200));
586
586
  }
587
- catch (e) {
587
+ catch (_e) {
588
588
  // Skip unreadable files
589
589
  continue;
590
590
  }
@@ -943,6 +943,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
943
943
  // infer action type implicitly from commands; no explicit enum needed
944
944
  let navigationTarget;
945
945
  let detectedCommands = []; // Store detected command metadata
946
+ let buttonPos; // Part-of-speech from Action.InsertText
946
947
  const commands = content.Commands?.Command || content.commands?.command;
947
948
  let predictionWords;
948
949
  // Resolve image for this cell using FileMap and coordinate heuristics
@@ -1229,8 +1230,11 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1229
1230
  break;
1230
1231
  }
1231
1232
  case 'Action.InsertText': {
1232
- // speak
1233
1233
  const insertText = getParam('text');
1234
+ const posParam = getParam('pos');
1235
+ if (posParam) {
1236
+ buttonPos = posParam;
1237
+ }
1234
1238
  semanticAction = {
1235
1239
  category: treeStructure_1.AACSemanticCategory.COMMUNICATION,
1236
1240
  intent: treeStructure_1.AACSemanticIntent.INSERT_TEXT,
@@ -1238,7 +1242,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1238
1242
  platformData: {
1239
1243
  grid3: {
1240
1244
  commandId,
1241
- parameters: { text: insertText },
1245
+ parameters: { text: insertText, pos: posParam },
1242
1246
  },
1243
1247
  },
1244
1248
  fallback: {
@@ -1457,8 +1461,10 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1457
1461
  }
1458
1462
  // Extract grammar tags from commands (Smart Grammar)
1459
1463
  const grammar = {};
1464
+ if (buttonPos)
1465
+ grammar.pos = buttonPos;
1460
1466
  detectedCommands.forEach((cmd) => {
1461
- if (cmd.parameters.pos)
1467
+ if (!grammar.pos && cmd.parameters.pos)
1462
1468
  grammar.pos = cmd.parameters.pos;
1463
1469
  if (cmd.parameters.person)
1464
1470
  grammar.person = cmd.parameters.person;
@@ -1468,6 +1474,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1468
1474
  grammar.feature = cmd.parameters.feature;
1469
1475
  });
1470
1476
  const isSmartGrammarCell = Object.keys(grammar).length > 0;
1477
+ const effectivePos = buttonPos || grammar.pos || undefined;
1471
1478
  const button = new treeStructure_1.AACButton({
1472
1479
  id: `${gridId}_btn_${idx}`,
1473
1480
  label: String(label),
@@ -1505,6 +1512,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1505
1512
  : gridPredictionWords.length > 0
1506
1513
  ? [...gridPredictionWords]
1507
1514
  : undefined,
1515
+ pos: effectivePos,
1508
1516
  parameters: {
1509
1517
  pluginMetadata: pluginMetadata, // Store full plugin metadata for future use
1510
1518
  grid3Commands: detectedCommands, // Store detected command metadata
@@ -1676,7 +1684,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1676
1684
  }
1677
1685
  }
1678
1686
  }
1679
- catch (e) {
1687
+ catch (_e) {
1680
1688
  // If settings.xml parsing fails, tree.rootId will default to first page
1681
1689
  }
1682
1690
  // Set metadata on tree
@@ -51,7 +51,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
51
51
  return null;
52
52
  }
53
53
  }
54
- catch (err) {
54
+ catch (_err) {
55
55
  continue;
56
56
  }
57
57
  }
@@ -97,7 +97,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
97
97
  return dataUrl;
98
98
  }
99
99
  }
100
- catch (err) {
100
+ catch (_err) {
101
101
  // Continue to next path
102
102
  continue;
103
103
  }
@@ -321,7 +321,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
321
321
  return obj;
322
322
  }
323
323
  }
324
- catch (error) {
324
+ catch (_error) {
325
325
  // Log parsing errors for debugging but don't throw
326
326
  }
327
327
  return null;
@@ -728,7 +728,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
728
728
  // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-return
729
729
  return require('../validation/obfValidator').ObfValidator;
730
730
  }
731
- catch (error) {
731
+ catch (_error) {
732
732
  throw new Error('Validation utilities are not available in this environment.');
733
733
  }
734
734
  }
@@ -37,7 +37,7 @@ async function collectFiles(root, matcher, maxDepth = 3, fileAdapter = io_1.defa
37
37
  try {
38
38
  entries = await listDir(current.dir);
39
39
  }
40
- catch (error) {
40
+ catch (_error) {
41
41
  continue;
42
42
  }
43
43
  for (const entry of entries) {
@@ -143,7 +143,7 @@ async function openImage(dbOrFile, entryPath, fileAdapter = io_1.defaultFileAdap
143
143
  const dir = dirname(dbPath);
144
144
  await removePath(dir);
145
145
  }
146
- catch (e) {
146
+ catch (_e) {
147
147
  // Ignore cleanup errors
148
148
  }
149
149
  }
@@ -187,7 +187,7 @@ async function findSnapPackages(packageNamePattern = 'TobiiDynavox', fileAdapter
187
187
  }
188
188
  }
189
189
  }
190
- catch (error) {
190
+ catch (_error) {
191
191
  // Silently fail if directory access fails
192
192
  }
193
193
  return results;
@@ -218,7 +218,7 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
218
218
  try {
219
219
  positions = JSON.parse(sg.SerializedGridPositions);
220
220
  }
221
- catch (e) {
221
+ catch (_e) {
222
222
  // Invalid JSON, skip this group
223
223
  return;
224
224
  }
@@ -439,7 +439,7 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
439
439
  }
440
440
  }
441
441
  }
442
- catch (e) {
442
+ catch (_e) {
443
443
  // Ignore JSON parse errors in commands
444
444
  }
445
445
  }
@@ -104,7 +104,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
104
104
  idMappings.set(mapping.numeric_id, mapping.string_id);
105
105
  });
106
106
  }
107
- catch (e) {
107
+ catch (_e) {
108
108
  // No mapping table, use numeric IDs as strings
109
109
  }
110
110
  // Load styles
@@ -122,7 +122,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
122
122
  pageStyles.set(style.id, style);
123
123
  });
124
124
  }
125
- catch (e) {
125
+ catch (_e) {
126
126
  // console.log('No styles found:', e);
127
127
  }
128
128
  // First, load all pages and get their names from resources
@@ -310,7 +310,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
310
310
  }
311
311
  });
312
312
  }
313
- catch (e) {
313
+ catch (_e) {
314
314
  // console.log('No button box cells found:', e);
315
315
  }
316
316
  // Load buttons directly linked to pages via resources
@@ -369,7 +369,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
369
369
  page.addButton(button);
370
370
  });
371
371
  }
372
- catch (e) {
372
+ catch (_e) {
373
373
  // console.log('No direct page buttons found:', e);
374
374
  }
375
375
  // Load navigation actions
@@ -416,7 +416,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
416
416
  }
417
417
  });
418
418
  }
419
- catch (e) {
419
+ catch (_e) {
420
420
  // console.log('No navigation actions found:', e);
421
421
  }
422
422
  // Try to load root ID from multiple sources in order of priority
@@ -457,7 +457,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
457
457
  tree.metadata.defaultHomePageId = rootPageId;
458
458
  }
459
459
  }
460
- catch (e) {
460
+ catch (_e) {
461
461
  // No metadata table or other error, use first page as root
462
462
  if (rootPageId) {
463
463
  tree.rootId = rootPageId;
@@ -39,6 +39,20 @@ export declare class MetricsCalculator {
39
39
  * Calculate what percentage of links to this board match semantic_id/clone_id
40
40
  */
41
41
  private calculateBoardLinkPercentages;
42
+ /**
43
+ * Quick check whether any button in the tree has a POS tag.
44
+ * Used to auto-enable smart grammar without requiring explicit opt-in.
45
+ */
46
+ private treeHasPosTags;
47
+ /**
48
+ * Expand morphological predictions from POS tags on buttons
49
+ *
50
+ * For each button that has a POS tag (e.g., 'Verb', 'Noun'), use the
51
+ * MorphologyEngine to generate inflected word forms and populate the
52
+ * button's predictions array. This is done as a pre-processing step
53
+ * before calculateWordFormMetrics assigns effort to each form.
54
+ */
55
+ private expandMorphologicalPredictions;
42
56
  /**
43
57
  * Calculate metrics for word forms (smart grammar predictions)
44
58
  *