@willwade/aac-processors 0.1.20 → 0.2.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.
Files changed (113) hide show
  1. package/dist/browser/core/baseProcessor.js +4 -0
  2. package/dist/browser/processors/applePanelsProcessor.js +33 -40
  3. package/dist/browser/processors/astericsGridProcessor.js +31 -26
  4. package/dist/browser/processors/dotProcessor.js +11 -12
  5. package/dist/browser/processors/gridset/colorUtils.js +354 -0
  6. package/dist/browser/processors/gridset/helpers.js +60 -53
  7. package/dist/browser/processors/gridset/index.js +61 -0
  8. package/dist/browser/processors/gridset/styleHelpers.js +205 -0
  9. package/dist/browser/processors/gridset/symbolExtractor.js +331 -0
  10. package/dist/browser/processors/gridset/symbolSearch.js +248 -0
  11. package/dist/browser/processors/gridset/symbols.js +39 -72
  12. package/dist/browser/processors/gridsetProcessor.js +39 -48
  13. package/dist/browser/processors/obfProcessor.js +39 -53
  14. package/dist/browser/processors/opmlProcessor.js +11 -12
  15. package/dist/browser/processors/snap/helpers.js +57 -49
  16. package/dist/browser/processors/snapProcessor.js +48 -51
  17. package/dist/browser/processors/touchchatProcessor.js +60 -52
  18. package/dist/browser/utilities/analytics/history.js +24 -18
  19. package/dist/browser/utilities/analytics/metrics/comparison.js +16 -16
  20. package/dist/browser/utilities/analytics/metrics/vocabulary.js +2 -2
  21. package/dist/browser/utilities/analytics/reference/browser.js +16 -16
  22. package/dist/browser/utilities/analytics/reference/index.js +44 -35
  23. package/dist/browser/utils/io.js +78 -21
  24. package/dist/browser/utils/sqlite.js +8 -10
  25. package/dist/browser/utils/zip.js +43 -43
  26. package/dist/browser/validation/baseValidator.js +5 -0
  27. package/dist/browser/validation/gridsetValidator.js +12 -20
  28. package/dist/browser/validation/obfValidator.js +6 -5
  29. package/dist/browser/validation/snapValidator.js +11 -7
  30. package/dist/browser/validation/touchChatValidator.js +23 -13
  31. package/dist/cli/index.js +22 -24
  32. package/dist/core/baseProcessor.d.ts +7 -7
  33. package/dist/core/baseProcessor.js +4 -0
  34. package/dist/processors/applePanelsProcessor.js +32 -39
  35. package/dist/processors/astericsGridProcessor.d.ts +4 -4
  36. package/dist/processors/astericsGridProcessor.js +30 -25
  37. package/dist/processors/dotProcessor.js +10 -11
  38. package/dist/processors/excelProcessor.d.ts +3 -3
  39. package/dist/processors/excelProcessor.js +14 -20
  40. package/dist/processors/gridset/helpers.d.ts +12 -14
  41. package/dist/processors/gridset/helpers.js +60 -79
  42. package/dist/processors/gridset/imageDebug.d.ts +3 -5
  43. package/dist/processors/gridset/imageDebug.js +4 -4
  44. package/dist/processors/gridset/password.d.ts +1 -1
  45. package/dist/processors/gridset/symbolExtractor.d.ts +5 -3
  46. package/dist/processors/gridset/symbolExtractor.js +15 -38
  47. package/dist/processors/gridset/symbolSearch.d.ts +11 -10
  48. package/dist/processors/gridset/symbolSearch.js +29 -51
  49. package/dist/processors/gridset/symbols.d.ts +8 -6
  50. package/dist/processors/gridset/symbols.js +38 -71
  51. package/dist/processors/gridset/wordlistHelpers.d.ts +4 -6
  52. package/dist/processors/gridset/wordlistHelpers.js +15 -74
  53. package/dist/processors/gridsetProcessor.d.ts +2 -2
  54. package/dist/processors/gridsetProcessor.js +38 -70
  55. package/dist/processors/obfProcessor.d.ts +2 -2
  56. package/dist/processors/obfProcessor.js +38 -75
  57. package/dist/processors/obfsetProcessor.js +2 -3
  58. package/dist/processors/opmlProcessor.js +10 -11
  59. package/dist/processors/snap/helpers.d.ts +9 -9
  60. package/dist/processors/snap/helpers.js +58 -76
  61. package/dist/processors/snapProcessor.d.ts +2 -2
  62. package/dist/processors/snapProcessor.js +47 -50
  63. package/dist/processors/touchchatProcessor.d.ts +2 -2
  64. package/dist/processors/touchchatProcessor.js +59 -51
  65. package/dist/types/aac.d.ts +2 -2
  66. package/dist/utilities/analytics/history.d.ts +8 -8
  67. package/dist/utilities/analytics/history.js +24 -18
  68. package/dist/utilities/analytics/index.d.ts +3 -2
  69. package/dist/utilities/analytics/index.js +9 -10
  70. package/dist/utilities/analytics/metrics/comparison.d.ts +1 -1
  71. package/dist/utilities/analytics/metrics/comparison.js +16 -16
  72. package/dist/utilities/analytics/metrics/vocabulary.d.ts +1 -1
  73. package/dist/utilities/analytics/metrics/vocabulary.js +2 -2
  74. package/dist/utilities/analytics/reference/browser.d.ts +9 -9
  75. package/dist/utilities/analytics/reference/browser.js +16 -16
  76. package/dist/utilities/analytics/reference/index.d.ts +25 -23
  77. package/dist/utilities/analytics/reference/index.js +43 -34
  78. package/dist/utilities/symbolTools.d.ts +8 -6
  79. package/dist/utilities/symbolTools.js +21 -18
  80. package/dist/utils/io.d.ts +24 -6
  81. package/dist/utils/io.js +79 -25
  82. package/dist/utils/sqlite.d.ts +3 -1
  83. package/dist/utils/sqlite.js +7 -9
  84. package/dist/utils/zip.d.ts +7 -3
  85. package/dist/utils/zip.js +43 -43
  86. package/dist/validation/applePanelsValidator.d.ts +2 -1
  87. package/dist/validation/applePanelsValidator.js +10 -11
  88. package/dist/validation/astericsValidator.d.ts +2 -1
  89. package/dist/validation/astericsValidator.js +5 -4
  90. package/dist/validation/baseValidator.d.ts +2 -2
  91. package/dist/validation/baseValidator.js +5 -0
  92. package/dist/validation/dotValidator.d.ts +2 -1
  93. package/dist/validation/dotValidator.js +5 -4
  94. package/dist/validation/excelValidator.d.ts +2 -1
  95. package/dist/validation/excelValidator.js +5 -4
  96. package/dist/validation/gridsetValidator.d.ts +2 -1
  97. package/dist/validation/gridsetValidator.js +11 -22
  98. package/dist/validation/index.d.ts +2 -2
  99. package/dist/validation/index.js +5 -4
  100. package/dist/validation/obfValidator.d.ts +2 -1
  101. package/dist/validation/obfValidator.js +5 -4
  102. package/dist/validation/obfsetValidator.d.ts +2 -1
  103. package/dist/validation/obfsetValidator.js +5 -4
  104. package/dist/validation/opmlValidator.d.ts +2 -1
  105. package/dist/validation/opmlValidator.js +5 -4
  106. package/dist/validation/snapValidator.d.ts +2 -1
  107. package/dist/validation/snapValidator.js +10 -6
  108. package/dist/validation/touchChatValidator.d.ts +4 -6
  109. package/dist/validation/touchChatValidator.js +22 -12
  110. package/dist/validation/validationTypes.d.ts +8 -1
  111. package/package.json +1 -1
  112. package/dist/core/fileProcessor.d.ts +0 -7
  113. package/dist/core/fileProcessor.js +0 -57
@@ -0,0 +1,354 @@
1
+ /**
2
+ * Grid3 Color Utilities
3
+ *
4
+ * Comprehensive color handling for Grid3 format, including:
5
+ * - CSS color name lookup (147 named colors)
6
+ * - Color format conversion (hex, RGB, RGBA, named colors)
7
+ * - Color manipulation (darkening, normalization)
8
+ * - Grid3-specific color formatting (8-digit ARGB hex)
9
+ */
10
+ /**
11
+ * CSS color names to RGB values
12
+ * Supports 147 standard CSS color names
13
+ */
14
+ const CSS_COLORS = {
15
+ aliceblue: [240, 248, 255],
16
+ antiquewhite: [250, 235, 215],
17
+ aqua: [0, 255, 255],
18
+ aquamarine: [127, 255, 212],
19
+ azure: [240, 255, 255],
20
+ beige: [245, 245, 220],
21
+ bisque: [255, 228, 196],
22
+ black: [0, 0, 0],
23
+ blanchedalmond: [255, 235, 205],
24
+ blue: [0, 0, 255],
25
+ blueviolet: [138, 43, 226],
26
+ brown: [165, 42, 42],
27
+ burlywood: [222, 184, 135],
28
+ cadetblue: [95, 158, 160],
29
+ chartreuse: [127, 255, 0],
30
+ chocolate: [210, 105, 30],
31
+ coral: [255, 127, 80],
32
+ cornflowerblue: [100, 149, 237],
33
+ cornsilk: [255, 248, 220],
34
+ crimson: [220, 20, 60],
35
+ cyan: [0, 255, 255],
36
+ darkblue: [0, 0, 139],
37
+ darkcyan: [0, 139, 139],
38
+ darkgoldenrod: [184, 134, 11],
39
+ darkgray: [169, 169, 169],
40
+ darkgreen: [0, 100, 0],
41
+ darkgrey: [169, 169, 169],
42
+ darkkhaki: [189, 183, 107],
43
+ darkmagenta: [139, 0, 139],
44
+ darkolivegreen: [85, 107, 47],
45
+ darkorange: [255, 140, 0],
46
+ darkorchid: [153, 50, 204],
47
+ darkred: [139, 0, 0],
48
+ darksalmon: [233, 150, 122],
49
+ darkseagreen: [143, 188, 143],
50
+ darkslateblue: [72, 61, 139],
51
+ darkslategray: [47, 79, 79],
52
+ darkslategrey: [47, 79, 79],
53
+ darkturquoise: [0, 206, 209],
54
+ darkviolet: [148, 0, 211],
55
+ deeppink: [255, 20, 147],
56
+ deepskyblue: [0, 191, 255],
57
+ dimgray: [105, 105, 105],
58
+ dimgrey: [105, 105, 105],
59
+ dodgerblue: [30, 144, 255],
60
+ firebrick: [178, 34, 34],
61
+ floralwhite: [255, 250, 240],
62
+ forestgreen: [34, 139, 34],
63
+ fuchsia: [255, 0, 255],
64
+ gainsboro: [220, 220, 220],
65
+ ghostwhite: [248, 248, 255],
66
+ gold: [255, 215, 0],
67
+ goldenrod: [218, 165, 32],
68
+ gray: [128, 128, 128],
69
+ grey: [128, 128, 128],
70
+ green: [0, 128, 0],
71
+ greenyellow: [173, 255, 47],
72
+ honeydew: [240, 255, 240],
73
+ hotpink: [255, 105, 180],
74
+ indianred: [205, 92, 92],
75
+ indigo: [75, 0, 130],
76
+ ivory: [255, 255, 240],
77
+ khaki: [240, 230, 140],
78
+ lavender: [230, 230, 250],
79
+ lavenderblush: [255, 240, 245],
80
+ lawngreen: [124, 252, 0],
81
+ lemonchiffon: [255, 250, 205],
82
+ lightblue: [173, 216, 230],
83
+ lightcoral: [240, 128, 128],
84
+ lightcyan: [224, 255, 255],
85
+ lightgoldenrodyellow: [250, 250, 210],
86
+ lightgray: [211, 211, 211],
87
+ lightgreen: [144, 238, 144],
88
+ lightgrey: [211, 211, 211],
89
+ lightpink: [255, 182, 193],
90
+ lightsalmon: [255, 160, 122],
91
+ lightseagreen: [32, 178, 170],
92
+ lightskyblue: [135, 206, 250],
93
+ lightslategray: [119, 136, 153],
94
+ lightslategrey: [119, 136, 153],
95
+ lightsteelblue: [176, 196, 222],
96
+ lightyellow: [255, 255, 224],
97
+ lime: [0, 255, 0],
98
+ limegreen: [50, 205, 50],
99
+ linen: [250, 240, 230],
100
+ magenta: [255, 0, 255],
101
+ maroon: [128, 0, 0],
102
+ mediumaquamarine: [102, 205, 170],
103
+ mediumblue: [0, 0, 205],
104
+ mediumorchid: [186, 85, 211],
105
+ mediumpurple: [147, 112, 219],
106
+ mediumseagreen: [60, 179, 113],
107
+ mediumslateblue: [123, 104, 238],
108
+ mediumspringgreen: [0, 250, 154],
109
+ mediumturquoise: [72, 209, 204],
110
+ mediumvioletred: [199, 21, 133],
111
+ midnightblue: [25, 25, 112],
112
+ mintcream: [245, 255, 250],
113
+ mistyrose: [255, 228, 225],
114
+ moccasin: [255, 228, 181],
115
+ navajowhite: [255, 222, 173],
116
+ navy: [0, 0, 128],
117
+ oldlace: [253, 245, 230],
118
+ olive: [128, 128, 0],
119
+ olivedrab: [107, 142, 35],
120
+ orange: [255, 165, 0],
121
+ orangered: [255, 69, 0],
122
+ orchid: [218, 112, 214],
123
+ palegoldenrod: [238, 232, 170],
124
+ palegreen: [152, 251, 152],
125
+ paleturquoise: [175, 238, 238],
126
+ palevioletred: [219, 112, 147],
127
+ papayawhip: [255, 239, 213],
128
+ peachpuff: [255, 218, 185],
129
+ peru: [205, 133, 63],
130
+ pink: [255, 192, 203],
131
+ plum: [221, 160, 221],
132
+ powderblue: [176, 224, 230],
133
+ purple: [128, 0, 128],
134
+ rebeccapurple: [102, 51, 153],
135
+ red: [255, 0, 0],
136
+ rosybrown: [188, 143, 143],
137
+ royalblue: [65, 105, 225],
138
+ saddlebrown: [139, 69, 19],
139
+ salmon: [250, 128, 114],
140
+ sandybrown: [244, 164, 96],
141
+ seagreen: [46, 139, 87],
142
+ seashell: [255, 245, 238],
143
+ sienna: [160, 82, 45],
144
+ silver: [192, 192, 192],
145
+ skyblue: [135, 206, 235],
146
+ slateblue: [106, 90, 205],
147
+ slategray: [112, 128, 144],
148
+ slategrey: [112, 128, 144],
149
+ snow: [255, 250, 250],
150
+ springgreen: [0, 255, 127],
151
+ steelblue: [70, 130, 180],
152
+ tan: [210, 180, 140],
153
+ teal: [0, 128, 128],
154
+ thistle: [216, 191, 216],
155
+ tomato: [255, 99, 71],
156
+ turquoise: [64, 224, 208],
157
+ violet: [238, 130, 238],
158
+ wheat: [245, 222, 179],
159
+ white: [255, 255, 255],
160
+ whitesmoke: [245, 245, 245],
161
+ yellow: [255, 255, 0],
162
+ yellowgreen: [154, 205, 50],
163
+ };
164
+ /**
165
+ * Get RGB values for a CSS color name
166
+ * @param name - CSS color name (case-insensitive)
167
+ * @returns RGB tuple [r, g, b] or undefined if not found
168
+ */
169
+ export function getNamedColor(name) {
170
+ const color = CSS_COLORS[name.toLowerCase()];
171
+ return color;
172
+ }
173
+ /**
174
+ * Convert RGBA values to hex format
175
+ * @param r - Red channel (0-255)
176
+ * @param g - Green channel (0-255)
177
+ * @param b - Blue channel (0-255)
178
+ * @param a - Alpha channel (0-1)
179
+ * @returns Hex color string in format #RRGGBBAA
180
+ */
181
+ export function rgbaToHex(r, g, b, a) {
182
+ const red = channelToHex(r);
183
+ const green = channelToHex(g);
184
+ const blue = channelToHex(b);
185
+ const alpha = channelToHex(Math.round(a * 255));
186
+ return `#${red}${green}${blue}${alpha}`;
187
+ }
188
+ /**
189
+ * Convert a single color channel value to hex
190
+ * @param value - Channel value (0-255)
191
+ * @returns Two-digit hex string
192
+ */
193
+ export function channelToHex(value) {
194
+ const clamped = Math.max(0, Math.min(255, Math.round(value)));
195
+ return clamped.toString(16).padStart(2, '0').toUpperCase();
196
+ }
197
+ /**
198
+ * Clamp RGB channel value to valid range
199
+ * @param value - Channel value
200
+ * @returns Clamped value (0-255)
201
+ */
202
+ export function clampColorChannel(value) {
203
+ if (Number.isNaN(value)) {
204
+ return 0;
205
+ }
206
+ return Math.max(0, Math.min(255, value));
207
+ }
208
+ /**
209
+ * Clamp alpha value to valid range
210
+ * @param value - Alpha value
211
+ * @returns Clamped value (0-1)
212
+ */
213
+ export function clampAlpha(value) {
214
+ if (Number.isNaN(value)) {
215
+ return 1;
216
+ }
217
+ return Math.max(0, Math.min(1, value));
218
+ }
219
+ /**
220
+ * Convert any color format to hex
221
+ * Supports: hex (#RGB, #RRGGBB, #RRGGBBAA), RGB/RGBA, and CSS color names
222
+ * @param value - Color string in any supported format
223
+ * @returns Hex color string (#RRGGBBAA) or undefined if invalid
224
+ */
225
+ export function toHexColor(value) {
226
+ // Try hex format
227
+ const hexMatch = value.match(/^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i);
228
+ if (hexMatch) {
229
+ const hex = hexMatch[1];
230
+ if (hex.length === 3 || hex.length === 4) {
231
+ return `#${hex
232
+ .split('')
233
+ .map((char) => char + char)
234
+ .join('')}`;
235
+ }
236
+ return `#${hex}`;
237
+ }
238
+ // Try RGB/RGBA format
239
+ const rgbMatch = value.match(/^rgba?\((.+)\)$/i);
240
+ if (rgbMatch) {
241
+ const parts = rgbMatch[1]
242
+ .split(',')
243
+ .map((part) => part.trim())
244
+ .filter(Boolean);
245
+ if (parts.length === 3 || parts.length === 4) {
246
+ const [r, g, b, a] = parts;
247
+ const red = clampColorChannel(parseFloat(r));
248
+ const green = clampColorChannel(parseFloat(g));
249
+ const blue = clampColorChannel(parseFloat(b));
250
+ const alpha = parts.length === 4 ? clampAlpha(parseFloat(a)) : 1;
251
+ return rgbaToHex(red, green, blue, alpha);
252
+ }
253
+ }
254
+ // Try CSS color name
255
+ const rgb = getNamedColor(value);
256
+ if (rgb) {
257
+ return rgbaToHex(rgb[0], rgb[1], rgb[2], 1);
258
+ }
259
+ return undefined;
260
+ }
261
+ /**
262
+ * Darken a hex color by a specified amount
263
+ * @param hex - Hex color string
264
+ * @param amount - Amount to darken (0-255)
265
+ * @returns Darkened hex color
266
+ */
267
+ export function darkenColor(hex, amount) {
268
+ const normalized = ensureAlphaChannel(hex).substring(1); // strip #
269
+ const rgb = normalized.substring(0, 6);
270
+ const alpha = normalized.substring(6) || 'FF';
271
+ const r = parseInt(rgb.substring(0, 2), 16);
272
+ const g = parseInt(rgb.substring(2, 4), 16);
273
+ const b = parseInt(rgb.substring(4, 6), 16);
274
+ const clamp = (val) => Math.max(0, Math.min(255, val));
275
+ const newR = clamp(r - amount);
276
+ const newG = clamp(g - amount);
277
+ const newB = clamp(b - amount);
278
+ return `#${channelToHex(newR)}${channelToHex(newG)}${channelToHex(newB)}${alpha.toUpperCase()}`;
279
+ }
280
+ /**
281
+ * Lighten a hex color by a specified amount
282
+ * @param hex - Hex color string
283
+ * @param amount - Amount to lighten (0-255)
284
+ * @returns Lightened hex color
285
+ */
286
+ export function lightenColor(hex, amount) {
287
+ const normalized = ensureAlphaChannel(hex).substring(1); // strip #
288
+ const rgb = normalized.substring(0, 6);
289
+ const alpha = normalized.substring(6) || 'FF';
290
+ const r = parseInt(rgb.substring(0, 2), 16);
291
+ const g = parseInt(rgb.substring(2, 4), 16);
292
+ const b = parseInt(rgb.substring(4, 6), 16);
293
+ const clamp = (val) => Math.max(0, Math.min(255, val));
294
+ const newR = clamp(r + amount);
295
+ const newG = clamp(g + amount);
296
+ const newB = clamp(b + amount);
297
+ return `#${channelToHex(newR)}${channelToHex(newG)}${channelToHex(newB)}${alpha.toUpperCase()}`;
298
+ }
299
+ /**
300
+ * Convert hex color to RGBA object
301
+ * @param hex - Hex color string (#RRGGBB or #RRGGBBAA)
302
+ * @returns RGBA object with r, g, b, a properties (0-1 for alpha)
303
+ */
304
+ export function hexToRgba(hex) {
305
+ const normalized = ensureAlphaChannel(hex).substring(1); // strip #
306
+ const rgb = normalized.substring(0, 6);
307
+ const alphaHex = normalized.substring(6) || 'FF';
308
+ const r = parseInt(rgb.substring(0, 2), 16);
309
+ const g = parseInt(rgb.substring(2, 4), 16);
310
+ const b = parseInt(rgb.substring(4, 6), 16);
311
+ const a = parseInt(alphaHex, 16) / 255;
312
+ return { r, g, b, a };
313
+ }
314
+ /**
315
+ * Normalize any color format to Grid3's 8-digit hex format
316
+ * @param input - Color string in any supported format
317
+ * @param fallback - Fallback color if input is invalid (default: white)
318
+ * @returns Normalized color in format #AARRGGBBFF
319
+ */
320
+ export function normalizeColor(input, fallback = '#FFFFFFFF') {
321
+ const trimmed = input.trim();
322
+ if (!trimmed) {
323
+ return fallback;
324
+ }
325
+ const hex = toHexColor(trimmed);
326
+ if (hex) {
327
+ return ensureAlphaChannel(hex).toUpperCase();
328
+ }
329
+ return fallback;
330
+ }
331
+ /**
332
+ * Ensure a color has an alpha channel (Grid3 format requires 8-digit ARGB)
333
+ * @param color - Color string (hex format)
334
+ * @returns Color with alpha channel in format #AARRGGBBFF
335
+ */
336
+ export function ensureAlphaChannel(color) {
337
+ if (!color)
338
+ return '#FFFFFFFF';
339
+ // If already 8 digits (with alpha), return as is
340
+ if (color.match(/^#[0-9A-Fa-f]{8}$/))
341
+ return color;
342
+ // If 6 digits (no alpha), add FF for fully opaque
343
+ if (color.match(/^#[0-9A-Fa-f]{6}$/))
344
+ return color + 'FF';
345
+ // If 3 digits (shorthand), expand to 8
346
+ if (color.match(/^#[0-9A-Fa-f]{3}$/)) {
347
+ const r = color[1];
348
+ const g = color[2];
349
+ const b = color[3];
350
+ return `#${r}${r}${g}${g}${b}${b}FF`;
351
+ }
352
+ // Invalid or unknown format, return white
353
+ return '#FFFFFFFF';
354
+ }
@@ -1,12 +1,10 @@
1
1
  import { XMLBuilder } from 'fast-xml-parser';
2
2
  import { AACSemanticCategory, AACSemanticIntent, } from '../../core/treeStructure';
3
- import * as fs from 'fs';
4
- import * as path from 'path';
5
- import { execSync } from 'child_process';
6
- import Database from 'better-sqlite3';
7
3
  import { dotNetTicksToDate } from '../../utils/dotnetTicks';
8
4
  import { getZipEntriesFromAdapter, resolveGridsetPasswordFromEnv } from './password';
9
- import { openZipFromInput } from '../../utils/zip';
5
+ import { defaultFileAdapter, extname, getNodeRequire, joinWin32, } from '../../utils/io';
6
+ import { getZipAdapter } from '../../utils/zip';
7
+ import { requireBetterSqlite3 } from '../../utils/sqlite';
10
8
  function normalizeZipPath(p) {
11
9
  const unified = p.replace(/\\/g, '/');
12
10
  try {
@@ -52,11 +50,11 @@ export function getAllowedImageEntries(tree) {
52
50
  * @param entryPath Entry name inside the zip
53
51
  * @returns Image data buffer or null if not found
54
52
  */
55
- export async function openImage(gridsetBuffer, entryPath, password = resolveGridsetPasswordFromEnv(), zipAdapter) {
53
+ export async function openImage(gridsetBuffer, entryPath, password = resolveGridsetPasswordFromEnv(), fileAdapter = defaultFileAdapter, zipAdapter) {
56
54
  try {
57
- const { zip } = zipAdapter
55
+ const zip = zipAdapter
58
56
  ? await zipAdapter(gridsetBuffer)
59
- : await openZipFromInput(gridsetBuffer);
57
+ : await getZipAdapter(gridsetBuffer, fileAdapter);
60
58
  const entries = getZipEntriesFromAdapter(zip, password);
61
59
  const want = normalizeZipPath(entryPath);
62
60
  const entry = entries.find((e) => normalizeZipPath(e.entryName) === want);
@@ -149,8 +147,9 @@ export function getCommonDocumentsPath() {
149
147
  }
150
148
  try {
151
149
  // Query registry for Common Documents path
150
+ const child_process = getNodeRequire()('child_process');
152
151
  const command = 'REG.EXE QUERY "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders" /V "Common Documents"';
153
- const output = execSync(command, { encoding: 'utf-8', windowsHide: true });
152
+ const output = child_process.execSync(command, { encoding: 'utf-8', windowsHide: true });
154
153
  // Parse the output to extract the path
155
154
  const match = output.match(/Common Documents\s+REG_SZ\s+(.+)/);
156
155
  if (match && match[1]) {
@@ -171,7 +170,8 @@ export function getCommonDocumentsPath() {
171
170
  * C:\Users\Public\Documents\Smartbox\Grid 3\Users\{UserName}\Grid Sets\
172
171
  * @returns Array of Grid3 user path information
173
172
  */
174
- export function findGrid3UserPaths() {
173
+ export async function findGrid3UserPaths(fileAdapter = defaultFileAdapter) {
174
+ const { pathExists, listDir, isDirectory } = fileAdapter;
175
175
  const results = [];
176
176
  // Only works on Windows
177
177
  if (process.platform !== 'win32') {
@@ -180,28 +180,28 @@ export function findGrid3UserPaths() {
180
180
  try {
181
181
  const commonDocs = getCommonDocumentsPath();
182
182
  // Use Windows path joining so tests that mock a Windows platform stay consistent even on POSIX runners
183
- const grid3BasePath = path.win32.join(commonDocs, 'Smartbox', 'Grid 3', 'Users');
183
+ const grid3BasePath = joinWin32(commonDocs, 'Smartbox', 'Grid 3', 'Users');
184
184
  // Check if Grid3 Users directory exists
185
- if (!fs.existsSync(grid3BasePath)) {
185
+ if (!(await pathExists(grid3BasePath))) {
186
186
  return results;
187
187
  }
188
188
  // Enumerate users
189
- const users = fs.readdirSync(grid3BasePath, { withFileTypes: true });
189
+ const users = await listDir(grid3BasePath);
190
190
  for (const userDir of users) {
191
- if (!userDir.isDirectory())
191
+ if (!(await isDirectory(userDir)))
192
192
  continue;
193
- const userName = userDir.name;
194
- const userPath = path.win32.join(grid3BasePath, userName);
193
+ const userName = userDir;
194
+ const userPath = joinWin32(grid3BasePath, userName);
195
195
  // Enumerate language codes
196
- const langDirs = fs.readdirSync(userPath, { withFileTypes: true });
196
+ const langDirs = await listDir(userPath);
197
197
  for (const langDir of langDirs) {
198
- if (!langDir.isDirectory())
198
+ if (!(await isDirectory(langDir)))
199
199
  continue;
200
- const langCode = langDir.name;
201
- const basePath = path.win32.join(userPath, langCode);
202
- const historyDbPath = path.win32.join(basePath, 'Phrases', 'history.sqlite');
200
+ const langCode = langDir;
201
+ const basePath = joinWin32(userPath, langCode);
202
+ const historyDbPath = joinWin32(basePath, 'Phrases', 'history.sqlite');
203
203
  // Only include if history database exists
204
- if (fs.existsSync(historyDbPath)) {
204
+ if (await pathExists(historyDbPath)) {
205
205
  results.push({
206
206
  userName,
207
207
  langCode,
@@ -222,50 +222,52 @@ export function findGrid3UserPaths() {
222
222
  * Convenience method that returns just the database file paths
223
223
  * @returns Array of paths to history.sqlite files
224
224
  */
225
- export function findGrid3HistoryDatabases() {
226
- return findGrid3UserPaths().map((userPath) => userPath.historyDbPath);
225
+ export async function findGrid3HistoryDatabases(fileAdapter) {
226
+ const userPaths = await findGrid3UserPaths(fileAdapter);
227
+ return userPaths.map((userPath) => userPath.historyDbPath);
227
228
  }
228
229
  /**
229
230
  * Get Grid 3 users (alias of findGrid3UserPaths for clarity)
230
231
  */
231
- export function findGrid3Users() {
232
- return findGrid3UserPaths();
232
+ export async function findGrid3Users() {
233
+ return await findGrid3UserPaths();
233
234
  }
234
235
  /**
235
236
  * Find Grid 3 gridset/vocabulary files for each user
236
237
  * @param userName Optional user filter; matches case-insensitively
237
238
  * @returns Array of user/gridset path pairs
238
239
  */
239
- export function findGrid3Vocabularies(userName) {
240
+ export async function findGrid3Vocabularies(userName, fileAdapter = defaultFileAdapter) {
241
+ const { pathExists, listDir, isDirectory } = fileAdapter;
240
242
  const results = [];
241
243
  if (process.platform !== 'win32') {
242
244
  return results;
243
245
  }
244
246
  const commonDocs = getCommonDocumentsPath();
245
- const grid3BasePath = path.win32.join(commonDocs, 'Smartbox', 'Grid 3', 'Users');
246
- if (!fs.existsSync(grid3BasePath)) {
247
+ const grid3BasePath = joinWin32(commonDocs, 'Smartbox', 'Grid 3', 'Users');
248
+ if (!(await pathExists(grid3BasePath))) {
247
249
  return results;
248
250
  }
249
251
  const normalizedUser = userName?.toLowerCase();
250
- const users = fs.readdirSync(grid3BasePath, { withFileTypes: true });
252
+ const users = await listDir(grid3BasePath);
251
253
  for (const userDir of users) {
252
- if (!userDir.isDirectory())
254
+ if (!(await isDirectory(userDir)))
253
255
  continue;
254
- if (normalizedUser && userDir.name.toLowerCase() !== normalizedUser)
256
+ if (normalizedUser && userDir.toLowerCase() !== normalizedUser)
255
257
  continue;
256
- const userRoot = path.win32.join(grid3BasePath, userDir.name);
257
- const gridSetsDir = path.win32.join(userRoot, 'Grid Sets');
258
- if (!fs.existsSync(gridSetsDir))
258
+ const userRoot = joinWin32(grid3BasePath, userDir);
259
+ const gridSetsDir = joinWin32(userRoot, 'Grid Sets');
260
+ if (!(await pathExists(gridSetsDir)))
259
261
  continue;
260
- const entries = fs.readdirSync(gridSetsDir, { withFileTypes: true });
262
+ const entries = await listDir(gridSetsDir);
261
263
  for (const entry of entries) {
262
- if (!entry.isFile())
264
+ if (!(await pathExists(entry)) || (await isDirectory(entry)))
263
265
  continue;
264
- const ext = path.extname(entry.name).toLowerCase();
266
+ const ext = extname(entry).toLowerCase();
265
267
  if (ext === '.gridset' || ext === '.gridsetx' || ext === '.grd' || ext === '.grdl') {
266
268
  results.push({
267
- userName: userDir.name,
268
- gridsetPath: path.win32.join(gridSetsDir, entry.name),
269
+ userName: userDir,
270
+ gridsetPath: joinWin32(gridSetsDir, entry),
269
271
  });
270
272
  }
271
273
  }
@@ -278,26 +280,28 @@ export function findGrid3Vocabularies(userName) {
278
280
  * @param langCode Optional language code filter (case-insensitive)
279
281
  * @returns Path to history.sqlite or null if not found
280
282
  */
281
- export function findGrid3UserHistory(userName, langCode) {
283
+ export async function findGrid3UserHistory(userName, langCode, fileAdapter) {
282
284
  if (!userName)
283
285
  return null;
284
286
  const normalizedUser = userName.toLowerCase();
285
287
  const normalizedLang = langCode?.toLowerCase();
286
- const match = findGrid3UserPaths().find((u) => u.userName.toLowerCase() === normalizedUser &&
288
+ const userPaths = await findGrid3UserPaths(fileAdapter);
289
+ const match = userPaths.find((u) => u.userName.toLowerCase() === normalizedUser &&
287
290
  (!normalizedLang || u.langCode.toLowerCase() === normalizedLang));
288
291
  return match?.historyDbPath ?? null;
289
292
  }
290
293
  /**
291
294
  * Check whether Grid 3 appears to be installed (Windows only)
292
295
  */
293
- export function isGrid3Installed() {
296
+ export async function isGrid3Installed(fileAdapter = defaultFileAdapter) {
297
+ const { pathExists } = fileAdapter;
294
298
  if (process.platform !== 'win32')
295
299
  return false;
296
300
  const commonDocs = getCommonDocumentsPath();
297
301
  if (!commonDocs)
298
302
  return false;
299
- const grid3BasePath = path.win32.join(commonDocs, 'Smartbox', 'Grid 3', 'Users');
300
- return fs.existsSync(grid3BasePath);
303
+ const grid3BasePath = joinWin32(commonDocs, 'Smartbox', 'Grid 3', 'Users');
304
+ return await pathExists(grid3BasePath);
301
305
  }
302
306
  function parseGrid3ContentXml(xmlContent) {
303
307
  const regex = /<r>(?:<!\[CDATA\[)?(.*?)(?:\]\]>)?<\/r>/gis;
@@ -316,9 +320,11 @@ function parseGrid3ContentXml(xmlContent) {
316
320
  * @param historyDbPath Absolute path to the history database
317
321
  * @returns Parsed history entries grouped by phrase
318
322
  */
319
- export function readGrid3History(historyDbPath) {
320
- if (!fs.existsSync(historyDbPath))
323
+ export async function readGrid3History(historyDbPath, fileAdapter = defaultFileAdapter) {
324
+ const { pathExists } = fileAdapter;
325
+ if (!(await pathExists(historyDbPath)))
321
326
  return [];
327
+ const Database = requireBetterSqlite3();
322
328
  const db = new Database(historyDbPath, { readonly: true });
323
329
  const rows = db
324
330
  .prepare(`
@@ -376,17 +382,18 @@ export function readGrid3History(historyDbPath) {
376
382
  * @param langCode Optional language code to narrow selection (case-insensitive)
377
383
  * @returns History entries for that user/language, or empty array if none
378
384
  */
379
- export function readGrid3HistoryForUser(userName, langCode) {
380
- const dbPath = findGrid3UserHistory(userName, langCode);
385
+ export async function readGrid3HistoryForUser(userName, langCode) {
386
+ const dbPath = await findGrid3UserHistory(userName, langCode);
381
387
  if (!dbPath)
382
388
  return [];
383
- return readGrid3History(dbPath);
389
+ return await readGrid3History(dbPath);
384
390
  }
385
391
  /**
386
392
  * Load all available Grid 3 histories on the machine.
387
393
  * @returns Combined history entries from every discovered history.sqlite
388
394
  */
389
- export function readAllGrid3History() {
390
- const paths = findGrid3HistoryDatabases();
391
- return paths.flatMap((p) => readGrid3History(p));
395
+ export async function readAllGrid3History() {
396
+ const paths = await findGrid3HistoryDatabases();
397
+ const history = await Promise.all(paths.map(async (p) => await readGrid3History(p)));
398
+ return history.flat();
392
399
  }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Grid 3 Enhanced Support Module
3
+ *
4
+ * This module exports all enhanced Grid 3 functionality including:
5
+ * - Cell shape detection and support
6
+ * - Plugin cell type detection (Workspace, LiveCell, AutoContent)
7
+ * - Comprehensive command definitions and detection
8
+ * - Color utilities and style helpers
9
+ * - Image resolution helpers
10
+ */
11
+ // Style helpers
12
+ export { CellBackgroundShape, SHAPE_NAMES, DEFAULT_GRID3_STYLES, CATEGORY_STYLES, createDefaultStylesXml, createCategoryStyle, } from './styleHelpers';
13
+ // Plugin cell type detection
14
+ export { detectPluginCellType, Grid3CellType, WORKSPACE_TYPES, LIVECELL_TYPES, AUTOCONTENT_TYPES, getCellTypeDisplayName, isWorkspaceCell, isLiveCell, isAutoContentCell, isRegularCell, } from './pluginTypes';
15
+ // Command definitions and detection
16
+ export { detectCommand, getCommandDefinition, getCommandsByPlugin, getCommandsByCategory, getAllCommandIds, getAllPluginIds, extractCommandParameters, GRID3_COMMANDS, Grid3CommandCategory, } from './commands';
17
+ // Import for local use in constant definitions
18
+ import { getAllCommandIds, getAllPluginIds } from './commands';
19
+ import { CellBackgroundShape } from './styleHelpers';
20
+ import { Grid3CellType } from './pluginTypes';
21
+ import { Grid3CommandCategory } from './commands';
22
+ // Color utilities
23
+ export { ensureAlphaChannel, darkenColor, lightenColor, hexToRgba, rgbaToHex } from './colorUtils';
24
+ // Password handling
25
+ export { resolveGridsetPassword, getZipEntriesWithPassword, getZipEntriesFromAdapter, resolveGridsetPasswordFromEnv, } from './password';
26
+ // Helper functions
27
+ export { getPageTokenImageMap, getAllowedImageEntries, openImage, generateGrid3Guid, createSettingsXml, createFileMapXml, getCommonDocumentsPath, findGrid3UserPaths, findGrid3HistoryDatabases, findGrid3Vocabularies, findGrid3UserHistory, findGrid3Users, isGrid3Installed, readGrid3History, readGrid3HistoryForUser, readAllGrid3History, } from './helpers';
28
+ // Symbol library handling
29
+ export { parseSymbolReference, isSymbolReference, resolveSymbolReference, getAvailableSymbolLibraries, getSymbolLibraryInfo, extractSymbolReferences, analyzeSymbolUsage, createSymbolReference, getSymbolLibraryName, getSymbolPath, isKnownSymbolLibrary, getSymbolLibraryDisplayName, getDefaultGrid3Path, getSymbolLibrariesDir, getSymbolSearchIndexesDir, symbolReferenceToFilename, SYMBOL_LIBRARIES, } from './symbols';
30
+ // Backward compatibility aliases for old function names
31
+ export { getSymbolsDir, getSymbolSearchDir } from './symbols';
32
+ // Image resolution
33
+ export { resolveGrid3CellImage, isSymbolLibraryReference, parseImageSymbolReference, } from './resolver';
34
+ // Symbol extraction and conversion
35
+ export { extractButtonImage, extractSymbolLibraryImage, convertToAstericsImage, analyzeSymbolExtraction, suggestExtractionStrategy, exportSymbolReferencesToCsv, createSymbolManifest, } from './symbolExtractor';
36
+ // Symbol search functionality
37
+ export { parsePixFile, loadSearchIndexes, searchSymbols, searchSymbolsWithReferences, getSymbolFilename, getSymbolDisplayName, getAllSearchTerms, getSearchSuggestions, countLibrarySymbols, getSymbolSearchStats, } from './symbolSearch';
38
+ /**
39
+ * Get all Grid 3 command IDs as a readonly array
40
+ * Useful for validation and autocomplete
41
+ */
42
+ export const GRID3_COMMAND_IDS = Object.freeze(getAllCommandIds());
43
+ /**
44
+ * Get all Grid 3 plugin IDs as a readonly array
45
+ */
46
+ export const GRID3_PLUGIN_IDS = Object.freeze(getAllPluginIds());
47
+ /**
48
+ * Grid 3 cell shapes enum values
49
+ */
50
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
51
+ export const GRID3_CELL_SHAPES = Object.freeze(Object.values(CellBackgroundShape));
52
+ /**
53
+ * Grid 3 cell types enum values
54
+ */
55
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
56
+ export const GRID3_CELL_TYPES = Object.freeze(Object.values(Grid3CellType));
57
+ /**
58
+ * Grid 3 command categories enum values
59
+ */
60
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
61
+ export const GRID3_COMMAND_CATEGORIES = Object.freeze(Object.values(Grid3CommandCategory));