@mentra/sdk 2.1.2-alpha.1 → 2.1.3
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/app/session/layouts.d.ts +63 -3
- package/dist/app/session/layouts.d.ts.map +1 -1
- package/dist/app/session/layouts.js +101 -7
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/types/enums.d.ts +3 -1
- package/dist/types/enums.d.ts.map +1 -1
- package/dist/types/enums.js +2 -0
- package/dist/types/layouts.d.ts +16 -1
- package/dist/types/layouts.d.ts.map +1 -1
- package/dist/types/models.d.ts +1 -0
- package/dist/types/models.d.ts.map +1 -1
- package/dist/types/models.js +1 -0
- package/dist/utils/animation-utils.d.ts +206 -0
- package/dist/utils/animation-utils.d.ts.map +1 -0
- package/dist/utils/animation-utils.js +339 -0
- package/dist/utils/bitmap-utils.d.ts +147 -0
- package/dist/utils/bitmap-utils.d.ts.map +1 -0
- package/dist/utils/bitmap-utils.js +338 -0
- package/package.json +1 -1
@@ -0,0 +1,338 @@
|
|
1
|
+
"use strict";
|
2
|
+
/**
|
3
|
+
* 🎨 Bitmap Utilities Module
|
4
|
+
*
|
5
|
+
* Provides helper functions for working with bitmap images in MentraOS applications.
|
6
|
+
* Includes file loading, data validation, and format conversion utilities.
|
7
|
+
*
|
8
|
+
* @example
|
9
|
+
* ```typescript
|
10
|
+
* import { BitmapUtils } from '@mentra/sdk';
|
11
|
+
*
|
12
|
+
* // Load a single BMP file
|
13
|
+
* const bmpHex = await BitmapUtils.loadBmpAsHex('./my-image.bmp');
|
14
|
+
* session.layouts.showBitmapView(bmpHex);
|
15
|
+
*
|
16
|
+
* // Load multiple animation frames
|
17
|
+
* const frames = await BitmapUtils.loadBmpFrames('./animations', 10);
|
18
|
+
* session.layouts.showBitmapAnimation(frames, 1500, true);
|
19
|
+
* ```
|
20
|
+
*/
|
21
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
22
|
+
if (k2 === undefined) k2 = k;
|
23
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
24
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
25
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
26
|
+
}
|
27
|
+
Object.defineProperty(o, k2, desc);
|
28
|
+
}) : (function(o, m, k, k2) {
|
29
|
+
if (k2 === undefined) k2 = k;
|
30
|
+
o[k2] = m[k];
|
31
|
+
}));
|
32
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
33
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
34
|
+
}) : function(o, v) {
|
35
|
+
o["default"] = v;
|
36
|
+
});
|
37
|
+
var __importStar = (this && this.__importStar) || (function () {
|
38
|
+
var ownKeys = function(o) {
|
39
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
40
|
+
var ar = [];
|
41
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
42
|
+
return ar;
|
43
|
+
};
|
44
|
+
return ownKeys(o);
|
45
|
+
};
|
46
|
+
return function (mod) {
|
47
|
+
if (mod && mod.__esModule) return mod;
|
48
|
+
var result = {};
|
49
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
50
|
+
__setModuleDefault(result, mod);
|
51
|
+
return result;
|
52
|
+
};
|
53
|
+
})();
|
54
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
55
|
+
exports.BitmapUtils = void 0;
|
56
|
+
const fs = __importStar(require("fs/promises"));
|
57
|
+
const path = __importStar(require("path"));
|
58
|
+
/**
|
59
|
+
* Utility class for working with bitmap images in MentraOS applications
|
60
|
+
*/
|
61
|
+
class BitmapUtils {
|
62
|
+
/**
|
63
|
+
* Load a BMP file as hex string from filesystem
|
64
|
+
*
|
65
|
+
* @param filePath - Path to the BMP file
|
66
|
+
* @returns Promise resolving to hex-encoded bitmap data
|
67
|
+
* @throws Error if file cannot be read or is not a valid BMP
|
68
|
+
*
|
69
|
+
* @example
|
70
|
+
* ```typescript
|
71
|
+
* const bmpHex = await BitmapUtils.loadBmpAsHex('./assets/icon.bmp');
|
72
|
+
* session.layouts.showBitmapView(bmpHex);
|
73
|
+
* ```
|
74
|
+
*/
|
75
|
+
static async loadBmpAsHex(filePath) {
|
76
|
+
try {
|
77
|
+
const bmpData = await fs.readFile(filePath);
|
78
|
+
// Basic BMP validation - check for BMP signature
|
79
|
+
if (bmpData.length < 14 || bmpData[0] !== 0x42 || bmpData[1] !== 0x4D) {
|
80
|
+
throw new Error(`File ${filePath} is not a valid BMP file (missing BM signature)`);
|
81
|
+
}
|
82
|
+
const hexString = bmpData.toString('hex');
|
83
|
+
console.log(`📁 Loaded BMP: ${path.basename(filePath)} (${bmpData.length} bytes)`);
|
84
|
+
return hexString;
|
85
|
+
}
|
86
|
+
catch (error) {
|
87
|
+
if (error instanceof Error) {
|
88
|
+
throw new Error(`Failed to load BMP file ${filePath}: ${error.message}`);
|
89
|
+
}
|
90
|
+
throw new Error(`Failed to load BMP file ${filePath}: Unknown error`);
|
91
|
+
}
|
92
|
+
}
|
93
|
+
/**
|
94
|
+
* Load multiple BMP frames as hex array for animations
|
95
|
+
*
|
96
|
+
* @param basePath - Directory containing the frame files
|
97
|
+
* @param frameCount - Number of frames to load
|
98
|
+
* @param options - Loading options and configuration
|
99
|
+
* @returns Promise resolving to array of hex-encoded bitmap data
|
100
|
+
*
|
101
|
+
* @example
|
102
|
+
* ```typescript
|
103
|
+
* // Load 10 frames with default pattern
|
104
|
+
* const frames = await BitmapUtils.loadBmpFrames('./animations', 10);
|
105
|
+
*
|
106
|
+
* // Load with custom pattern
|
107
|
+
* const customFrames = await BitmapUtils.loadBmpFrames('./sprites', 8, {
|
108
|
+
* filePattern: 'sprite_{i}.bmp',
|
109
|
+
* startFrame: 0
|
110
|
+
* });
|
111
|
+
* ```
|
112
|
+
*/
|
113
|
+
static async loadBmpFrames(basePath, frameCount, options = {}) {
|
114
|
+
const { filePattern = 'animation_10_frame_{i}.bmp', startFrame = 1, validateFrames = true, skipMissingFrames = false } = options;
|
115
|
+
const frames = [];
|
116
|
+
const errors = [];
|
117
|
+
for (let i = 0; i < frameCount; i++) {
|
118
|
+
const frameNumber = startFrame + i;
|
119
|
+
const fileName = filePattern.replace('{i}', frameNumber.toString());
|
120
|
+
const filePath = path.join(basePath, fileName);
|
121
|
+
try {
|
122
|
+
const frameHex = await this.loadBmpAsHex(filePath);
|
123
|
+
if (validateFrames) {
|
124
|
+
const validation = this.validateBmpHex(frameHex);
|
125
|
+
if (!validation.isValid) {
|
126
|
+
const errorMsg = `Frame ${frameNumber} validation failed: ${validation.errors.join(', ')}`;
|
127
|
+
if (skipMissingFrames) {
|
128
|
+
console.warn(`⚠️ ${errorMsg} - skipping`);
|
129
|
+
continue;
|
130
|
+
}
|
131
|
+
else {
|
132
|
+
throw new Error(errorMsg);
|
133
|
+
}
|
134
|
+
}
|
135
|
+
console.log(`✅ Frame ${frameNumber} validated (${validation.blackPixels} black pixels)`);
|
136
|
+
}
|
137
|
+
frames.push(frameHex);
|
138
|
+
}
|
139
|
+
catch (error) {
|
140
|
+
const errorMsg = `Failed to load frame ${frameNumber} (${fileName}): ${error instanceof Error ? error.message : 'Unknown error'}`;
|
141
|
+
if (skipMissingFrames) {
|
142
|
+
console.warn(`⚠️ ${errorMsg} - skipping`);
|
143
|
+
continue;
|
144
|
+
}
|
145
|
+
else {
|
146
|
+
errors.push(errorMsg);
|
147
|
+
}
|
148
|
+
}
|
149
|
+
}
|
150
|
+
if (errors.length > 0) {
|
151
|
+
throw new Error(`Failed to load frames:\n${errors.join('\n')}`);
|
152
|
+
}
|
153
|
+
if (frames.length === 0) {
|
154
|
+
throw new Error(`No valid frames loaded from ${basePath}`);
|
155
|
+
}
|
156
|
+
console.log(`📚 Loaded ${frames.length} animation frames from ${basePath}`);
|
157
|
+
return frames;
|
158
|
+
}
|
159
|
+
/**
|
160
|
+
* Validate BMP hex data integrity and extract metadata
|
161
|
+
*
|
162
|
+
* @param hexString - Hex-encoded bitmap data
|
163
|
+
* @returns Validation result with detailed information
|
164
|
+
*
|
165
|
+
* @example
|
166
|
+
* ```typescript
|
167
|
+
* const validation = BitmapUtils.validateBmpHex(bmpHex);
|
168
|
+
* if (!validation.isValid) {
|
169
|
+
* console.error('Invalid bitmap:', validation.errors);
|
170
|
+
* } else {
|
171
|
+
* console.log(`Valid bitmap: ${validation.blackPixels} black pixels`);
|
172
|
+
* }
|
173
|
+
* ```
|
174
|
+
*/
|
175
|
+
static validateBmpHex(hexString) {
|
176
|
+
const errors = [];
|
177
|
+
let byteCount = 0;
|
178
|
+
let blackPixels = 0;
|
179
|
+
let metadata = {};
|
180
|
+
try {
|
181
|
+
// Basic hex validation
|
182
|
+
if (typeof hexString !== 'string' || hexString.length === 0) {
|
183
|
+
errors.push('Hex string is empty or invalid');
|
184
|
+
return { isValid: false, byteCount: 0, blackPixels: 0, errors };
|
185
|
+
}
|
186
|
+
if (hexString.length % 2 !== 0) {
|
187
|
+
errors.push('Hex string length must be even');
|
188
|
+
return { isValid: false, byteCount: 0, blackPixels: 0, errors };
|
189
|
+
}
|
190
|
+
// Convert to buffer
|
191
|
+
const buffer = Buffer.from(hexString, 'hex');
|
192
|
+
byteCount = buffer.length;
|
193
|
+
// BMP signature validation
|
194
|
+
if (buffer.length < 14) {
|
195
|
+
errors.push('File too small to be a valid BMP (minimum 14 bytes for header)');
|
196
|
+
}
|
197
|
+
else {
|
198
|
+
if (buffer[0] !== 0x42 || buffer[1] !== 0x4D) {
|
199
|
+
errors.push('Invalid BMP signature (should start with "BM")');
|
200
|
+
}
|
201
|
+
}
|
202
|
+
// Size validation for MentraOS (576x135 = ~9782 bytes expected)
|
203
|
+
const expectedSize = 9782;
|
204
|
+
if (buffer.length < expectedSize - 100) { // Allow some tolerance
|
205
|
+
errors.push(`BMP too small (${buffer.length} bytes, expected ~${expectedSize})`);
|
206
|
+
}
|
207
|
+
else if (buffer.length > expectedSize + 1000) { // Allow some tolerance
|
208
|
+
errors.push(`BMP too large (${buffer.length} bytes, expected ~${expectedSize})`);
|
209
|
+
}
|
210
|
+
// Extract BMP metadata if header is valid
|
211
|
+
if (buffer.length >= 54) {
|
212
|
+
try {
|
213
|
+
// BMP width and height are at offsets 18 and 22 (little-endian)
|
214
|
+
const width = buffer.readUInt32LE(18);
|
215
|
+
const height = buffer.readUInt32LE(22);
|
216
|
+
metadata.dimensions = { width, height };
|
217
|
+
metadata.format = 'BMP';
|
218
|
+
// Validate dimensions for MentraOS glasses
|
219
|
+
if (width !== 576 || height !== 135) {
|
220
|
+
errors.push(`Invalid dimensions (${width}x${height}, expected 576x135 for MentraOS)`);
|
221
|
+
}
|
222
|
+
}
|
223
|
+
catch (e) {
|
224
|
+
errors.push('Failed to parse BMP header metadata');
|
225
|
+
}
|
226
|
+
}
|
227
|
+
// Pixel data validation (assumes 54-byte header + pixel data)
|
228
|
+
if (buffer.length > 62) {
|
229
|
+
const pixelData = buffer.slice(62); // Skip BMP header
|
230
|
+
blackPixels = Array.from(pixelData).filter(b => b !== 0xFF).length;
|
231
|
+
if (blackPixels === 0) {
|
232
|
+
errors.push('No black pixels found (image appears to be all white)');
|
233
|
+
}
|
234
|
+
}
|
235
|
+
else {
|
236
|
+
errors.push('File too small to contain pixel data');
|
237
|
+
}
|
238
|
+
}
|
239
|
+
catch (error) {
|
240
|
+
errors.push(`Failed to parse hex data: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
241
|
+
}
|
242
|
+
return {
|
243
|
+
isValid: errors.length === 0,
|
244
|
+
byteCount,
|
245
|
+
blackPixels,
|
246
|
+
errors,
|
247
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : undefined
|
248
|
+
};
|
249
|
+
}
|
250
|
+
/**
|
251
|
+
* Convert bitmap data between different formats
|
252
|
+
*
|
253
|
+
* @param data - Input bitmap data
|
254
|
+
* @param fromFormat - Source format ('hex' | 'base64' | 'buffer')
|
255
|
+
* @param toFormat - Target format ('hex' | 'base64' | 'buffer')
|
256
|
+
* @returns Converted bitmap data
|
257
|
+
*
|
258
|
+
* @example
|
259
|
+
* ```typescript
|
260
|
+
* const base64Data = BitmapUtils.convertFormat(hexData, 'hex', 'base64');
|
261
|
+
* const bufferData = BitmapUtils.convertFormat(base64Data, 'base64', 'buffer');
|
262
|
+
* ```
|
263
|
+
*/
|
264
|
+
static convertFormat(data, fromFormat, toFormat) {
|
265
|
+
let buffer;
|
266
|
+
// Convert input to buffer
|
267
|
+
switch (fromFormat) {
|
268
|
+
case 'hex':
|
269
|
+
buffer = Buffer.from(data, 'hex');
|
270
|
+
break;
|
271
|
+
case 'base64':
|
272
|
+
buffer = Buffer.from(data, 'base64');
|
273
|
+
break;
|
274
|
+
case 'buffer':
|
275
|
+
buffer = data;
|
276
|
+
break;
|
277
|
+
default:
|
278
|
+
throw new Error(`Unsupported source format: ${fromFormat}`);
|
279
|
+
}
|
280
|
+
// Convert buffer to target format
|
281
|
+
switch (toFormat) {
|
282
|
+
case 'hex':
|
283
|
+
return buffer.toString('hex');
|
284
|
+
case 'base64':
|
285
|
+
return buffer.toString('base64');
|
286
|
+
case 'buffer':
|
287
|
+
return buffer;
|
288
|
+
default:
|
289
|
+
throw new Error(`Unsupported target format: ${toFormat}`);
|
290
|
+
}
|
291
|
+
}
|
292
|
+
/**
|
293
|
+
* Get bitmap information without full validation
|
294
|
+
*
|
295
|
+
* @param hexString - Hex-encoded bitmap data
|
296
|
+
* @returns Basic bitmap information
|
297
|
+
*
|
298
|
+
* @example
|
299
|
+
* ```typescript
|
300
|
+
* const info = BitmapUtils.getBitmapInfo(bmpHex);
|
301
|
+
* console.log(`Bitmap: ${info.width}x${info.height}, ${info.blackPixels} black pixels`);
|
302
|
+
* ```
|
303
|
+
*/
|
304
|
+
static getBitmapInfo(hexString) {
|
305
|
+
try {
|
306
|
+
const buffer = Buffer.from(hexString, 'hex');
|
307
|
+
const isValidBmp = buffer.length >= 14 && buffer[0] === 0x42 && buffer[1] === 0x4D;
|
308
|
+
let width;
|
309
|
+
let height;
|
310
|
+
if (isValidBmp && buffer.length >= 54) {
|
311
|
+
try {
|
312
|
+
width = buffer.readUInt32LE(18);
|
313
|
+
height = buffer.readUInt32LE(22);
|
314
|
+
}
|
315
|
+
catch (e) {
|
316
|
+
// Ignore metadata parsing errors
|
317
|
+
}
|
318
|
+
}
|
319
|
+
const pixelData = buffer.slice(62);
|
320
|
+
const blackPixels = Array.from(pixelData).filter(b => b !== 0xFF).length;
|
321
|
+
return {
|
322
|
+
byteCount: buffer.length,
|
323
|
+
blackPixels,
|
324
|
+
width,
|
325
|
+
height,
|
326
|
+
isValidBmp
|
327
|
+
};
|
328
|
+
}
|
329
|
+
catch (error) {
|
330
|
+
return {
|
331
|
+
byteCount: 0,
|
332
|
+
blackPixels: 0,
|
333
|
+
isValidBmp: false
|
334
|
+
};
|
335
|
+
}
|
336
|
+
}
|
337
|
+
}
|
338
|
+
exports.BitmapUtils = BitmapUtils;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@mentra/sdk",
|
3
|
-
"version": "2.1.
|
3
|
+
"version": "2.1.3",
|
4
4
|
"description": "Build apps for MentraOS smartglasses. This SDK provides everything you need to create real-time smartglasses applications.",
|
5
5
|
"source": "src/index.ts",
|
6
6
|
"main": "dist/index.js",
|