@willwade/aac-processors 0.0.11 → 0.0.12

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 (38) hide show
  1. package/dist/cli/index.js +7 -0
  2. package/dist/core/analyze.js +1 -0
  3. package/dist/core/treeStructure.d.ts +12 -2
  4. package/dist/core/treeStructure.js +6 -2
  5. package/dist/index.d.ts +2 -1
  6. package/dist/index.js +20 -3
  7. package/dist/{analytics → optional/analytics}/history.d.ts +3 -3
  8. package/dist/{analytics → optional/analytics}/history.js +3 -3
  9. package/dist/optional/analytics/index.d.ts +28 -0
  10. package/dist/optional/analytics/index.js +73 -0
  11. package/dist/optional/analytics/metrics/comparison.d.ts +36 -0
  12. package/dist/optional/analytics/metrics/comparison.js +330 -0
  13. package/dist/optional/analytics/metrics/core.d.ts +36 -0
  14. package/dist/optional/analytics/metrics/core.js +422 -0
  15. package/dist/optional/analytics/metrics/effort.d.ts +137 -0
  16. package/dist/optional/analytics/metrics/effort.js +198 -0
  17. package/dist/optional/analytics/metrics/index.d.ts +15 -0
  18. package/dist/optional/analytics/metrics/index.js +36 -0
  19. package/dist/optional/analytics/metrics/sentence.d.ts +49 -0
  20. package/dist/optional/analytics/metrics/sentence.js +112 -0
  21. package/dist/optional/analytics/metrics/types.d.ts +157 -0
  22. package/dist/optional/analytics/metrics/types.js +7 -0
  23. package/dist/optional/analytics/metrics/vocabulary.d.ts +65 -0
  24. package/dist/optional/analytics/metrics/vocabulary.js +140 -0
  25. package/dist/optional/analytics/reference/index.d.ts +51 -0
  26. package/dist/optional/analytics/reference/index.js +102 -0
  27. package/dist/optional/analytics/utils/idGenerator.d.ts +59 -0
  28. package/dist/optional/analytics/utils/idGenerator.js +96 -0
  29. package/dist/processors/gridsetProcessor.js +25 -0
  30. package/dist/processors/index.d.ts +1 -0
  31. package/dist/processors/index.js +5 -3
  32. package/dist/processors/obfProcessor.js +25 -2
  33. package/dist/processors/obfsetProcessor.d.ts +26 -0
  34. package/dist/processors/obfsetProcessor.js +179 -0
  35. package/dist/processors/snapProcessor.js +29 -1
  36. package/dist/processors/touchchatProcessor.js +27 -0
  37. package/dist/types/aac.d.ts +4 -0
  38. package/package.json +1 -1
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ /**
3
+ * OBF Set Processor - Handles JSON-formatted .obfset files
4
+ * These are pre-extracted board sets in JSON array format
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.ObfsetProcessor = void 0;
11
+ const treeStructure_1 = require("../core/treeStructure");
12
+ const treeStructure_2 = require("../core/treeStructure");
13
+ const fs_1 = __importDefault(require("fs"));
14
+ const baseProcessor_1 = require("../core/baseProcessor");
15
+ class ObfsetProcessor extends baseProcessor_1.BaseProcessor {
16
+ constructor(options = {}) {
17
+ super(options);
18
+ }
19
+ /**
20
+ * Extract all text content
21
+ */
22
+ extractTexts(filePathOrBuffer) {
23
+ const tree = this.loadIntoTree(filePathOrBuffer);
24
+ const texts = new Set();
25
+ Object.values(tree.pages).forEach((page) => {
26
+ if (page.name)
27
+ texts.add(page.name);
28
+ page.buttons.forEach((button) => {
29
+ if (button.label)
30
+ texts.add(button.label);
31
+ });
32
+ });
33
+ return Array.from(texts);
34
+ }
35
+ /**
36
+ * Load an .obfset file (JSON array of boards)
37
+ */
38
+ loadIntoTree(filePathOrBuffer) {
39
+ const tree = new treeStructure_1.AACTree();
40
+ let content;
41
+ if (Buffer.isBuffer(filePathOrBuffer)) {
42
+ content = filePathOrBuffer.toString('utf-8');
43
+ }
44
+ else {
45
+ content = fs_1.default.readFileSync(filePathOrBuffer, 'utf-8');
46
+ }
47
+ const boards = JSON.parse(content);
48
+ // Track board ID mappings
49
+ const boardMap = new Map();
50
+ // First pass: create all boards
51
+ boards.forEach((boardData) => {
52
+ const rows = boardData.grid?.rows || 4;
53
+ const cols = boardData.grid?.columns || 6;
54
+ const name = boardData.name || boardData.id || `Board ${boardData.id}`;
55
+ const page = new treeStructure_2.AACPage({
56
+ id: boardData.id,
57
+ name,
58
+ grid: { columns: cols, rows: rows },
59
+ buttons: [],
60
+ });
61
+ tree.addPage(page);
62
+ boardMap.set(boardData.id, page);
63
+ });
64
+ // Second pass: process buttons and establish parent relationships
65
+ boards.forEach((boardData) => {
66
+ const page = boardMap.get(boardData.id);
67
+ if (!page)
68
+ return;
69
+ const rows = boardData.grid?.rows || 4;
70
+ const cols = boardData.grid?.columns || 6;
71
+ // Initialize grid with nulls
72
+ page.grid = Array.from({ length: rows }, () => Array.from({ length: cols }, () => null));
73
+ // Create button map by ID
74
+ const buttonMap = new Map();
75
+ const buttons = boardData.buttons || [];
76
+ buttons.forEach((btnData) => {
77
+ buttonMap.set(btnData.id, btnData);
78
+ });
79
+ // Process grid order to place buttons in correct positions
80
+ const gridOrder = boardData.grid?.order || [];
81
+ const semanticIds = [];
82
+ const cloneIds = [];
83
+ gridOrder.forEach((row, rowIndex) => {
84
+ row.forEach((buttonId, colIndex) => {
85
+ const btnData = buttonMap.get(buttonId);
86
+ if (btnData) {
87
+ // Create semantic action
88
+ let semanticAction;
89
+ if (btnData.load_board?.id) {
90
+ // Navigation button
91
+ semanticAction = {
92
+ category: treeStructure_2.AACSemanticCategory.NAVIGATION,
93
+ intent: treeStructure_2.AACSemanticIntent.NAVIGATE_TO,
94
+ targetId: btnData.load_board.id,
95
+ fallback: {
96
+ type: 'NAVIGATE',
97
+ targetPageId: btnData.load_board.id,
98
+ add_to_sentence: btnData.load_board.add_to_sentence,
99
+ temporary_home: btnData.load_board.temporary_home,
100
+ },
101
+ platformData: {
102
+ grid3: {
103
+ commandId: 'GO_TO_BOARD',
104
+ parameters: {
105
+ add_to_sentence: btnData.load_board.add_to_sentence,
106
+ temporary_home: btnData.load_board.temporary_home,
107
+ },
108
+ },
109
+ },
110
+ };
111
+ }
112
+ else {
113
+ // Speaking button
114
+ semanticAction = {
115
+ category: treeStructure_2.AACSemanticCategory.COMMUNICATION,
116
+ intent: treeStructure_2.AACSemanticIntent.SPEAK_TEXT,
117
+ text: btnData.label || '',
118
+ fallback: { type: 'SPEAK', message: btnData.label || '' },
119
+ };
120
+ }
121
+ const button = new treeStructure_2.AACButton({
122
+ id: btnData.id,
123
+ label: btnData.label || '',
124
+ message: btnData.label || '',
125
+ targetPageId: btnData.load_board?.id,
126
+ semanticAction,
127
+ semantic_id: btnData.semantic_id,
128
+ clone_id: btnData.clone_id,
129
+ });
130
+ // Add to grid at the correct position
131
+ if (rowIndex < rows && colIndex < cols) {
132
+ page.grid[rowIndex][colIndex] = button;
133
+ }
134
+ page.buttons.push(button);
135
+ // Track IDs
136
+ if (btnData.semantic_id) {
137
+ semanticIds.push(String(btnData.semantic_id));
138
+ }
139
+ if (btnData.clone_id) {
140
+ cloneIds.push(String(btnData.clone_id));
141
+ }
142
+ // Establish parent relationship if this button links to another board
143
+ if (btnData.load_board?.id) {
144
+ const targetPage = boardMap.get(String(btnData.load_board.id));
145
+ if (targetPage) {
146
+ targetPage.parentId = page.id;
147
+ }
148
+ }
149
+ }
150
+ });
151
+ });
152
+ // Store IDs on page
153
+ page.semantic_ids = semanticIds;
154
+ page.clone_ids = cloneIds;
155
+ });
156
+ // Set root board (first board or one with no parent)
157
+ const rootBoard = Array.from(boardMap.values()).find((p) => !p.parentId);
158
+ if (rootBoard) {
159
+ tree.rootId = rootBoard.id;
160
+ }
161
+ return tree;
162
+ }
163
+ /**
164
+ * Process texts (not supported for .obfset currently)
165
+ */
166
+ processTexts(_filePathOrBuffer, _translations, _outputPath) {
167
+ throw new Error('processTexts is not supported for .obfset currently');
168
+ }
169
+ /**
170
+ * Save tree structure back to file
171
+ */
172
+ saveFromTree(_tree, _outputPath) {
173
+ throw new Error('saveFromTree is not supported for .obfset currently');
174
+ }
175
+ supportsExtension(extension) {
176
+ return extension === '.obfset';
177
+ }
178
+ }
179
+ exports.ObfsetProcessor = ObfsetProcessor;
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.SnapProcessor = void 0;
7
7
  const baseProcessor_1 = require("../core/baseProcessor");
8
8
  const treeStructure_1 = require("../core/treeStructure");
9
- // Removed unused import: FileProcessor
9
+ const idGenerator_1 = require("../optional/analytics/utils/idGenerator");
10
10
  const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
11
11
  const path_1 = __importDefault(require("path"));
12
12
  const fs_1 = __importDefault(require("fs"));
@@ -226,6 +226,9 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
226
226
  targetPageId: targetPageUniqueId,
227
227
  semanticAction: semanticAction,
228
228
  audioRecording: audioRecording,
229
+ semantic_id: btnRow.LibrarySymbolId
230
+ ? `snap_symbol_${btnRow.LibrarySymbolId}`
231
+ : undefined, // Extract semantic_id from LibrarySymbolId
229
232
  style: {
230
233
  backgroundColor: btnRow.BackgroundColor
231
234
  ? `#${btnRow.BackgroundColor.toString(16)}`
@@ -258,6 +261,10 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
258
261
  gridX < 10 &&
259
262
  pageGrid[gridY] &&
260
263
  pageGrid[gridY][gridX] === null) {
264
+ // Generate clone_id for button at this position
265
+ const rows = pageGrid.length;
266
+ const cols = pageGrid[0] ? pageGrid[0].length : 10;
267
+ button.clone_id = (0, idGenerator_1.generateCloneId)(rows, cols, gridY, gridX, button.label);
261
268
  pageGrid[gridY][gridX] = button;
262
269
  }
263
270
  }
@@ -274,6 +281,27 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
274
281
  const currentPage = tree.getPage(uniqueId);
275
282
  if (currentPage && pageGrid) {
276
283
  currentPage.grid = pageGrid;
284
+ // Track semantic_ids and clone_ids on the page
285
+ const semanticIds = [];
286
+ const cloneIds = [];
287
+ pageGrid.forEach((row) => {
288
+ row.forEach((btn) => {
289
+ if (btn) {
290
+ if (btn.semantic_id) {
291
+ semanticIds.push(btn.semantic_id);
292
+ }
293
+ if (btn.clone_id) {
294
+ cloneIds.push(btn.clone_id);
295
+ }
296
+ }
297
+ });
298
+ });
299
+ if (semanticIds.length > 0) {
300
+ currentPage.semantic_ids = semanticIds;
301
+ }
302
+ if (cloneIds.length > 0) {
303
+ currentPage.clone_ids = cloneIds;
304
+ }
277
305
  }
278
306
  }
279
307
  return tree;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.TouchChatProcessor = void 0;
7
7
  const baseProcessor_1 = require("../core/baseProcessor");
8
8
  const treeStructure_1 = require("../core/treeStructure");
9
+ const idGenerator_1 = require("../optional/analytics/utils/idGenerator");
9
10
  const stringCasing_1 = require("../core/stringCasing");
10
11
  const adm_zip_1 = __importDefault(require("adm-zip"));
11
12
  const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
@@ -165,6 +166,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
165
166
  label: cell.label || '',
166
167
  message: cell.message || '',
167
168
  semanticAction: semanticAction,
169
+ semantic_id: cell.symbol_link_id || cell.symbolLinkId || undefined, // Extract semantic_id from symbol_link_id
168
170
  style: {
169
171
  backgroundColor: intToHex(style?.body_color),
170
172
  borderColor: intToHex(style?.border_color),
@@ -241,6 +243,31 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
241
243
  const page = tree.getPage(pageId);
242
244
  if (page) {
243
245
  page.grid = grid;
246
+ // Generate clone_id for each button in the grid
247
+ const semanticIds = [];
248
+ const cloneIds = [];
249
+ grid.forEach((row, rowIndex) => {
250
+ row.forEach((btn, colIndex) => {
251
+ if (btn) {
252
+ // Generate clone_id based on position and label
253
+ const rows = grid.length;
254
+ const cols = grid[0] ? grid[0].length : 10;
255
+ btn.clone_id = (0, idGenerator_1.generateCloneId)(rows, cols, rowIndex, colIndex, btn.label);
256
+ cloneIds.push(btn.clone_id);
257
+ // Track semantic_id if present
258
+ if (btn.semantic_id) {
259
+ semanticIds.push(btn.semantic_id);
260
+ }
261
+ }
262
+ });
263
+ });
264
+ // Track IDs on the page
265
+ if (semanticIds.length > 0) {
266
+ page.semantic_ids = semanticIds;
267
+ }
268
+ if (cloneIds.length > 0) {
269
+ page.clone_ids = cloneIds;
270
+ }
244
271
  }
245
272
  });
246
273
  }
@@ -42,6 +42,8 @@ export interface AACButton {
42
42
  parameters?: {
43
43
  [key: string]: any;
44
44
  };
45
+ semantic_id?: string;
46
+ clone_id?: string;
45
47
  }
46
48
  export interface AACPage {
47
49
  id: string;
@@ -54,6 +56,8 @@ export interface AACPage {
54
56
  descriptionHtml?: string;
55
57
  images?: any[];
56
58
  sounds?: any[];
59
+ semantic_ids?: string[];
60
+ clone_ids?: string[];
57
61
  }
58
62
  export interface AACTree {
59
63
  pages: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@willwade/aac-processors",
3
- "version": "0.0.11",
3
+ "version": "0.0.12",
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",