@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.
- package/dist/cli/index.js +7 -0
- package/dist/core/analyze.js +1 -0
- package/dist/core/treeStructure.d.ts +12 -2
- package/dist/core/treeStructure.js +6 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +20 -3
- package/dist/{analytics → optional/analytics}/history.d.ts +3 -3
- package/dist/{analytics → optional/analytics}/history.js +3 -3
- package/dist/optional/analytics/index.d.ts +28 -0
- package/dist/optional/analytics/index.js +73 -0
- package/dist/optional/analytics/metrics/comparison.d.ts +36 -0
- package/dist/optional/analytics/metrics/comparison.js +330 -0
- package/dist/optional/analytics/metrics/core.d.ts +36 -0
- package/dist/optional/analytics/metrics/core.js +422 -0
- package/dist/optional/analytics/metrics/effort.d.ts +137 -0
- package/dist/optional/analytics/metrics/effort.js +198 -0
- package/dist/optional/analytics/metrics/index.d.ts +15 -0
- package/dist/optional/analytics/metrics/index.js +36 -0
- package/dist/optional/analytics/metrics/sentence.d.ts +49 -0
- package/dist/optional/analytics/metrics/sentence.js +112 -0
- package/dist/optional/analytics/metrics/types.d.ts +157 -0
- package/dist/optional/analytics/metrics/types.js +7 -0
- package/dist/optional/analytics/metrics/vocabulary.d.ts +65 -0
- package/dist/optional/analytics/metrics/vocabulary.js +140 -0
- package/dist/optional/analytics/reference/index.d.ts +51 -0
- package/dist/optional/analytics/reference/index.js +102 -0
- package/dist/optional/analytics/utils/idGenerator.d.ts +59 -0
- package/dist/optional/analytics/utils/idGenerator.js +96 -0
- package/dist/processors/gridsetProcessor.js +25 -0
- package/dist/processors/index.d.ts +1 -0
- package/dist/processors/index.js +5 -3
- package/dist/processors/obfProcessor.js +25 -2
- package/dist/processors/obfsetProcessor.d.ts +26 -0
- package/dist/processors/obfsetProcessor.js +179 -0
- package/dist/processors/snapProcessor.js +29 -1
- package/dist/processors/touchchatProcessor.js +27 -0
- package/dist/types/aac.d.ts +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Vocabulary Coverage Analysis
|
|
4
|
+
*
|
|
5
|
+
* Analyzes how well an AAC board set covers core vocabulary
|
|
6
|
+
* and identifies missing/extra words compared to reference lists.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.VocabularyAnalyzer = void 0;
|
|
10
|
+
const index_1 = require("../reference/index");
|
|
11
|
+
const effort_1 = require("./effort");
|
|
12
|
+
class VocabularyAnalyzer {
|
|
13
|
+
constructor(referenceLoader) {
|
|
14
|
+
this.referenceLoader = referenceLoader || new index_1.ReferenceLoader();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Analyze vocabulary coverage against core lists
|
|
18
|
+
*/
|
|
19
|
+
analyze(metrics, options) {
|
|
20
|
+
// const locale = options?.locale || metrics.locale || 'en';
|
|
21
|
+
const highEffortThreshold = options?.highEffortThreshold || 5.0;
|
|
22
|
+
const lowEffortThreshold = options?.lowEffortThreshold || 2.0;
|
|
23
|
+
// Load reference data
|
|
24
|
+
const coreLists = this.referenceLoader.loadCoreLists();
|
|
25
|
+
// Create word to effort map
|
|
26
|
+
const wordEffortMap = new Map();
|
|
27
|
+
metrics.buttons.forEach((btn) => {
|
|
28
|
+
const existing = wordEffortMap.get(btn.label);
|
|
29
|
+
if (!existing || btn.effort < existing) {
|
|
30
|
+
wordEffortMap.set(btn.label, btn.effort);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
// Analyze each core list
|
|
34
|
+
const core_coverage = {};
|
|
35
|
+
coreLists.forEach((list) => {
|
|
36
|
+
const analysis = this.analyzeCoreList(list, wordEffortMap);
|
|
37
|
+
core_coverage[list.id] = analysis;
|
|
38
|
+
});
|
|
39
|
+
// Find extra words (words not in any core list)
|
|
40
|
+
const allCoreWords = new Set();
|
|
41
|
+
coreLists.forEach((list) => {
|
|
42
|
+
list.words.forEach((word) => allCoreWords.add(word.toLowerCase()));
|
|
43
|
+
});
|
|
44
|
+
const extraWords = [];
|
|
45
|
+
wordEffortMap.forEach((effort, word) => {
|
|
46
|
+
if (!allCoreWords.has(word.toLowerCase())) {
|
|
47
|
+
extraWords.push(word);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
extraWords.sort((a, b) => a.localeCompare(b));
|
|
51
|
+
// Find high/low effort words
|
|
52
|
+
const highEffortWords = [];
|
|
53
|
+
const lowEffortWords = [];
|
|
54
|
+
wordEffortMap.forEach((effort, word) => {
|
|
55
|
+
if (effort > highEffortThreshold) {
|
|
56
|
+
highEffortWords.push({ word, effort });
|
|
57
|
+
}
|
|
58
|
+
else if (effort < lowEffortThreshold) {
|
|
59
|
+
lowEffortWords.push({ word, effort });
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
highEffortWords.sort((a, b) => b.effort - a.effort);
|
|
63
|
+
lowEffortWords.sort((a, b) => a.effort - b.effort);
|
|
64
|
+
return {
|
|
65
|
+
core_coverage,
|
|
66
|
+
total_unique_words: wordEffortMap.size,
|
|
67
|
+
words_with_effort: wordEffortMap.size,
|
|
68
|
+
words_requiring_spelling: 0, // Calculated during sentence analysis
|
|
69
|
+
extra_words: extraWords,
|
|
70
|
+
high_effort_words: highEffortWords.slice(0, 50), // Top 50
|
|
71
|
+
low_effort_words: lowEffortWords.slice(0, 50), // Bottom 50
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Analyze coverage for a single core list
|
|
76
|
+
*/
|
|
77
|
+
analyzeCoreList(list, wordEffortMap) {
|
|
78
|
+
const covered = [];
|
|
79
|
+
const missing = [];
|
|
80
|
+
let totalEffort = 0;
|
|
81
|
+
list.words.forEach((word) => {
|
|
82
|
+
const effort = wordEffortMap.get(word);
|
|
83
|
+
if (effort !== undefined) {
|
|
84
|
+
covered.push(word);
|
|
85
|
+
totalEffort += effort;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
missing.push(word);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
const averageEffort = covered.length > 0 ? totalEffort / covered.length : 0;
|
|
92
|
+
return {
|
|
93
|
+
name: list.name,
|
|
94
|
+
total_words: list.words.length,
|
|
95
|
+
covered: covered.length,
|
|
96
|
+
missing: missing.length,
|
|
97
|
+
coverage_percent: (covered.length / list.words.length) * 100,
|
|
98
|
+
missing_words: missing,
|
|
99
|
+
average_effort: averageEffort,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Calculate coverage percentage for a specific word list
|
|
104
|
+
*/
|
|
105
|
+
calculateCoverage(wordList, metrics) {
|
|
106
|
+
const wordSet = new Set(metrics.buttons.map((btn) => btn.label.toLowerCase()));
|
|
107
|
+
const covered = [];
|
|
108
|
+
const missing = [];
|
|
109
|
+
wordList.forEach((word) => {
|
|
110
|
+
if (wordSet.has(word.toLowerCase())) {
|
|
111
|
+
covered.push(word);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
missing.push(word);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
covered,
|
|
119
|
+
missing,
|
|
120
|
+
coverage_percent: (covered.length / wordList.length) * 100,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get effort for a word, or calculate spelling effort if missing
|
|
125
|
+
*/
|
|
126
|
+
getWordEffort(word, metrics) {
|
|
127
|
+
const btn = metrics.buttons.find((b) => b.label.toLowerCase() === word.toLowerCase());
|
|
128
|
+
if (btn) {
|
|
129
|
+
return btn.effort;
|
|
130
|
+
}
|
|
131
|
+
return (0, effort_1.spellingEffort)(word);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Check if a word is in the board set
|
|
135
|
+
*/
|
|
136
|
+
hasWord(word, metrics) {
|
|
137
|
+
return metrics.buttons.some((b) => b.label.toLowerCase() === word.toLowerCase());
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
exports.VocabularyAnalyzer = VocabularyAnalyzer;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference Data Loader
|
|
3
|
+
*
|
|
4
|
+
* Loads reference vocabulary lists, core lists, and sentences
|
|
5
|
+
* for AAC metrics analysis.
|
|
6
|
+
*/
|
|
7
|
+
import { CoreList, CommonWordsData, SynonymsData } from '../metrics/types';
|
|
8
|
+
export declare class ReferenceLoader {
|
|
9
|
+
private dataDir;
|
|
10
|
+
private locale;
|
|
11
|
+
constructor(dataDir?: string, locale?: string);
|
|
12
|
+
/**
|
|
13
|
+
* Load core vocabulary lists
|
|
14
|
+
*/
|
|
15
|
+
loadCoreLists(): CoreList[];
|
|
16
|
+
/**
|
|
17
|
+
* Load common words with baseline effort scores
|
|
18
|
+
*/
|
|
19
|
+
loadCommonWords(): CommonWordsData;
|
|
20
|
+
/**
|
|
21
|
+
* Load synonym mappings
|
|
22
|
+
*/
|
|
23
|
+
loadSynonyms(): SynonymsData;
|
|
24
|
+
/**
|
|
25
|
+
* Load test sentences
|
|
26
|
+
*/
|
|
27
|
+
loadSentences(): string[][];
|
|
28
|
+
/**
|
|
29
|
+
* Load fringe vocabulary
|
|
30
|
+
*/
|
|
31
|
+
loadFringe(): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Load base words hash map
|
|
34
|
+
*/
|
|
35
|
+
loadBaseWords(): {
|
|
36
|
+
[word: string]: boolean;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Get all reference data at once
|
|
40
|
+
*/
|
|
41
|
+
loadAll(): {
|
|
42
|
+
coreLists: CoreList[];
|
|
43
|
+
commonWords: CommonWordsData;
|
|
44
|
+
synonyms: SynonymsData;
|
|
45
|
+
sentences: string[][];
|
|
46
|
+
fringe: string[];
|
|
47
|
+
baseWords: {
|
|
48
|
+
[word: string]: boolean;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Reference Data Loader
|
|
4
|
+
*
|
|
5
|
+
* Loads reference vocabulary lists, core lists, and sentences
|
|
6
|
+
* for AAC metrics analysis.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
25
|
+
if (mod && mod.__esModule) return mod;
|
|
26
|
+
var result = {};
|
|
27
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
28
|
+
__setModuleDefault(result, mod);
|
|
29
|
+
return result;
|
|
30
|
+
};
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.ReferenceLoader = void 0;
|
|
33
|
+
const fs = __importStar(require("fs"));
|
|
34
|
+
const path = __importStar(require("path"));
|
|
35
|
+
class ReferenceLoader {
|
|
36
|
+
constructor(dataDir = path.join(__dirname, 'data'), locale = 'en') {
|
|
37
|
+
this.dataDir = dataDir;
|
|
38
|
+
this.locale = locale;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Load core vocabulary lists
|
|
42
|
+
*/
|
|
43
|
+
loadCoreLists() {
|
|
44
|
+
const filePath = path.join(this.dataDir, `core_lists.${this.locale}.json`);
|
|
45
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
46
|
+
return JSON.parse(content);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Load common words with baseline effort scores
|
|
50
|
+
*/
|
|
51
|
+
loadCommonWords() {
|
|
52
|
+
const filePath = path.join(this.dataDir, `common_words.${this.locale}.json`);
|
|
53
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
54
|
+
return JSON.parse(content);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Load synonym mappings
|
|
58
|
+
*/
|
|
59
|
+
loadSynonyms() {
|
|
60
|
+
const filePath = path.join(this.dataDir, `synonyms.${this.locale}.json`);
|
|
61
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
62
|
+
return JSON.parse(content);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Load test sentences
|
|
66
|
+
*/
|
|
67
|
+
loadSentences() {
|
|
68
|
+
const filePath = path.join(this.dataDir, `sentences.${this.locale}.json`);
|
|
69
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
70
|
+
return JSON.parse(content);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Load fringe vocabulary
|
|
74
|
+
*/
|
|
75
|
+
loadFringe() {
|
|
76
|
+
const filePath = path.join(this.dataDir, `fringe.${this.locale}.json`);
|
|
77
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
78
|
+
return JSON.parse(content);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Load base words hash map
|
|
82
|
+
*/
|
|
83
|
+
loadBaseWords() {
|
|
84
|
+
const filePath = path.join(this.dataDir, `base_words.${this.locale}.json`);
|
|
85
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
86
|
+
return JSON.parse(content);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get all reference data at once
|
|
90
|
+
*/
|
|
91
|
+
loadAll() {
|
|
92
|
+
return {
|
|
93
|
+
coreLists: this.loadCoreLists(),
|
|
94
|
+
commonWords: this.loadCommonWords(),
|
|
95
|
+
synonyms: this.loadSynonyms(),
|
|
96
|
+
sentences: this.loadSentences(),
|
|
97
|
+
fringe: this.loadFringe(),
|
|
98
|
+
baseWords: this.loadBaseWords(),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.ReferenceLoader = ReferenceLoader;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ID Generator Utility for AAC Metrics
|
|
3
|
+
*
|
|
4
|
+
* Generates clone_id values based on grid location and button label.
|
|
5
|
+
* Clone IDs help identify buttons that appear in the same location
|
|
6
|
+
* across different boards in an AAC system.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Normalize a label for use in clone_id generation
|
|
10
|
+
* Converts to lowercase, removes apostrophes, trims whitespace
|
|
11
|
+
*
|
|
12
|
+
* @param label - The button label to normalize
|
|
13
|
+
* @returns Normalized label string
|
|
14
|
+
*/
|
|
15
|
+
export declare function normalizeLabelForCloneId(label: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Generate a clone_id based on grid location and button label
|
|
18
|
+
*
|
|
19
|
+
* Clone ID format: "{rows}x{cols}-{row}.{col}-{label_normalized}"
|
|
20
|
+
* Example: "6x4-2.3-more" for button "more" at row 2, col 3 in a 6x4 grid
|
|
21
|
+
*
|
|
22
|
+
* @param rows - Total number of rows in the grid
|
|
23
|
+
* @param cols - Total number of columns in the grid
|
|
24
|
+
* @param row - Zero-based row index of the button
|
|
25
|
+
* @param col - Zero-based column index of the button
|
|
26
|
+
* @param label - The button label
|
|
27
|
+
* @returns A clone_id string
|
|
28
|
+
*/
|
|
29
|
+
export declare function generateCloneId(rows: number, cols: number, row: number, col: number, label: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Generate a semantic_id based on button content
|
|
32
|
+
*
|
|
33
|
+
* Semantic IDs identify buttons with the same semantic meaning across boards.
|
|
34
|
+
* This is a fallback for formats that don't have explicit semantic IDs.
|
|
35
|
+
* Based on hash of message + label
|
|
36
|
+
*
|
|
37
|
+
* @param message - The button message/vocalization
|
|
38
|
+
* @param label - The button label
|
|
39
|
+
* @returns A semantic_id string (hash-based)
|
|
40
|
+
*/
|
|
41
|
+
export declare function generateSemanticId(message: string, label: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Extract all semantic_ids from a page's buttons
|
|
44
|
+
*
|
|
45
|
+
* @param buttons - Array of buttons to scan
|
|
46
|
+
* @returns Array of unique semantic_id strings
|
|
47
|
+
*/
|
|
48
|
+
export declare function extractSemanticIds(buttons: Array<{
|
|
49
|
+
semantic_id?: string;
|
|
50
|
+
}>): string[];
|
|
51
|
+
/**
|
|
52
|
+
* Extract all clone_ids from a page's buttons
|
|
53
|
+
*
|
|
54
|
+
* @param buttons - Array of buttons to scan
|
|
55
|
+
* @returns Array of unique clone_id strings
|
|
56
|
+
*/
|
|
57
|
+
export declare function extractCloneIds(buttons: Array<{
|
|
58
|
+
clone_id?: string;
|
|
59
|
+
}>): string[];
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ID Generator Utility for AAC Metrics
|
|
4
|
+
*
|
|
5
|
+
* Generates clone_id values based on grid location and button label.
|
|
6
|
+
* Clone IDs help identify buttons that appear in the same location
|
|
7
|
+
* across different boards in an AAC system.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.normalizeLabelForCloneId = normalizeLabelForCloneId;
|
|
11
|
+
exports.generateCloneId = generateCloneId;
|
|
12
|
+
exports.generateSemanticId = generateSemanticId;
|
|
13
|
+
exports.extractSemanticIds = extractSemanticIds;
|
|
14
|
+
exports.extractCloneIds = extractCloneIds;
|
|
15
|
+
/**
|
|
16
|
+
* Normalize a label for use in clone_id generation
|
|
17
|
+
* Converts to lowercase, removes apostrophes, trims whitespace
|
|
18
|
+
*
|
|
19
|
+
* @param label - The button label to normalize
|
|
20
|
+
* @returns Normalized label string
|
|
21
|
+
*/
|
|
22
|
+
function normalizeLabelForCloneId(label) {
|
|
23
|
+
return label
|
|
24
|
+
.toLowerCase()
|
|
25
|
+
.replace(/['']/g, '') // Remove apostrophes
|
|
26
|
+
.replace(/\s+/g, '_') // Replace spaces with underscores
|
|
27
|
+
.trim();
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Generate a clone_id based on grid location and button label
|
|
31
|
+
*
|
|
32
|
+
* Clone ID format: "{rows}x{cols}-{row}.{col}-{label_normalized}"
|
|
33
|
+
* Example: "6x4-2.3-more" for button "more" at row 2, col 3 in a 6x4 grid
|
|
34
|
+
*
|
|
35
|
+
* @param rows - Total number of rows in the grid
|
|
36
|
+
* @param cols - Total number of columns in the grid
|
|
37
|
+
* @param row - Zero-based row index of the button
|
|
38
|
+
* @param col - Zero-based column index of the button
|
|
39
|
+
* @param label - The button label
|
|
40
|
+
* @returns A clone_id string
|
|
41
|
+
*/
|
|
42
|
+
function generateCloneId(rows, cols, row, col, label) {
|
|
43
|
+
const normalizedLabel = normalizeLabelForCloneId(label);
|
|
44
|
+
return `${rows}x${cols}-${row}.${col}-${normalizedLabel}`;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Generate a semantic_id based on button content
|
|
48
|
+
*
|
|
49
|
+
* Semantic IDs identify buttons with the same semantic meaning across boards.
|
|
50
|
+
* This is a fallback for formats that don't have explicit semantic IDs.
|
|
51
|
+
* Based on hash of message + label
|
|
52
|
+
*
|
|
53
|
+
* @param message - The button message/vocalization
|
|
54
|
+
* @param label - The button label
|
|
55
|
+
* @returns A semantic_id string (hash-based)
|
|
56
|
+
*/
|
|
57
|
+
function generateSemanticId(message, label) {
|
|
58
|
+
const content = `${message || ''}::${label || ''}`;
|
|
59
|
+
// Simple hash function (djb2 algorithm)
|
|
60
|
+
let hash = 5381;
|
|
61
|
+
for (let i = 0; i < content.length; i++) {
|
|
62
|
+
hash = (hash * 33) ^ content.charCodeAt(i);
|
|
63
|
+
}
|
|
64
|
+
// Convert to positive hex string
|
|
65
|
+
return `semantic_${(hash >>> 0).toString(16)}`;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Extract all semantic_ids from a page's buttons
|
|
69
|
+
*
|
|
70
|
+
* @param buttons - Array of buttons to scan
|
|
71
|
+
* @returns Array of unique semantic_id strings
|
|
72
|
+
*/
|
|
73
|
+
function extractSemanticIds(buttons) {
|
|
74
|
+
const ids = new Set();
|
|
75
|
+
for (const button of buttons) {
|
|
76
|
+
if (button.semantic_id) {
|
|
77
|
+
ids.add(button.semantic_id);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return Array.from(ids);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Extract all clone_ids from a page's buttons
|
|
84
|
+
*
|
|
85
|
+
* @param buttons - Array of buttons to scan
|
|
86
|
+
* @returns Array of unique clone_id strings
|
|
87
|
+
*/
|
|
88
|
+
function extractCloneIds(buttons) {
|
|
89
|
+
const ids = new Set();
|
|
90
|
+
for (const button of buttons) {
|
|
91
|
+
if (button.clone_id) {
|
|
92
|
+
ids.add(button.clone_id);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return Array.from(ids);
|
|
96
|
+
}
|
|
@@ -19,6 +19,7 @@ const pluginTypes_1 = require("./gridset/pluginTypes");
|
|
|
19
19
|
const commands_1 = require("./gridset/commands");
|
|
20
20
|
const symbols_1 = require("./gridset/symbols");
|
|
21
21
|
const resolver_2 = require("./gridset/resolver");
|
|
22
|
+
const idGenerator_1 = require("../optional/analytics/utils/idGenerator");
|
|
22
23
|
class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
23
24
|
constructor(options) {
|
|
24
25
|
super(options);
|
|
@@ -922,6 +923,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
922
923
|
message: String(message),
|
|
923
924
|
targetPageId: navigationTarget ? String(navigationTarget) : undefined,
|
|
924
925
|
semanticAction: semanticAction,
|
|
926
|
+
semantic_id: cell.semantic_id || cell.SemanticId || undefined, // Extract semantic_id if present
|
|
925
927
|
image: declaredImageName,
|
|
926
928
|
resolvedImageEntry: resolvedImageEntry,
|
|
927
929
|
x: cellX,
|
|
@@ -963,6 +965,29 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
963
965
|
});
|
|
964
966
|
// Set the page's grid layout
|
|
965
967
|
page.grid = gridLayout;
|
|
968
|
+
// Generate clone_id for each button in the grid
|
|
969
|
+
const semanticIds = [];
|
|
970
|
+
const cloneIds = [];
|
|
971
|
+
gridLayout.forEach((row, rowIndex) => {
|
|
972
|
+
row.forEach((btn, colIndex) => {
|
|
973
|
+
if (btn) {
|
|
974
|
+
// Generate clone_id based on position and label
|
|
975
|
+
btn.clone_id = (0, idGenerator_1.generateCloneId)(maxRows, maxCols, rowIndex, colIndex, btn.label);
|
|
976
|
+
cloneIds.push(btn.clone_id);
|
|
977
|
+
// Track semantic_id if present
|
|
978
|
+
if (btn.semantic_id) {
|
|
979
|
+
semanticIds.push(btn.semantic_id);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
});
|
|
983
|
+
});
|
|
984
|
+
// Track IDs on the page
|
|
985
|
+
if (semanticIds.length > 0) {
|
|
986
|
+
page.semantic_ids = semanticIds;
|
|
987
|
+
}
|
|
988
|
+
if (cloneIds.length > 0) {
|
|
989
|
+
page.clone_ids = cloneIds;
|
|
990
|
+
}
|
|
966
991
|
}
|
|
967
992
|
tree.addPage(page);
|
|
968
993
|
}
|
|
@@ -7,6 +7,7 @@ export { OpmlProcessor } from './opmlProcessor';
|
|
|
7
7
|
export { SnapProcessor } from './snapProcessor';
|
|
8
8
|
export { TouchChatProcessor } from './touchchatProcessor';
|
|
9
9
|
export { AstericsGridProcessor } from './astericsGridProcessor';
|
|
10
|
+
export { ObfsetProcessor } from './obfsetProcessor';
|
|
10
11
|
export { getPageTokenImageMap, getAllowedImageEntries, openImage, generateGrid3Guid, createSettingsXml, createFileMapXml, getCommonDocumentsPath, findGrid3UserPaths, findGrid3HistoryDatabases, findGrid3Users, findGrid3Vocabularies, findGrid3UserHistory, isGrid3Installed, readGrid3History, readGrid3HistoryForUser, readAllGrid3History, type Grid3UserPath, type Grid3VocabularyPath, type Grid3HistoryEntry, } from './gridset/helpers';
|
|
11
12
|
export { getPageTokenImageMap as getGridsetPageTokenImageMap, getAllowedImageEntries as getGridsetAllowedImageEntries, openImage as openGridsetImage, generateGrid3Guid as generateGridsetGuid, createSettingsXml as createGridsetSettingsXml, createFileMapXml as createGridsetFileMapXml, getCommonDocumentsPath as getGridsetCommonDocumentsPath, findGrid3UserPaths as findGridsetUserPaths, findGrid3HistoryDatabases as findGridsetHistoryDatabases, findGrid3Users as findGridsetUsers, findGrid3Vocabularies as findGridsetVocabularies, findGrid3UserHistory as findGridsetUserHistory, isGrid3Installed as isGridsetInstalled, readGrid3History as readGridsetHistory, readGrid3HistoryForUser as readGridsetHistoryForUser, readAllGrid3History as readAllGridsetHistory, } from './gridset/helpers';
|
|
12
13
|
export { resolveGrid3CellImage } from './gridset/resolver';
|
package/dist/processors/index.js
CHANGED
|
@@ -14,9 +14,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.
|
|
18
|
-
exports.
|
|
19
|
-
exports.openTouchChatImage = exports.getTouchChatAllowedImageEntries = exports.getTouchChatPageTokenImageMap = exports.readSnapUsageForUser = exports.readSnapUsage = exports.isSnapInstalled = exports.findSnapUserHistory = exports.findSnapUserVocabularies = exports.findSnapUsers = exports.findSnapPackagePath = exports.findSnapPackages = exports.openSnapImage = exports.getSnapAllowedImageEntries = exports.getSnapPageTokenImageMap = exports.getSymbolSearchStats = exports.countLibrarySymbols = exports.getSearchSuggestions = exports.getAllSearchTerms = exports.getSymbolDisplayName = exports.getSymbolFilename = exports.searchSymbolsWithReferences = exports.searchSymbols = exports.loadSearchIndexes = exports.parsePixFile = exports.createSymbolManifest = exports.exportSymbolReferencesToCsv = exports.suggestExtractionStrategy = exports.analyzeSymbolExtraction = exports.convertToAstericsImage = exports.extractSymbolLibraryImage = exports.extractButtonImage = exports.getSymbolSearchDir = exports.getSymbolsDir = exports.parseImageSymbolReference = exports.isSymbolLibraryReference = void 0;
|
|
17
|
+
exports.getNamedColor = exports.resolveGridsetPasswordFromEnv = exports.resolveGridsetPassword = exports.wordlistToXml = exports.updateWordlist = exports.extractWordlists = exports.createWordlist = exports.resolveGrid3CellImage = exports.readAllGridsetHistory = exports.readGridsetHistoryForUser = exports.readGridsetHistory = exports.isGridsetInstalled = exports.findGridsetUserHistory = exports.findGridsetVocabularies = exports.findGridsetUsers = exports.findGridsetHistoryDatabases = exports.findGridsetUserPaths = exports.getGridsetCommonDocumentsPath = exports.createGridsetFileMapXml = exports.createGridsetSettingsXml = exports.generateGridsetGuid = exports.openGridsetImage = exports.getGridsetAllowedImageEntries = exports.getGridsetPageTokenImageMap = exports.readAllGrid3History = exports.readGrid3HistoryForUser = exports.readGrid3History = exports.isGrid3Installed = exports.findGrid3UserHistory = exports.findGrid3Vocabularies = exports.findGrid3Users = exports.findGrid3HistoryDatabases = exports.findGrid3UserPaths = exports.getCommonDocumentsPath = exports.createFileMapXml = exports.createSettingsXml = exports.generateGrid3Guid = exports.openImage = exports.getAllowedImageEntries = exports.getPageTokenImageMap = exports.ObfsetProcessor = exports.AstericsGridProcessor = exports.TouchChatProcessor = exports.SnapProcessor = exports.OpmlProcessor = exports.ObfProcessor = exports.GridsetProcessor = exports.ExcelProcessor = exports.DotProcessor = exports.ApplePanelsProcessor = void 0;
|
|
18
|
+
exports.symbolReferenceToFilename = exports.getSymbolSearchIndexesDir = exports.getSymbolLibrariesDir = exports.getDefaultGrid3Path = exports.getSymbolLibraryDisplayName = exports.isKnownSymbolLibrary = exports.getSymbolPath = exports.getSymbolLibraryName = exports.createSymbolReference = exports.analyzeSymbolUsage = exports.extractSymbolReferences = exports.getSymbolLibraryInfo = exports.getAvailableSymbolLibraries = exports.resolveSymbolReference = exports.isSymbolReference = exports.parseSymbolReference = exports.Grid3CommandCategory = exports.GRID3_COMMANDS = exports.extractCommandParameters = exports.getAllPluginIds = exports.getAllCommandIds = exports.getCommandsByCategory = exports.getCommandsByPlugin = exports.getCommandDefinition = exports.detectCommand = exports.isRegularCell = exports.isAutoContentCell = exports.isLiveCell = exports.isWorkspaceCell = exports.getCellTypeDisplayName = exports.AUTOCONTENT_TYPES = exports.LIVECELL_TYPES = exports.WORKSPACE_TYPES = exports.Grid3CellType = exports.detectPluginCellType = exports.ensureAlphaChannelFromStyles = exports.SHAPE_NAMES = exports.CellBackgroundShape = exports.createCategoryStyle = exports.createDefaultStylesXml = exports.CATEGORY_STYLES = exports.DEFAULT_GRID3_STYLES = exports.ensureAlphaChannel = exports.normalizeColor = exports.darkenColor = exports.toHexColor = exports.clampAlpha = exports.clampColorChannel = exports.channelToHex = exports.rgbaToHex = void 0;
|
|
19
|
+
exports.openTouchChatImage = exports.getTouchChatAllowedImageEntries = exports.getTouchChatPageTokenImageMap = exports.readSnapUsageForUser = exports.readSnapUsage = exports.isSnapInstalled = exports.findSnapUserHistory = exports.findSnapUserVocabularies = exports.findSnapUsers = exports.findSnapPackagePath = exports.findSnapPackages = exports.openSnapImage = exports.getSnapAllowedImageEntries = exports.getSnapPageTokenImageMap = exports.getSymbolSearchStats = exports.countLibrarySymbols = exports.getSearchSuggestions = exports.getAllSearchTerms = exports.getSymbolDisplayName = exports.getSymbolFilename = exports.searchSymbolsWithReferences = exports.searchSymbols = exports.loadSearchIndexes = exports.parsePixFile = exports.createSymbolManifest = exports.exportSymbolReferencesToCsv = exports.suggestExtractionStrategy = exports.analyzeSymbolExtraction = exports.convertToAstericsImage = exports.extractSymbolLibraryImage = exports.extractButtonImage = exports.getSymbolSearchDir = exports.getSymbolsDir = exports.parseImageSymbolReference = exports.isSymbolLibraryReference = exports.SYMBOL_LIBRARIES = void 0;
|
|
20
20
|
var applePanelsProcessor_1 = require("./applePanelsProcessor");
|
|
21
21
|
Object.defineProperty(exports, "ApplePanelsProcessor", { enumerable: true, get: function () { return applePanelsProcessor_1.ApplePanelsProcessor; } });
|
|
22
22
|
var dotProcessor_1 = require("./dotProcessor");
|
|
@@ -35,6 +35,8 @@ var touchchatProcessor_1 = require("./touchchatProcessor");
|
|
|
35
35
|
Object.defineProperty(exports, "TouchChatProcessor", { enumerable: true, get: function () { return touchchatProcessor_1.TouchChatProcessor; } });
|
|
36
36
|
var astericsGridProcessor_1 = require("./astericsGridProcessor");
|
|
37
37
|
Object.defineProperty(exports, "AstericsGridProcessor", { enumerable: true, get: function () { return astericsGridProcessor_1.AstericsGridProcessor; } });
|
|
38
|
+
var obfsetProcessor_1 = require("./obfsetProcessor");
|
|
39
|
+
Object.defineProperty(exports, "ObfsetProcessor", { enumerable: true, get: function () { return obfsetProcessor_1.ObfsetProcessor; } });
|
|
38
40
|
// Gridset (Grid 3) helpers
|
|
39
41
|
var helpers_1 = require("./gridset/helpers");
|
|
40
42
|
Object.defineProperty(exports, "getPageTokenImageMap", { enumerable: true, get: function () { return helpers_1.getPageTokenImageMap; } });
|
|
@@ -6,10 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.ObfProcessor = void 0;
|
|
7
7
|
const baseProcessor_1 = require("../core/baseProcessor");
|
|
8
8
|
const treeStructure_1 = require("../core/treeStructure");
|
|
9
|
-
|
|
9
|
+
const idGenerator_1 = require("../optional/analytics/utils/idGenerator");
|
|
10
10
|
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
11
11
|
const fs_1 = __importDefault(require("fs"));
|
|
12
|
-
// Removed unused import: path
|
|
13
12
|
const obfValidator_1 = require("../validation/obfValidator");
|
|
14
13
|
const OBF_FORMAT_VERSION = 'open-board-0.1';
|
|
15
14
|
class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
@@ -48,6 +47,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
48
47
|
},
|
|
49
48
|
semanticAction,
|
|
50
49
|
targetPageId: btn.load_board?.path,
|
|
50
|
+
semantic_id: btn.semantic_id, // Extract semantic_id if present
|
|
51
51
|
});
|
|
52
52
|
});
|
|
53
53
|
const buttonMap = new Map(buttons.map((btn) => [btn.id, btn]));
|
|
@@ -105,6 +105,29 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
page.grid = grid;
|
|
108
|
+
// Generate clone_id for buttons in the grid
|
|
109
|
+
const semanticIds = [];
|
|
110
|
+
const cloneIds = [];
|
|
111
|
+
grid.forEach((row, rowIndex) => {
|
|
112
|
+
row.forEach((btn, colIndex) => {
|
|
113
|
+
if (btn) {
|
|
114
|
+
// Generate clone_id based on position and label
|
|
115
|
+
btn.clone_id = (0, idGenerator_1.generateCloneId)(rows, cols, rowIndex, colIndex, btn.label);
|
|
116
|
+
cloneIds.push(btn.clone_id);
|
|
117
|
+
// Track semantic_id if present
|
|
118
|
+
if (btn.semantic_id) {
|
|
119
|
+
semanticIds.push(btn.semantic_id);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
// Track IDs on the page
|
|
125
|
+
if (semanticIds.length > 0) {
|
|
126
|
+
page.semantic_ids = semanticIds;
|
|
127
|
+
}
|
|
128
|
+
if (cloneIds.length > 0) {
|
|
129
|
+
page.clone_ids = cloneIds;
|
|
130
|
+
}
|
|
108
131
|
}
|
|
109
132
|
}
|
|
110
133
|
return page;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OBF Set Processor - Handles JSON-formatted .obfset files
|
|
3
|
+
* These are pre-extracted board sets in JSON array format
|
|
4
|
+
*/
|
|
5
|
+
import { AACTree } from '../core/treeStructure';
|
|
6
|
+
import { BaseProcessor, ProcessorOptions } from '../core/baseProcessor';
|
|
7
|
+
export declare class ObfsetProcessor extends BaseProcessor {
|
|
8
|
+
constructor(options?: ProcessorOptions);
|
|
9
|
+
/**
|
|
10
|
+
* Extract all text content
|
|
11
|
+
*/
|
|
12
|
+
extractTexts(filePathOrBuffer: string | Buffer): string[];
|
|
13
|
+
/**
|
|
14
|
+
* Load an .obfset file (JSON array of boards)
|
|
15
|
+
*/
|
|
16
|
+
loadIntoTree(filePathOrBuffer: string | Buffer): AACTree;
|
|
17
|
+
/**
|
|
18
|
+
* Process texts (not supported for .obfset currently)
|
|
19
|
+
*/
|
|
20
|
+
processTexts(_filePathOrBuffer: string | Buffer, _translations: Map<string, string>, _outputPath: string): Buffer;
|
|
21
|
+
/**
|
|
22
|
+
* Save tree structure back to file
|
|
23
|
+
*/
|
|
24
|
+
saveFromTree(_tree: AACTree, _outputPath: string): void;
|
|
25
|
+
supportsExtension(extension: string): boolean;
|
|
26
|
+
}
|