@vanira/sdk-react-native 0.0.2

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 (148) hide show
  1. package/README.md +239 -0
  2. package/package.json +53 -0
  3. package/src/__tests__/WebRTCClient.integration.test.ts +396 -0
  4. package/src/__tests__/adapters.test.ts +475 -0
  5. package/src/__tests__/httpResponse.test.ts +25 -0
  6. package/src/__tests__/mocks/react-native-incall-manager.ts +8 -0
  7. package/src/__tests__/mocks/react-native-permissions.ts +15 -0
  8. package/src/__tests__/mocks/react-native-webrtc.ts +6 -0
  9. package/src/__tests__/mocks/react-native.ts +28 -0
  10. package/src/__tests__/preset.test.ts +239 -0
  11. package/src/__tests__/resolveRuntimeConfig.test.ts +90 -0
  12. package/src/__tests__/storage.test.ts +211 -0
  13. package/src/__tests__/webrtcSignaling.test.ts +42 -0
  14. package/src/adapters/PeerConnectionAdapter.ts +101 -0
  15. package/src/adapters/browser/BrowserAudioAdapter.ts +43 -0
  16. package/src/adapters/browser/BrowserDataChannelAdapter.ts +69 -0
  17. package/src/adapters/browser/BrowserMediaAdapter.ts +15 -0
  18. package/src/adapters/browser/BrowserPeerAdapter.ts +14 -0
  19. package/src/adapters/browser/index.ts +4 -0
  20. package/src/adapters/interfaces.ts +84 -0
  21. package/src/adapters/react-native/RNAudioAdapter.ts +42 -0
  22. package/src/adapters/react-native/RNDataChannelAdapter.ts +79 -0
  23. package/src/adapters/react-native/RNMediaAdapter.ts +46 -0
  24. package/src/adapters/react-native/RNPeerAdapter.ts +28 -0
  25. package/src/adapters/react-native/callAudioRouting.ts +115 -0
  26. package/src/adapters/react-native/decodeUtf8.ts +72 -0
  27. package/src/adapters/react-native/index.ts +4 -0
  28. package/src/adapters/react-native/rnUploadFile.ts +76 -0
  29. package/src/adapters/storage/BrowserDualStorageAdapter.ts +71 -0
  30. package/src/adapters/storage/MemoryStorageAdapter.ts +50 -0
  31. package/src/adapters/storage/StorageAdapter.ts +21 -0
  32. package/src/adapters/storage/createSyncStorageAdapter.ts +40 -0
  33. package/src/adapters/storage/index.ts +7 -0
  34. package/src/api/services/ChatService.ts +304 -0
  35. package/src/api/services/ConfigService.ts +33 -0
  36. package/src/assets/icons.js +35 -0
  37. package/src/cdn.ts +68 -0
  38. package/src/core/CallSessionStore.ts +137 -0
  39. package/src/core/DraggableController.ts +83 -0
  40. package/src/core/SessionManager.ts +322 -0
  41. package/src/core/VaniraAI.ts +464 -0
  42. package/src/core/WebRTCClient.ts +1012 -0
  43. package/src/core/httpResponse.ts +22 -0
  44. package/src/core/iceServers.ts +18 -0
  45. package/src/core/toolCallNormalize.ts +80 -0
  46. package/src/core/voice-client.js +236 -0
  47. package/src/core/webrtcSignaling.ts +72 -0
  48. package/src/index.js +34 -0
  49. package/src/index.ts +6 -0
  50. package/src/platforms/browser.ts +67 -0
  51. package/src/platforms/react-native.ts +105 -0
  52. package/src/presets/BookingCalendarModal.tsx +457 -0
  53. package/src/presets/CameraModal.tsx +576 -0
  54. package/src/presets/DynamicFormModal.tsx +378 -0
  55. package/src/presets/NativePresetRenderer.tsx +350 -0
  56. package/src/presets/NavigateHandler.tsx +75 -0
  57. package/src/presets/PresetHost.tsx +155 -0
  58. package/src/presets/PresetShellModal.tsx +97 -0
  59. package/src/presets/UploadModal.tsx +321 -0
  60. package/src/presets/calendar/calendarUtils.ts +386 -0
  61. package/src/presets/call/CallSpeakerToggle.tsx +59 -0
  62. package/src/presets/call/callAudioRouting.ts +2 -0
  63. package/src/presets/call/useCallSpeaker.ts +31 -0
  64. package/src/presets/camera/cameraPermissions.ts +18 -0
  65. package/src/presets/camera/cameraStream.ts +19 -0
  66. package/src/presets/camera/cameraUtils.ts +21 -0
  67. package/src/presets/camera/useLivenessFlow.ts +95 -0
  68. package/src/presets/chalkboard/ChalkboardOverlay.tsx +156 -0
  69. package/src/presets/chalkboard/EraseTextHandler.tsx +95 -0
  70. package/src/presets/chalkboard/TypeTextHandler.tsx +107 -0
  71. package/src/presets/chalkboard/boardAbort.ts +36 -0
  72. package/src/presets/chalkboard/boardQueue.ts +620 -0
  73. package/src/presets/chalkboard/chalkboardSession.ts +75 -0
  74. package/src/presets/chalkboard/drawUtils.ts +123 -0
  75. package/src/presets/chalkboard/textUtils.ts +109 -0
  76. package/src/presets/clipRegion/ClipRegionModal.tsx +261 -0
  77. package/src/presets/clipRegion/clipRegionBridge.ts +19 -0
  78. package/src/presets/form/formValidation.ts +104 -0
  79. package/src/presets/form/parseFormFields.ts +171 -0
  80. package/src/presets/host/HostElementPresetHandler.tsx +155 -0
  81. package/src/presets/host/hostPresetBridge.ts +71 -0
  82. package/src/presets/index.ts +63 -0
  83. package/src/presets/liveScreen/CloseLiveScreenHandler.tsx +36 -0
  84. package/src/presets/liveScreen/LiveScreenCaptureHost.tsx +312 -0
  85. package/src/presets/liveScreen/LiveScreenHandler.tsx +25 -0
  86. package/src/presets/liveScreen/LiveScreenPipOverlay.tsx +6 -0
  87. package/src/presets/liveScreen/liveScreenSession.ts +73 -0
  88. package/src/presets/liveVision/CloseLiveVisionHandler.tsx +29 -0
  89. package/src/presets/liveVision/LiveVisionCameraHost.tsx +317 -0
  90. package/src/presets/liveVision/LiveVisionHandler.tsx +26 -0
  91. package/src/presets/liveVision/LiveVisionPipOverlay.tsx +7 -0
  92. package/src/presets/liveVision/liveVisionFrameLoop.ts +38 -0
  93. package/src/presets/liveVision/liveVisionSession.ts +75 -0
  94. package/src/presets/liveVision/liveVisionUpload.ts +62 -0
  95. package/src/presets/navigation/internalRouteRegistry.ts +25 -0
  96. package/src/presets/navigation/navigationBridge.ts +76 -0
  97. package/src/presets/navigation/navigationTypes.ts +12 -0
  98. package/src/presets/parseToolCall.ts +60 -0
  99. package/src/presets/presetClientAdapter.ts +29 -0
  100. package/src/presets/presetCompletion.ts +91 -0
  101. package/src/presets/presetEventHelpers.ts +45 -0
  102. package/src/presets/registry.ts +128 -0
  103. package/src/presets/streaming/mediaFrameUpload.ts +93 -0
  104. package/src/presets/types.ts +74 -0
  105. package/src/presets/upload/pickUploadFile.ts +256 -0
  106. package/src/presets/upload/uploadFormats.ts +163 -0
  107. package/src/presets/upload/uploadUtils.ts +68 -0
  108. package/src/react/PresetRenderer.tsx +144 -0
  109. package/src/react/index.ts +1 -0
  110. package/src/runtime/browserRuntime.ts +54 -0
  111. package/src/runtime/platform.ts +17 -0
  112. package/src/runtime/reactNativeRuntime.ts +68 -0
  113. package/src/runtime/resolveRuntimeConfig.ts +75 -0
  114. package/src/runtime/runtimeBundles.ts +74 -0
  115. package/src/runtime/types.ts +135 -0
  116. package/src/types/react-native-incall-manager.d.ts +17 -0
  117. package/src/types/react-native-webrtc.d.ts +47 -0
  118. package/src/types.ts +133 -0
  119. package/src/ui/VaniraWidget.ts +87 -0
  120. package/src/ui/abstraction/AbstractWidgetProvider.ts +18 -0
  121. package/src/ui/abstraction/interfaces.ts +12 -0
  122. package/src/ui/adapters/VaniraChatAdapter.ts +42 -0
  123. package/src/ui/components/AvatarView.ts +81 -0
  124. package/src/ui/components/ChatWindow.ts +263 -0
  125. package/src/ui/components/FloatingButton.ts +163 -0
  126. package/src/ui/components/FloatingWelcomeChips.ts +137 -0
  127. package/src/ui/components/Panel.ts +120 -0
  128. package/src/ui/components/VoiceOrb.ts +79 -0
  129. package/src/ui/components/VoiceOverlay.ts +497 -0
  130. package/src/ui/components/index.ts +7 -0
  131. package/src/ui/factory/WidgetFactory.ts +16 -0
  132. package/src/ui/icons_data.ts +2 -0
  133. package/src/ui/presets/WidgetPresetRenderer.ts +1802 -0
  134. package/src/ui/presets/types.ts +16 -0
  135. package/src/ui/providers/VaniraInternalProvider.ts +1066 -0
  136. package/src/ui/styles/index.ts +323 -0
  137. package/src/ui/styles/keyframes.ts +76 -0
  138. package/src/ui/styles/theme.ts +57 -0
  139. package/src/ui/styles/widget.css.ts +838 -0
  140. package/src/ui/utils.ts +37 -0
  141. package/src/ui/views/AbstractChatView.ts +93 -0
  142. package/src/ui/views/AbstractVoiceView.ts +57 -0
  143. package/src/ui/views/AvatarOnlyView.ts +78 -0
  144. package/src/ui/views/ChatAvatarView.ts +66 -0
  145. package/src/ui/views/ChatOnlyView.ts +28 -0
  146. package/src/ui/views/ChatVoiceView.ts +15 -0
  147. package/src/ui/views/VoiceOnlyView.ts +25 -0
  148. package/src/ui/views/index.ts +5 -0
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Platform detection helpers for the Vanira SDK runtime layer.
3
+ * Used by resolveRuntimeConfig and (in Task 0.4+) preset DOM dispatch.
4
+ */
5
+
6
+ /**
7
+ * Whether CustomEvent and globalThis.dispatchEvent are available.
8
+ * Used to gate browser-only `vanira:preset` window events without relying on
9
+ * runtime.capabilities.supportsDom alone.
10
+ */
11
+ export function canDispatchBrowserEvent(): boolean {
12
+ return (
13
+ typeof CustomEvent !== 'undefined' &&
14
+ typeof globalThis !== 'undefined' &&
15
+ typeof globalThis.dispatchEvent === 'function'
16
+ );
17
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * React Native Runtime
3
+ *
4
+ * Pre-wires all React Native adapters into a VaniraRuntime.
5
+ * Import this module only in React Native environments.
6
+ *
7
+ * Prerequisites:
8
+ * 1. Install react-native-webrtc and call registerGlobals() at app entry.
9
+ * 2. Link native modules per the react-native-webrtc installation guide.
10
+ * 3. Add microphone permissions to AndroidManifest.xml and Info.plist.
11
+ *
12
+ * Usage:
13
+ * import { createReactNativeClient } from 'vanira-sdk/runtime/react-native';
14
+ * const client = createReactNativeClient({ agentId: '...', apiKey: '...' });
15
+ *
16
+ * Platform differences versus browser:
17
+ * - supportsAudioEndedEvent: false — audio routes via native stack, no onended
18
+ * - supportsDom: false — no document/window
19
+ * - supportsHtmlAudio: false — no Audio element
20
+ * - supportsCustomElements: false — no customElements API
21
+ * - supportsLocalStorage: false — use AsyncStorage instead
22
+ * - supportsBroadcastChannel: false — no cross-tab (no tabs on mobile)
23
+ * - callType: 'web' — API only accepts web | sip; mobile is indicated via runtimeName
24
+ * - runtimeName: 'react-native' — explicit backend runtime identification
25
+ */
26
+
27
+ import type { WebRTCClientConfig } from '../types';
28
+
29
+ import { WebRTCClient } from '../core/WebRTCClient';
30
+ import { VaniraAI } from '../core/VaniraAI';
31
+
32
+ import {
33
+ reactNativeDefaultBundle,
34
+ reactNativeCapabilities,
35
+ } from './runtimeBundles';
36
+
37
+ // Re-export bundle + capabilities (single source of truth in runtimeBundles.ts)
38
+ export { reactNativeCapabilities };
39
+ export const reactNativeRuntime = reactNativeDefaultBundle;
40
+
41
+ // ─── Factory functions ────────────────────────────────────────────────────────
42
+
43
+ /**
44
+ * Create a WebRTCClient pre-wired for React Native.
45
+ *
46
+ * The callId is auto-generated with an 'rn_' prefix for tracing.
47
+ * The /calls/create request body will include `{ type: 'web', runtime: 'react-native' }`.
48
+ */
49
+ export function createReactNativeClient(
50
+ config: Omit<WebRTCClientConfig, 'audioAdapter' | 'mediaAdapter' | 'peerAdapter' | 'dataChannelAdapter'>
51
+ ): WebRTCClient {
52
+ return new WebRTCClient({
53
+ ...config,
54
+ runtime: reactNativeRuntime,
55
+ });
56
+ }
57
+
58
+ /**
59
+ * Create a VaniraAI instance pre-wired for React Native.
60
+ */
61
+ export function createReactNativeAI(
62
+ config: Omit<WebRTCClientConfig, 'audioAdapter' | 'mediaAdapter' | 'peerAdapter' | 'dataChannelAdapter'>
63
+ ): VaniraAI {
64
+ return new VaniraAI({
65
+ ...config,
66
+ runtime: reactNativeRuntime,
67
+ });
68
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Runtime resolution for WebRTCClient (wired in Task 0.2).
3
+ *
4
+ * Layered resolution:
5
+ * 1. config.runtime provided → use it (factories inject browser/RN bundle)
6
+ * 2. isReactNative() → throw (RN must use @vanira/sdk-react-native factories)
7
+ * 3. else → browserDefaultBundle (bare new WebRTCClient() on web)
8
+ */
9
+
10
+ import type { WebRTCClientConfig } from '../types';
11
+ import type { VaniraRuntime, RuntimeCapabilities } from './types';
12
+ import type { AudioAdapter, MediaAdapter } from '../adapters/interfaces';
13
+ import type { PeerConnectionAdapter, DataChannelAdapter } from '../adapters/PeerConnectionAdapter';
14
+ import type { StorageAdapter } from '../adapters/storage/StorageAdapter';
15
+
16
+ import { browserDefaultBundle } from './runtimeBundles';
17
+
18
+ /** Resolved adapters + metadata after config override merge. */
19
+ export interface ResolvedRuntimeConfig {
20
+ readonly name: VaniraRuntime['name'];
21
+ readonly runtimeName: string;
22
+ readonly callType: 'web';
23
+ readonly callIdPrefix: string;
24
+ readonly capabilities: RuntimeCapabilities;
25
+ readonly audio: AudioAdapter;
26
+ readonly media: MediaAdapter;
27
+ readonly peer: PeerConnectionAdapter;
28
+ readonly dataChannel: DataChannelAdapter;
29
+ readonly storage: StorageAdapter;
30
+ }
31
+
32
+ const RN_RUNTIME_REQUIRED_MESSAGE =
33
+ '[VaniraSDK] runtime is required. Import createReactNativeClient/createReactNativeAI from \'@vanira/sdk-react-native\'.';
34
+
35
+ /**
36
+ * True when running in React Native (after react-native-webrtc registerGlobals()).
37
+ * Do not infer platform from typeof window — RN polyfills window.
38
+ */
39
+ export function isReactNative(): boolean {
40
+ return (
41
+ typeof navigator !== 'undefined' &&
42
+ (navigator as Navigator & { product?: string }).product === 'ReactNative'
43
+ );
44
+ }
45
+
46
+ function mergeRuntime(base: VaniraRuntime, config: WebRTCClientConfig): ResolvedRuntimeConfig {
47
+ return {
48
+ name: base.name,
49
+ runtimeName: base.runtimeName,
50
+ callType: base.callType,
51
+ callIdPrefix: base.callIdPrefix,
52
+ capabilities: base.capabilities,
53
+ audio: config.audioAdapter ?? base.audio,
54
+ media: config.mediaAdapter ?? base.media,
55
+ peer: config.peerAdapter ?? base.peer,
56
+ dataChannel: config.dataChannelAdapter ?? base.dataChannel,
57
+ storage: config.storageAdapter ?? base.storage,
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Resolve platform adapters and metadata from WebRTCClientConfig.
63
+ * Individual adapter fields on config override the bundle defaults.
64
+ */
65
+ export function resolveRuntimeConfig(config: WebRTCClientConfig): ResolvedRuntimeConfig {
66
+ if (config.runtime) {
67
+ return mergeRuntime(config.runtime, config);
68
+ }
69
+
70
+ if (isReactNative()) {
71
+ throw new Error(RN_RUNTIME_REQUIRED_MESSAGE);
72
+ }
73
+
74
+ return mergeRuntime(browserDefaultBundle, config);
75
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Platform runtime bundles — adapter singletons and metadata.
3
+ *
4
+ * Must NOT import from core/ (WebRTCClient, VaniraAI) to avoid circular deps.
5
+ * Consumed by browserRuntime.ts, reactNativeRuntime.ts, and resolveRuntimeConfig.ts.
6
+ */
7
+
8
+ import type { VaniraRuntime, RuntimeCapabilities } from './types';
9
+
10
+ import { BrowserAudioAdapter } from '../adapters/browser/BrowserAudioAdapter';
11
+ import { BrowserMediaAdapter } from '../adapters/browser/BrowserMediaAdapter';
12
+ import { BrowserPeerAdapter } from '../adapters/browser/BrowserPeerAdapter';
13
+ import { BrowserDataChannelAdapter } from '../adapters/browser/BrowserDataChannelAdapter';
14
+
15
+ import { RNAudioAdapter } from '../adapters/react-native/RNAudioAdapter';
16
+ import { RNMediaAdapter } from '../adapters/react-native/RNMediaAdapter';
17
+ import { RNPeerAdapter } from '../adapters/react-native/RNPeerAdapter';
18
+ import { RNDataChannelAdapter } from '../adapters/react-native/RNDataChannelAdapter';
19
+
20
+ import { BrowserDualStorageAdapter } from '../adapters/storage/BrowserDualStorageAdapter';
21
+ import { MemoryStorageAdapter } from '../adapters/storage/MemoryStorageAdapter';
22
+
23
+ // ─── Capabilities ─────────────────────────────────────────────────────────────
24
+
25
+ export const browserCapabilities: RuntimeCapabilities = {
26
+ supportsAudioEndedEvent: true,
27
+ supportsScreenShare:
28
+ typeof navigator !== 'undefined' &&
29
+ typeof (navigator.mediaDevices as MediaDevices & { getDisplayMedia?: unknown })
30
+ ?.getDisplayMedia === 'function',
31
+ supportsDom: typeof document !== 'undefined',
32
+ supportsHtmlAudio: typeof Audio !== 'undefined',
33
+ supportsCustomElements: typeof customElements !== 'undefined',
34
+ supportsLocalStorage: typeof localStorage !== 'undefined',
35
+ supportsBroadcastChannel: typeof BroadcastChannel !== 'undefined',
36
+ };
37
+
38
+ export const reactNativeCapabilities: RuntimeCapabilities = {
39
+ supportsAudioEndedEvent: false,
40
+ supportsScreenShare: false,
41
+ supportsDom: false,
42
+ supportsHtmlAudio: false,
43
+ supportsCustomElements: false,
44
+ supportsLocalStorage: false,
45
+ supportsBroadcastChannel: false,
46
+ };
47
+
48
+ // ─── Default bundles ────────────────────────────────────────────────────────
49
+
50
+ export const browserDefaultBundle: VaniraRuntime = {
51
+ name: 'browser',
52
+ callType: 'web',
53
+ runtimeName: 'browser',
54
+ callIdPrefix: 'web_',
55
+ capabilities: browserCapabilities,
56
+ audio: new BrowserAudioAdapter(),
57
+ media: new BrowserMediaAdapter(),
58
+ peer: new BrowserPeerAdapter(),
59
+ dataChannel: new BrowserDataChannelAdapter(),
60
+ storage: new BrowserDualStorageAdapter(),
61
+ };
62
+
63
+ export const reactNativeDefaultBundle: VaniraRuntime = {
64
+ name: 'react-native',
65
+ callType: 'web',
66
+ runtimeName: 'react-native',
67
+ callIdPrefix: 'rn_',
68
+ capabilities: reactNativeCapabilities,
69
+ audio: new RNAudioAdapter(),
70
+ media: new RNMediaAdapter(),
71
+ peer: new RNPeerAdapter(),
72
+ dataChannel: new RNDataChannelAdapter(),
73
+ storage: new MemoryStorageAdapter(),
74
+ };
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Runtime types for the Vanira SDK cross-platform layer.
3
+ *
4
+ * A VaniraRuntime bundles all platform adapters, capability flags, and
5
+ * protocol metadata into a single composable object. The WebRTCClient
6
+ * accepts a VaniraRuntime in its config so all platform details are
7
+ * injected from a single source of truth per platform.
8
+ *
9
+ * Current runtimes:
10
+ * browserRuntime — Web browsers
11
+ * reactNativeRuntime — React Native via react-native-webrtc
12
+ *
13
+ * Future:
14
+ * flutterRuntime — Dart, implemented separately in flutter-sdk/
15
+ */
16
+
17
+ import type { AudioAdapter, MediaAdapter } from '../adapters/interfaces';
18
+ import type { PeerConnectionAdapter, DataChannelAdapter } from '../adapters/PeerConnectionAdapter';
19
+ import type { StorageAdapter } from '../adapters/storage/StorageAdapter';
20
+
21
+ // ─── RuntimeCapabilities ──────────────────────────────────────────────────────
22
+
23
+ /**
24
+ * Declarative flags describing what the current platform supports.
25
+ * Use these instead of hardcoded platform checks throughout the codebase.
26
+ *
27
+ * The backend also receives the runtime name and uses it to skip
28
+ * `playedStream` waiting for platforms that don't fire `onended`.
29
+ */
30
+ export interface RuntimeCapabilities {
31
+ /**
32
+ * Whether HTMLAudioElement.onended fires after a TTS stream finishes.
33
+ * Browser: true — fires naturally after the stream ends.
34
+ * RN: false — react-native-webrtc routes audio natively, no onended event.
35
+ * Flutter: false — flutter_webrtc routes audio natively.
36
+ */
37
+ supportsAudioEndedEvent: boolean;
38
+
39
+ /**
40
+ * Whether getDisplayMedia (screen capture) is available.
41
+ * Browser: true (desktop), false on mobile browsers.
42
+ * RN: false — no screen capture API.
43
+ * Flutter: false — flutter_webrtc does not expose getDisplayMedia.
44
+ */
45
+ supportsScreenShare: boolean;
46
+
47
+ /**
48
+ * Whether document, window, and DOM APIs are present.
49
+ * Browser: true. RN: false. Flutter: false.
50
+ */
51
+ supportsDom: boolean;
52
+
53
+ /**
54
+ * Whether new Audio() / HTMLAudioElement is constructable.
55
+ * Browser: true. RN: false. Flutter: false.
56
+ */
57
+ supportsHtmlAudio: boolean;
58
+
59
+ /**
60
+ * Whether customElements.define() is available for widget registration.
61
+ * Browser: true. RN: false. Flutter: false.
62
+ */
63
+ supportsCustomElements: boolean;
64
+
65
+ /**
66
+ * Whether localStorage / sessionStorage are available.
67
+ * Browser: true. RN: false (AsyncStorage used instead). Flutter: false.
68
+ */
69
+ supportsLocalStorage: boolean;
70
+
71
+ /**
72
+ * Whether BroadcastChannel (cross-tab communication) is available.
73
+ * Browser: true. RN: false. Flutter: false.
74
+ */
75
+ supportsBroadcastChannel: boolean;
76
+ }
77
+
78
+ // ─── VaniraRuntime ────────────────────────────────────────────────────────────
79
+
80
+ /**
81
+ * A VaniraRuntime encapsulates everything the WebRTCClient needs to know
82
+ * about the current platform:
83
+ *
84
+ * - Which adapters to use for media, audio, peer connection, data channel
85
+ * - What metadata to send to the backend (type, runtime)
86
+ * - How to generate call IDs
87
+ * - What features are available (capabilities)
88
+ *
89
+ * The WebRTCClient accepts a runtime via config.runtime and extracts all
90
+ * adapter + metadata from it. Individual adapter fields on config are still
91
+ * supported for backward compatibility.
92
+ */
93
+ export interface VaniraRuntime {
94
+ /** Human-readable runtime identifier */
95
+ readonly name: 'browser' | 'react-native' | 'flutter' | string;
96
+
97
+ /**
98
+ * Sent as `type` in the /calls/create request body.
99
+ * Must be `web` for all WebRTC flows — the API only accepts `web` | `sip`.
100
+ * Use `runtimeName` for browser vs react-native vs flutter.
101
+ */
102
+ readonly callType: 'web';
103
+
104
+ /**
105
+ * Sent as `runtime` in the /calls/create request body.
106
+ * Replaces the old callId prefix convention.
107
+ * Values: 'browser' | 'react-native' | 'flutter'
108
+ */
109
+ readonly runtimeName: string;
110
+
111
+ /**
112
+ * Prefix used when auto-generating a callId (no prefix conflicts with
113
+ * runtime field, but kept for logging / tracing convenience).
114
+ * 'web_' | 'rn_' | 'flutter_'
115
+ */
116
+ readonly callIdPrefix: string;
117
+
118
+ /** Runtime capability flags */
119
+ readonly capabilities: RuntimeCapabilities;
120
+
121
+ /** Audio output adapter (remote TTS playback) */
122
+ readonly audio: AudioAdapter;
123
+
124
+ /** Media input adapter (microphone access) */
125
+ readonly media: MediaAdapter;
126
+
127
+ /** RTCPeerConnection factory adapter */
128
+ readonly peer: PeerConnectionAdapter;
129
+
130
+ /** DataChannel event binding + message normalisation adapter */
131
+ readonly dataChannel: DataChannelAdapter;
132
+
133
+ /** Session persistence for prospect/call id (continue mode) */
134
+ readonly storage: StorageAdapter;
135
+ }
@@ -0,0 +1,17 @@
1
+ declare module 'react-native-incall-manager' {
2
+ type StartOptions = {
3
+ media?: 'audio' | 'video';
4
+ auto?: boolean;
5
+ ringback?: string;
6
+ };
7
+
8
+ const InCallManager: {
9
+ start(options?: StartOptions): void;
10
+ stop(): void;
11
+ setSpeakerphoneOn(enabled: boolean): void;
12
+ setForceSpeakerphoneOn(enabled: boolean | null): void;
13
+ setKeepScreenOn(enabled: boolean): void;
14
+ };
15
+
16
+ export default InCallManager;
17
+ }
@@ -0,0 +1,47 @@
1
+ declare module 'react-native-webrtc' {
2
+ import type {Component} from 'react';
3
+ import type {ViewProps} from 'react-native';
4
+
5
+ export interface MediaStreamTrack {
6
+ stop(): void;
7
+ }
8
+
9
+ export interface MediaStream {
10
+ getTracks(): MediaStreamTrack[];
11
+ toURL(): string;
12
+ }
13
+
14
+ export const mediaDevices: {
15
+ getUserMedia(constraints: {
16
+ video?: boolean | {facingMode?: string};
17
+ audio?: boolean;
18
+ }): Promise<MediaStream>;
19
+ };
20
+
21
+ export type RTCViewProps = ViewProps & {
22
+ streamURL: string | null;
23
+ objectFit?: 'contain' | 'cover';
24
+ mirror?: boolean;
25
+ };
26
+
27
+ export class RTCView extends Component<RTCViewProps> {}
28
+ }
29
+
30
+ declare module 'react-native-view-shot' {
31
+ import type {Component} from 'react';
32
+ import type {ViewProps} from 'react-native';
33
+
34
+ export type CaptureOptions = {
35
+ format?: 'jpg' | 'png' | 'webm' | 'raw';
36
+ quality?: number;
37
+ result?: 'tmpfile' | 'base64' | 'data-uri' | 'zip-base64';
38
+ };
39
+
40
+ export type ViewShotProperties = ViewProps & {
41
+ options?: CaptureOptions;
42
+ };
43
+
44
+ export default class ViewShot extends Component<ViewShotProperties> {
45
+ capture?(options?: CaptureOptions): Promise<string>;
46
+ }
47
+ }
package/src/types.ts ADDED
@@ -0,0 +1,133 @@
1
+ export interface TTSVoice {
2
+ tts_provider: string;
3
+ }
4
+
5
+ export interface Agent {
6
+ tts_voice?: TTSVoice;
7
+ chat_welcome_message?: any;
8
+ }
9
+
10
+ export type WidgetMode = 'voice_only' | 'chat_voice' | 'avatar_only' | 'chat_avatar' | 'chat_only';
11
+
12
+ export interface WidgetConfig {
13
+ agent_id: string;
14
+ client_id: string;
15
+ mode: WidgetMode;
16
+ icon?: string;
17
+ gradient?: string;
18
+ primary_color?: string;
19
+ secondary_color?: string;
20
+ icon_config?: {
21
+ logo_text?: string;
22
+ title?: string;
23
+ button_text?: string;
24
+ };
25
+ agent?: Agent;
26
+ client?: {
27
+ base_prospect_group_id?: string;
28
+ };
29
+ }
30
+
31
+ export interface WebRTCClientConfig {
32
+ agentId: string;
33
+ /** Your sk_live_* or pk_live_* key. Required when using createCall(). */
34
+ apiKey?: string;
35
+ /** Override the Vanira API base URL. Defaults to https://api.vanira.io */
36
+ backendUrl?: string;
37
+ /** WebRTC worker URL. Required when calling connect() directly; set automatically by createCall(). */
38
+ serverUrl?: string;
39
+ /** Call ID. Set automatically by createCall(); supply manually if managing the session yourself. */
40
+ callId?: string;
41
+ /** Optional prospect ID. If supplied, the SDK resumes the prospect context. */
42
+ prospectId?: string;
43
+ /** Lifecycle behavior when starting a connection ('continue' or 'new') */
44
+ sessionBehavior?: 'continue' | 'new';
45
+ /** Callback when session starts or resumes with resolved IDs and server URL */
46
+ onSessionStarted?: (payload: { prospectId: string; callId: string; serverUrl: string }) => void;
47
+ onConnected?: () => void;
48
+ onDisconnected?: () => void;
49
+ onError?: (error: unknown) => void;
50
+ onTranscription?: (text: string, isFinal: boolean) => void;
51
+ onLocalStream?: (stream: MediaStream) => void;
52
+ onRemoteTrack?: (track: MediaStreamTrack, stream: MediaStream) => void;
53
+ onClientToolCall?: (toolCall: unknown) => void;
54
+ /**
55
+ * @deprecated ICE comes from POST /calls/create — do not pass manually.
56
+ */
57
+ iceServers?: RTCIceServer[];
58
+ token?: string;
59
+
60
+ // ── Runtime (preferred — sets all adapters + protocol metadata at once) ──
61
+ //
62
+ // Provide a VaniraRuntime to inject all platform adapters and backend
63
+ // metadata (callType, runtimeName) in a single object.
64
+ //
65
+ // import { browserRuntime } from 'vanira-sdk/runtime/browser';
66
+ // import { reactNativeRuntime } from 'vanira-sdk/runtime/react-native';
67
+ //
68
+ // Individual adapter fields below are still supported for custom setups
69
+ // and remain the source of truth when runtime is not provided.
70
+ runtime?: import('./runtime/types').VaniraRuntime;
71
+
72
+ // ── Platform adapters (individual — overridden by runtime if provided) ───
73
+
74
+ /**
75
+ * Audio output adapter. Controls how incoming remote audio is played.
76
+ * Default (browser): HTMLAudioElement-backed player.
77
+ * React Native: no-op stub (react-native-webrtc routes audio natively).
78
+ */
79
+ audioAdapter?: import('./adapters/interfaces').AudioAdapter;
80
+
81
+ /**
82
+ * Media input adapter. Controls how the microphone is accessed.
83
+ * Default (browser): navigator.mediaDevices.getUserMedia.
84
+ * React Native: react-native-webrtc mediaDevices.getUserMedia polyfill.
85
+ */
86
+ mediaAdapter?: import('./adapters/interfaces').MediaAdapter;
87
+
88
+ /**
89
+ * RTCPeerConnection factory adapter.
90
+ * Default (browser): BrowserPeerAdapter (wraps new RTCPeerConnection()).
91
+ * React Native: RNPeerAdapter (wraps react-native-webrtc global).
92
+ */
93
+ peerAdapter?: import('./adapters/PeerConnectionAdapter').PeerConnectionAdapter;
94
+
95
+ /**
96
+ * DataChannel event binding + message normalisation adapter.
97
+ * Default (browser): BrowserDataChannelAdapter (handles string/ArrayBuffer/Blob).
98
+ * React Native: RNDataChannelAdapter (string-only, no binary normalisation).
99
+ */
100
+ dataChannelAdapter?: import('./adapters/PeerConnectionAdapter').DataChannelAdapter;
101
+
102
+ /**
103
+ * Session persistence adapter for prospect/call id when sessionBehavior is 'continue'.
104
+ * Default (browser): BrowserDualStorageAdapter (sessionStorage + localStorage).
105
+ * React Native: MemoryStorageAdapter unless overridden — inject createSyncStorageAdapter for production.
106
+ */
107
+ storageAdapter?: import('./adapters/storage/StorageAdapter').StorageAdapter;
108
+
109
+ /**
110
+ * Fired when a client_tool_call includes a preset_id, before onClientToolCall and
111
+ * before the browser vanira:preset DOM event (when dispatch is available).
112
+ * Primary preset delivery path on React Native (no DOM).
113
+ */
114
+ onPreset?: (payload: PresetEventPayload) => void;
115
+ }
116
+
117
+ /** Payload for onPreset and VaniraAI 'preset' event */
118
+ export interface PresetEventPayload {
119
+ toolCall: unknown;
120
+ client: import('./core/WebRTCClient').WebRTCClient;
121
+ }
122
+
123
+ export interface ControlEvent {
124
+ event: string;
125
+ text?: string;
126
+ isFinal?: boolean;
127
+ name?: string;
128
+ tool_call?: unknown;
129
+ data?: unknown;
130
+ // Index signature allows accessing arbitrary fields from server events
131
+ // without casting, while the named fields above provide IDE autocomplete.
132
+ [key: string]: unknown;
133
+ }
@@ -0,0 +1,87 @@
1
+ import { ConfigService } from '../api/services/ConfigService';
2
+ import { WidgetProviderFactory } from './factory/WidgetFactory';
3
+ import { IWidgetProvider } from './abstraction/interfaces';
4
+
5
+ export class VaniraWidget extends HTMLElement {
6
+ private shadow: ShadowRoot;
7
+ private provider: IWidgetProvider | null = null;
8
+
9
+ // Config attributes
10
+ private widgetId: string = "";
11
+ private agentId: string = "";
12
+ private pkKey: string = "";
13
+ private serverUrl: string = "https://inboxapi.vanira.io";
14
+ private position: string = "bottom-right";
15
+ private primaryColor: string = "#000000";
16
+ private secondaryColor: string = "#000000";
17
+ private gradient: string | null = null;
18
+ private positionType: string = "fixed";
19
+
20
+ static get observedAttributes() {
21
+ return ['widget-id', 'agent-id', 'position', 'primary-color', 'secondary-color', 'server-url', 'gradient', 'position-type', 'pk-key'];
22
+ }
23
+
24
+ constructor() {
25
+ super();
26
+ this.shadow = this.attachShadow({ mode: 'open' });
27
+ }
28
+
29
+ connectedCallback() {
30
+ console.log("[VaniraAI Widget] Initializing...");
31
+ this.readAttributes();
32
+
33
+ // Initial Config based on attributes
34
+ const initialConfig = {
35
+ widgetId: this.widgetId,
36
+ agentId: this.agentId,
37
+ pkKey: this.pkKey,
38
+ serverUrl: this.serverUrl,
39
+ position: this.position,
40
+ primaryColor: this.primaryColor,
41
+ secondaryColor: this.secondaryColor,
42
+ gradient: this.gradient,
43
+ positionType: this.positionType
44
+ };
45
+
46
+ // Factory Pattern: Create specific provider
47
+ this.provider = WidgetProviderFactory.getProvider(initialConfig);
48
+
49
+ // UI Renderer: Delegate to provider (SRP)
50
+ if (this.provider) {
51
+ this.provider.ui_renderer(this.shadow);
52
+ }
53
+
54
+ // Fetch expanded config if widgetId is present
55
+ if (this.widgetId && !this.agentId) {
56
+ this.initializeExpandedConfig();
57
+ }
58
+ }
59
+
60
+ private readAttributes() {
61
+ this.widgetId = this.getAttribute('widget-id') || "";
62
+ this.agentId = this.getAttribute('agent-id') || "";
63
+ this.pkKey = this.getAttribute('pk-key') || "";
64
+ this.serverUrl = this.getAttribute('server-url') || "https://inboxapi.vanira.io";
65
+ this.position = this.getAttribute('position') || "bottom-right";
66
+ this.primaryColor = this.getAttribute('primary-color') || "#000000";
67
+ this.secondaryColor = this.getAttribute('secondary-color') || this.primaryColor;
68
+ this.gradient = this.getAttribute('gradient');
69
+ this.positionType = this.getAttribute('position-type') || "fixed";
70
+ }
71
+
72
+ async initializeExpandedConfig() {
73
+ try {
74
+ const config = await ConfigService.fetchWidgetConfig(this.widgetId, this.pkKey || undefined);
75
+
76
+ // Pass API config to provider to update state/UI
77
+ if (this.provider) {
78
+ // This will trigger internal re-render in provider
79
+ await this.provider.initialize(config);
80
+ }
81
+ } catch (error) {
82
+ console.error("[VaniraAI Widget] Config failed:", error);
83
+ // specific error handling or Fallback_principle logic can be added here
84
+ // Provider already has default state (Graceful degradation)
85
+ }
86
+ }
87
+ }
@@ -0,0 +1,18 @@
1
+ import { IWidgetProvider } from './interfaces';
2
+
3
+ export abstract class AbstractWidgetProvider implements IWidgetProvider {
4
+ protected config: any;
5
+ protected root: ShadowRoot | null = null;
6
+
7
+ constructor(config: any) {
8
+ this.config = config;
9
+ }
10
+
11
+ abstract create_call(): Promise<void>;
12
+ abstract end_call(): void;
13
+ abstract ui_renderer(root: ShadowRoot): void;
14
+
15
+ async initialize(config: any): Promise<void> {
16
+ this.config = config;
17
+ }
18
+ }