@mentra/sdk 2.1.3 → 2.1.5

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.
@@ -15,7 +15,7 @@
15
15
  * layouts.showReferenceCard('Weather', 'Sunny and 75°F');
16
16
  * ```
17
17
  */
18
- import { DisplayRequest, ViewType } from '../../types';
18
+ import { DisplayRequest, ViewType } from "../../types";
19
19
  export declare class LayoutManager {
20
20
  private packageName;
21
21
  private sendMessage;
@@ -109,28 +109,25 @@ export declare class LayoutManager {
109
109
  durationMs?: number;
110
110
  }): void;
111
111
  /**
112
- * 📇 Shows a bitmap
113
- *
114
- * Uses the proven animation system internally for proper L/R eye synchronization.
115
- * This ensures single bitmap displays work consistently with the same
116
- * hardware-optimized timing as animations.
117
- *
118
- * @param data - hex or base64 encoded bitmap data
119
- * @param options - Optional parameters (view, duration)
120
- *
121
- * @example
122
- * ```typescript
123
- * layouts.showBitmapView(
124
- * yourHexOrBase64EncodedBitmapDataString
125
- * );
126
- * ```
127
- */
112
+ * 📇 Shows a bitmap
113
+ *
114
+ * Uses the proven animation system internally for proper L/R eye synchronization.
115
+ * This ensures single bitmap displays work consistently with the same
116
+ * hardware-optimized timing as animations.
117
+ *
118
+ * @param data - hex or base64 encoded bitmap data
119
+ * @param options - Optional parameters (view, duration)
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * layouts.showBitmapView(
124
+ * yourHexOrBase64EncodedBitmapDataString
125
+ * );
126
+ * ```
127
+ */
128
128
  showBitmapView(data: string, options?: {
129
129
  view?: ViewType;
130
- durationMs?: number;
131
- }): {
132
- stop: () => void;
133
- };
130
+ }): void;
134
131
  /**
135
132
  * 📊 Shows a dashboard card with left and right text
136
133
  *
@@ -1 +1 @@
1
- {"version":3,"file":"layouts.d.ts","sourceRoot":"","sources":["../../../src/app/session/layouts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EACL,cAAc,EAOd,QAAQ,EAKT,MAAM,aAAa,CAAC;AAErB,qBAAa,aAAa;IAQtB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,WAAW;IARrB;;;;;OAKG;gBAEO,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI;IAGxD;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAuG1B;;;;;;;;;;;;;;;;;OAiBG;IACH,YAAY,CACV,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAC;IAuCnD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,kBAAkB,CAChB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAcpD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,iBAAiB,CACf,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAclD;;;;;;;;;;;;;;;;KAgBC;IACH,cAAc,CACZ,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;cA0HzC,MAAM,IAAI;;IA/FrB;;;;;;;;;;;;;;;;OAgBG;IACH,iBAAiB,CACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAcpD;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,QAAQ,CAAA;KAAE;IAUvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,mBAAmB,CACjB,eAAe,EAAE,MAAM,EAAE,EACzB,UAAU,GAAE,MAAa,EACzB,MAAM,GAAE,OAAe,EACvB,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,QAAQ,CAAA;KAAE,GAC5B;QAAE,IAAI,EAAE,MAAM,IAAI,CAAA;KAAE;CA8BxB"}
1
+ {"version":3,"file":"layouts.d.ts","sourceRoot":"","sources":["../../../src/app/session/layouts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EACL,cAAc,EAOd,QAAQ,EAKT,MAAM,aAAa,CAAC;AAErB,qBAAa,aAAa;IAQtB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,WAAW;IARrB;;;;;OAKG;gBAEO,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI;IAGxD;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAoH1B;;;;;;;;;;;;;;;;;OAiBG;IACH,YAAY,CACV,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAuCpD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,kBAAkB,CAChB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAYpD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,iBAAiB,CACf,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAYpD;;;;;;;;;;;;;;;;OAgBG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,QAAQ,CAAA;KAAE;IAQ1D;;;;;;;;;;;;;;;;OAgBG;IACH,iBAAiB,CACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAgBpD;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,QAAQ,CAAA;KAAE;IAOvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,mBAAmB,CACjB,eAAe,EAAE,MAAM,EAAE,EACzB,UAAU,GAAE,MAAa,EACzB,MAAM,GAAE,OAAe,EACvB,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,QAAQ,CAAA;KAAE,GAC5B;QAAE,IAAI,EAAE,MAAM,IAAI,CAAA;KAAE;CAiCxB"}
@@ -51,7 +51,7 @@ class LayoutManager {
51
51
  // Layout-specific validations
52
52
  switch (layout.layoutType) {
53
53
  case types_1.LayoutType.TEXT_WALL:
54
- if (typeof layout.text !== 'string') {
54
+ if (typeof layout.text !== "string") {
55
55
  throw new Error("TextWall layout must have a text property");
56
56
  }
57
57
  // Ensure text is not too long (prevent performance issues)
@@ -61,38 +61,39 @@ class LayoutManager {
61
61
  break;
62
62
  case types_1.LayoutType.DOUBLE_TEXT_WALL:
63
63
  const doubleText = layout;
64
- if (typeof doubleText.topText !== 'string') {
64
+ if (typeof doubleText.topText !== "string") {
65
65
  throw new Error("DoubleTextWall layout must have a topText property");
66
66
  }
67
- if (typeof doubleText.bottomText !== 'string') {
67
+ if (typeof doubleText.bottomText !== "string") {
68
68
  throw new Error("DoubleTextWall layout must have a bottomText property");
69
69
  }
70
70
  break;
71
71
  case types_1.LayoutType.REFERENCE_CARD:
72
72
  const refCard = layout;
73
- if (typeof refCard.title !== 'string') {
73
+ if (typeof refCard.title !== "string") {
74
74
  throw new Error("ReferenceCard layout must have a title property");
75
75
  }
76
- if (typeof refCard.text !== 'string') {
76
+ if (typeof refCard.text !== "string") {
77
77
  throw new Error("ReferenceCard layout must have a text property");
78
78
  }
79
79
  break;
80
80
  case types_1.LayoutType.DASHBOARD_CARD:
81
81
  const dashCard = layout;
82
- if (typeof dashCard.leftText !== 'string') {
82
+ if (typeof dashCard.leftText !== "string") {
83
83
  throw new Error("DashboardCard layout must have a leftText property");
84
84
  }
85
- if (typeof dashCard.rightText !== 'string') {
85
+ if (typeof dashCard.rightText !== "string") {
86
86
  throw new Error("DashboardCard layout must have a rightText property");
87
87
  }
88
88
  break;
89
89
  case types_1.LayoutType.BITMAP_VIEW:
90
90
  const bitmapView = layout;
91
- if (typeof bitmapView.data !== 'string') {
91
+ if (typeof bitmapView.data !== "string") {
92
92
  throw new Error("BitmapView layout must have a data property");
93
93
  }
94
94
  // Check if data is too large (prevent OOM errors)
95
- if (bitmapView.data.length > 1000000) { // 1MB limit
95
+ if (bitmapView.data.length > 1000000) {
96
+ // 1MB limit
96
97
  throw new Error("Bitmap data is too large (>1MB), please reduce size");
97
98
  }
98
99
  break;
@@ -107,7 +108,7 @@ class LayoutManager {
107
108
  }
108
109
  // Validate duration if provided
109
110
  if (durationMs !== undefined) {
110
- if (typeof durationMs !== 'number' || durationMs < 0) {
111
+ if (typeof durationMs !== "number" || durationMs < 0) {
111
112
  console.warn(`Invalid duration: ${durationMs}, ignoring`);
112
113
  durationMs = undefined;
113
114
  }
@@ -115,12 +116,12 @@ class LayoutManager {
115
116
  // Create the display request with validated data
116
117
  return {
117
118
  timestamp: new Date(),
118
- sessionId: '', // Will be filled by session
119
+ sessionId: "", // Will be filled by session
119
120
  type: types_1.AppToCloudMessageType.DISPLAY_REQUEST,
120
121
  packageName: this.packageName,
121
122
  view,
122
123
  layout,
123
- durationMs
124
+ durationMs,
124
125
  };
125
126
  }
126
127
  catch (error) {
@@ -154,14 +155,14 @@ class LayoutManager {
154
155
  console.warn("showTextWall called with null/undefined text");
155
156
  }
156
157
  // Ensure text is a string
157
- if (typeof text !== 'string') {
158
+ if (typeof text !== "string") {
158
159
  text = String(text); // Convert to string
159
160
  console.warn("showTextWall: Non-string input converted to string");
160
161
  }
161
162
  // Create layout with validated text
162
163
  const layout = {
163
164
  layoutType: types_1.LayoutType.TEXT_WALL,
164
- text
165
+ text,
165
166
  };
166
167
  // Create and send display event with error handling
167
168
  try {
@@ -203,7 +204,7 @@ class LayoutManager {
203
204
  const layout = {
204
205
  layoutType: types_1.LayoutType.DOUBLE_TEXT_WALL,
205
206
  topText,
206
- bottomText
207
+ bottomText,
207
208
  };
208
209
  this.sendMessage(this.createDisplayEvent(layout, options?.view, options?.durationMs));
209
210
  }
@@ -232,45 +233,33 @@ class LayoutManager {
232
233
  const layout = {
233
234
  layoutType: types_1.LayoutType.REFERENCE_CARD,
234
235
  title,
235
- text
236
+ text,
236
237
  };
237
238
  this.sendMessage(this.createDisplayEvent(layout, options?.view, options?.durationMs));
238
239
  }
239
240
  /**
240
- * 📇 Shows a bitmap
241
- *
242
- * Uses the proven animation system internally for proper L/R eye synchronization.
243
- * This ensures single bitmap displays work consistently with the same
244
- * hardware-optimized timing as animations.
245
- *
246
- * @param data - hex or base64 encoded bitmap data
247
- * @param options - Optional parameters (view, duration)
248
- *
249
- * @example
250
- * ```typescript
251
- * layouts.showBitmapView(
252
- * yourHexOrBase64EncodedBitmapDataString
253
- * );
254
- * ```
255
- */
241
+ * 📇 Shows a bitmap
242
+ *
243
+ * Uses the proven animation system internally for proper L/R eye synchronization.
244
+ * This ensures single bitmap displays work consistently with the same
245
+ * hardware-optimized timing as animations.
246
+ *
247
+ * @param data - hex or base64 encoded bitmap data
248
+ * @param options - Optional parameters (view, duration)
249
+ *
250
+ * @example
251
+ * ```typescript
252
+ * layouts.showBitmapView(
253
+ * yourHexOrBase64EncodedBitmapDataString
254
+ * );
255
+ * ```
256
+ */
256
257
  showBitmapView(data, options) {
257
- // Use the proven animation system for single frame display
258
- // This ensures proper L/R eye synchronization that works with
259
- // the optimized iOS BLE transmission system
260
- const duration = options?.durationMs || 5000; // Default 5 seconds
261
- const frameInterval = 1650; // Use same interval as working animations
262
- // Create short multi-frame animation for better L/R sync
263
- // Repeat the same frame a few times to mimic continuous animation behavior
264
- const frames = [data, data, data]; // Triple the frame for stability
265
- const animation = this.showBitmapAnimation(frames, // Multi-frame array (same image repeated)
266
- frameInterval, // Use proven 1650ms interval
267
- false, // Don't repeat infinitely
268
- { view: options?.view });
269
- // Auto-stop after duration
270
- setTimeout(() => {
271
- animation.stop();
272
- }, duration);
273
- return animation;
258
+ const layout = {
259
+ layoutType: types_1.LayoutType.BITMAP_VIEW,
260
+ data: data,
261
+ };
262
+ this.sendMessage(this.createDisplayEvent(layout, options?.view));
274
263
  }
275
264
  /**
276
265
  * 📊 Shows a dashboard card with left and right text
@@ -293,7 +282,7 @@ class LayoutManager {
293
282
  const layout = {
294
283
  layoutType: types_1.LayoutType.DASHBOARD_CARD,
295
284
  leftText,
296
- rightText
285
+ rightText,
297
286
  };
298
287
  this.sendMessage(this.createDisplayEvent(layout, options?.view || types_1.ViewType.DASHBOARD, options?.durationMs));
299
288
  }
@@ -315,7 +304,7 @@ class LayoutManager {
315
304
  */
316
305
  clearView(options) {
317
306
  const layout = {
318
- layoutType: types_1.LayoutType.CLEAR_VIEW
307
+ layoutType: types_1.LayoutType.CLEAR_VIEW,
319
308
  };
320
309
  this.sendMessage(this.createDisplayEvent(layout, options?.view));
321
310
  }
@@ -359,17 +348,17 @@ class LayoutManager {
359
348
  layoutType: types_1.LayoutType.BITMAP_ANIMATION,
360
349
  frames: bitmapDataArray,
361
350
  interval: intervalMs,
362
- repeat: repeat
351
+ repeat: repeat,
363
352
  };
364
353
  this.sendMessage(this.createDisplayEvent(layout, options?.view));
365
- console.log(`🎬 Sent batched animation to iOS: ${bitmapDataArray.length} frames at ${intervalMs}ms${repeat ? ' (repeating)' : ''}`);
354
+ console.log(`🎬 Sent batched animation to iOS: ${bitmapDataArray.length} frames at ${intervalMs}ms${repeat ? " (repeating)" : ""}`);
366
355
  // Return controller for compatibility
367
356
  return {
368
357
  stop: () => {
369
358
  // Send stop command to iOS
370
359
  this.clearView();
371
- console.log('🛑 Animation stop requested');
372
- }
360
+ console.log("🛑 Animation stop requested");
361
+ },
373
362
  };
374
363
  }
375
364
  }
@@ -1,10 +1,10 @@
1
- import { WebSocket } from 'ws';
2
- import { AppI, TranscriptI } from './models';
3
- import { DisplayRequest } from './layouts';
4
- import { Transform } from 'stream';
5
- import { ConversationTranscriber, PushAudioInputStream } from 'microsoft-cognitiveservices-speech-sdk';
6
- import { ExtendedStreamType } from './streams';
7
- import pino from 'pino';
1
+ import { WebSocket } from "ws";
2
+ import { AppI, TranscriptI } from "./models";
3
+ import { DisplayRequest } from "./layouts";
4
+ import { Transform } from "stream";
5
+ import { ConversationTranscriber, PushAudioInputStream } from "microsoft-cognitiveservices-speech-sdk";
6
+ import { ExtendedStreamType } from "./streams";
7
+ import pino from "pino";
8
8
  /**
9
9
  * Session for an application
10
10
  */
@@ -53,7 +53,7 @@ export interface UserSession {
53
53
  installedApps: AppI[];
54
54
  activeAppSessions: string[];
55
55
  loadingApps: Set<string>;
56
- appSubscriptions: Map<string, ExtendedStreamType[]> | Object;
56
+ appSubscriptions: Map<string, ExtendedStreamType[]> | object;
57
57
  appConnections: Map<string, WebSocket>;
58
58
  websocket: WebSocket;
59
59
  transcript: TranscriptI;
@@ -1 +1 @@
1
- {"version":3,"file":"user-session.d.ts","sourceRoot":"","sources":["../../src/types/user-session.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EAAE,IAAI,EAAe,WAAW,EAAE,MAAM,UAAU,CAAC;AAE1D,OAAO,EAAE,cAAc,EAAU,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACrB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAc,MAAM,WAAW,CAAC;AAE3D,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;GAEG;AAkBH;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAgB,SAAQ,SAAS;CAAG;AAErD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,kBAAkB,CAAC,cAAc,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC;IACtF,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IACpE,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;CACpE;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED;;GAEG;AAEH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;IAChB,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IAG5B,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;IAGpB,aAAa,EAAE,IAAI,EAAE,CAAC;IACtB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,CAAC,GAAG,MAAM,CAAC;IAC7D,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAEvC,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,WAAW,CAAA;IAGvB,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,UAAU,CAAC,EAAE,uBAAuB,CAAC;IACrC,cAAc,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAG9B,aAAa,EAAE,eAAe,EAAE,CAAC;IAGjC,cAAc,CAAC,EAAE,eAAe,CAAC;IACjC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAG5B,YAAY,EAAE,kBAAkB,EAAE,CAAC;CACpC;AAED;;GAEG"}
1
+ {"version":3,"file":"user-session.d.ts","sourceRoot":"","sources":["../../src/types/user-session.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EAAE,IAAI,EAAe,WAAW,EAAE,MAAM,UAAU,CAAC;AAE1D,OAAO,EAAE,cAAc,EAAU,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACrB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAc,MAAM,WAAW,CAAC;AAE3D,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;GAEG;AAkBH;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAgB,SAAQ,SAAS;CAAG;AAErD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,kBAAkB,CAChB,cAAc,EAAE,cAAc,EAC9B,WAAW,EAAE,WAAW,GACvB,OAAO,CAAC;IACX,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IACpE,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;CACpE;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED;;GAEG;AAEH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;IAChB,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IAG5B,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;IAGpB,aAAa,EAAE,IAAI,EAAE,CAAC;IACtB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,CAAC,GAAG,MAAM,CAAC;IAC7D,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAEvC,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,WAAW,CAAC;IAGxB,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,UAAU,CAAC,EAAE,uBAAuB,CAAC;IACrC,cAAc,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAG9B,aAAa,EAAE,eAAe,EAAE,CAAC;IAGjC,cAAc,CAAC,EAAE,eAAe,CAAC;IACjC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAG5B,YAAY,EAAE,kBAAkB,EAAE,CAAC;CACpC;AAED;;GAEG"}
@@ -20,8 +20,8 @@
20
20
  * animation.stop();
21
21
  * ```
22
22
  */
23
- import { AppSession } from '../app/session';
24
- import { LoadFramesOptions } from './bitmap-utils';
23
+ import { AppSession } from "../app/session";
24
+ import { LoadFramesOptions } from "./bitmap-utils";
25
25
  /**
26
26
  * Configuration options for bitmap animations
27
27
  */
@@ -132,7 +132,7 @@ export declare class AnimationUtils {
132
132
  * );
133
133
  * ```
134
134
  */
135
- static createBitmapAnimationFromFrames(session: AppSession, frames: string[], config?: Omit<AnimationConfig, 'loadOptions' | 'validateFrames'>): AnimationController;
135
+ static createBitmapAnimationFromFrames(session: AppSession, frames: string[], config?: Omit<AnimationConfig, "loadOptions" | "validateFrames">): AnimationController;
136
136
  /**
137
137
  * Create a sequence of bitmap displays with custom timing
138
138
  *
@@ -182,7 +182,7 @@ export declare class AnimationUtils {
182
182
  * );
183
183
  * ```
184
184
  */
185
- static getOptimizedConfig(deviceType: 'even-realities-g1' | 'generic'): AnimationConfig;
185
+ static getOptimizedConfig(deviceType: "even-realities-g1" | "generic"): AnimationConfig;
186
186
  /**
187
187
  * Preload and cache animation frames for better performance
188
188
  *
@@ -1 +1 @@
1
- {"version":3,"file":"animation-utils.d.ts","sourceRoot":"","sources":["../../src/utils/animation-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAe,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gEAAgE;IAChE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,sDAAsD;IACtD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,yBAAyB;IACzB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,OAAO,CAAC;IACzB,8BAA8B;IAC9B,eAAe,EAAE,MAAM,MAAM,CAAC;IAC9B,4BAA4B;IAC5B,cAAc,EAAE,MAAM,MAAM,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB,qCAAqC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,cAAc,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,qBAAa,cAAc;IAEvB;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;WACU,qBAAqB,CAC9B,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,eAAoB,GAC7B,OAAO,CAAC,mBAAmB,CAAC;IAgD/B;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,+BAA+B,CAClC,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,EAAE,EAChB,MAAM,GAAE,IAAI,CAAC,eAAe,EAAE,aAAa,GAAG,gBAAgB,CAAM,GACrE,mBAAmB;IAyFtB;;;;;;;;;;;;;;;OAeG;WACU,oBAAoB,CAC7B,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,GACrD,OAAO,CAAC,IAAI,CAAC;IAuBhB;;;;;;;;;;;;;OAaG;WACU,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,eAAe,GAAE,MAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IA0CxG;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,kBAAkB,CAAC,UAAU,EAAE,mBAAmB,GAAG,SAAS,GAAG,eAAe;IA2BvF;;;;;;;;;;;;;;;;;OAiBG;WACU,aAAa,CACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,iBAAsB,GAChC,OAAO,CAAC,MAAM,EAAE,CAAC;CAYvB"}
1
+ {"version":3,"file":"animation-utils.d.ts","sourceRoot":"","sources":["../../src/utils/animation-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAe,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gEAAgE;IAChE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,sDAAsD;IACtD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,yBAAyB;IACzB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,OAAO,CAAC;IACzB,8BAA8B;IAC9B,eAAe,EAAE,MAAM,MAAM,CAAC;IAC9B,4BAA4B;IAC5B,cAAc,EAAE,MAAM,MAAM,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,qCAAqC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,cAAc,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,qBAAa,cAAc;IACzB;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;WACU,qBAAqB,CAChC,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,eAAoB,GAC3B,OAAO,CAAC,mBAAmB,CAAC;IAkD/B;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,+BAA+B,CACpC,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,EAAE,EAChB,MAAM,GAAE,IAAI,CAAC,eAAe,EAAE,aAAa,GAAG,gBAAgB,CAAM,GACnE,mBAAmB;IA6FtB;;;;;;;;;;;;;;;OAeG;WACU,oBAAoB,CAC/B,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,GACnD,OAAO,CAAC,IAAI,CAAC;IA2BhB;;;;;;;;;;;;;OAaG;WACU,aAAa,CACxB,cAAc,EAAE,MAAM,EACtB,eAAe,GAAE,MAAc,GAC9B,OAAO,CAAC,UAAU,CAAC;IA2CtB;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,kBAAkB,CACvB,UAAU,EAAE,mBAAmB,GAAG,SAAS,GAC1C,eAAe;IA2BlB;;;;;;;;;;;;;;;;;OAiBG;WACU,aAAa,CACxB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,MAAM,EAAE,CAAC;CAcrB"}
@@ -42,7 +42,7 @@ class AnimationUtils {
42
42
  * ```
43
43
  */
44
44
  static delay(ms) {
45
- return new Promise(resolve => setTimeout(resolve, ms));
45
+ return new Promise((resolve) => setTimeout(resolve, ms));
46
46
  }
47
47
  /**
48
48
  * Create bitmap animation from files with advanced configuration
@@ -74,16 +74,16 @@ class AnimationUtils {
74
74
  */
75
75
  static async createBitmapAnimation(session, basePath, frameCount, config = {}) {
76
76
  const { intervalMs = 1750, // Optimized for MentraOS hardware
77
- repeat = false, validateFrames = true, loadOptions = {}, onStart, onStop, onFrame, onError } = config;
77
+ repeat = false, validateFrames = true, loadOptions = {}, onStart, onStop, onFrame, onError, } = config;
78
78
  try {
79
79
  console.log(`🎬 Loading ${frameCount} animation frames from ${basePath}...`);
80
80
  // Load frames with validation
81
81
  const frames = await bitmap_utils_1.BitmapUtils.loadBmpFrames(basePath, frameCount, {
82
82
  validateFrames,
83
- ...loadOptions
83
+ ...loadOptions,
84
84
  });
85
85
  if (frames.length === 0) {
86
- throw new Error('No frames loaded for animation');
86
+ throw new Error("No frames loaded for animation");
87
87
  }
88
88
  console.log(`📚 Animation ready: ${frames.length} frames at ${intervalMs}ms intervals`);
89
89
  // Create enhanced animation with the loaded frames
@@ -93,11 +93,11 @@ class AnimationUtils {
93
93
  onStart,
94
94
  onStop,
95
95
  onFrame,
96
- onError
96
+ onError,
97
97
  });
98
98
  }
99
99
  catch (error) {
100
- const errorMsg = `Failed to create animation: ${error instanceof Error ? error.message : 'Unknown error'}`;
100
+ const errorMsg = `Failed to create animation: ${error instanceof Error ? error.message : "Unknown error"}`;
101
101
  console.error(`❌ ${errorMsg}`);
102
102
  if (onError) {
103
103
  onError(errorMsg);
@@ -122,9 +122,9 @@ class AnimationUtils {
122
122
  * ```
123
123
  */
124
124
  static createBitmapAnimationFromFrames(session, frames, config = {}) {
125
- const { intervalMs = 1750, repeat = false, onStart, onStop, onFrame, onError } = config;
125
+ const { intervalMs = 1750, repeat = false, onStart, onStop, onFrame, onError, } = config;
126
126
  let isRunning = false;
127
- let currentFrame = 0;
127
+ const currentFrame = 0;
128
128
  let animationController = null;
129
129
  const controller = {
130
130
  stop: () => {
@@ -136,11 +136,11 @@ class AnimationUtils {
136
136
  if (onStop) {
137
137
  onStop();
138
138
  }
139
- console.log('🛑 Animation stopped');
139
+ console.log("🛑 Animation stopped");
140
140
  },
141
141
  isRunning: () => isRunning,
142
142
  getCurrentFrame: () => currentFrame,
143
- getTotalFrames: () => frames.length
143
+ getTotalFrames: () => frames.length,
144
144
  };
145
145
  try {
146
146
  // Start the animation using the session's built-in method
@@ -149,7 +149,7 @@ class AnimationUtils {
149
149
  if (onStart) {
150
150
  onStart();
151
151
  }
152
- console.log(`🎬 Animation started: ${frames.length} frames at ${intervalMs}ms${repeat ? ' (repeating)' : ''}`);
152
+ console.log(`🎬 Animation started: ${frames.length} frames at ${intervalMs}ms${repeat ? " (repeating)" : ""}`);
153
153
  // If we have frame callbacks, we need to track timing manually
154
154
  // This is a limitation of the current SDK - we can't hook into individual frame displays
155
155
  if (onFrame) {
@@ -177,7 +177,7 @@ class AnimationUtils {
177
177
  }
178
178
  }
179
179
  catch (error) {
180
- const errorMsg = `Failed to start animation: ${error instanceof Error ? error.message : 'Unknown error'}`;
180
+ const errorMsg = `Failed to start animation: ${error instanceof Error ? error.message : "Unknown error"}`;
181
181
  console.error(`❌ ${errorMsg}`);
182
182
  if (onError) {
183
183
  onError(errorMsg);
@@ -209,7 +209,8 @@ class AnimationUtils {
209
209
  try {
210
210
  console.log(`📽️ Sequence frame ${i + 1}/${sequence.length} (${duration}ms)`);
211
211
  session.layouts.showBitmapView(frame);
212
- if (i < sequence.length - 1) { // Don't delay after the last frame
212
+ if (i < sequence.length - 1) {
213
+ // Don't delay after the last frame
213
214
  await this.delay(duration);
214
215
  }
215
216
  }
@@ -218,7 +219,7 @@ class AnimationUtils {
218
219
  throw error;
219
220
  }
220
221
  }
221
- console.log('✅ Bitmap sequence completed');
222
+ console.log("✅ Bitmap sequence completed");
222
223
  }
223
224
  /**
224
225
  * Measure animation timing performance
@@ -237,7 +238,7 @@ class AnimationUtils {
237
238
  static async measureTiming(targetInterval, measureDuration = 10000) {
238
239
  return new Promise((resolve) => {
239
240
  const timestamps = [];
240
- let startTime = Date.now();
241
+ const startTime = Date.now();
241
242
  const measureInterval = setInterval(() => {
242
243
  timestamps.push(Date.now());
243
244
  }, targetInterval);
@@ -248,7 +249,7 @@ class AnimationUtils {
248
249
  targetInterval,
249
250
  actualInterval: targetInterval,
250
251
  drift: 0,
251
- fps: 1000 / targetInterval
252
+ fps: 1000 / targetInterval,
252
253
  });
253
254
  return;
254
255
  }
@@ -264,7 +265,7 @@ class AnimationUtils {
264
265
  targetInterval,
265
266
  actualInterval,
266
267
  drift,
267
- fps
268
+ fps,
268
269
  });
269
270
  }, measureDuration);
270
271
  });
@@ -285,17 +286,17 @@ class AnimationUtils {
285
286
  */
286
287
  static getOptimizedConfig(deviceType) {
287
288
  switch (deviceType) {
288
- case 'even-realities-g1':
289
+ case "even-realities-g1":
289
290
  return {
290
291
  intervalMs: 1650, // Tested optimal timing for Even Realities G1
291
292
  repeat: false,
292
293
  validateFrames: true,
293
294
  loadOptions: {
294
295
  validateFrames: true,
295
- skipMissingFrames: false
296
- }
296
+ skipMissingFrames: false,
297
+ },
297
298
  };
298
- case 'generic':
299
+ case "generic":
299
300
  default:
300
301
  return {
301
302
  intervalMs: 1000,
@@ -303,8 +304,8 @@ class AnimationUtils {
303
304
  validateFrames: true,
304
305
  loadOptions: {
305
306
  validateFrames: true,
306
- skipMissingFrames: false
307
- }
307
+ skipMissingFrames: false,
308
+ },
308
309
  };
309
310
  }
310
311
  }
@@ -330,7 +331,7 @@ class AnimationUtils {
330
331
  console.log(`📦 Preloading ${frameCount} frames from ${basePath}...`);
331
332
  const frames = await bitmap_utils_1.BitmapUtils.loadBmpFrames(basePath, frameCount, {
332
333
  validateFrames: true,
333
- ...options
334
+ ...options,
334
335
  });
335
336
  console.log(`✅ Preloaded ${frames.length} frames (${frames.reduce((total, frame) => total + frame.length, 0)} total characters)`);
336
337
  return frames;
@@ -70,7 +70,9 @@ export declare class BitmapUtils {
70
70
  * session.layouts.showBitmapView(bmpHex);
71
71
  * ```
72
72
  */
73
- static loadBmpAsHex(filePath: string): Promise<string>;
73
+ static loadBmpFromFileAsHex(filePath: string): Promise<string>;
74
+ static convert24BitTo1BitBMP(input24BitBmp: Buffer): Promise<Buffer>;
75
+ static loadBmpFromDataAsHex(bmpData: Buffer): Promise<string>;
74
76
  /**
75
77
  * Load multiple BMP frames as hex array for animations
76
78
  *
@@ -123,7 +125,7 @@ export declare class BitmapUtils {
123
125
  * const bufferData = BitmapUtils.convertFormat(base64Data, 'base64', 'buffer');
124
126
  * ```
125
127
  */
126
- static convertFormat(data: string | Buffer, fromFormat: 'hex' | 'base64' | 'buffer', toFormat: 'hex' | 'base64' | 'buffer'): string | Buffer;
128
+ static convertFormat(data: string | Buffer, fromFormat: "hex" | "base64" | "buffer", toFormat: "hex" | "base64" | "buffer"): string | Buffer;
127
129
  /**
128
130
  * Get bitmap information without full validation
129
131
  *
@@ -1 +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"}
1
+ {"version":3,"file":"bitmap-utils.d.ts","sourceRoot":"","sources":["../../src/utils/bitmap-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH;;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;IACtB;;;;;;;;;;;;OAYG;WACU,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;WAevD,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;WA+G7D,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiDnE;;;;;;;;;;;;;;;;;;;OAmBG;WACU,aAAa,CACxB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,MAAM,EAAE,CAAC;IAgEpB;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB;IA+F1D;;;;;;;;;;;;;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;CAsCF"}
@@ -55,6 +55,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
55
55
  exports.BitmapUtils = void 0;
56
56
  const fs = __importStar(require("fs/promises"));
57
57
  const path = __importStar(require("path"));
58
+ const jimp_1 = require("jimp");
58
59
  /**
59
60
  * Utility class for working with bitmap images in MentraOS applications
60
61
  */
@@ -72,16 +73,10 @@ class BitmapUtils {
72
73
  * session.layouts.showBitmapView(bmpHex);
73
74
  * ```
74
75
  */
75
- static async loadBmpAsHex(filePath) {
76
+ static async loadBmpFromFileAsHex(filePath) {
76
77
  try {
77
78
  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;
79
+ return this.loadBmpFromDataAsHex(bmpData);
85
80
  }
86
81
  catch (error) {
87
82
  if (error instanceof Error) {
@@ -90,6 +85,136 @@ class BitmapUtils {
90
85
  throw new Error(`Failed to load BMP file ${filePath}: Unknown error`);
91
86
  }
92
87
  }
88
+ static async convert24BitTo1BitBMP(input24BitBmp) {
89
+ // Read header information from 24-bit BMP
90
+ const width = input24BitBmp.readUInt32LE(18);
91
+ const height = Math.abs(input24BitBmp.readInt32LE(22)); // Height can be negative (top-down BMP)
92
+ const isTopDown = input24BitBmp.readInt32LE(22) < 0;
93
+ const bitsPerPixel = input24BitBmp.readUInt16LE(28);
94
+ if (bitsPerPixel !== 24) {
95
+ throw new Error("Input must be a 24-bit BMP");
96
+ }
97
+ // Calculate row sizes (both must be 4-byte aligned)
98
+ const rowSize24 = Math.ceil((width * 3) / 4) * 4;
99
+ const rowSize1 = Math.ceil(width / 32) * 4; // 32 pixels per 4 bytes
100
+ // Calculate sizes for 1-bit BMP
101
+ const colorTableSize = 8; // 2 colors * 4 bytes each
102
+ const headerSize = 54 + colorTableSize;
103
+ const pixelDataSize = rowSize1 * height;
104
+ const fileSize = headerSize + pixelDataSize;
105
+ // Create new buffer for 1-bit BMP
106
+ const output1BitBmp = Buffer.alloc(fileSize);
107
+ let offset = 0;
108
+ // Write BMP file header (14 bytes)
109
+ output1BitBmp.write("BM", offset);
110
+ offset += 2; // Signature
111
+ output1BitBmp.writeUInt32LE(fileSize, offset);
112
+ offset += 4; // File size
113
+ output1BitBmp.writeUInt16LE(0, offset);
114
+ offset += 2; // Reserved 1
115
+ output1BitBmp.writeUInt16LE(0, offset);
116
+ offset += 2; // Reserved 2
117
+ output1BitBmp.writeUInt32LE(headerSize, offset);
118
+ offset += 4; // Pixel data offset
119
+ // Write DIB header (40 bytes)
120
+ output1BitBmp.writeUInt32LE(40, offset);
121
+ offset += 4; // DIB header size
122
+ output1BitBmp.writeInt32LE(width, offset);
123
+ offset += 4; // Width
124
+ output1BitBmp.writeInt32LE(height, offset);
125
+ offset += 4; // Height (positive for bottom-up)
126
+ output1BitBmp.writeUInt16LE(1, offset);
127
+ offset += 2; // Planes
128
+ output1BitBmp.writeUInt16LE(1, offset);
129
+ offset += 2; // Bits per pixel (1-bit)
130
+ output1BitBmp.writeUInt32LE(0, offset);
131
+ offset += 4; // Compression (none)
132
+ output1BitBmp.writeUInt32LE(pixelDataSize, offset);
133
+ offset += 4; // Image size
134
+ output1BitBmp.writeInt32LE(2835, offset);
135
+ offset += 4; // X pixels per meter (72 DPI)
136
+ output1BitBmp.writeInt32LE(2835, offset);
137
+ offset += 4; // Y pixels per meter (72 DPI)
138
+ output1BitBmp.writeUInt32LE(2, offset);
139
+ offset += 4; // Colors used
140
+ output1BitBmp.writeUInt32LE(2, offset);
141
+ offset += 4; // Important colors
142
+ // Write color table (8 bytes)
143
+ // Black (index 0): B=0, G=0, R=0, Reserved=0
144
+ output1BitBmp.writeUInt32LE(0x00000000, offset);
145
+ offset += 4;
146
+ // White (index 1): B=255, G=255, R=255, Reserved=0
147
+ output1BitBmp.writeUInt8(255, offset++); // Blue
148
+ output1BitBmp.writeUInt8(255, offset++); // Green
149
+ output1BitBmp.writeUInt8(255, offset++); // Red
150
+ output1BitBmp.writeUInt8(0, offset++); // Reserved
151
+ // Convert pixel data from 24-bit to 1-bit
152
+ const pixelDataStart24 = 54; // 24-bit BMP has no color table
153
+ for (let y = 0; y < height; y++) {
154
+ // BMP files are usually stored bottom-up
155
+ const sourceY = isTopDown ? y : height - 1 - y;
156
+ const destY = height - 1 - y; // Always write bottom-up for compatibility
157
+ // Initialize the row with zeros
158
+ const rowData = Buffer.alloc(rowSize1);
159
+ for (let x = 0; x < width; x++) {
160
+ // Get pixel from 24-bit BMP
161
+ const offset24 = pixelDataStart24 + sourceY * rowSize24 + x * 3;
162
+ const blue = input24BitBmp[offset24];
163
+ const green = input24BitBmp[offset24 + 1];
164
+ const red = input24BitBmp[offset24 + 2];
165
+ // Determine if pixel is white (assuming pure black or white)
166
+ // White = 1, Black = 0
167
+ const isWhite = red > 128 || green > 128 || blue > 128 ? 1 : 0;
168
+ // Calculate bit position
169
+ const byteIndex = Math.floor(x / 8);
170
+ const bitPosition = 7 - (x % 8); // MSB first
171
+ // Set bit if white
172
+ if (isWhite) {
173
+ rowData[byteIndex] |= 1 << bitPosition;
174
+ }
175
+ }
176
+ // Write row to output buffer
177
+ const destOffset = offset + destY * rowSize1;
178
+ rowData.copy(output1BitBmp, destOffset);
179
+ }
180
+ return output1BitBmp;
181
+ }
182
+ static async loadBmpFromDataAsHex(bmpData) {
183
+ try {
184
+ // Basic BMP validation - check for BMP signature
185
+ if (bmpData.length < 14 || bmpData[0] !== 0x42 || bmpData[1] !== 0x4d) {
186
+ throw new Error(`Bmp data is not a valid BMP file (missing BM signature)`);
187
+ }
188
+ let finalBmpData = bmpData;
189
+ // Load the image with Jimp
190
+ const image = await jimp_1.Jimp.read(bmpData);
191
+ // Check if we need to add padding
192
+ if (image.width !== 576 || image.height !== 135) {
193
+ console.log(`Adding padding to BMP since it isn't 576x135 (current: ${image.width}x${image.height})`);
194
+ // Create a new 576x135 white canvas
195
+ const paddedImage = new jimp_1.Jimp({
196
+ width: 576,
197
+ height: 135,
198
+ color: 0xffffffff,
199
+ });
200
+ // // Calculate position to place the original image (with padding)
201
+ const leftPadding = 40; // 40px padding on left
202
+ const topPadding = 35; // 35px padding on top
203
+ // Composite the original image onto the white canvas
204
+ paddedImage.composite(image, leftPadding, topPadding);
205
+ finalBmpData = await this.convert24BitTo1BitBMP(await paddedImage.getBuffer("image/bmp"));
206
+ }
207
+ // No padding needed, just return as hex
208
+ console.log(`finalBmpData: ${finalBmpData.length} bytes`);
209
+ return finalBmpData.toString("hex");
210
+ }
211
+ catch (error) {
212
+ if (error instanceof Error) {
213
+ throw new Error(`Failed to load BMP data: ${error.message}`);
214
+ }
215
+ throw new Error(`Failed to load BMP data: Unknown error`);
216
+ }
217
+ }
93
218
  /**
94
219
  * Load multiple BMP frames as hex array for animations
95
220
  *
@@ -111,19 +236,19 @@ class BitmapUtils {
111
236
  * ```
112
237
  */
113
238
  static async loadBmpFrames(basePath, frameCount, options = {}) {
114
- const { filePattern = 'animation_10_frame_{i}.bmp', startFrame = 1, validateFrames = true, skipMissingFrames = false } = options;
239
+ const { filePattern = "animation_10_frame_{i}.bmp", startFrame = 1, validateFrames = true, skipMissingFrames = false, } = options;
115
240
  const frames = [];
116
241
  const errors = [];
117
242
  for (let i = 0; i < frameCount; i++) {
118
243
  const frameNumber = startFrame + i;
119
- const fileName = filePattern.replace('{i}', frameNumber.toString());
244
+ const fileName = filePattern.replace("{i}", frameNumber.toString());
120
245
  const filePath = path.join(basePath, fileName);
121
246
  try {
122
- const frameHex = await this.loadBmpAsHex(filePath);
247
+ const frameHex = await this.loadBmpFromFileAsHex(filePath);
123
248
  if (validateFrames) {
124
249
  const validation = this.validateBmpHex(frameHex);
125
250
  if (!validation.isValid) {
126
- const errorMsg = `Frame ${frameNumber} validation failed: ${validation.errors.join(', ')}`;
251
+ const errorMsg = `Frame ${frameNumber} validation failed: ${validation.errors.join(", ")}`;
127
252
  if (skipMissingFrames) {
128
253
  console.warn(`⚠️ ${errorMsg} - skipping`);
129
254
  continue;
@@ -137,7 +262,7 @@ class BitmapUtils {
137
262
  frames.push(frameHex);
138
263
  }
139
264
  catch (error) {
140
- const errorMsg = `Failed to load frame ${frameNumber} (${fileName}): ${error instanceof Error ? error.message : 'Unknown error'}`;
265
+ const errorMsg = `Failed to load frame ${frameNumber} (${fileName}): ${error instanceof Error ? error.message : "Unknown error"}`;
141
266
  if (skipMissingFrames) {
142
267
  console.warn(`⚠️ ${errorMsg} - skipping`);
143
268
  continue;
@@ -148,7 +273,7 @@ class BitmapUtils {
148
273
  }
149
274
  }
150
275
  if (errors.length > 0) {
151
- throw new Error(`Failed to load frames:\n${errors.join('\n')}`);
276
+ throw new Error(`Failed to load frames:\n${errors.join("\n")}`);
152
277
  }
153
278
  if (frames.length === 0) {
154
279
  throw new Error(`No valid frames loaded from ${basePath}`);
@@ -176,35 +301,37 @@ class BitmapUtils {
176
301
  const errors = [];
177
302
  let byteCount = 0;
178
303
  let blackPixels = 0;
179
- let metadata = {};
304
+ const metadata = {};
180
305
  try {
181
306
  // Basic hex validation
182
- if (typeof hexString !== 'string' || hexString.length === 0) {
183
- errors.push('Hex string is empty or invalid');
307
+ if (typeof hexString !== "string" || hexString.length === 0) {
308
+ errors.push("Hex string is empty or invalid");
184
309
  return { isValid: false, byteCount: 0, blackPixels: 0, errors };
185
310
  }
186
311
  if (hexString.length % 2 !== 0) {
187
- errors.push('Hex string length must be even');
312
+ errors.push("Hex string length must be even");
188
313
  return { isValid: false, byteCount: 0, blackPixels: 0, errors };
189
314
  }
190
315
  // Convert to buffer
191
- const buffer = Buffer.from(hexString, 'hex');
316
+ const buffer = Buffer.from(hexString, "hex");
192
317
  byteCount = buffer.length;
193
318
  // BMP signature validation
194
319
  if (buffer.length < 14) {
195
- errors.push('File too small to be a valid BMP (minimum 14 bytes for header)');
320
+ errors.push("File too small to be a valid BMP (minimum 14 bytes for header)");
196
321
  }
197
322
  else {
198
- if (buffer[0] !== 0x42 || buffer[1] !== 0x4D) {
323
+ if (buffer[0] !== 0x42 || buffer[1] !== 0x4d) {
199
324
  errors.push('Invalid BMP signature (should start with "BM")');
200
325
  }
201
326
  }
202
327
  // Size validation for MentraOS (576x135 = ~9782 bytes expected)
203
328
  const expectedSize = 9782;
204
- if (buffer.length < expectedSize - 100) { // Allow some tolerance
329
+ if (buffer.length < expectedSize - 100) {
330
+ // Allow some tolerance
205
331
  errors.push(`BMP too small (${buffer.length} bytes, expected ~${expectedSize})`);
206
332
  }
207
- else if (buffer.length > expectedSize + 1000) { // Allow some tolerance
333
+ else if (buffer.length > expectedSize + 1000) {
334
+ // Allow some tolerance
208
335
  errors.push(`BMP too large (${buffer.length} bytes, expected ~${expectedSize})`);
209
336
  }
210
337
  // Extract BMP metadata if header is valid
@@ -214,37 +341,37 @@ class BitmapUtils {
214
341
  const width = buffer.readUInt32LE(18);
215
342
  const height = buffer.readUInt32LE(22);
216
343
  metadata.dimensions = { width, height };
217
- metadata.format = 'BMP';
344
+ metadata.format = "BMP";
218
345
  // Validate dimensions for MentraOS glasses
219
346
  if (width !== 576 || height !== 135) {
220
347
  errors.push(`Invalid dimensions (${width}x${height}, expected 576x135 for MentraOS)`);
221
348
  }
222
349
  }
223
350
  catch (e) {
224
- errors.push('Failed to parse BMP header metadata');
351
+ errors.push("Failed to parse BMP header metadata");
225
352
  }
226
353
  }
227
354
  // Pixel data validation (assumes 54-byte header + pixel data)
228
355
  if (buffer.length > 62) {
229
356
  const pixelData = buffer.slice(62); // Skip BMP header
230
- blackPixels = Array.from(pixelData).filter(b => b !== 0xFF).length;
357
+ blackPixels = Array.from(pixelData).filter((b) => b !== 0xff).length;
231
358
  if (blackPixels === 0) {
232
- errors.push('No black pixels found (image appears to be all white)');
359
+ errors.push("No black pixels found (image appears to be all white)");
233
360
  }
234
361
  }
235
362
  else {
236
- errors.push('File too small to contain pixel data');
363
+ errors.push("File too small to contain pixel data");
237
364
  }
238
365
  }
239
366
  catch (error) {
240
- errors.push(`Failed to parse hex data: ${error instanceof Error ? error.message : 'Unknown error'}`);
367
+ errors.push(`Failed to parse hex data: ${error instanceof Error ? error.message : "Unknown error"}`);
241
368
  }
242
369
  return {
243
370
  isValid: errors.length === 0,
244
371
  byteCount,
245
372
  blackPixels,
246
373
  errors,
247
- metadata: Object.keys(metadata).length > 0 ? metadata : undefined
374
+ metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
248
375
  };
249
376
  }
250
377
  /**
@@ -265,13 +392,13 @@ class BitmapUtils {
265
392
  let buffer;
266
393
  // Convert input to buffer
267
394
  switch (fromFormat) {
268
- case 'hex':
269
- buffer = Buffer.from(data, 'hex');
395
+ case "hex":
396
+ buffer = Buffer.from(data, "hex");
270
397
  break;
271
- case 'base64':
272
- buffer = Buffer.from(data, 'base64');
398
+ case "base64":
399
+ buffer = Buffer.from(data, "base64");
273
400
  break;
274
- case 'buffer':
401
+ case "buffer":
275
402
  buffer = data;
276
403
  break;
277
404
  default:
@@ -279,11 +406,11 @@ class BitmapUtils {
279
406
  }
280
407
  // Convert buffer to target format
281
408
  switch (toFormat) {
282
- case 'hex':
283
- return buffer.toString('hex');
284
- case 'base64':
285
- return buffer.toString('base64');
286
- case 'buffer':
409
+ case "hex":
410
+ return buffer.toString("hex");
411
+ case "base64":
412
+ return buffer.toString("base64");
413
+ case "buffer":
287
414
  return buffer;
288
415
  default:
289
416
  throw new Error(`Unsupported target format: ${toFormat}`);
@@ -303,8 +430,8 @@ class BitmapUtils {
303
430
  */
304
431
  static getBitmapInfo(hexString) {
305
432
  try {
306
- const buffer = Buffer.from(hexString, 'hex');
307
- const isValidBmp = buffer.length >= 14 && buffer[0] === 0x42 && buffer[1] === 0x4D;
433
+ const buffer = Buffer.from(hexString, "hex");
434
+ const isValidBmp = buffer.length >= 14 && buffer[0] === 0x42 && buffer[1] === 0x4d;
308
435
  let width;
309
436
  let height;
310
437
  if (isValidBmp && buffer.length >= 54) {
@@ -317,20 +444,20 @@ class BitmapUtils {
317
444
  }
318
445
  }
319
446
  const pixelData = buffer.slice(62);
320
- const blackPixels = Array.from(pixelData).filter(b => b !== 0xFF).length;
447
+ const blackPixels = Array.from(pixelData).filter((b) => b !== 0xff).length;
321
448
  return {
322
449
  byteCount: buffer.length,
323
450
  blackPixels,
324
451
  width,
325
452
  height,
326
- isValidBmp
453
+ isValidBmp,
327
454
  };
328
455
  }
329
456
  catch (error) {
330
457
  return {
331
458
  byteCount: 0,
332
459
  blackPixels: 0,
333
- isValidBmp: false
460
+ isValidBmp: false,
334
461
  };
335
462
  }
336
463
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/sdk",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
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",
@@ -29,6 +29,7 @@
29
29
  "cookie-parser": "^1.4.7",
30
30
  "dotenv": "^16.4.0",
31
31
  "express": "^4.18.2",
32
+ "jimp": "^1.6.0",
32
33
  "jsonwebtoken": "^8.5.1",
33
34
  "jsrsasign": "^11.1.0",
34
35
  "multer": "^2.0.1",