@kindly-ai/react-native 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/KindlyReactNative.podspec +36 -0
  2. package/LICENSE +20 -0
  3. package/README.md +127 -0
  4. package/android/build.gradle +113 -0
  5. package/android/maven-repo/ai/kindly/sdk/1.0.93/sdk-1.0.93.aar +0 -0
  6. package/android/maven-repo/ai/kindly/sdk/1.0.93/sdk-1.0.93.pom +125 -0
  7. package/android/src/main/AndroidManifest.xml +2 -0
  8. package/android/src/main/java/com/kindlyai/reactnative/KindlyReactNativeModule.kt +445 -0
  9. package/android/src/main/java/com/kindlyai/reactnative/KindlyReactNativePackage.kt +38 -0
  10. package/ios/Frameworks/Kindly.xcframework/Info.plist +44 -0
  11. package/ios/Frameworks/Kindly.xcframework/_CodeSignature/CodeDirectory +0 -0
  12. package/ios/Frameworks/Kindly.xcframework/_CodeSignature/CodeRequirements +0 -0
  13. package/ios/Frameworks/Kindly.xcframework/_CodeSignature/CodeRequirements-1 +0 -0
  14. package/ios/Frameworks/Kindly.xcframework/_CodeSignature/CodeResources +578 -0
  15. package/ios/Frameworks/Kindly.xcframework/_CodeSignature/CodeSignature +0 -0
  16. package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Assets.car +0 -0
  17. package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Config.plist +0 -0
  18. package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/ConfirmationWindow.nib +0 -0
  19. package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Headers/Kindly-Swift.h +331 -0
  20. package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Headers/Kindly.h +18 -0
  21. package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Info.plist +0 -0
  22. package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Kindly +0 -0
  23. package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios.abi.json +28769 -0
  24. package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios.private.swiftinterface +547 -0
  25. package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  26. package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios.swiftinterface +543 -0
  27. package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Modules/module.modulemap +11 -0
  28. package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/PrivacyInfo.xcprivacy +83 -0
  29. package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/_CodeSignature/CodeResources +223 -0
  30. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Assets.car +0 -0
  31. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Config.plist +0 -0
  32. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/ConfirmationWindow.nib +0 -0
  33. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Headers/Kindly-Swift.h +658 -0
  34. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Headers/Kindly.h +18 -0
  35. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Info.plist +0 -0
  36. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Kindly +0 -0
  37. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios-simulator.abi.json +28769 -0
  38. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +547 -0
  39. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  40. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios-simulator.swiftinterface +543 -0
  41. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/x86_64-apple-ios-simulator.abi.json +28769 -0
  42. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +547 -0
  43. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  44. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +543 -0
  45. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/module.modulemap +11 -0
  46. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/PrivacyInfo.xcprivacy +83 -0
  47. package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/_CodeSignature/CodeResources +267 -0
  48. package/ios/KindlyReactNative.h +18 -0
  49. package/ios/KindlyReactNative.mm +234 -0
  50. package/ios/KindlyReactNativeImpl.swift +410 -0
  51. package/lib/module/NativeKindlyReactNative.js +22 -0
  52. package/lib/module/NativeKindlyReactNative.js.map +1 -0
  53. package/lib/module/index.js +280 -0
  54. package/lib/module/index.js.map +1 -0
  55. package/lib/module/package.json +1 -0
  56. package/lib/typescript/package.json +1 -0
  57. package/lib/typescript/src/NativeKindlyReactNative.d.ts +47 -0
  58. package/lib/typescript/src/NativeKindlyReactNative.d.ts.map +1 -0
  59. package/lib/typescript/src/index.d.ts +150 -0
  60. package/lib/typescript/src/index.d.ts.map +1 -0
  61. package/package.json +121 -0
  62. package/src/NativeKindlyReactNative.ts +80 -0
  63. package/src/index.tsx +353 -0
@@ -0,0 +1,410 @@
1
+ // Swift implementation of the Kindly RN bridge — receives forwarded calls
2
+ // from `KindlyReactNative.mm` and delegates to the public Kindly iOS SDK
3
+ // (`import Kindly`, then `KindlyChatClient.start(...)` etc.).
4
+ //
5
+ // The bridge between this Swift class and the codegen-generated Obj-C++
6
+ // TurboModule is the auto-generated `KindlyReactNative-Swift.h` header
7
+ // (filename derives from the pod's `s.module_name`/`s.name`).
8
+ //
9
+ // Symmetric with `flutter-sdk/ios/Classes/KindlyPlugin.swift`. The biggest
10
+ // difference: RN can't make synchronous calls JS→native→JS→native, so the
11
+ // blocking-bool callbacks (`shouldHandleLink`, `shouldHandleNotification`)
12
+ // use a request-id round-trip — we put a `CompletableDeferred`-equivalent
13
+ // (`DispatchSemaphore` + locked dictionary) keyed by UUID, fire the request
14
+ // event, JS replies via `respondToShouldHandle(requestId, value)`, native
15
+ // signals the semaphore. 5-second timeout, defaults to `true`. Same
16
+ // semantics as the Flutter pattern.
17
+
18
+ import Combine
19
+ import Foundation
20
+ import Kindly
21
+ import React
22
+ import UIKit
23
+
24
+ @objc public class KindlyReactNativeImpl: NSObject {
25
+
26
+ // The `RCTEventEmitter`-typed Obj-C class instance. Held weakly so we
27
+ // don't extend its lifetime past RN's bridge teardown.
28
+ private weak var emitter: KindlyReactNative?
29
+
30
+ // State publisher cache so `isChatDisplayed` can answer synchronously.
31
+ private var stateSubscription: AnyCancellable?
32
+ private var lastIsChatDisplayed: Bool = false
33
+
34
+ // Pending request maps for the sync round-trips. Each entry pairs a
35
+ // request UUID with a (semaphore, answer) tuple. Native blocks on the
36
+ // semaphore (5s); JS replies via respondToAuthToken / respondToShouldHandle
37
+ // which signals the semaphore.
38
+ private var authPending: [String: PendingAuth] = [:]
39
+ private var shouldHandlePending: [String: PendingBool] = [:]
40
+ private let pendingLock = NSLock()
41
+
42
+ private struct PendingAuth {
43
+ let semaphore: DispatchSemaphore
44
+ var token: String?
45
+ }
46
+ private struct PendingBool {
47
+ let semaphore: DispatchSemaphore
48
+ var value: Bool = true
49
+ }
50
+
51
+ // KindlyChatClientDelegate bridge — set as the delegate when at least one
52
+ // delegate flag is on. The bridge calls back into this class's helpers.
53
+ private var delegateBridge: KindlyDelegateBridge?
54
+
55
+ @objc public init(emitter: KindlyReactNative) {
56
+ self.emitter = emitter
57
+ super.init()
58
+ // Subscribe to the SDK's state publisher so isChatDisplayed is sync.
59
+ self.stateSubscription = KindlyChatClient.state.sink { [weak self] state in
60
+ self?.lastIsChatDisplayed = state.isChatDisplayed
61
+ }
62
+ }
63
+
64
+ // MARK: - Lifecycle
65
+
66
+ @objc public func start(botKey: String,
67
+ languageCode: String?,
68
+ hasAuthCallback: Bool) {
69
+ let lang = languageCode ?? "en"
70
+
71
+ let authCallback: AuthTokenCallback? = hasAuthCallback ? { [weak self] _, promise in
72
+ // Build a UUID-keyed pending entry, fire the JS event, block native
73
+ // for up to 5s waiting for `respondToAuthToken(requestId:token:)`.
74
+ guard let self = self else {
75
+ promise.reject(NSError(domain: "KindlyReactNative",
76
+ code: -1,
77
+ userInfo: [NSLocalizedDescriptionKey: "self deallocated"]))
78
+ return
79
+ }
80
+ let requestId = UUID().uuidString
81
+ let semaphore = DispatchSemaphore(value: 0)
82
+
83
+ self.pendingLock.lock()
84
+ self.authPending[requestId] = PendingAuth(semaphore: semaphore, token: nil)
85
+ self.pendingLock.unlock()
86
+
87
+ self.emit(name: "KindlyGetAuthToken", body: ["requestId": requestId])
88
+
89
+ // Wait off the calling thread (KindlyChatClient invokes the auth
90
+ // callback on a background queue; blocking is OK there).
91
+ DispatchQueue.global().async {
92
+ _ = semaphore.wait(timeout: .now() + 30.0)
93
+ self.pendingLock.lock()
94
+ let entry = self.authPending.removeValue(forKey: requestId)
95
+ self.pendingLock.unlock()
96
+ if let token = entry?.token {
97
+ promise.fulfill(token)
98
+ } else {
99
+ promise.reject(NSError(domain: "KindlyReactNative",
100
+ code: -1,
101
+ userInfo: [NSLocalizedDescriptionKey: "Auth token not provided"]))
102
+ }
103
+ }
104
+ } : nil
105
+
106
+ KindlyChatClient.start(botKey: botKey, languageCode: lang, authTokenCallback: authCallback)
107
+ }
108
+
109
+ @objc public func displayChat(languageCode: String?, triggerDialogueId: String?) {
110
+ KindlyChatClient.displayChat(languageCode: languageCode, triggerDialogueId: triggerDialogueId)
111
+ }
112
+
113
+ @objc public func launchChat(triggerDialogueId: String?) {
114
+ KindlyChatClient.displayChat(languageCode: nil, triggerDialogueId: triggerDialogueId)
115
+ }
116
+
117
+ @objc public func closeChat() {
118
+ KindlyChatClient.closeChat()
119
+ }
120
+
121
+ @objc public func endChat(resolve: @escaping RCTPromiseResolveBlock,
122
+ reject: @escaping RCTPromiseRejectBlock) {
123
+ KindlyChatClient.endChat().then { _ in
124
+ resolve(NSNull())
125
+ }.catch { error in
126
+ reject("END_CHAT_FAILED", error.localizedDescription, error)
127
+ }
128
+ }
129
+
130
+ @objc public func kill(resolve: @escaping RCTPromiseResolveBlock,
131
+ reject: @escaping RCTPromiseRejectBlock) {
132
+ KindlyChatClient.kill().then { _ in
133
+ resolve(NSNull())
134
+ }.catch { error in
135
+ reject("KILL_FAILED", error.localizedDescription, error)
136
+ }
137
+ }
138
+
139
+ @objc public func setLanguage(code: String,
140
+ resolve: @escaping RCTPromiseResolveBlock,
141
+ reject: @escaping RCTPromiseRejectBlock) {
142
+ KindlyChatClient.setLanguage(code).then { _ in
143
+ resolve(NSNull())
144
+ }.catch { error in
145
+ reject("SET_LANGUAGE_FAILED", error.localizedDescription, error)
146
+ }
147
+ }
148
+
149
+ // MARK: - Messaging
150
+
151
+ @objc public func sendMessage(text: String,
152
+ newContext: [String: String]?,
153
+ resolve: @escaping RCTPromiseResolveBlock,
154
+ reject: @escaping RCTPromiseRejectBlock) {
155
+ KindlyChatClient.sendMessage(text, newContext: newContext).then { reconciled in
156
+ resolve(serializeChatMessage(reconciled))
157
+ }.catch { error in
158
+ reject("SEND_MESSAGE_FAILED", error.localizedDescription, error)
159
+ }
160
+ }
161
+
162
+ @objc public func setNewContext(_ context: [String: String]) {
163
+ KindlyChatClient.setNewContext(context)
164
+ }
165
+
166
+ @objc public func clearNewContext() {
167
+ KindlyChatClient.clearNewContext()
168
+ }
169
+
170
+ @objc public func triggerDialogue(_ dialogueId: String) {
171
+ KindlyChatClient.triggerDialogue(id: dialogueId)
172
+ }
173
+
174
+ @objc public func handleUrl(_ urlString: String) -> Bool {
175
+ guard let url = URL(string: urlString) else { return false }
176
+ return KindlyChatClient.handleURL(url)
177
+ }
178
+
179
+ // MARK: - Theme
180
+
181
+ @objc public func setCustomTheme(_ colors: [String: Any]) {
182
+ func color(_ key: String) -> UIColor? {
183
+ guard let raw = colors[key] as? Int else { return nil }
184
+ return UIColor(argb: raw)
185
+ }
186
+ KindlyChatClient.setCustomTheme(
187
+ background: color("background"),
188
+ botMessageBackground: color("botMessageBackground"),
189
+ botMessageText: color("botMessageText"),
190
+ buttonBackground: color("buttonBackground"),
191
+ buttonOutline: color("buttonOutline"),
192
+ buttonSelectedBackground: color("buttonSelectedBackground"),
193
+ buttonText: color("buttonText"),
194
+ defaultShadow: color("defaultShadow"),
195
+ inputBackground: color("inputBackground"),
196
+ inputCursor: color("inputCursor"),
197
+ inputText: color("inputText"),
198
+ navBarBackground: color("navBarBackground"),
199
+ navBarText: color("navBarText"),
200
+ userMessageBackground: color("userMessageBackground"),
201
+ userMessageText: color("userMessageText")
202
+ )
203
+ }
204
+
205
+ @objc public func clearCustomTheme() {
206
+ KindlyChatClient.clearCustomTheme()
207
+ }
208
+
209
+ // MARK: - Push
210
+
211
+ @objc public func setAPNSDeviceToken(_ token: String) {
212
+ KindlyChatClient.setAPNSDeviceToken(token)
213
+ }
214
+
215
+ @objc public func handleNotification(_ data: [AnyHashable: Any]) {
216
+ KindlyChatClient.notificationReceived(data)
217
+ }
218
+
219
+ @objc public func isKindlyNotification(_ data: [AnyHashable: Any]) -> Bool {
220
+ return KindlyChatClient.isKindlyNotification(data)
221
+ }
222
+
223
+ // MARK: - State / settings
224
+
225
+ @objc public func saveAuthToken(_ token: String) -> Bool {
226
+ return KindlyChatClient.saveAuthToken(token)
227
+ }
228
+
229
+ @objc public func isChatDisplayed() -> Bool {
230
+ return lastIsChatDisplayed
231
+ }
232
+
233
+ @objc public func setVerboseLogging(_ enabled: Bool) {
234
+ KindlyChatClient.verboseLogging = enabled
235
+ }
236
+
237
+ @objc public func setCrashReporting(_ enabled: Bool) {
238
+ KindlyChatClient.enableCrashReporting = enabled
239
+ }
240
+
241
+ // MARK: - Round-trip responders (called by JS)
242
+
243
+ @objc public func respondToAuthToken(_ requestId: String, token: String?) {
244
+ pendingLock.lock()
245
+ defer { pendingLock.unlock() }
246
+ guard var entry = authPending[requestId] else { return }
247
+ entry.token = token
248
+ authPending[requestId] = entry
249
+ entry.semaphore.signal()
250
+ }
251
+
252
+ @objc public func respondToShouldHandle(_ requestId: String, value: Bool) {
253
+ pendingLock.lock()
254
+ defer { pendingLock.unlock() }
255
+ guard var entry = shouldHandlePending[requestId] else { return }
256
+ entry.value = value
257
+ shouldHandlePending[requestId] = entry
258
+ entry.semaphore.signal()
259
+ }
260
+
261
+ // MARK: - Delegate gating
262
+
263
+ @objc public func setDelegate(buttonPressed: Bool,
264
+ shouldHandleLink: Bool,
265
+ shouldHandleNotification: Bool,
266
+ onHandover: Bool) {
267
+ let bridge = delegateBridge ?? KindlyDelegateBridge(impl: self)
268
+ bridge.hasButtonPressed = buttonPressed
269
+ bridge.hasShouldHandleLink = shouldHandleLink
270
+ bridge.hasShouldHandleNotification = shouldHandleNotification
271
+ delegateBridge = bridge
272
+
273
+ let anySet = buttonPressed || shouldHandleLink || shouldHandleNotification
274
+ KindlyChatClient.delegate = anySet ? bridge : nil
275
+ }
276
+
277
+ // MARK: - Internal helpers used by the bridge
278
+
279
+ fileprivate func emit(name: String, body: Any?) {
280
+ emitter?.emitEvent(name, body: body)
281
+ }
282
+
283
+ /// Block native for up to 5s while JS computes a Bool answer. Default
284
+ /// `true` on timeout / no-listener — never hang the chat UI.
285
+ fileprivate func blockingShouldHandle(eventName: String, payload: [String: Any]) -> Bool {
286
+ let requestId = UUID().uuidString
287
+ let semaphore = DispatchSemaphore(value: 0)
288
+
289
+ pendingLock.lock()
290
+ shouldHandlePending[requestId] = PendingBool(semaphore: semaphore, value: true)
291
+ pendingLock.unlock()
292
+
293
+ var body = payload
294
+ body["requestId"] = requestId
295
+ DispatchQueue.main.async { [weak self] in
296
+ self?.emit(name: eventName, body: body)
297
+ }
298
+
299
+ _ = semaphore.wait(timeout: .now() + 5.0)
300
+
301
+ pendingLock.lock()
302
+ let entry = shouldHandlePending.removeValue(forKey: requestId)
303
+ pendingLock.unlock()
304
+ return entry?.value ?? true
305
+ }
306
+
307
+ fileprivate func emitButtonPressed(button: ExternalChatButton, chatLog: [ExternalChatMessage]) {
308
+ let payload: [String: Any] = [
309
+ "button": serializeExternalChatButton(button),
310
+ "chatLog": chatLog.map { serializeChatMessage($0) },
311
+ ]
312
+ DispatchQueue.main.async { [weak self] in
313
+ self?.emit(name: "KindlyOnButtonPressed", body: payload)
314
+ }
315
+ }
316
+ }
317
+
318
+ // MARK: - KindlyChatClientDelegate bridge
319
+
320
+ /// Bridges the iOS SDK's `KindlyChatClientDelegate` protocol to the JS-side
321
+ /// callbacks registered via `KindlySDK.setDelegate(...)`. Symmetric with the
322
+ /// Flutter SDK's bridge — fire-and-forget for didPressButton, blocking-bool
323
+ /// (5s timeout, default true) for shouldHandleLink / shouldHandleNotification.
324
+ private final class KindlyDelegateBridge: NSObject, KindlyChatClientDelegate {
325
+ weak var impl: KindlyReactNativeImpl?
326
+ init(impl: KindlyReactNativeImpl) { self.impl = impl; super.init() }
327
+
328
+ var hasButtonPressed: Bool = false
329
+ var hasShouldHandleLink: Bool = false
330
+ var hasShouldHandleNotification: Bool = false
331
+
332
+ func didPressButton(chatButton: ExternalChatButton, chatLog: [ExternalChatMessage]) {
333
+ guard hasButtonPressed else { return }
334
+ impl?.emitButtonPressed(button: chatButton, chatLog: chatLog)
335
+ }
336
+
337
+ func shouldHandleLink(url: URL) -> Bool {
338
+ guard hasShouldHandleLink, let impl = impl else { return true }
339
+ return impl.blockingShouldHandle(
340
+ eventName: "KindlyShouldHandleLink",
341
+ payload: ["url": url.absoluteString]
342
+ )
343
+ }
344
+
345
+ func shouldHandleNotification(notification: ExternalNotification) -> Bool {
346
+ guard hasShouldHandleNotification, let impl = impl else { return true }
347
+ let payload: [String: Any] = [
348
+ "notification": [
349
+ "id": notification.id,
350
+ "title": notification.title,
351
+ "body": notification.body,
352
+ "data": stringifyUserInfo(notification.userInfo),
353
+ ],
354
+ ]
355
+ return impl.blockingShouldHandle(
356
+ eventName: "KindlyShouldHandleNotification",
357
+ payload: payload
358
+ )
359
+ }
360
+ }
361
+
362
+ // MARK: - Serialization helpers (file-private)
363
+
364
+ private func serializeChatMessage(_ message: ExternalChatMessage) -> [String: Any] {
365
+ var dict = message.toDictionary()
366
+ if let created = dict["created"] as? Date {
367
+ dict["created"] = ISO8601DateFormatter().string(from: created)
368
+ }
369
+ return dict
370
+ }
371
+
372
+ /// `ExternalChatButton`'s fields are package-internal — reflect at runtime.
373
+ private func serializeExternalChatButton(_ button: ExternalChatButton) -> [String: Any] {
374
+ var dict: [String: Any] = [:]
375
+ for child in Mirror(reflecting: button).children {
376
+ guard let label = child.label else { continue }
377
+ let value = child.value
378
+ let valueMirror = Mirror(reflecting: value)
379
+ if valueMirror.displayStyle == .optional {
380
+ if let unwrapped = valueMirror.children.first?.value {
381
+ dict[label] = unwrapped
382
+ }
383
+ } else {
384
+ dict[label] = value
385
+ }
386
+ }
387
+ return dict
388
+ }
389
+
390
+ private func stringifyUserInfo(_ userInfo: [AnyHashable: Any]) -> [String: String] {
391
+ var out: [String: String] = [:]
392
+ for (k, v) in userInfo {
393
+ if let key = k as? String {
394
+ out[key] = String(describing: v)
395
+ }
396
+ }
397
+ return out
398
+ }
399
+
400
+ extension UIColor {
401
+ /// Initialise from a 32-bit ARGB integer (Flutter `Color.value` convention).
402
+ fileprivate convenience init(argb: Int) {
403
+ self.init(
404
+ red: CGFloat((argb >> 16) & 0xFF) / 255.0,
405
+ green: CGFloat((argb >> 8) & 0xFF) / 255.0,
406
+ blue: CGFloat(argb & 0xFF) / 255.0,
407
+ alpha: CGFloat((argb >> 24) & 0xFF) / 255.0
408
+ )
409
+ }
410
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+
3
+ // TurboModule spec for the @kindly-ai/react-native package.
4
+ //
5
+ // Codegen INPUT — `react-native` reads this at build time and generates the
6
+ // abstract Spec class on iOS (Obj-C++ header) and Android (Kotlin abstract
7
+ // class). The native module classes extend those.
8
+ //
9
+ // Convention used here: every method takes flat primitive parameters and
10
+ // returns either a primitive or a Promise. We deliberately avoid nested
11
+ // struct args (`(args: { ... })`) because codegen generates JS::Native*Args
12
+ // C++ structs for those, which add fragility without buying anything for
13
+ // our use case. This matches the scaffolded `multiply()` example pattern.
14
+ //
15
+ // Filename MUST match `Native<ModuleName>.ts`. ModuleName here is
16
+ // `KindlyReactNative` — this becomes the native module name returned by
17
+ // `getName()` and the registry key. Customer-facing imports come from the
18
+ // package, not the module name.
19
+
20
+ import { TurboModuleRegistry } from 'react-native';
21
+ export default TurboModuleRegistry.getEnforcing('KindlyReactNative');
22
+ //# sourceMappingURL=NativeKindlyReactNative.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeKindlyReactNative.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,mBAAmB,QAAQ,cAAc;AA8DlD,eAAeA,mBAAmB,CAACC,YAAY,CAAO,mBAAmB,CAAC","ignoreList":[]}