@willwade/aac-processors 0.0.4 → 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/README.md +1 -1
- 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/styleHelpers.d.ts +46 -0
- package/dist/processors/gridset/styleHelpers.js +211 -0
- package/dist/processors/gridset/wordlistHelpers.js +3 -2
- package/dist/processors/gridsetProcessor.js +97 -105
- package/dist/processors/index.d.ts +1 -0
- package/dist/processors/index.js +8 -1
- 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/docs/Grid3-Styling-Guide.md +287 -0
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -142,7 +142,7 @@ commander_1.program
|
|
|
142
142
|
.option('--no-exclude-navigation', "Don't exclude navigation buttons (Home, Back)")
|
|
143
143
|
.option('--no-exclude-system', "Don't exclude system buttons (Delete, Clear, etc.)")
|
|
144
144
|
.option('--exclude-buttons <list>', 'Comma-separated list of button labels/terms to exclude')
|
|
145
|
-
.action((input, output, options) => {
|
|
145
|
+
.action(async (input, output, options) => {
|
|
146
146
|
try {
|
|
147
147
|
if (!options.format) {
|
|
148
148
|
console.error('Error: --format option is required for convert command');
|
|
@@ -157,7 +157,7 @@ commander_1.program
|
|
|
157
157
|
const tree = inputProcessor.loadIntoTree(input);
|
|
158
158
|
// Save using output format with same filtering options
|
|
159
159
|
const outputProcessor = (0, analyze_1.getProcessor)(options.format, filteringOptions);
|
|
160
|
-
outputProcessor.saveFromTree(tree, output);
|
|
160
|
+
await outputProcessor.saveFromTree(tree, output);
|
|
161
161
|
// Show filtering summary
|
|
162
162
|
let filteringSummary = '';
|
|
163
163
|
if (filteringOptions.preserveAllButtons) {
|
|
@@ -163,7 +163,11 @@ export declare class AACPage implements IAACPage {
|
|
|
163
163
|
buttons: AACButton[];
|
|
164
164
|
parentId: string | null;
|
|
165
165
|
style?: AACStyle;
|
|
166
|
-
|
|
166
|
+
locale?: string;
|
|
167
|
+
descriptionHtml?: string;
|
|
168
|
+
images?: any[];
|
|
169
|
+
sounds?: any[];
|
|
170
|
+
constructor({ id, name, grid, buttons, parentId, style, locale, descriptionHtml, images, sounds, }: {
|
|
167
171
|
id: string;
|
|
168
172
|
name?: string;
|
|
169
173
|
grid?: Array<Array<AACButton | null>> | {
|
|
@@ -173,6 +177,10 @@ export declare class AACPage implements IAACPage {
|
|
|
173
177
|
buttons?: AACButton[];
|
|
174
178
|
parentId?: string | null;
|
|
175
179
|
style?: AACStyle;
|
|
180
|
+
locale?: string;
|
|
181
|
+
descriptionHtml?: string;
|
|
182
|
+
images?: any[];
|
|
183
|
+
sounds?: any[];
|
|
176
184
|
});
|
|
177
185
|
addButton(button: AACButton): void;
|
|
178
186
|
}
|
|
@@ -152,7 +152,7 @@ class AACButton {
|
|
|
152
152
|
}
|
|
153
153
|
exports.AACButton = AACButton;
|
|
154
154
|
class AACPage {
|
|
155
|
-
constructor({ id, name = '', grid = [], buttons = [], parentId = null, style, }) {
|
|
155
|
+
constructor({ id, name = '', grid = [], buttons = [], parentId = null, style, locale, descriptionHtml, images, sounds, }) {
|
|
156
156
|
this.id = id;
|
|
157
157
|
this.name = name;
|
|
158
158
|
if (Array.isArray(grid)) {
|
|
@@ -169,6 +169,10 @@ class AACPage {
|
|
|
169
169
|
this.buttons = buttons;
|
|
170
170
|
this.parentId = parentId;
|
|
171
171
|
this.style = style;
|
|
172
|
+
this.locale = locale;
|
|
173
|
+
this.descriptionHtml = descriptionHtml;
|
|
174
|
+
this.images = images;
|
|
175
|
+
this.sounds = sounds;
|
|
172
176
|
}
|
|
173
177
|
addButton(button) {
|
|
174
178
|
this.buttons.push(button);
|
|
@@ -10,6 +10,45 @@ const treeStructure_1 = require("../core/treeStructure");
|
|
|
10
10
|
const plist_1 = __importDefault(require("plist"));
|
|
11
11
|
const fs_1 = __importDefault(require("fs"));
|
|
12
12
|
const path_1 = __importDefault(require("path"));
|
|
13
|
+
function isNormalizedPanel(panel) {
|
|
14
|
+
return typeof panel.id === 'string';
|
|
15
|
+
}
|
|
16
|
+
function normalizePanel(panel, fallbackId) {
|
|
17
|
+
const rawId = panel.ID || fallbackId;
|
|
18
|
+
const buttons = Array.isArray(panel.PanelObjects)
|
|
19
|
+
? panel.PanelObjects.filter((obj) => obj.PanelObjectType === 'Button')
|
|
20
|
+
: [];
|
|
21
|
+
const normalizedButtons = buttons.map((btn) => {
|
|
22
|
+
const firstAction = Array.isArray(btn.Actions) && btn.Actions.length > 0 ? btn.Actions[0] : undefined;
|
|
23
|
+
const isCharSequence = firstAction &&
|
|
24
|
+
(firstAction.ActionType === 'ActionPressKeyCharSequence' ||
|
|
25
|
+
firstAction.ActionType === 'ActionSendKeys');
|
|
26
|
+
const charString = isCharSequence ? firstAction?.ActionParam?.CharString : undefined;
|
|
27
|
+
const targetPanel = firstAction && firstAction.ActionType === 'ActionOpenPanel'
|
|
28
|
+
? firstAction.ActionParam?.PanelID?.replace(/^USER\./, '')
|
|
29
|
+
: undefined;
|
|
30
|
+
return {
|
|
31
|
+
label: btn.DisplayText || 'Button',
|
|
32
|
+
message: charString || btn.DisplayText || 'Button',
|
|
33
|
+
DisplayColor: btn.DisplayColor,
|
|
34
|
+
DisplayImageWeight: btn.DisplayImageWeight,
|
|
35
|
+
FontSize: btn.FontSize,
|
|
36
|
+
Rect: btn.Rect,
|
|
37
|
+
targetPanel,
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
id: rawId.replace(/^USER\./, ''),
|
|
42
|
+
name: panel.Name || 'Panel',
|
|
43
|
+
buttons: normalizedButtons,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function normalizeActionParameters(input) {
|
|
47
|
+
if (typeof input === 'object' && input !== null) {
|
|
48
|
+
return { ...input };
|
|
49
|
+
}
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
13
52
|
class ApplePanelsProcessor extends baseProcessor_1.BaseProcessor {
|
|
14
53
|
constructor(options) {
|
|
15
54
|
super(options);
|
|
@@ -81,41 +120,21 @@ class ApplePanelsProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
81
120
|
// Handle both old format (panels array) and new Apple Panels format (Panels dict)
|
|
82
121
|
let panelsData = [];
|
|
83
122
|
if (Array.isArray(parsedData.panels)) {
|
|
84
|
-
|
|
85
|
-
|
|
123
|
+
panelsData = parsedData.panels.map((panel, index) => {
|
|
124
|
+
if (isNormalizedPanel(panel)) {
|
|
125
|
+
return panel;
|
|
126
|
+
}
|
|
127
|
+
const panelData = panel || {
|
|
128
|
+
PanelObjects: [],
|
|
129
|
+
};
|
|
130
|
+
return normalizePanel(panelData, `panel_${index}`);
|
|
131
|
+
});
|
|
86
132
|
}
|
|
87
133
|
else if (parsedData.Panels) {
|
|
88
|
-
// Apple Panels format: convert Panels dict to array
|
|
89
134
|
const panelsDict = parsedData.Panels;
|
|
90
135
|
panelsData = Object.keys(panelsDict).map((panelId) => {
|
|
91
|
-
const
|
|
92
|
-
return
|
|
93
|
-
id: (panel.ID || panelId).replace(/^USER\./, ''), // Strip USER. prefix to maintain original IDs
|
|
94
|
-
name: panel.Name || 'Panel',
|
|
95
|
-
buttons: (panel.PanelObjects || [])
|
|
96
|
-
.filter((obj) => obj.PanelObjectType === 'Button')
|
|
97
|
-
.map((btn) => {
|
|
98
|
-
const firstAction = Array.isArray(btn.Actions) && btn.Actions.length > 0 ? btn.Actions[0] : undefined;
|
|
99
|
-
const isCharSequence = firstAction &&
|
|
100
|
-
(firstAction.ActionType === 'ActionPressKeyCharSequence' ||
|
|
101
|
-
firstAction.ActionType === 'ActionSendKeys');
|
|
102
|
-
const charString = isCharSequence
|
|
103
|
-
? (firstAction.ActionParam?.CharString ?? undefined)
|
|
104
|
-
: undefined;
|
|
105
|
-
const targetPanel = firstAction && firstAction.ActionType === 'ActionOpenPanel'
|
|
106
|
-
? firstAction.ActionParam?.PanelID?.replace(/^USER\./, '')
|
|
107
|
-
: undefined;
|
|
108
|
-
return {
|
|
109
|
-
label: btn.DisplayText || 'Button',
|
|
110
|
-
message: charString || btn.DisplayText || 'Button',
|
|
111
|
-
DisplayColor: btn.DisplayColor,
|
|
112
|
-
DisplayImageWeight: btn.DisplayImageWeight,
|
|
113
|
-
FontSize: btn.FontSize,
|
|
114
|
-
Rect: btn.Rect,
|
|
115
|
-
targetPanel,
|
|
116
|
-
};
|
|
117
|
-
}),
|
|
118
|
-
};
|
|
136
|
+
const rawPanel = panelsDict[panelId] || { PanelObjects: [] };
|
|
137
|
+
return normalizePanel(rawPanel, panelId);
|
|
119
138
|
});
|
|
120
139
|
}
|
|
121
140
|
const data = { panels: panelsData };
|
|
@@ -219,15 +238,24 @@ class ApplePanelsProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
219
238
|
Object.values(tree.pages).forEach((page) => {
|
|
220
239
|
// Translate page names
|
|
221
240
|
if (page.name && translations.has(page.name)) {
|
|
222
|
-
|
|
241
|
+
const translatedName = translations.get(page.name);
|
|
242
|
+
if (translatedName !== undefined) {
|
|
243
|
+
page.name = translatedName;
|
|
244
|
+
}
|
|
223
245
|
}
|
|
224
246
|
// Translate button labels and messages
|
|
225
247
|
page.buttons.forEach((button) => {
|
|
226
248
|
if (button.label && translations.has(button.label)) {
|
|
227
|
-
|
|
249
|
+
const translatedLabel = translations.get(button.label);
|
|
250
|
+
if (translatedLabel !== undefined) {
|
|
251
|
+
button.label = translatedLabel;
|
|
252
|
+
}
|
|
228
253
|
}
|
|
229
254
|
if (button.message && translations.has(button.message)) {
|
|
230
|
-
|
|
255
|
+
const translatedMessage = translations.get(button.message);
|
|
256
|
+
if (translatedMessage !== undefined) {
|
|
257
|
+
button.message = translatedMessage;
|
|
258
|
+
}
|
|
231
259
|
}
|
|
232
260
|
if (button.semanticAction) {
|
|
233
261
|
const intentStr = String(button.semanticAction.intent);
|
|
@@ -304,10 +332,8 @@ class ApplePanelsProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
304
332
|
const panelId = `USER.${page.id}`;
|
|
305
333
|
// Detect actual grid dimensions from the source data
|
|
306
334
|
let gridCols = 4; // Default fallback
|
|
307
|
-
let gridRows = Math.ceil(page.buttons.length / gridCols);
|
|
308
335
|
if (page.grid && page.grid.length > 0) {
|
|
309
336
|
// Use actual grid dimensions from source
|
|
310
|
-
gridRows = page.grid.length;
|
|
311
337
|
gridCols = page.grid[0] ? page.grid[0].length : 4;
|
|
312
338
|
// Find the actual used area to avoid empty space
|
|
313
339
|
let maxUsedX = 0, maxUsedY = 0;
|
|
@@ -322,7 +348,6 @@ class ApplePanelsProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
322
348
|
// Use the actual used dimensions if they're reasonable
|
|
323
349
|
if (maxUsedX > 0 && maxUsedY > 0) {
|
|
324
350
|
gridCols = maxUsedX + 1;
|
|
325
|
-
gridRows = maxUsedY + 1;
|
|
326
351
|
}
|
|
327
352
|
}
|
|
328
353
|
else {
|
|
@@ -340,7 +365,6 @@ class ApplePanelsProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
340
365
|
else {
|
|
341
366
|
gridCols = 8; // 8 columns for very large sets
|
|
342
367
|
}
|
|
343
|
-
gridRows = Math.ceil(buttonCount / gridCols);
|
|
344
368
|
}
|
|
345
369
|
const panelObjects = page.buttons.map((button, buttonIndex) => {
|
|
346
370
|
// Find button position in grid layout and convert to Rect format
|
|
@@ -379,7 +403,8 @@ class ApplePanelsProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
379
403
|
FontSize: button.style?.fontSize || 12,
|
|
380
404
|
ID: `Button.${button.id}`,
|
|
381
405
|
PanelObjectType: 'Button',
|
|
382
|
-
Rect: rect,
|
|
406
|
+
Rect: rect ?? '{{0, 0}, {100, 25}}',
|
|
407
|
+
Actions: [],
|
|
383
408
|
};
|
|
384
409
|
if (button.style?.backgroundColor) {
|
|
385
410
|
buttonObj.DisplayColor = button.style.backgroundColor;
|
|
@@ -414,8 +439,9 @@ class ApplePanelsProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
414
439
|
UsesPinnedResizing: false,
|
|
415
440
|
};
|
|
416
441
|
});
|
|
442
|
+
const panelsValue = Object.fromEntries(Object.entries(panelsDict).map(([key, value]) => [key, value]));
|
|
417
443
|
const panelDefinitions = {
|
|
418
|
-
Panels:
|
|
444
|
+
Panels: panelsValue,
|
|
419
445
|
ToolbarOrdering: {
|
|
420
446
|
ToolbarIdentifiersAfterBasePanel: [],
|
|
421
447
|
ToolbarIdentifiersPriorToBasePanel: [],
|
|
@@ -439,7 +465,7 @@ class ApplePanelsProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
439
465
|
if (button.semanticAction?.platformData?.applePanels) {
|
|
440
466
|
const applePanelsData = button.semanticAction.platformData.applePanels;
|
|
441
467
|
return {
|
|
442
|
-
ActionParam: applePanelsData.parameters,
|
|
468
|
+
ActionParam: normalizeActionParameters(applePanelsData.parameters),
|
|
443
469
|
ActionRecordedOffset: 0.0,
|
|
444
470
|
ActionType: applePanelsData.actionType,
|
|
445
471
|
ID: `Action.${button.id}`,
|