@willwade/aac-processors 0.2.3 → 0.2.5

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 +269 -239
  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 +269 -239
  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
@@ -462,7 +462,7 @@ class GridsetProcessor extends BaseProcessor {
462
462
  }
463
463
  }
464
464
  }
465
- catch (e) {
465
+ catch (_e) {
466
466
  /* ignore: optional FileMap.xml may be missing or malformed */
467
467
  }
468
468
  // First, load styles from Settings0/Styles/styles.xml (Grid3 format)
@@ -533,7 +533,7 @@ class GridsetProcessor extends BaseProcessor {
533
533
  const normalizedEntry = imageEntry.entryName.replace(/\\/g, '/');
534
534
  imageDataCache.set(normalizedEntry, data);
535
535
  }
536
- catch (err) {
536
+ catch (_err) {
537
537
  // Silently fail - individual image loading failures shouldn't break the entire load
538
538
  }
539
539
  }
@@ -566,7 +566,7 @@ class GridsetProcessor extends BaseProcessor {
566
566
  }
567
567
  }
568
568
  }
569
- catch (e) {
569
+ catch (_e) {
570
570
  // Skip errors in first pass
571
571
  }
572
572
  }
@@ -581,7 +581,7 @@ class GridsetProcessor extends BaseProcessor {
581
581
  xmlContent = decodeText(buffer);
582
582
  console.log(`[Gridset] Raw XML content (first 200 chars) for ${entry.entryName}:`, xmlContent.substring(0, 200));
583
583
  }
584
- catch (e) {
584
+ catch (_e) {
585
585
  // Skip unreadable files
586
586
  continue;
587
587
  }
@@ -936,10 +936,11 @@ class GridsetProcessor extends BaseProcessor {
936
936
  }
937
937
  // Parse all command types from Grid3 and create semantic actions
938
938
  let semanticAction;
939
- let legacyAction = null;
939
+ let _legacyAction = null;
940
940
  // infer action type implicitly from commands; no explicit enum needed
941
941
  let navigationTarget;
942
942
  let detectedCommands = []; // Store detected command metadata
943
+ let buttonPos; // Part-of-speech from Action.InsertText
943
944
  const commands = content.Commands?.Command || content.commands?.command;
944
945
  let predictionWords;
945
946
  // Resolve image for this cell using FileMap and coordinate heuristics
@@ -1061,114 +1062,125 @@ class GridsetProcessor extends BaseProcessor {
1061
1062
  if (gridTarget) {
1062
1063
  // Resolve grid name to grid ID for navigation
1063
1064
  const targetGridId = gridNameToIdMap.get(gridTarget) || gridTarget;
1065
+ // Always set navigationTarget even if another command already
1066
+ // set semanticAction (e.g. Jump.SetBookmark + Jump.To).
1064
1067
  navigationTarget = targetGridId;
1065
- // navigate action
1068
+ // Only set semanticAction if not already set by a prior command
1069
+ if (!semanticAction) {
1070
+ semanticAction = {
1071
+ category: AACSemanticCategory.NAVIGATION,
1072
+ intent: AACSemanticIntent.NAVIGATE_TO,
1073
+ targetId: targetGridId,
1074
+ platformData: {
1075
+ grid3: {
1076
+ commandId,
1077
+ parameters: { grid: gridTarget },
1078
+ },
1079
+ },
1080
+ fallback: {
1081
+ type: 'NAVIGATE',
1082
+ targetPageId: targetGridId,
1083
+ },
1084
+ };
1085
+ _legacyAction = {
1086
+ type: 'NAVIGATE',
1087
+ targetPageId: targetGridId,
1088
+ };
1089
+ }
1090
+ }
1091
+ break;
1092
+ }
1093
+ case 'Jump.Back':
1094
+ if (!semanticAction) {
1066
1095
  semanticAction = {
1067
1096
  category: AACSemanticCategory.NAVIGATION,
1068
- intent: AACSemanticIntent.NAVIGATE_TO,
1069
- targetId: targetGridId,
1097
+ intent: AACSemanticIntent.GO_BACK,
1070
1098
  platformData: {
1071
1099
  grid3: {
1072
1100
  commandId,
1073
- parameters: { grid: gridTarget },
1101
+ parameters: {},
1074
1102
  },
1075
1103
  },
1076
1104
  fallback: {
1077
- type: 'NAVIGATE',
1078
- targetPageId: targetGridId,
1105
+ type: 'ACTION',
1106
+ message: 'Go back',
1079
1107
  },
1080
1108
  };
1081
- legacyAction = {
1082
- type: 'NAVIGATE',
1083
- targetPageId: targetGridId,
1109
+ _legacyAction = {
1110
+ type: 'GO_BACK',
1084
1111
  };
1085
1112
  }
1086
1113
  break;
1087
- }
1088
- case 'Jump.Back':
1089
- // action
1090
- semanticAction = {
1091
- category: AACSemanticCategory.NAVIGATION,
1092
- intent: AACSemanticIntent.GO_BACK,
1093
- platformData: {
1094
- grid3: {
1095
- commandId,
1096
- parameters: {},
1097
- },
1098
- },
1099
- fallback: {
1100
- type: 'ACTION',
1101
- message: 'Go back',
1102
- },
1103
- };
1104
- legacyAction = {
1105
- type: 'GO_BACK',
1106
- };
1107
- break;
1108
1114
  case 'Jump.Home':
1109
1115
  case 'Jump.SetHome':
1110
- // action
1111
- navigationTarget = tree.rootId || undefined;
1112
- semanticAction = {
1113
- category: AACSemanticCategory.NAVIGATION,
1114
- intent: AACSemanticIntent.GO_HOME,
1115
- targetId: tree.rootId || undefined,
1116
- platformData: {
1117
- grid3: {
1118
- commandId,
1119
- parameters: {},
1116
+ if (!navigationTarget)
1117
+ navigationTarget = tree.rootId || undefined;
1118
+ if (!semanticAction) {
1119
+ semanticAction = {
1120
+ category: AACSemanticCategory.NAVIGATION,
1121
+ intent: AACSemanticIntent.GO_HOME,
1122
+ targetId: tree.rootId || undefined,
1123
+ platformData: {
1124
+ grid3: {
1125
+ commandId,
1126
+ parameters: {},
1127
+ },
1120
1128
  },
1121
- },
1122
- fallback: {
1123
- type: 'ACTION',
1124
- message: 'Go home',
1125
- },
1126
- };
1127
- legacyAction = {
1128
- type: 'GO_HOME',
1129
- };
1129
+ fallback: {
1130
+ type: 'ACTION',
1131
+ message: 'Go home',
1132
+ },
1133
+ };
1134
+ _legacyAction = {
1135
+ type: 'GO_HOME',
1136
+ };
1137
+ }
1130
1138
  break;
1131
1139
  case 'Jump.ToKeyboard': {
1132
1140
  // Navigate to the set keyboard if we found one in settings
1133
1141
  const keyboardGridName = tree.keyboardGridName;
1134
1142
  const keyboardPageId = gridNameToIdMap.get(keyboardGridName);
1135
- if (keyboardPageId) {
1143
+ if (keyboardPageId && !navigationTarget) {
1136
1144
  navigationTarget = keyboardPageId;
1137
1145
  }
1138
- semanticAction = {
1139
- category: AACSemanticCategory.NAVIGATION,
1140
- intent: AACSemanticIntent.GO_HOME, // Close enough to 'navigation to keyboard'
1141
- targetId: keyboardPageId,
1142
- platformData: {
1143
- grid3: {
1144
- commandId,
1145
- parameters: {},
1146
+ if (!semanticAction) {
1147
+ semanticAction = {
1148
+ category: AACSemanticCategory.NAVIGATION,
1149
+ intent: AACSemanticIntent.GO_HOME, // Close enough to 'navigation to keyboard'
1150
+ targetId: keyboardPageId,
1151
+ platformData: {
1152
+ grid3: {
1153
+ commandId,
1154
+ parameters: {},
1155
+ },
1146
1156
  },
1147
- },
1148
- fallback: {
1149
- type: 'NAVIGATE',
1150
- targetPageId: keyboardPageId,
1151
- },
1152
- };
1157
+ fallback: {
1158
+ type: 'NAVIGATE',
1159
+ targetPageId: keyboardPageId,
1160
+ },
1161
+ };
1162
+ }
1153
1163
  break;
1154
1164
  }
1155
1165
  case 'Action.InsertTextAndSpeak': {
1156
- const insertText = getParam('text');
1157
- semanticAction = {
1158
- category: AACSemanticCategory.COMMUNICATION,
1159
- intent: AACSemanticIntent.SPEAK_IMMEDIATE,
1160
- text: insertText,
1161
- platformData: {
1162
- grid3: {
1163
- commandId,
1164
- parameters: { text: insertText },
1166
+ if (!semanticAction) {
1167
+ const insertText = getParam('text');
1168
+ semanticAction = {
1169
+ category: AACSemanticCategory.COMMUNICATION,
1170
+ intent: AACSemanticIntent.SPEAK_IMMEDIATE,
1171
+ text: insertText,
1172
+ platformData: {
1173
+ grid3: {
1174
+ commandId,
1175
+ parameters: { text: insertText },
1176
+ },
1165
1177
  },
1166
- },
1167
- fallback: {
1168
- type: 'SPEAK',
1169
- message: insertText,
1170
- },
1171
- };
1178
+ fallback: {
1179
+ type: 'SPEAK',
1180
+ message: insertText,
1181
+ },
1182
+ };
1183
+ }
1172
1184
  break;
1173
1185
  }
1174
1186
  case 'Prediction.PredictThis': {
@@ -1201,190 +1213,207 @@ class GridsetProcessor extends BaseProcessor {
1201
1213
  // speak
1202
1214
  const speakUnit = getParam('unit');
1203
1215
  const moveCaret = getParam('movecaret');
1204
- semanticAction = {
1205
- category: AACSemanticCategory.COMMUNICATION,
1206
- intent: AACSemanticIntent.SPEAK_TEXT,
1207
- platformData: {
1208
- grid3: {
1209
- commandId,
1210
- parameters: {
1211
- unit: speakUnit,
1212
- movecaret: moveCaret,
1216
+ if (!semanticAction) {
1217
+ semanticAction = {
1218
+ category: AACSemanticCategory.COMMUNICATION,
1219
+ intent: AACSemanticIntent.SPEAK_TEXT,
1220
+ platformData: {
1221
+ grid3: {
1222
+ commandId,
1223
+ parameters: {
1224
+ unit: speakUnit,
1225
+ movecaret: moveCaret,
1226
+ },
1213
1227
  },
1214
1228
  },
1215
- },
1216
- fallback: {
1229
+ fallback: {
1230
+ type: 'SPEAK',
1231
+ message: 'Speak text',
1232
+ },
1233
+ };
1234
+ _legacyAction = {
1217
1235
  type: 'SPEAK',
1218
- message: 'Speak text',
1219
- },
1220
- };
1221
- legacyAction = {
1222
- type: 'SPEAK',
1223
- unit: speakUnit,
1224
- moveCaret: moveCaret ? parseInt(String(moveCaret)) : undefined,
1225
- };
1236
+ unit: speakUnit,
1237
+ moveCaret: moveCaret ? parseInt(String(moveCaret)) : undefined,
1238
+ };
1239
+ }
1226
1240
  break;
1227
1241
  }
1228
1242
  case 'Action.InsertText': {
1229
- // speak
1230
1243
  const insertText = getParam('text');
1231
- semanticAction = {
1232
- category: AACSemanticCategory.COMMUNICATION,
1233
- intent: AACSemanticIntent.INSERT_TEXT,
1234
- text: insertText,
1235
- platformData: {
1236
- grid3: {
1237
- commandId,
1238
- parameters: { text: insertText },
1244
+ const posParam = getParam('pos');
1245
+ // Always extract POS even if semanticAction is already set
1246
+ if (posParam) {
1247
+ buttonPos = posParam;
1248
+ }
1249
+ if (!semanticAction) {
1250
+ semanticAction = {
1251
+ category: AACSemanticCategory.COMMUNICATION,
1252
+ intent: AACSemanticIntent.INSERT_TEXT,
1253
+ text: insertText,
1254
+ platformData: {
1255
+ grid3: {
1256
+ commandId,
1257
+ parameters: { text: insertText, pos: posParam },
1258
+ },
1239
1259
  },
1240
- },
1241
- fallback: {
1242
- type: 'SPEAK',
1243
- message: insertText,
1244
- },
1245
- };
1246
- legacyAction = {
1247
- type: 'INSERT_TEXT',
1248
- text: insertText,
1249
- };
1260
+ fallback: {
1261
+ type: 'SPEAK',
1262
+ message: insertText,
1263
+ },
1264
+ };
1265
+ _legacyAction = {
1266
+ type: 'INSERT_TEXT',
1267
+ text: insertText,
1268
+ };
1269
+ }
1250
1270
  break;
1251
1271
  }
1252
1272
  case 'Action.DeleteWord':
1253
- // action
1254
- semanticAction = {
1255
- category: AACSemanticCategory.TEXT_EDITING,
1256
- intent: AACSemanticIntent.DELETE_WORD,
1257
- platformData: {
1258
- grid3: {
1259
- commandId,
1260
- parameters: {},
1273
+ if (!semanticAction) {
1274
+ semanticAction = {
1275
+ category: AACSemanticCategory.TEXT_EDITING,
1276
+ intent: AACSemanticIntent.DELETE_WORD,
1277
+ platformData: {
1278
+ grid3: {
1279
+ commandId,
1280
+ parameters: {},
1281
+ },
1261
1282
  },
1262
- },
1263
- fallback: {
1264
- type: 'ACTION',
1265
- message: 'Delete word',
1266
- },
1267
- };
1268
- legacyAction = {
1269
- type: 'DELETE_WORD',
1270
- };
1283
+ fallback: {
1284
+ type: 'ACTION',
1285
+ message: 'Delete word',
1286
+ },
1287
+ };
1288
+ _legacyAction = {
1289
+ type: 'DELETE_WORD',
1290
+ };
1291
+ }
1271
1292
  break;
1272
1293
  case 'Action.DeleteLetter':
1273
- // action
1274
- semanticAction = {
1275
- category: AACSemanticCategory.TEXT_EDITING,
1276
- intent: AACSemanticIntent.DELETE_CHARACTER,
1277
- platformData: {
1278
- grid3: {
1279
- commandId,
1280
- parameters: {},
1294
+ if (!semanticAction) {
1295
+ semanticAction = {
1296
+ category: AACSemanticCategory.TEXT_EDITING,
1297
+ intent: AACSemanticIntent.DELETE_CHARACTER,
1298
+ platformData: {
1299
+ grid3: {
1300
+ commandId,
1301
+ parameters: {},
1302
+ },
1281
1303
  },
1282
- },
1283
- fallback: {
1284
- type: 'ACTION',
1285
- message: 'Delete character',
1286
- },
1287
- };
1288
- legacyAction = {
1289
- type: 'DELETE_CHARACTER',
1290
- };
1304
+ fallback: {
1305
+ type: 'ACTION',
1306
+ message: 'Delete character',
1307
+ },
1308
+ };
1309
+ _legacyAction = {
1310
+ type: 'DELETE_CHARACTER',
1311
+ };
1312
+ }
1291
1313
  break;
1292
1314
  case 'Action.Clear':
1293
1315
  // action
1294
- semanticAction = {
1295
- category: AACSemanticCategory.TEXT_EDITING,
1296
- intent: AACSemanticIntent.CLEAR_TEXT,
1297
- platformData: {
1298
- grid3: {
1299
- commandId,
1300
- parameters: {},
1316
+ if (!semanticAction) {
1317
+ semanticAction = {
1318
+ category: AACSemanticCategory.TEXT_EDITING,
1319
+ intent: AACSemanticIntent.CLEAR_TEXT,
1320
+ platformData: {
1321
+ grid3: {
1322
+ commandId,
1323
+ parameters: {},
1324
+ },
1301
1325
  },
1302
- },
1303
- fallback: {
1304
- type: 'ACTION',
1305
- message: 'Clear text',
1306
- },
1307
- };
1308
- legacyAction = {
1309
- type: 'CLEAR_TEXT',
1310
- };
1326
+ fallback: {
1327
+ type: 'ACTION',
1328
+ message: 'Clear text',
1329
+ },
1330
+ };
1331
+ _legacyAction = {
1332
+ type: 'CLEAR_TEXT',
1333
+ };
1334
+ }
1311
1335
  break;
1312
1336
  case 'Action.Letter': {
1313
1337
  // action
1314
1338
  const letter = getParam('letter');
1315
- semanticAction = {
1316
- category: AACSemanticCategory.TEXT_EDITING,
1317
- intent: AACSemanticIntent.INSERT_TEXT,
1318
- text: letter,
1319
- platformData: {
1320
- grid3: {
1321
- commandId,
1322
- parameters: { letter },
1339
+ if (!semanticAction) {
1340
+ semanticAction = {
1341
+ category: AACSemanticCategory.TEXT_EDITING,
1342
+ intent: AACSemanticIntent.INSERT_TEXT,
1343
+ text: letter,
1344
+ platformData: {
1345
+ grid3: {
1346
+ commandId,
1347
+ parameters: { letter },
1348
+ },
1323
1349
  },
1324
- },
1325
- fallback: {
1326
- type: 'ACTION',
1327
- message: letter,
1328
- },
1329
- };
1330
- legacyAction = {
1331
- type: 'INSERT_LETTER',
1332
- letter,
1333
- };
1350
+ fallback: {
1351
+ type: 'ACTION',
1352
+ message: letter,
1353
+ },
1354
+ };
1355
+ _legacyAction = {
1356
+ type: 'INSERT_LETTER',
1357
+ letter,
1358
+ };
1359
+ }
1334
1360
  break;
1335
1361
  }
1336
1362
  case 'Settings.RestAll':
1337
1363
  // action
1338
- semanticAction = {
1339
- category: AACSemanticCategory.CUSTOM,
1340
- intent: AACSemanticIntent.PLATFORM_SPECIFIC,
1341
- platformData: {
1342
- grid3: {
1343
- commandId,
1344
- parameters: {
1345
- indicatorenabled: getParam('indicatorenabled'),
1346
- action: getParam('action'),
1364
+ if (!semanticAction) {
1365
+ semanticAction = {
1366
+ category: AACSemanticCategory.CUSTOM,
1367
+ intent: AACSemanticIntent.PLATFORM_SPECIFIC,
1368
+ platformData: {
1369
+ grid3: {
1370
+ commandId,
1371
+ parameters: {
1372
+ indicatorenabled: getParam('indicatorenabled'),
1373
+ action: getParam('action'),
1374
+ },
1347
1375
  },
1348
1376
  },
1349
- },
1350
- fallback: {
1351
- type: 'ACTION',
1352
- message: 'Settings action',
1353
- },
1354
- };
1355
- legacyAction = {
1356
- type: 'SETTINGS',
1357
- indicatorEnabled: getParam('indicatorenabled') === '1',
1358
- settingsAction: getParam('action'),
1359
- };
1377
+ fallback: {
1378
+ type: 'ACTION',
1379
+ message: 'Settings action',
1380
+ },
1381
+ };
1382
+ _legacyAction = {
1383
+ type: 'SETTINGS',
1384
+ indicatorEnabled: getParam('indicatorenabled') === '1',
1385
+ settingsAction: getParam('action'),
1386
+ };
1387
+ }
1360
1388
  break;
1361
1389
  case 'AutoContent.Activate':
1362
1390
  // action
1363
- semanticAction = {
1364
- category: AACSemanticCategory.CUSTOM,
1365
- intent: AACSemanticIntent.PLATFORM_SPECIFIC,
1366
- platformData: {
1367
- grid3: {
1368
- commandId,
1369
- parameters: {
1370
- autocontenttype: getParam('autocontenttype'),
1391
+ if (!semanticAction) {
1392
+ semanticAction = {
1393
+ category: AACSemanticCategory.CUSTOM,
1394
+ intent: AACSemanticIntent.PLATFORM_SPECIFIC,
1395
+ platformData: {
1396
+ grid3: {
1397
+ commandId,
1398
+ parameters: {
1399
+ autocontenttype: getParam('autocontenttype'),
1400
+ },
1371
1401
  },
1372
1402
  },
1373
- },
1374
- fallback: {
1375
- type: 'ACTION',
1376
- message: 'Auto content',
1377
- },
1378
- };
1379
- legacyAction = {
1380
- type: 'AUTO_CONTENT',
1381
- autoContentType: getParam('autocontenttype'),
1382
- };
1403
+ fallback: {
1404
+ type: 'ACTION',
1405
+ message: 'Auto content',
1406
+ },
1407
+ };
1408
+ _legacyAction = {
1409
+ type: 'AUTO_CONTENT',
1410
+ autoContentType: getParam('autocontenttype'),
1411
+ };
1412
+ }
1383
1413
  break;
1384
1414
  default:
1385
1415
  // Unknown command - preserve as generic action
1386
- if (commandId) {
1387
- // action
1416
+ if (commandId && !semanticAction) {
1388
1417
  const allParams = Object.fromEntries(paramArr.map((p) => [p.Key || p.key, p['#text']]));
1389
1418
  semanticAction = {
1390
1419
  category: AACSemanticCategory.CUSTOM,
@@ -1400,16 +1429,13 @@ class GridsetProcessor extends BaseProcessor {
1400
1429
  message: 'Unknown command',
1401
1430
  },
1402
1431
  };
1403
- legacyAction = {
1404
- type: 'SPEAK',
1405
- parameters: { commandId, ...allParams },
1406
- };
1432
+ // legacy action not needed for unknown commands
1407
1433
  }
1408
1434
  break;
1409
1435
  }
1410
- // Use first recognized command
1411
- if (semanticAction || legacyAction)
1412
- break;
1436
+ // Continue processing remaining commands so that navigation
1437
+ // targets (Jump.To) are discovered even when a non-navigation
1438
+ // command (e.g. Jump.SetBookmark, Action.InsertText) appears first.
1413
1439
  }
1414
1440
  }
1415
1441
  // Create default semantic action if none was created from commands
@@ -1454,8 +1480,10 @@ class GridsetProcessor extends BaseProcessor {
1454
1480
  }
1455
1481
  // Extract grammar tags from commands (Smart Grammar)
1456
1482
  const grammar = {};
1483
+ if (buttonPos)
1484
+ grammar.pos = buttonPos;
1457
1485
  detectedCommands.forEach((cmd) => {
1458
- if (cmd.parameters.pos)
1486
+ if (!grammar.pos && cmd.parameters.pos)
1459
1487
  grammar.pos = cmd.parameters.pos;
1460
1488
  if (cmd.parameters.person)
1461
1489
  grammar.person = cmd.parameters.person;
@@ -1465,6 +1493,7 @@ class GridsetProcessor extends BaseProcessor {
1465
1493
  grammar.feature = cmd.parameters.feature;
1466
1494
  });
1467
1495
  const isSmartGrammarCell = Object.keys(grammar).length > 0;
1496
+ const effectivePos = buttonPos || grammar.pos || undefined;
1468
1497
  const button = new AACButton({
1469
1498
  id: `${gridId}_btn_${idx}`,
1470
1499
  label: String(label),
@@ -1502,6 +1531,7 @@ class GridsetProcessor extends BaseProcessor {
1502
1531
  : gridPredictionWords.length > 0
1503
1532
  ? [...gridPredictionWords]
1504
1533
  : undefined,
1534
+ pos: effectivePos,
1505
1535
  parameters: {
1506
1536
  pluginMetadata: pluginMetadata, // Store full plugin metadata for future use
1507
1537
  grid3Commands: detectedCommands, // Store detected command metadata
@@ -1673,7 +1703,7 @@ class GridsetProcessor extends BaseProcessor {
1673
1703
  }
1674
1704
  }
1675
1705
  }
1676
- catch (e) {
1706
+ catch (_e) {
1677
1707
  // If settings.xml parsing fails, tree.rootId will default to first page
1678
1708
  }
1679
1709
  // Set metadata on tree