@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.
@@ -0,0 +1,339 @@
1
+ "use strict";
2
+ /**
3
+ * 🎬 Animation Utilities Module
4
+ *
5
+ * Provides helper functions for creating and managing bitmap animations in MentraOS applications.
6
+ * Includes timing utilities, animation factories, and performance optimization helpers.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { AnimationUtils } from '@mentra/sdk';
11
+ *
12
+ * // Create animation from files
13
+ * const animation = await AnimationUtils.createBitmapAnimation(
14
+ * session, './frames', 10, 1750, true
15
+ * );
16
+ *
17
+ * // Simple delay utility
18
+ * await AnimationUtils.delay(2000);
19
+ *
20
+ * // Stop animation
21
+ * animation.stop();
22
+ * ```
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.AnimationUtils = void 0;
26
+ const bitmap_utils_1 = require("./bitmap-utils");
27
+ /**
28
+ * Utility class for creating and managing animations in MentraOS applications
29
+ */
30
+ class AnimationUtils {
31
+ /**
32
+ * Simple async delay helper
33
+ *
34
+ * @param ms - Milliseconds to delay
35
+ * @returns Promise that resolves after the specified delay
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * console.log('Starting...');
40
+ * await AnimationUtils.delay(2000);
41
+ * console.log('2 seconds later!');
42
+ * ```
43
+ */
44
+ static delay(ms) {
45
+ return new Promise(resolve => setTimeout(resolve, ms));
46
+ }
47
+ /**
48
+ * Create bitmap animation from files with advanced configuration
49
+ *
50
+ * @param session - MentraOS app session
51
+ * @param basePath - Directory containing animation frames
52
+ * @param frameCount - Number of frames to load
53
+ * @param config - Animation configuration options
54
+ * @returns Promise resolving to animation controller
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * // Simple animation
59
+ * const animation = await AnimationUtils.createBitmapAnimation(
60
+ * session, './animations', 10
61
+ * );
62
+ *
63
+ * // Advanced configuration
64
+ * const advancedAnimation = await AnimationUtils.createBitmapAnimation(
65
+ * session, './sprites', 8, {
66
+ * intervalMs: 1000,
67
+ * repeat: true,
68
+ * loadOptions: { filePattern: 'sprite_{i}.bmp', startFrame: 0 },
69
+ * onFrame: (frame, total) => console.log(`Frame ${frame}/${total}`),
70
+ * onError: (error) => console.error('Animation error:', error)
71
+ * }
72
+ * );
73
+ * ```
74
+ */
75
+ static async createBitmapAnimation(session, basePath, frameCount, config = {}) {
76
+ const { intervalMs = 1750, // Optimized for MentraOS hardware
77
+ repeat = false, validateFrames = true, loadOptions = {}, onStart, onStop, onFrame, onError } = config;
78
+ try {
79
+ console.log(`🎬 Loading ${frameCount} animation frames from ${basePath}...`);
80
+ // Load frames with validation
81
+ const frames = await bitmap_utils_1.BitmapUtils.loadBmpFrames(basePath, frameCount, {
82
+ validateFrames,
83
+ ...loadOptions
84
+ });
85
+ if (frames.length === 0) {
86
+ throw new Error('No frames loaded for animation');
87
+ }
88
+ console.log(`📚 Animation ready: ${frames.length} frames at ${intervalMs}ms intervals`);
89
+ // Create enhanced animation with the loaded frames
90
+ return this.createBitmapAnimationFromFrames(session, frames, {
91
+ intervalMs,
92
+ repeat,
93
+ onStart,
94
+ onStop,
95
+ onFrame,
96
+ onError
97
+ });
98
+ }
99
+ catch (error) {
100
+ const errorMsg = `Failed to create animation: ${error instanceof Error ? error.message : 'Unknown error'}`;
101
+ console.error(`❌ ${errorMsg}`);
102
+ if (onError) {
103
+ onError(errorMsg);
104
+ }
105
+ throw new Error(errorMsg);
106
+ }
107
+ }
108
+ /**
109
+ * Create bitmap animation from pre-loaded frame data
110
+ *
111
+ * @param session - MentraOS app session
112
+ * @param frames - Array of hex-encoded bitmap data
113
+ * @param config - Animation configuration options
114
+ * @returns Animation controller
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const frames = ['424d461a...', '424d461b...', '424d461c...'];
119
+ * const animation = AnimationUtils.createBitmapAnimationFromFrames(
120
+ * session, frames, { intervalMs: 1500, repeat: true }
121
+ * );
122
+ * ```
123
+ */
124
+ static createBitmapAnimationFromFrames(session, frames, config = {}) {
125
+ const { intervalMs = 1750, repeat = false, onStart, onStop, onFrame, onError } = config;
126
+ let isRunning = false;
127
+ let currentFrame = 0;
128
+ let animationController = null;
129
+ const controller = {
130
+ stop: () => {
131
+ if (animationController) {
132
+ animationController.stop();
133
+ animationController = null;
134
+ }
135
+ isRunning = false;
136
+ if (onStop) {
137
+ onStop();
138
+ }
139
+ console.log('🛑 Animation stopped');
140
+ },
141
+ isRunning: () => isRunning,
142
+ getCurrentFrame: () => currentFrame,
143
+ getTotalFrames: () => frames.length
144
+ };
145
+ try {
146
+ // Start the animation using the session's built-in method
147
+ animationController = session.layouts.showBitmapAnimation(frames, intervalMs, repeat);
148
+ isRunning = true;
149
+ if (onStart) {
150
+ onStart();
151
+ }
152
+ console.log(`🎬 Animation started: ${frames.length} frames at ${intervalMs}ms${repeat ? ' (repeating)' : ''}`);
153
+ // If we have frame callbacks, we need to track timing manually
154
+ // This is a limitation of the current SDK - we can't hook into individual frame displays
155
+ if (onFrame) {
156
+ let frameTracker = 0;
157
+ // Call onFrame for the first frame immediately
158
+ onFrame(frameTracker, frames.length);
159
+ const frameInterval = setInterval(() => {
160
+ if (!isRunning) {
161
+ clearInterval(frameInterval);
162
+ return;
163
+ }
164
+ frameTracker = (frameTracker + 1) % frames.length;
165
+ onFrame(frameTracker, frames.length);
166
+ // If not repeating and we've shown all frames, stop tracking
167
+ if (!repeat && frameTracker === frames.length - 1) {
168
+ clearInterval(frameInterval);
169
+ }
170
+ }, intervalMs);
171
+ // Override stop to also clear frame tracking
172
+ const originalStop = controller.stop;
173
+ controller.stop = () => {
174
+ clearInterval(frameInterval);
175
+ originalStop();
176
+ };
177
+ }
178
+ }
179
+ catch (error) {
180
+ const errorMsg = `Failed to start animation: ${error instanceof Error ? error.message : 'Unknown error'}`;
181
+ console.error(`❌ ${errorMsg}`);
182
+ if (onError) {
183
+ onError(errorMsg);
184
+ }
185
+ throw new Error(errorMsg);
186
+ }
187
+ return controller;
188
+ }
189
+ /**
190
+ * Create a sequence of bitmap displays with custom timing
191
+ *
192
+ * @param session - MentraOS app session
193
+ * @param sequence - Array of frame data with individual timing
194
+ * @returns Promise that resolves when sequence completes
195
+ *
196
+ * @example
197
+ * ```typescript
198
+ * await AnimationUtils.createBitmapSequence(session, [
199
+ * { frame: frame1Hex, duration: 1000 },
200
+ * { frame: frame2Hex, duration: 500 },
201
+ * { frame: frame3Hex, duration: 2000 }
202
+ * ]);
203
+ * ```
204
+ */
205
+ static async createBitmapSequence(session, sequence) {
206
+ console.log(`🎭 Starting bitmap sequence: ${sequence.length} frames with custom timing`);
207
+ for (let i = 0; i < sequence.length; i++) {
208
+ const { frame, duration } = sequence[i];
209
+ try {
210
+ console.log(`📽️ Sequence frame ${i + 1}/${sequence.length} (${duration}ms)`);
211
+ session.layouts.showBitmapView(frame);
212
+ if (i < sequence.length - 1) { // Don't delay after the last frame
213
+ await this.delay(duration);
214
+ }
215
+ }
216
+ catch (error) {
217
+ console.error(`❌ Error in sequence frame ${i + 1}:`, error);
218
+ throw error;
219
+ }
220
+ }
221
+ console.log('✅ Bitmap sequence completed');
222
+ }
223
+ /**
224
+ * Measure animation timing performance
225
+ *
226
+ * @param targetInterval - Expected interval between frames in ms
227
+ * @param measureDuration - How long to measure in ms (default: 10 seconds)
228
+ * @returns Promise resolving to timing performance data
229
+ *
230
+ * @example
231
+ * ```typescript
232
+ * const timing = await AnimationUtils.measureTiming(1750, 10000);
233
+ * console.log(`Target: ${timing.targetInterval}ms, Actual: ${timing.actualInterval}ms`);
234
+ * console.log(`Drift: ${timing.drift}ms, FPS: ${timing.fps.toFixed(1)}`);
235
+ * ```
236
+ */
237
+ static async measureTiming(targetInterval, measureDuration = 10000) {
238
+ return new Promise((resolve) => {
239
+ const timestamps = [];
240
+ let startTime = Date.now();
241
+ const measureInterval = setInterval(() => {
242
+ timestamps.push(Date.now());
243
+ }, targetInterval);
244
+ setTimeout(() => {
245
+ clearInterval(measureInterval);
246
+ if (timestamps.length < 2) {
247
+ resolve({
248
+ targetInterval,
249
+ actualInterval: targetInterval,
250
+ drift: 0,
251
+ fps: 1000 / targetInterval
252
+ });
253
+ return;
254
+ }
255
+ // Calculate actual interval
256
+ const intervals = [];
257
+ for (let i = 1; i < timestamps.length; i++) {
258
+ intervals.push(timestamps[i] - timestamps[i - 1]);
259
+ }
260
+ const actualInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length;
261
+ const drift = actualInterval - targetInterval;
262
+ const fps = 1000 / actualInterval;
263
+ resolve({
264
+ targetInterval,
265
+ actualInterval,
266
+ drift,
267
+ fps
268
+ });
269
+ }, measureDuration);
270
+ });
271
+ }
272
+ /**
273
+ * Create optimized animation settings for different hardware
274
+ *
275
+ * @param deviceType - Target device type
276
+ * @returns Recommended animation configuration
277
+ *
278
+ * @example
279
+ * ```typescript
280
+ * const config = AnimationUtils.getOptimizedConfig('even-realities-g1');
281
+ * const animation = await AnimationUtils.createBitmapAnimation(
282
+ * session, './frames', 10, config
283
+ * );
284
+ * ```
285
+ */
286
+ static getOptimizedConfig(deviceType) {
287
+ switch (deviceType) {
288
+ case 'even-realities-g1':
289
+ return {
290
+ intervalMs: 1650, // Tested optimal timing for Even Realities G1
291
+ repeat: false,
292
+ validateFrames: true,
293
+ loadOptions: {
294
+ validateFrames: true,
295
+ skipMissingFrames: false
296
+ }
297
+ };
298
+ case 'generic':
299
+ default:
300
+ return {
301
+ intervalMs: 1000,
302
+ repeat: false,
303
+ validateFrames: true,
304
+ loadOptions: {
305
+ validateFrames: true,
306
+ skipMissingFrames: false
307
+ }
308
+ };
309
+ }
310
+ }
311
+ /**
312
+ * Preload and cache animation frames for better performance
313
+ *
314
+ * @param basePath - Directory containing frames
315
+ * @param frameCount - Number of frames to preload
316
+ * @param options - Loading options
317
+ * @returns Promise resolving to cached frame data
318
+ *
319
+ * @example
320
+ * ```typescript
321
+ * // Preload frames
322
+ * const cachedFrames = await AnimationUtils.preloadFrames('./animations', 10);
323
+ *
324
+ * // Use cached frames multiple times
325
+ * const animation1 = AnimationUtils.createBitmapAnimationFromFrames(session, cachedFrames);
326
+ * const animation2 = AnimationUtils.createBitmapAnimationFromFrames(session, cachedFrames);
327
+ * ```
328
+ */
329
+ static async preloadFrames(basePath, frameCount, options = {}) {
330
+ console.log(`📦 Preloading ${frameCount} frames from ${basePath}...`);
331
+ const frames = await bitmap_utils_1.BitmapUtils.loadBmpFrames(basePath, frameCount, {
332
+ validateFrames: true,
333
+ ...options
334
+ });
335
+ console.log(`✅ Preloaded ${frames.length} frames (${frames.reduce((total, frame) => total + frame.length, 0)} total characters)`);
336
+ return frames;
337
+ }
338
+ }
339
+ exports.AnimationUtils = AnimationUtils;
@@ -0,0 +1,147 @@
1
+ /**
2
+ * 🎨 Bitmap Utilities Module
3
+ *
4
+ * Provides helper functions for working with bitmap images in MentraOS applications.
5
+ * Includes file loading, data validation, and format conversion utilities.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { BitmapUtils } from '@mentra/sdk';
10
+ *
11
+ * // Load a single BMP file
12
+ * const bmpHex = await BitmapUtils.loadBmpAsHex('./my-image.bmp');
13
+ * session.layouts.showBitmapView(bmpHex);
14
+ *
15
+ * // Load multiple animation frames
16
+ * const frames = await BitmapUtils.loadBmpFrames('./animations', 10);
17
+ * session.layouts.showBitmapAnimation(frames, 1500, true);
18
+ * ```
19
+ */
20
+ /**
21
+ * Validation result for bitmap data
22
+ */
23
+ export interface BitmapValidation {
24
+ /** Whether the bitmap data is valid */
25
+ isValid: boolean;
26
+ /** Total byte count of the bitmap */
27
+ byteCount: number;
28
+ /** Number of black (non-FF) pixels found */
29
+ blackPixels: number;
30
+ /** Array of validation error messages */
31
+ errors: string[];
32
+ /** Additional metadata about the bitmap */
33
+ metadata?: {
34
+ /** Expected bitmap dimensions (if detectable) */
35
+ dimensions?: {
36
+ width: number;
37
+ height: number;
38
+ };
39
+ /** Bitmap file format info */
40
+ format?: string;
41
+ };
42
+ }
43
+ /**
44
+ * Options for loading bitmap frames
45
+ */
46
+ export interface LoadFramesOptions {
47
+ /** File name pattern (default: 'animation_10_frame_{i}.bmp') */
48
+ filePattern?: string;
49
+ /** Starting frame number (default: 1) */
50
+ startFrame?: number;
51
+ /** Validate each frame during loading (default: true) */
52
+ validateFrames?: boolean;
53
+ /** Continue loading if a frame is missing (default: false) */
54
+ skipMissingFrames?: boolean;
55
+ }
56
+ /**
57
+ * Utility class for working with bitmap images in MentraOS applications
58
+ */
59
+ export declare class BitmapUtils {
60
+ /**
61
+ * Load a BMP file as hex string from filesystem
62
+ *
63
+ * @param filePath - Path to the BMP file
64
+ * @returns Promise resolving to hex-encoded bitmap data
65
+ * @throws Error if file cannot be read or is not a valid BMP
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * const bmpHex = await BitmapUtils.loadBmpAsHex('./assets/icon.bmp');
70
+ * session.layouts.showBitmapView(bmpHex);
71
+ * ```
72
+ */
73
+ static loadBmpAsHex(filePath: string): Promise<string>;
74
+ /**
75
+ * Load multiple BMP frames as hex array for animations
76
+ *
77
+ * @param basePath - Directory containing the frame files
78
+ * @param frameCount - Number of frames to load
79
+ * @param options - Loading options and configuration
80
+ * @returns Promise resolving to array of hex-encoded bitmap data
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * // Load 10 frames with default pattern
85
+ * const frames = await BitmapUtils.loadBmpFrames('./animations', 10);
86
+ *
87
+ * // Load with custom pattern
88
+ * const customFrames = await BitmapUtils.loadBmpFrames('./sprites', 8, {
89
+ * filePattern: 'sprite_{i}.bmp',
90
+ * startFrame: 0
91
+ * });
92
+ * ```
93
+ */
94
+ static loadBmpFrames(basePath: string, frameCount: number, options?: LoadFramesOptions): Promise<string[]>;
95
+ /**
96
+ * Validate BMP hex data integrity and extract metadata
97
+ *
98
+ * @param hexString - Hex-encoded bitmap data
99
+ * @returns Validation result with detailed information
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const validation = BitmapUtils.validateBmpHex(bmpHex);
104
+ * if (!validation.isValid) {
105
+ * console.error('Invalid bitmap:', validation.errors);
106
+ * } else {
107
+ * console.log(`Valid bitmap: ${validation.blackPixels} black pixels`);
108
+ * }
109
+ * ```
110
+ */
111
+ static validateBmpHex(hexString: string): BitmapValidation;
112
+ /**
113
+ * Convert bitmap data between different formats
114
+ *
115
+ * @param data - Input bitmap data
116
+ * @param fromFormat - Source format ('hex' | 'base64' | 'buffer')
117
+ * @param toFormat - Target format ('hex' | 'base64' | 'buffer')
118
+ * @returns Converted bitmap data
119
+ *
120
+ * @example
121
+ * ```typescript
122
+ * const base64Data = BitmapUtils.convertFormat(hexData, 'hex', 'base64');
123
+ * const bufferData = BitmapUtils.convertFormat(base64Data, 'base64', 'buffer');
124
+ * ```
125
+ */
126
+ static convertFormat(data: string | Buffer, fromFormat: 'hex' | 'base64' | 'buffer', toFormat: 'hex' | 'base64' | 'buffer'): string | Buffer;
127
+ /**
128
+ * Get bitmap information without full validation
129
+ *
130
+ * @param hexString - Hex-encoded bitmap data
131
+ * @returns Basic bitmap information
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * const info = BitmapUtils.getBitmapInfo(bmpHex);
136
+ * console.log(`Bitmap: ${info.width}x${info.height}, ${info.blackPixels} black pixels`);
137
+ * ```
138
+ */
139
+ static getBitmapInfo(hexString: string): {
140
+ byteCount: number;
141
+ blackPixels: number;
142
+ width?: number;
143
+ height?: number;
144
+ isValidBmp: boolean;
145
+ };
146
+ }
147
+ //# sourceMappingURL=bitmap-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bitmap-utils.d.ts","sourceRoot":"","sources":["../../src/utils/bitmap-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE;QACT,iDAAiD;QACjD,UAAU,CAAC,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAC/C,8BAA8B;QAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8DAA8D;IAC9D,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;GAEG;AACH,qBAAa,WAAW;IAEtB;;;;;;;;;;;;OAYG;WACU,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAqB5D;;;;;;;;;;;;;;;;;;;OAmBG;WACU,aAAa,CACxB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,MAAM,EAAE,CAAC;IA0DpB;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB;IAkF1D;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,aAAa,CAClB,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,UAAU,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,EACvC,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,GACpC,MAAM,GAAG,MAAM;IA+BlB;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG;QACvC,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,OAAO,CAAC;KACrB;CAmCF"}