@willwade/aac-processors 0.0.29 → 0.1.0
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 +52 -852
- package/dist/browser/core/baseProcessor.js +241 -0
- package/dist/browser/core/stringCasing.js +179 -0
- package/dist/browser/core/treeStructure.js +255 -0
- package/dist/browser/index.browser.js +73 -0
- package/dist/browser/processors/applePanelsProcessor.js +582 -0
- package/dist/browser/processors/astericsGridProcessor.js +1509 -0
- package/dist/browser/processors/dotProcessor.js +221 -0
- package/dist/browser/processors/gridset/commands.js +962 -0
- package/dist/browser/processors/gridset/crypto.js +53 -0
- package/dist/browser/processors/gridset/password.js +43 -0
- package/dist/browser/processors/gridset/pluginTypes.js +277 -0
- package/dist/browser/processors/gridset/resolver.js +137 -0
- package/dist/browser/processors/gridset/symbolAlignment.js +276 -0
- package/dist/browser/processors/gridset/symbols.js +421 -0
- package/dist/browser/processors/gridsetProcessor.js +2002 -0
- package/dist/browser/processors/obfProcessor.js +705 -0
- package/dist/browser/processors/opmlProcessor.js +274 -0
- package/dist/browser/types/aac.js +38 -0
- package/dist/browser/utilities/analytics/utils/idGenerator.js +89 -0
- package/dist/browser/utilities/translation/translationProcessor.js +200 -0
- package/dist/browser/utils/io.js +95 -0
- package/dist/browser/validation/baseValidator.js +156 -0
- package/dist/browser/validation/gridsetValidator.js +355 -0
- package/dist/browser/validation/obfValidator.js +500 -0
- package/dist/browser/validation/validationTypes.js +46 -0
- package/dist/cli/index.js +5 -5
- package/dist/core/analyze.d.ts +2 -2
- package/dist/core/analyze.js +2 -2
- package/dist/core/baseProcessor.d.ts +5 -4
- package/dist/core/baseProcessor.js +22 -27
- package/dist/core/treeStructure.d.ts +5 -5
- package/dist/core/treeStructure.js +1 -4
- package/dist/index.browser.d.ts +37 -0
- package/dist/index.browser.js +99 -0
- package/dist/index.d.ts +1 -48
- package/dist/index.js +1 -136
- package/dist/index.node.d.ts +48 -0
- package/dist/index.node.js +152 -0
- package/dist/processors/applePanelsProcessor.d.ts +5 -4
- package/dist/processors/applePanelsProcessor.js +58 -62
- package/dist/processors/astericsGridProcessor.d.ts +7 -6
- package/dist/processors/astericsGridProcessor.js +31 -42
- package/dist/processors/dotProcessor.d.ts +5 -4
- package/dist/processors/dotProcessor.js +25 -33
- package/dist/processors/excelProcessor.d.ts +4 -3
- package/dist/processors/excelProcessor.js +6 -3
- package/dist/processors/gridset/crypto.d.ts +18 -0
- package/dist/processors/gridset/crypto.js +57 -0
- package/dist/processors/gridset/helpers.d.ts +1 -1
- package/dist/processors/gridset/helpers.js +18 -8
- package/dist/processors/gridset/password.d.ts +20 -3
- package/dist/processors/gridset/password.js +17 -3
- package/dist/processors/gridset/wordlistHelpers.d.ts +3 -3
- package/dist/processors/gridset/wordlistHelpers.js +21 -20
- package/dist/processors/gridsetProcessor.d.ts +7 -12
- package/dist/processors/gridsetProcessor.js +118 -77
- package/dist/processors/obfProcessor.d.ts +9 -7
- package/dist/processors/obfProcessor.js +131 -56
- package/dist/processors/obfsetProcessor.d.ts +5 -4
- package/dist/processors/obfsetProcessor.js +10 -16
- package/dist/processors/opmlProcessor.d.ts +5 -4
- package/dist/processors/opmlProcessor.js +27 -34
- package/dist/processors/snapProcessor.d.ts +8 -7
- package/dist/processors/snapProcessor.js +15 -12
- package/dist/processors/touchchatProcessor.d.ts +8 -7
- package/dist/processors/touchchatProcessor.js +22 -17
- package/dist/types/aac.d.ts +0 -2
- package/dist/types/aac.js +2 -0
- package/dist/utils/io.d.ts +12 -0
- package/dist/utils/io.js +107 -0
- package/dist/validation/gridsetValidator.js +7 -7
- package/dist/validation/snapValidator.js +28 -35
- package/docs/BROWSER_USAGE.md +618 -0
- package/examples/README.md +77 -0
- package/examples/browser-test-server.js +81 -0
- package/examples/browser-test.html +331 -0
- package/examples/vitedemo/QUICKSTART.md +74 -0
- package/examples/vitedemo/README.md +157 -0
- package/examples/vitedemo/index.html +376 -0
- package/examples/vitedemo/package-lock.json +1221 -0
- package/examples/vitedemo/package.json +18 -0
- package/examples/vitedemo/src/main.ts +519 -0
- package/examples/vitedemo/test-files/example.dot +14 -0
- package/examples/vitedemo/test-files/example.grd +1 -0
- package/examples/vitedemo/test-files/example.gridset +0 -0
- package/examples/vitedemo/test-files/example.obz +0 -0
- package/examples/vitedemo/test-files/example.opml +18 -0
- package/examples/vitedemo/test-files/simple.obf +53 -0
- package/examples/vitedemo/tsconfig.json +24 -0
- package/examples/vitedemo/vite.config.ts +34 -0
- package/package.json +20 -4
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crypto utilities for Gridsetx (encrypted Grid3 files)
|
|
3
|
+
* This module is only needed for .gridsetx files and uses Node-only crypto/zlib
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Decrypt and inflate a Grid3 encrypted payload (DesktopContentEncrypter).
|
|
7
|
+
* Uses AES-256-CBC with key/IV derived from the password padded with spaces
|
|
8
|
+
* and then Deflate decompression.
|
|
9
|
+
*
|
|
10
|
+
* @param buffer - Encrypted buffer
|
|
11
|
+
* @param password - Password (defaults to 'Chocolate')
|
|
12
|
+
* @returns Decrypted and inflated buffer
|
|
13
|
+
*/
|
|
14
|
+
export function decryptGridsetEntry(buffer, password) {
|
|
15
|
+
// Dynamic require to avoid breaking in browser environments
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-return
|
|
17
|
+
const crypto = require('crypto');
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-return
|
|
19
|
+
const zlib = require('zlib');
|
|
20
|
+
const pwd = (password || 'Chocolate').padEnd(32, ' ');
|
|
21
|
+
const key = Buffer.from(pwd.slice(0, 32), 'utf8');
|
|
22
|
+
const iv = Buffer.from(pwd.slice(0, 16), 'utf8');
|
|
23
|
+
try {
|
|
24
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
|
|
25
|
+
const decrypted = Buffer.concat([decipher.update(buffer), decipher.final()]);
|
|
26
|
+
try {
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
28
|
+
return zlib.inflateSync(decrypted);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// If data isn't deflated, return raw decrypted bytes
|
|
32
|
+
return decrypted;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return buffer;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Check if crypto operations are available in the current environment
|
|
41
|
+
*/
|
|
42
|
+
export function isCryptoAvailable() {
|
|
43
|
+
try {
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
45
|
+
require('crypto');
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
47
|
+
require('zlib');
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the password to use for Grid3 archives.
|
|
4
|
+
* Preference order:
|
|
5
|
+
* 1. Explicit processor option
|
|
6
|
+
* 2. GRIDSET_PASSWORD env var
|
|
7
|
+
*/
|
|
8
|
+
export function resolveGridsetPassword(options, source) {
|
|
9
|
+
if (options?.gridsetPassword)
|
|
10
|
+
return options.gridsetPassword;
|
|
11
|
+
if (process.env.GRIDSET_PASSWORD)
|
|
12
|
+
return process.env.GRIDSET_PASSWORD;
|
|
13
|
+
if (typeof source === 'string') {
|
|
14
|
+
const ext = path.extname(source).toLowerCase();
|
|
15
|
+
if (ext === '.gridsetx')
|
|
16
|
+
return process.env.GRIDSET_PASSWORD;
|
|
17
|
+
}
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
export function resolveGridsetPasswordFromEnv() {
|
|
21
|
+
return process.env.GRIDSET_PASSWORD;
|
|
22
|
+
}
|
|
23
|
+
export function getZipEntriesWithPassword(zip, password) {
|
|
24
|
+
const entries = [];
|
|
25
|
+
// Note: JSZip doesn't support zip-level password protection like AdmZip
|
|
26
|
+
// Password protection for .gridsetx files is handled at the encrypted archive level
|
|
27
|
+
// in crypto.ts before the zip is loaded
|
|
28
|
+
if (password) {
|
|
29
|
+
console.warn('JSZip does not support zip-level password protection. For .gridsetx encrypted files, password is handled at the archive level.');
|
|
30
|
+
}
|
|
31
|
+
zip.forEach((relativePath, file) => {
|
|
32
|
+
entries.push({
|
|
33
|
+
name: relativePath,
|
|
34
|
+
entryName: relativePath,
|
|
35
|
+
dir: file.dir || false,
|
|
36
|
+
getData: async () => {
|
|
37
|
+
// Use 'uint8array' which is supported everywhere
|
|
38
|
+
return await file.async('uint8array');
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
return entries;
|
|
43
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grid 3 Plugin Cell Type Detection
|
|
3
|
+
*
|
|
4
|
+
* Grid 3 uses three special cell types for different plugin functionalities:
|
|
5
|
+
* - Workspace: Full editing workspaces (Email, Chat, WordProcessor, etc.)
|
|
6
|
+
* - LiveCell: Dynamic content displays (Clock, Volume indicators, etc.)
|
|
7
|
+
* - AutoContent: Dynamic word/content suggestions
|
|
8
|
+
*
|
|
9
|
+
* This module provides detection and metadata extraction for these cell types.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Cell types in Grid 3
|
|
13
|
+
*/
|
|
14
|
+
export var Grid3CellType;
|
|
15
|
+
(function (Grid3CellType) {
|
|
16
|
+
Grid3CellType["Regular"] = "regular";
|
|
17
|
+
Grid3CellType["Workspace"] = "workspace";
|
|
18
|
+
Grid3CellType["LiveCell"] = "livecell";
|
|
19
|
+
Grid3CellType["AutoContent"] = "autocontent";
|
|
20
|
+
})(Grid3CellType || (Grid3CellType = {}));
|
|
21
|
+
/**
|
|
22
|
+
* Known workspace types in Grid 3
|
|
23
|
+
*/
|
|
24
|
+
export const WORKSPACE_TYPES = {
|
|
25
|
+
CHAT: 'Chat',
|
|
26
|
+
EMAIL: 'Email',
|
|
27
|
+
WORD_PROCESSOR: 'WordProcessor',
|
|
28
|
+
PHONE: 'Phone',
|
|
29
|
+
SMS: 'Sms',
|
|
30
|
+
WEB_BROWSER: 'WebBrowser',
|
|
31
|
+
COMPUTER_CONTROL: 'ComputerControl',
|
|
32
|
+
CALCULATOR: 'Calculator',
|
|
33
|
+
TIMER: 'Timer',
|
|
34
|
+
MUSIC_VIDEO: 'MusicVideo',
|
|
35
|
+
PHOTOS: 'Photos',
|
|
36
|
+
CONTACTS: 'Contacts',
|
|
37
|
+
INTERACTIVE_LEARNING: 'InteractiveLearning',
|
|
38
|
+
MESSAGE_BANKING: 'MessageBanking',
|
|
39
|
+
ENVIRONMENT_CONTROL: 'EnvironmentControl',
|
|
40
|
+
SETTINGS: 'Settings',
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Known live cell types in Grid 3
|
|
44
|
+
*/
|
|
45
|
+
export const LIVECELL_TYPES = {
|
|
46
|
+
DIGITAL_CLOCK: 'DigitalClock',
|
|
47
|
+
ANALOG_CLOCK: 'AnalogClock',
|
|
48
|
+
DATE_DISPLAY: 'DateDisplay',
|
|
49
|
+
PUBLIC_VOLUME: 'PublicVolume',
|
|
50
|
+
PUBLIC_SPEED: 'PublicSpeed',
|
|
51
|
+
PUBLIC_VOICE: 'PublicVoice',
|
|
52
|
+
MESSAGES: 'Messages',
|
|
53
|
+
BATTERY: 'Battery',
|
|
54
|
+
WIFI_STRENGTH: 'WifiStrength',
|
|
55
|
+
BLUETOOTH_STATUS: 'BluetoothStatus',
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Known auto content types in Grid 3
|
|
59
|
+
*/
|
|
60
|
+
export const AUTOCONTENT_TYPES = {
|
|
61
|
+
CHANGE_PUBLIC_VOICE: 'ChangePublicVoice',
|
|
62
|
+
CHANGE_PUBLIC_SPEED: 'ChangePublicSpeed',
|
|
63
|
+
EMAIL_CONTACTS: 'EmailContacts',
|
|
64
|
+
EMAIL_RECIPIENTS: 'EmailRecipients',
|
|
65
|
+
PHONE_CONTACTS: 'PhoneContacts',
|
|
66
|
+
SMS_CONTACTS: 'SmsContacts',
|
|
67
|
+
WEB_FAVORITES: 'WebFavorites',
|
|
68
|
+
WEB_HISTORY: 'WebHistory',
|
|
69
|
+
PREDICTION: 'Prediction',
|
|
70
|
+
GRAMMAR: 'Grammar',
|
|
71
|
+
CONTEXTUAL: 'Contextual',
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Human-readable names for cell types
|
|
75
|
+
*/
|
|
76
|
+
export function getCellTypeDisplayName(cellType) {
|
|
77
|
+
switch (cellType) {
|
|
78
|
+
case Grid3CellType.Workspace:
|
|
79
|
+
return 'Workspace';
|
|
80
|
+
case Grid3CellType.LiveCell:
|
|
81
|
+
return 'Live Cell';
|
|
82
|
+
case Grid3CellType.AutoContent:
|
|
83
|
+
return 'Auto Content';
|
|
84
|
+
case Grid3CellType.Regular:
|
|
85
|
+
return 'Regular';
|
|
86
|
+
default:
|
|
87
|
+
return 'Unknown';
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Detect plugin cell type from Grid 3 cell content
|
|
92
|
+
*
|
|
93
|
+
* @param content - Grid 3 cell content object
|
|
94
|
+
* @returns Plugin metadata with detected type
|
|
95
|
+
*/
|
|
96
|
+
export function detectPluginCellType(content) {
|
|
97
|
+
if (!content) {
|
|
98
|
+
return { cellType: Grid3CellType.Regular };
|
|
99
|
+
}
|
|
100
|
+
const contentType = content.ContentType || content.contenttype;
|
|
101
|
+
const contentSubType = content.ContentSubType || content.contentsubtype;
|
|
102
|
+
// Workspace cells - full editing workspaces
|
|
103
|
+
if (contentType === 'Workspace' || content.Style?.BasedOnStyle === 'Workspace') {
|
|
104
|
+
return {
|
|
105
|
+
cellType: Grid3CellType.Workspace,
|
|
106
|
+
subType: contentSubType || undefined,
|
|
107
|
+
pluginId: inferWorkspacePlugin(String(contentSubType || '')),
|
|
108
|
+
displayName: contentSubType ? `${contentSubType} Workspace` : 'Workspace',
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// LiveCell detection - dynamic content displays
|
|
112
|
+
if (contentType === 'LiveCell' || content.Style?.BasedOnStyle === 'LiveCell') {
|
|
113
|
+
return {
|
|
114
|
+
cellType: Grid3CellType.LiveCell,
|
|
115
|
+
liveCellType: contentSubType || undefined,
|
|
116
|
+
pluginId: inferLiveCellPlugin(String(contentSubType || '')),
|
|
117
|
+
displayName: contentSubType || 'Live Cell',
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
// AutoContent detection - dynamic word/content suggestions
|
|
121
|
+
if (contentType === 'AutoContent' || content.Style?.BasedOnStyle === 'AutoContent') {
|
|
122
|
+
const autoContentType = extractAutoContentType(content) || contentSubType;
|
|
123
|
+
return {
|
|
124
|
+
cellType: Grid3CellType.AutoContent,
|
|
125
|
+
autoContentType: autoContentType ? String(autoContentType) : undefined,
|
|
126
|
+
pluginId: inferAutoContentPlugin(autoContentType ? String(autoContentType) : undefined),
|
|
127
|
+
displayName: autoContentType ? String(autoContentType) : 'Auto Content',
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
// Regular cell
|
|
131
|
+
return {
|
|
132
|
+
cellType: Grid3CellType.Regular,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Extract auto content type from AutoContent.Activate command
|
|
137
|
+
*/
|
|
138
|
+
function extractAutoContentType(content) {
|
|
139
|
+
const commands = content.Commands?.Command || content.commands?.command;
|
|
140
|
+
if (!commands)
|
|
141
|
+
return undefined;
|
|
142
|
+
const commandArr = Array.isArray(commands) ? commands : [commands];
|
|
143
|
+
for (const command of commandArr) {
|
|
144
|
+
const commandId = command['@_ID'] || command.ID || command.id;
|
|
145
|
+
if (commandId === 'AutoContent.Activate') {
|
|
146
|
+
const parameters = command.Parameter || command.parameter;
|
|
147
|
+
const paramArr = Array.isArray(parameters) ? parameters : parameters ? [parameters] : [];
|
|
148
|
+
for (const param of paramArr) {
|
|
149
|
+
const key = param['@_Key'] || param.Key || param.key;
|
|
150
|
+
if (key === 'autocontenttype') {
|
|
151
|
+
return String(param['#text'] || param.text || param.value || '');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Infer plugin ID from workspace subtype
|
|
160
|
+
*/
|
|
161
|
+
function inferWorkspacePlugin(subType) {
|
|
162
|
+
if (!subType)
|
|
163
|
+
return undefined;
|
|
164
|
+
const normalized = subType.toLowerCase();
|
|
165
|
+
if (normalized.includes('chat'))
|
|
166
|
+
return 'Grid3.Chat';
|
|
167
|
+
if (normalized.includes('email') || normalized.includes('mail'))
|
|
168
|
+
return 'Grid3.Email';
|
|
169
|
+
if (normalized.includes('word') || normalized.includes('doc'))
|
|
170
|
+
return 'Grid3.WordProcessor';
|
|
171
|
+
if (normalized.includes('phone'))
|
|
172
|
+
return 'Grid3.Phone';
|
|
173
|
+
if (normalized.includes('sms') || normalized.includes('text'))
|
|
174
|
+
return 'Grid3.Sms';
|
|
175
|
+
if (normalized.includes('browser') || normalized.includes('web'))
|
|
176
|
+
return 'Grid3.WebBrowser';
|
|
177
|
+
if (normalized.includes('computer'))
|
|
178
|
+
return 'Grid3.ComputerControl';
|
|
179
|
+
if (normalized.includes('calc'))
|
|
180
|
+
return 'Grid3.Calculator';
|
|
181
|
+
if (normalized.includes('timer'))
|
|
182
|
+
return 'Grid3.Timer';
|
|
183
|
+
if (normalized.includes('music') || normalized.includes('video'))
|
|
184
|
+
return 'Grid3.MusicVideo';
|
|
185
|
+
if (normalized.includes('photo') || normalized.includes('image'))
|
|
186
|
+
return 'Grid3.Photos';
|
|
187
|
+
if (normalized.includes('contact'))
|
|
188
|
+
return 'Grid3.Contacts';
|
|
189
|
+
if (normalized.includes('learning'))
|
|
190
|
+
return 'Grid3.InteractiveLearning';
|
|
191
|
+
if (normalized.includes('message') && normalized.includes('banking'))
|
|
192
|
+
return 'Grid3.MessageBanking';
|
|
193
|
+
if (normalized.includes('control'))
|
|
194
|
+
return 'Grid3.EnvironmentControl';
|
|
195
|
+
if (normalized.includes('settings'))
|
|
196
|
+
return 'Grid3.Settings';
|
|
197
|
+
return `Grid3.${subType}`;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Infer plugin ID from live cell type
|
|
201
|
+
*/
|
|
202
|
+
function inferLiveCellPlugin(liveCellType) {
|
|
203
|
+
if (!liveCellType)
|
|
204
|
+
return undefined;
|
|
205
|
+
const normalized = liveCellType.toLowerCase();
|
|
206
|
+
if (normalized.includes('clock'))
|
|
207
|
+
return 'Grid3.Clock';
|
|
208
|
+
if (normalized.includes('date'))
|
|
209
|
+
return 'Grid3.Clock';
|
|
210
|
+
if (normalized.includes('volume'))
|
|
211
|
+
return 'Grid3.Volume';
|
|
212
|
+
if (normalized.includes('speed'))
|
|
213
|
+
return 'Grid3.Speed';
|
|
214
|
+
if (normalized.includes('voice'))
|
|
215
|
+
return 'Grid3.Speech';
|
|
216
|
+
if (normalized.includes('message'))
|
|
217
|
+
return 'Grid3.Chat';
|
|
218
|
+
if (normalized.includes('battery'))
|
|
219
|
+
return 'Grid3.Battery';
|
|
220
|
+
if (normalized.includes('wifi'))
|
|
221
|
+
return 'Grid3.Wifi';
|
|
222
|
+
if (normalized.includes('bluetooth'))
|
|
223
|
+
return 'Grid3.Bluetooth';
|
|
224
|
+
return `Grid3.${liveCellType}`;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Infer plugin ID from auto content type
|
|
228
|
+
*/
|
|
229
|
+
function inferAutoContentPlugin(autoContentType) {
|
|
230
|
+
if (!autoContentType)
|
|
231
|
+
return undefined;
|
|
232
|
+
const normalized = autoContentType.toLowerCase();
|
|
233
|
+
if (normalized.includes('voice') || normalized.includes('speed'))
|
|
234
|
+
return 'Grid3.Speech';
|
|
235
|
+
if (normalized.includes('email') || normalized.includes('mail'))
|
|
236
|
+
return 'Grid3.Email';
|
|
237
|
+
if (normalized.includes('phone'))
|
|
238
|
+
return 'Grid3.Phone';
|
|
239
|
+
if (normalized.includes('sms') || normalized.includes('text'))
|
|
240
|
+
return 'Grid3.Sms';
|
|
241
|
+
if (normalized.includes('web') ||
|
|
242
|
+
normalized.includes('favorite') ||
|
|
243
|
+
normalized.includes('history')) {
|
|
244
|
+
return 'Grid3.WebBrowser';
|
|
245
|
+
}
|
|
246
|
+
if (normalized.includes('prediction'))
|
|
247
|
+
return 'Grid3.Prediction';
|
|
248
|
+
if (normalized.includes('grammar'))
|
|
249
|
+
return 'Grid3.Grammar';
|
|
250
|
+
if (normalized.includes('context'))
|
|
251
|
+
return 'Grid3.AutoContent';
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Check if a cell is a workspace cell
|
|
256
|
+
*/
|
|
257
|
+
export function isWorkspaceCell(metadata) {
|
|
258
|
+
return metadata.cellType === Grid3CellType.Workspace;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Check if a cell is a live cell
|
|
262
|
+
*/
|
|
263
|
+
export function isLiveCell(metadata) {
|
|
264
|
+
return metadata.cellType === Grid3CellType.LiveCell;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Check if a cell is an auto content cell
|
|
268
|
+
*/
|
|
269
|
+
export function isAutoContentCell(metadata) {
|
|
270
|
+
return metadata.cellType === Grid3CellType.AutoContent;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Check if a cell is a regular (non-plugin) cell
|
|
274
|
+
*/
|
|
275
|
+
export function isRegularCell(metadata) {
|
|
276
|
+
return metadata.cellType === Grid3CellType.Regular;
|
|
277
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { isSymbolReference, parseSymbolReference } from './symbols';
|
|
2
|
+
function normalizeZipPathLocal(p) {
|
|
3
|
+
const unified = p.replace(/\\/g, '/');
|
|
4
|
+
try {
|
|
5
|
+
return unified.normalize('NFC');
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return unified;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function listZipEntries(zip, zipEntries) {
|
|
12
|
+
try {
|
|
13
|
+
const raw = Array.isArray(zipEntries) && zipEntries.length > 0
|
|
14
|
+
? zipEntries
|
|
15
|
+
: typeof zip?.getEntries === 'function'
|
|
16
|
+
? zip.getEntries()
|
|
17
|
+
: [];
|
|
18
|
+
let entries = [];
|
|
19
|
+
if (Array.isArray(raw))
|
|
20
|
+
entries = raw;
|
|
21
|
+
const arr = entries;
|
|
22
|
+
return arr.map((e) => normalizeZipPathLocal(String(e.entryName)));
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function extFromName(name) {
|
|
29
|
+
if (!name)
|
|
30
|
+
return undefined;
|
|
31
|
+
const m = name.match(/\.([A-Za-z0-9]+)$/);
|
|
32
|
+
return m ? `.${m[1].toLowerCase()}` : undefined;
|
|
33
|
+
}
|
|
34
|
+
function joinBaseDir(baseDir, leaf) {
|
|
35
|
+
const base = normalizeZipPathLocal(baseDir).replace(/\/?$/, '/');
|
|
36
|
+
return normalizeZipPathLocal(base + leaf.replace(/^\//, ''));
|
|
37
|
+
}
|
|
38
|
+
export function resolveGrid3CellImage(zip, args, zipEntries) {
|
|
39
|
+
const { baseDir, dynamicFiles } = args;
|
|
40
|
+
const imageName = args.imageName?.trim();
|
|
41
|
+
const x = args.x;
|
|
42
|
+
const y = args.y;
|
|
43
|
+
const entries = new Set(listZipEntries(zip, zipEntries));
|
|
44
|
+
const has = (p) => entries.has(normalizeZipPathLocal(p));
|
|
45
|
+
// Built-in resource like [grid3x]... (old format, not symbol library)
|
|
46
|
+
// Check this BEFORE general symbol references to avoid misclassification
|
|
47
|
+
if (imageName && imageName.startsWith('[')) {
|
|
48
|
+
// Check if it's a symbol library reference like [widgit]/food/apple.png
|
|
49
|
+
// Symbol library references have a path after the library name
|
|
50
|
+
if (isSymbolReference(imageName)) {
|
|
51
|
+
const parsed = parseSymbolReference(imageName);
|
|
52
|
+
// If it's grid3x, it's a built-in resource, not a symbol library
|
|
53
|
+
if (parsed.library !== 'grid3x') {
|
|
54
|
+
// Symbol library references are NOT stored as files in the gridset
|
|
55
|
+
// They are resolved from the external Grid 3 installation
|
|
56
|
+
// Return null to indicate this is an external symbol reference
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// For grid3x and other built-in resources, use the builtinHandler
|
|
61
|
+
if (args.builtinHandler) {
|
|
62
|
+
const mapped = args.builtinHandler(imageName);
|
|
63
|
+
if (mapped)
|
|
64
|
+
return mapped;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
// Direct declared file
|
|
69
|
+
if (imageName) {
|
|
70
|
+
const p1 = joinBaseDir(baseDir, imageName);
|
|
71
|
+
if (has(p1))
|
|
72
|
+
return p1;
|
|
73
|
+
const p2 = joinBaseDir(baseDir, `Images/${imageName}`);
|
|
74
|
+
if (has(p2))
|
|
75
|
+
return p2;
|
|
76
|
+
}
|
|
77
|
+
// FileMap.xml dynamic files
|
|
78
|
+
if (x != null && y != null && dynamicFiles && dynamicFiles.length > 0) {
|
|
79
|
+
const prefix = joinBaseDir(baseDir, `${x}-${y}-`);
|
|
80
|
+
const matches = dynamicFiles
|
|
81
|
+
.map((f) => normalizeZipPathLocal(f))
|
|
82
|
+
.filter((f) => f.startsWith(prefix));
|
|
83
|
+
if (matches.length > 0) {
|
|
84
|
+
const preferred = matches.find((m) => /text/i.test(m)) || matches[0];
|
|
85
|
+
if (has(preferred))
|
|
86
|
+
return preferred;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Coordinate-based guesses
|
|
90
|
+
if (x != null && y != null) {
|
|
91
|
+
const ext = extFromName(imageName);
|
|
92
|
+
if (ext) {
|
|
93
|
+
const c1 = joinBaseDir(baseDir, `${x}-${y}-0-text-0${ext}`);
|
|
94
|
+
if (has(c1))
|
|
95
|
+
return c1;
|
|
96
|
+
const c2 = joinBaseDir(baseDir, `${x}-${y}${ext}`);
|
|
97
|
+
if (has(c2))
|
|
98
|
+
return c2;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const candidates = [
|
|
102
|
+
`${x}-${y}-0-text-0.jpeg`,
|
|
103
|
+
`${x}-${y}-0-text-0.jpg`,
|
|
104
|
+
`${x}-${y}-0-text-0.png`,
|
|
105
|
+
`${x}-${y}.jpeg`,
|
|
106
|
+
`${x}-${y}.jpg`,
|
|
107
|
+
`${x}-${y}.png`,
|
|
108
|
+
].map((n) => joinBaseDir(baseDir, n));
|
|
109
|
+
for (const c of candidates) {
|
|
110
|
+
if (has(c))
|
|
111
|
+
return c;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Check if an image reference is a symbol library reference
|
|
119
|
+
* @param imageName - Image reference from Grid 3
|
|
120
|
+
* @returns True if it's a symbol library reference
|
|
121
|
+
*/
|
|
122
|
+
export function isSymbolLibraryReference(imageName) {
|
|
123
|
+
if (!imageName)
|
|
124
|
+
return false;
|
|
125
|
+
return isSymbolReference(imageName.trim());
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Parse a symbol library reference from an image name
|
|
129
|
+
* @param imageName - Image reference from Grid 3
|
|
130
|
+
* @returns Parsed reference or null if not a symbol reference
|
|
131
|
+
*/
|
|
132
|
+
export function parseImageSymbolReference(imageName) {
|
|
133
|
+
if (!isSymbolLibraryReference(imageName)) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
return parseSymbolReference(imageName.trim());
|
|
137
|
+
}
|