@mentra/bluetooth-sdk 0.1.8 → 0.1.10

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 (147) hide show
  1. package/README.md +116 -18
  2. package/android/build.gradle +289 -16
  3. package/android/gradle.properties.example +9 -0
  4. package/android/lc3Lib/build.gradle +95 -6
  5. package/android/settings.gradle +3 -0
  6. package/android/silero/LICENSE.md +17 -0
  7. package/android/silero/build.gradle +42 -0
  8. package/android/silero/proguard-rules.pro +24 -0
  9. package/android/silero/src/androidTest/assets/hello.wav +0 -0
  10. package/android/silero/src/androidTest/java/com/konovalov/vad/silero/VadSileroTest.kt +79 -0
  11. package/android/silero/src/main/assets/LICENSE +21 -0
  12. package/android/silero/src/main/assets/silero_vad.onnx +0 -0
  13. package/android/silero/src/main/java/com/konovalov/vad/silero/Vad.kt +160 -0
  14. package/android/silero/src/main/java/com/konovalov/vad/silero/VadSilero.kt +449 -0
  15. package/android/silero/src/main/java/com/konovalov/vad/silero/config/FrameSize.kt +15 -0
  16. package/android/silero/src/main/java/com/konovalov/vad/silero/config/Mode.kt +14 -0
  17. package/android/silero/src/main/java/com/konovalov/vad/silero/config/SampleRate.kt +12 -0
  18. package/android/silero/src/main/java/com/konovalov/vad/silero/utils/AudioUtils.kt +47 -0
  19. package/android/silero/src/main/java/com/konovalov/vad/silero/utils/TensorMap.kt +41 -0
  20. package/android/silero/src/test/java/com/konovalov/vad/silero/utils/AudioUtilsTest.kt +44 -0
  21. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkDefaults.kt +6 -0
  22. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +165 -27
  23. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +89 -21
  24. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +189 -16
  25. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +22 -2
  26. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +787 -85
  27. package/android/src/main/java/com/mentra/bluetoothsdk/audio/AudioModels.kt +6 -2
  28. package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +123 -10
  29. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +12 -2
  30. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +8 -2
  31. package/android/src/main/java/com/mentra/bluetoothsdk/debug/BleTraceLogger.kt +33 -17
  32. package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +101 -0
  33. package/android/src/main/java/com/mentra/bluetoothsdk/internal/MapParsing.kt +8 -0
  34. package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistry.java +108 -0
  35. package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/LocalPhotoUploadServer.kt +399 -74
  36. package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/MentraPhotoReceiverModule.kt +100 -17
  37. package/android/src/main/java/com/mentra/bluetoothsdk/requests/DisplayRequests.kt +17 -0
  38. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.java +2 -2
  39. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +569 -220
  40. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.java +2 -2
  41. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.java +271 -66
  42. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +176 -104
  43. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraosBle.java +12753 -27036
  44. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +54 -2
  45. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +16 -4
  46. package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +60 -3
  47. package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +12 -3
  48. package/android/src/main/java/com/mentra/bluetoothsdk/stt/STTTools.kt +87 -36
  49. package/android/src/main/java/com/mentra/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +13 -4
  50. package/android/src/main/java/com/mentra/bluetoothsdk/stt/VadGateSpeechPolicy.kt +229 -0
  51. package/android/src/main/java/com/mentra/bluetoothsdk/tts/TTSTools.kt +180 -0
  52. package/android/src/main/java/com/mentra/bluetoothsdk/utils/AvifExifStripper.java +573 -0
  53. package/android/src/main/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadService.java +488 -50
  54. package/android/src/main/java/com/mentra/bluetoothsdk/utils/HeifExifTagReader.java +199 -0
  55. package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunkReassembler.java +18 -1
  56. package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunker.java +64 -18
  57. package/android/src/main/java/com/mentra/bluetoothsdk/utils/NexSGCUtils.kt +48 -39
  58. package/android/src/test/java/com/mentra/bluetoothsdk/camera/PhotoRequestTest.kt +23 -0
  59. package/android/src/test/java/com/mentra/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistryTest.java +75 -0
  60. package/android/src/test/java/com/mentra/bluetoothsdk/utils/AvifExifStripperTest.java +30 -0
  61. package/android/src/test/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadServiceTest.java +98 -0
  62. package/android/src/test/resources/avif_with_exif.avif +0 -0
  63. package/build/BluetoothSdk.types.d.ts +241 -43
  64. package/build/BluetoothSdk.types.d.ts.map +1 -1
  65. package/build/BluetoothSdk.types.js +11 -10
  66. package/build/BluetoothSdk.types.js.map +1 -1
  67. package/build/_private/BluetoothSdkModule.d.ts +36 -23
  68. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  69. package/build/_private/BluetoothSdkModule.js +30 -52
  70. package/build/_private/BluetoothSdkModule.js.map +1 -1
  71. package/build/_private/photoRequestPayload.d.ts +4 -0
  72. package/build/_private/photoRequestPayload.d.ts.map +1 -0
  73. package/build/_private/photoRequestPayload.js +25 -0
  74. package/build/_private/photoRequestPayload.js.map +1 -0
  75. package/build/index.d.ts +1 -1
  76. package/build/index.d.ts.map +1 -1
  77. package/build/index.js +23 -0
  78. package/build/index.js.map +1 -1
  79. package/build/react/useMentraBluetooth.d.ts +2 -2
  80. package/build/react/useMentraBluetooth.d.ts.map +1 -1
  81. package/build/react/useMentraBluetooth.js +3 -3
  82. package/build/react/useMentraBluetooth.js.map +1 -1
  83. package/ios/BluetoothSdkModule.swift +174 -70
  84. package/ios/LocalPhotoUploadServer.swift +370 -105
  85. package/ios/MentraBluetoothSDK.podspec +3 -1
  86. package/ios/MentraPhotoReceiverModule.swift +62 -10
  87. package/ios/Packages/CoreObjC/include/PcmConverter.h +22 -0
  88. package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +668 -26
  89. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +13 -9
  90. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/c-api.h +4551 -0
  91. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/cxx-api.h +1745 -0
  92. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
  93. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/c-api.h +4551 -0
  94. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/cxx-api.h +1745 -0
  95. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
  96. package/ios/Source/BluetoothSdkDefaults.swift +6 -0
  97. package/ios/Source/Bridge.swift +58 -19
  98. package/ios/Source/DeviceManager.swift +149 -17
  99. package/ios/Source/DeviceStore.swift +16 -6
  100. package/ios/Source/MentraBluetoothSDK.swift +803 -47
  101. package/ios/Source/ObservableStore.swift +8 -2
  102. package/ios/Source/{Audio → audio}/AudioModels.swift +9 -7
  103. package/ios/Source/{Camera → camera}/CameraModels.swift +225 -16
  104. package/ios/Source/controllers/ControllerManager.swift +4 -3
  105. package/ios/Source/controllers/R1.swift +3 -3
  106. package/ios/Source/{Events → events}/BluetoothEvents.swift +183 -1
  107. package/ios/Source/{Internal → internal}/ValueParsing.swift +10 -1
  108. package/ios/Source/{Requests → requests}/DisplayRequests.swift +28 -4
  109. package/ios/Source/services/PhoneMic.swift +1 -0
  110. package/ios/Source/sgcs/G1.swift +2 -2
  111. package/ios/Source/sgcs/G2.swift +645 -403
  112. package/ios/Source/sgcs/Mach1.swift +2 -2
  113. package/ios/Source/sgcs/MentraLive.swift +812 -319
  114. package/ios/Source/sgcs/MentraNex.swift +412 -182
  115. package/ios/Source/sgcs/SGCManager.swift +57 -3
  116. package/ios/Source/sgcs/Simulated.swift +7 -3
  117. package/ios/Source/sgcs/mentraos_ble.pb.swift +3073 -3134
  118. package/ios/Source/status/DeviceStatus.swift +974 -0
  119. package/ios/Source/{Status → status}/WifiHotspotStatus.swift +4 -4
  120. package/ios/Source/{Streaming → streaming}/StreamModels.swift +24 -9
  121. package/ios/Source/stt/STTTools.swift +27 -12
  122. package/ios/Source/stt/SherpaOnnxTranscriber.swift +31 -8
  123. package/ios/Source/tts/TTSTools.swift +164 -0
  124. package/ios/Source/utils/JSCExperiment.swift +7 -7
  125. package/ios/Source/utils/MessageChunkReassembler.swift +20 -1
  126. package/ios/Source/utils/MessageChunker.swift +78 -21
  127. package/ios/Source/utils/TarBz2Extractor.swift +53 -2
  128. package/package.json +1 -1
  129. package/plugin/build/withAndroid.js +68 -4
  130. package/src/BluetoothSdk.types.ts +331 -59
  131. package/src/_private/BluetoothSdkModule.ts +113 -102
  132. package/src/_private/photoRequestPayload.ts +28 -0
  133. package/src/index.ts +58 -15
  134. package/src/react/useMentraBluetooth.ts +6 -4
  135. package/android/.project +0 -28
  136. package/android/lc3Lib/.project +0 -28
  137. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/cargs.h +0 -162
  138. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/c-api.h +0 -1852
  139. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/cxx-api.h +0 -674
  140. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/sherpa-onnx.a +0 -0
  141. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/sherpa-onnx.a +0 -0
  142. package/ios/Source/Status/DeviceStatus.swift +0 -471
  143. /package/ios/Source/{Connection → connection}/ScanSession.swift +0 -0
  144. /package/ios/Source/{Errors → errors}/BluetoothError.swift +0 -0
  145. /package/ios/Source/{Internal → internal}/BluetoothAvailability.swift +0 -0
  146. /package/ios/Source/{Status → status}/RuntimeState.swift +0 -0
  147. /package/ios/Source/{Types → types}/DeviceModels.swift +0 -0
@@ -49,7 +49,8 @@ public struct VoiceActivityDetectionStatusEvent: CustomStringConvertible {
49
49
  public let values: [String: Any]
50
50
 
51
51
  public init(values: [String: Any]) {
52
- voiceActivityDetectionEnabled = boolValue(values, "voiceActivityDetectionEnabled") ?? true
52
+ voiceActivityDetectionEnabled =
53
+ boolValue(values, "voiceActivityDetectionEnabled") ?? BluetoothSdkDefaults.voiceActivityDetectionEnabled
53
54
  self.values = values
54
55
  }
55
56
 
@@ -72,6 +73,166 @@ public struct SpeakingStatusEvent: CustomStringConvertible {
72
73
  }
73
74
  }
74
75
 
76
+ public struct OtaUpdateAvailableEvent: CustomStringConvertible {
77
+ public let versionCode: Int?
78
+ public let versionName: String?
79
+ public let updates: [String]
80
+ public let totalSize: Int?
81
+ public let cacheReady: Bool?
82
+ public let values: [String: Any]
83
+
84
+ public init(values: [String: Any]) {
85
+ versionCode = intValue(values["version_code"])
86
+ versionName = stringValue(values, "version_name")
87
+ updates = values["updates"] as? [String] ?? []
88
+ totalSize = intValue(values["total_size"])
89
+ cacheReady = boolValue(values, "cache_ready")
90
+ self.values = values
91
+ }
92
+
93
+ public var description: String {
94
+ "OtaUpdateAvailableEvent(versionName: \(versionName ?? "unknown"), updates: \(updates.joined(separator: ",")))"
95
+ }
96
+ }
97
+
98
+ public struct OtaStartAckEvent: CustomStringConvertible {
99
+ public let timestamp: Int?
100
+ public let values: [String: Any]
101
+
102
+ public init(values: [String: Any]) {
103
+ timestamp = intValue(values["timestamp"])
104
+ self.values = values
105
+ }
106
+
107
+ public var description: String {
108
+ "OtaStartAckEvent(timestamp: \(timestamp.map(String.init) ?? "unknown"))"
109
+ }
110
+ }
111
+
112
+ public struct OtaStatusEvent: CustomStringConvertible {
113
+ public let sessionId: String
114
+ public let totalSteps: Int
115
+ public let currentStep: Int
116
+ public let stepType: String
117
+ public let phase: String
118
+ public let stepPercent: Int
119
+ public let overallPercent: Int
120
+ public let status: String
121
+ public let errorMessage: String?
122
+ public let glassesTimeMs: Int?
123
+ public let values: [String: Any]
124
+
125
+ public init(values: [String: Any]) {
126
+ sessionId = stringValue(values, "session_id") ?? ""
127
+ totalSteps = intValue(values["total_steps"]) ?? 0
128
+ currentStep = intValue(values["current_step"]) ?? 0
129
+ stepType = stringValue(values, "step_type") ?? ""
130
+ phase = stringValue(values, "phase") ?? ""
131
+ stepPercent = intValue(values["step_percent"]) ?? 0
132
+ overallPercent = intValue(values["overall_percent"]) ?? 0
133
+ status = stringValue(values, "status") ?? ""
134
+ errorMessage = stringValue(values, "error_message")
135
+ glassesTimeMs = intValue(values["glasses_time_ms"])
136
+ self.values = values
137
+ }
138
+
139
+ public var description: String {
140
+ "OtaStatusEvent(status: \(status), overallPercent: \(overallPercent))"
141
+ }
142
+ }
143
+
144
+ public struct OtaQueryResult: CustomStringConvertible {
145
+ public let values: [String: Any]
146
+
147
+ public init(values: [String: Any]) {
148
+ self.values = values
149
+ }
150
+
151
+ public var type: String {
152
+ stringValue(values, "type") ?? ""
153
+ }
154
+
155
+ public var status: String? {
156
+ stringValue(values, "status")
157
+ }
158
+
159
+ public var description: String {
160
+ "OtaQueryResult(type: \(type), status: \(status ?? "unknown"))"
161
+ }
162
+ }
163
+
164
+ public struct SettingsAckEvent: CustomStringConvertible {
165
+ public let values: [String: Any]
166
+
167
+ public init(values: [String: Any]) {
168
+ self.values = values
169
+ }
170
+
171
+ public var requestId: String {
172
+ stringValue(values, "requestId") ?? ""
173
+ }
174
+
175
+ public var setting: String {
176
+ stringValue(values, "setting") ?? ""
177
+ }
178
+
179
+ public var status: String {
180
+ stringValue(values, "status") ?? "applied"
181
+ }
182
+
183
+ public var timestamp: Int {
184
+ intValue(values["timestamp"]) ?? Int(Date().timeIntervalSince1970 * 1000)
185
+ }
186
+
187
+ public var fov: Int? {
188
+ intValue(values["fov"])
189
+ }
190
+
191
+ public var roiPosition: Int? {
192
+ intValue(values["roiPosition"]) ?? intValue(values["roi_position"])
193
+ }
194
+
195
+ public var hardwareApplied: Bool {
196
+ boolValue(values, "hardwareApplied") ?? boolValue(values, "hardware_applied") ?? false
197
+ }
198
+
199
+ public var errorCode: String? {
200
+ stringValue(values, "errorCode")
201
+ }
202
+
203
+ public var errorMessage: String? {
204
+ stringValue(values, "errorMessage")
205
+ }
206
+
207
+ public var description: String {
208
+ "SettingsAckEvent(setting: \(setting), status: \(status))"
209
+ }
210
+ }
211
+
212
+ public struct RgbLedControlResponseEvent: CustomStringConvertible {
213
+ public let values: [String: Any]
214
+
215
+ public init(values: [String: Any]) {
216
+ self.values = values
217
+ }
218
+
219
+ public var requestId: String {
220
+ stringValue(values, "requestId") ?? ""
221
+ }
222
+
223
+ public var state: String {
224
+ stringValue(values, "state") ?? "error"
225
+ }
226
+
227
+ public var errorCode: String? {
228
+ stringValue(values, "errorCode")
229
+ }
230
+
231
+ public var description: String {
232
+ "RgbLedControlResponseEvent(requestId: \(requestId), state: \(state))"
233
+ }
234
+ }
235
+
75
236
  public enum BluetoothEvent: CustomStringConvertible {
76
237
  case buttonPress(ButtonPressEvent)
77
238
  case touch(TouchEvent)
@@ -82,8 +243,15 @@ public enum BluetoothEvent: CustomStringConvertible {
82
243
  case hotspotError(HotspotErrorEvent)
83
244
  case photoResponse(PhotoResponseEvent)
84
245
  case photoStatus(PhotoStatusEvent)
246
+ case videoRecordingStatus(VideoRecordingStatusEvent)
247
+ case rgbLedControlResponse(RgbLedControlResponseEvent)
85
248
  case streamStatus(StreamStatusEvent)
86
249
  case keepAliveAck(KeepAliveAckEvent)
250
+ case otaUpdateAvailable(OtaUpdateAvailableEvent)
251
+ case otaStartAck(OtaStartAckEvent)
252
+ case otaStatus(OtaStatusEvent)
253
+ case settingsAck(SettingsAckEvent)
254
+ case versionInfo(VersionInfoResult)
87
255
  case localTranscription(LocalTranscriptionEvent)
88
256
  case raw(name: String, values: [String: Any])
89
257
 
@@ -107,10 +275,24 @@ public enum BluetoothEvent: CustomStringConvertible {
107
275
  event.description
108
276
  case let .photoStatus(event):
109
277
  event.description
278
+ case let .videoRecordingStatus(event):
279
+ event.description
280
+ case let .rgbLedControlResponse(event):
281
+ event.description
110
282
  case let .streamStatus(event):
111
283
  event.description
112
284
  case let .keepAliveAck(event):
113
285
  event.description
286
+ case let .otaUpdateAvailable(event):
287
+ event.description
288
+ case let .otaStartAck(event):
289
+ event.description
290
+ case let .otaStatus(event):
291
+ event.description
292
+ case let .settingsAck(event):
293
+ event.description
294
+ case let .versionInfo(event):
295
+ event.description
114
296
  case let .localTranscription(event):
115
297
  event.description
116
298
  case let .raw(name, values):
@@ -3,7 +3,16 @@ import Foundation
3
3
  func intValue(_ value: Any?) -> Int? {
4
4
  if let int = value as? Int { return int }
5
5
  if let double = value as? Double { return Int(double) }
6
- if let number = value as? NSNumber { return number.intValue }
6
+ // Read the 64-bit value: NSNumber.intValue is Int32 and would truncate large
7
+ // values (e.g. millisecond timestamps or byte counts, which bridge in as Int64).
8
+ if let number = value as? NSNumber { return Int(truncatingIfNeeded: number.int64Value) }
9
+ return nil
10
+ }
11
+
12
+ func doubleValue(_ value: Any?) -> Double? {
13
+ if let double = value as? Double { return double }
14
+ if let int = value as? Int { return Double(int) }
15
+ if let number = value as? NSNumber { return number.doubleValue }
7
16
  return nil
8
17
  }
9
18
 
@@ -25,10 +25,6 @@ public struct DisplayTextRequest {
25
25
 
26
26
  struct DisplayEventRequest {
27
27
  let values: [String: Any]
28
-
29
- init(values: [String: Any]) {
30
- self.values = values
31
- }
32
28
  }
33
29
 
34
30
  public struct DashboardPositionRequest {
@@ -56,3 +52,31 @@ struct DashboardMenuItem {
56
52
  values.merging(["title": title, "packageName": packageName]) { _, new in new }
57
53
  }
58
54
  }
55
+
56
+ /// Mirrors the TS `CalendarEvent` shape: { title, location?, time, endDate }
57
+ /// where `endDate` is unix seconds.
58
+ struct CalendarEvent {
59
+ let title: String
60
+ let location: String?
61
+ let time: String
62
+ let endDate: Double
63
+
64
+ init(title: String, location: String? = nil, time: String, endDate: Double) {
65
+ self.title = title
66
+ self.location = location
67
+ self.time = time
68
+ self.endDate = endDate
69
+ }
70
+
71
+ var dictionary: [String: Any] {
72
+ var dict: [String: Any] = [
73
+ "title": title,
74
+ "time": time,
75
+ "endDate": endDate,
76
+ ]
77
+ if let location {
78
+ dict["location"] = location
79
+ }
80
+ return dict
81
+ }
82
+ }
@@ -8,6 +8,7 @@
8
8
  import AVFoundation
9
9
  import Combine
10
10
  import Foundation
11
+ import UIKit
11
12
 
12
13
  @MainActor
13
14
  class PhoneMic {
@@ -278,7 +278,7 @@ class G1: NSObject, SGCManager {
278
278
 
279
279
  func requestPhoto(
280
280
  _: String, appId _: String, size _: String?, webhookUrl _: String?, authToken _: String?,
281
- compress _: String?, flash _: Bool, sound _: Bool, exposureTimeNs _: Double?, iso _: Int?
281
+ compress _: String?, flash _: Bool, save _: Bool, sound _: Bool, exposureTimeNs _: Double?, iso _: Int?
282
282
  ) {}
283
283
 
284
284
  func startStream(_: [String: Any]) {}
@@ -1871,7 +1871,7 @@ extension G1 {
1871
1871
 
1872
1872
  // MARK: - Enhanced BMP Display Methods
1873
1873
 
1874
- func displayBitmap(base64ImageData: String) async -> Bool {
1874
+ func displayBitmap(base64ImageData: String, x _: Int32? = nil, y _: Int32? = nil, width _: Int32? = nil, height _: Int32? = nil) async -> Bool {
1875
1875
  guard let bmpData = Data(base64Encoded: base64ImageData) else {
1876
1876
  Bridge.log("G1: Failed to decode base64 image data")
1877
1877
  return false