@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,37 @@
1
+ /**
2
+ * Clean up LaTeX mathematical formatting from a string into a clean, human-readable plain text.
3
+ */
4
+ export function cleanLatexText(text: string): string {
5
+ if (!text) return '';
6
+ return text
7
+ // 1. Fractions: \frac{a}{b} -> a/b
8
+ .replace(/\\frac\{([^}]+)\}\{([^}]+)\}/g, '$1/$2')
9
+ // 2. Square roots: \sqrt{a} -> √a
10
+ .replace(/\\sqrt\{([^}]+)\}/g, '√$1')
11
+ .replace(/\\sqrt(?![a-zA-Z])/g, '√')
12
+ .replace(/\bsqrt\b/gi, '√')
13
+ // 3. Delimiters: \left and \right
14
+ .replace(/\\left(?![a-zA-Z])/g, '')
15
+ .replace(/\\right(?![a-zA-Z])/g, '')
16
+ // 4. Text block: \text{abc} -> abc
17
+ .replace(/\\text\{([^}]+)\}/g, '$1')
18
+ .replace(/\\text\{([^}]+)$/g, '$1') // in case of missing closing brace like \text{ (cannot be simplified further)
19
+ .replace(/\\text(?![a-zA-Z])/g, '') // remove any dangling \text
20
+ // 5. Common math symbols
21
+ .replace(/\\times(?![a-zA-Z])/g, '×')
22
+ .replace(/\\div(?![a-zA-Z])/g, '÷')
23
+ .replace(/\\pm(?![a-zA-Z])/g, '±')
24
+ .replace(/\\ge(q)?(?![a-zA-Z])/g, '≥')
25
+ .replace(/\\le(q)?(?![a-zA-Z])/g, '≤')
26
+ .replace(/\\ne(q)?(?![a-zA-Z])/g, '≠')
27
+ .replace(/\\approx(?![a-zA-Z])/g, '≈')
28
+ .replace(/\\cdot(?![a-zA-Z])/g, '·')
29
+ .replace(/\\degree(?![a-zA-Z])/g, '°')
30
+ .replace(/\\pi(?![a-zA-Z])/g, 'π')
31
+ .replace(/\\theta(?![a-zA-Z])/g, 'θ')
32
+ .replace(/\\infty(?![a-zA-Z])/g, '∞')
33
+ // Strip any remaining curly braces from LaTeX commands
34
+ .replace(/[{}]/g, '')
35
+ // Clean up double/multiple spaces to a single space
36
+ .replace(/ +/g, ' ');
37
+ }
@@ -0,0 +1,93 @@
1
+ import { ChatWindow, VoiceOverlay } from '../components';
2
+ import { icons } from '../styles';
3
+
4
+ export abstract class AbstractChatView {
5
+ protected element: HTMLDivElement;
6
+ protected chatWindow!: ChatWindow;
7
+ protected overlay!: VoiceOverlay;
8
+ protected voiceTrigger!: HTMLButtonElement;
9
+
10
+ constructor(
11
+ protected onSendMessage: (msg: string) => void,
12
+ protected onStartCall: () => void,
13
+ protected onHangup: () => void,
14
+ protected primaryColor: string = '#000000',
15
+ protected secondaryColor: string = '#111111',
16
+ protected onCallEnded?: (durationMs: number, startedAt: number) => void
17
+ ) {
18
+ this.element = document.createElement('div');
19
+ this.element.style.flex = '1'; // Fill parent (widget-body)
20
+ this.element.style.display = 'flex';
21
+ this.element.style.flexDirection = 'column';
22
+ this.element.style.minHeight = '0'; // Crucial for flex scrolling
23
+ this.element.style.position = 'relative'; // Added from VoiceView
24
+
25
+ // Child classes will append specific elements (Avatar, etc)
26
+ // Then call setupChat()
27
+ }
28
+
29
+ protected setupChat() {
30
+ this.chatWindow = new ChatWindow((msg) => this.onSendMessage(msg));
31
+ const chatEl = this.chatWindow.getElement();
32
+ chatEl.style.flex = '1';
33
+ this.element.appendChild(chatEl);
34
+
35
+ this.injectVoiceTrigger();
36
+
37
+ this.overlay = new VoiceOverlay(() => this.onHangup(), this.primaryColor, this.secondaryColor, this.onCallEnded);
38
+ this.element.appendChild(this.overlay.getElement());
39
+ }
40
+
41
+ protected injectVoiceTrigger() {
42
+ const triggerBtn = document.createElement('button');
43
+ this.voiceTrigger = triggerBtn;
44
+ triggerBtn.className = 'voice-btn-simple';
45
+ triggerBtn.innerHTML = `<div class="voice-btn-icon">${icons.audioLines}</div>`;
46
+ triggerBtn.onclick = () => this.onStartCall();
47
+
48
+ // Synchronous injection since ChatWindow is already constructed
49
+ const inputArea = this.chatWindow.getElement().querySelector('.chat-input-area');
50
+
51
+ if (inputArea) {
52
+ inputArea.insertBefore(this.voiceTrigger, inputArea.firstChild);
53
+ } else {
54
+ console.warn('VaniraSDK: Failed to inject voice trigger', { inputArea });
55
+ }
56
+ }
57
+
58
+ public getElement(): HTMLDivElement {
59
+ return this.element;
60
+ }
61
+
62
+ public getChatWindow(): ChatWindow {
63
+ return this.chatWindow;
64
+ }
65
+
66
+ public setCallActive(active: boolean) {
67
+ if (active) {
68
+ this.overlay.show();
69
+ // User Request: Hide Chat Welcome & Chat Box during interaction
70
+ if (this.chatWindow) {
71
+ this.chatWindow.getElement().style.display = 'none';
72
+ }
73
+ } else {
74
+ this.overlay.hide();
75
+ // Restore functionality on hangup
76
+ if (this.chatWindow) {
77
+ this.chatWindow.getElement().style.display = 'flex';
78
+ }
79
+ }
80
+ }
81
+
82
+ public setStatus(status: 'connecting' | 'connected' | 'disconnected' | 'error', errorMsg?: string) {
83
+ this.overlay.setStatus(status, errorMsg);
84
+ }
85
+
86
+ public setTranscription(text: string, isFinal: boolean) {
87
+ this.overlay.setTranscription(text, isFinal);
88
+ }
89
+
90
+ public setVideoTrack(track: MediaStreamTrack) {
91
+ this.overlay.setVideoTrack(track);
92
+ }
93
+ }
@@ -0,0 +1,57 @@
1
+ import { VoiceOverlay } from '../components';
2
+
3
+ export abstract class AbstractVoiceView {
4
+ protected element: HTMLDivElement;
5
+ protected overlay!: VoiceOverlay;
6
+ protected onStartCall: (behavior?: 'continue' | 'new') => void;
7
+ protected onHangup: () => void;
8
+ protected primaryColor: string;
9
+ protected secondaryColor: string;
10
+ protected onCallEnded?: (durationMs: number, startedAt: number) => void;
11
+
12
+ constructor(
13
+ onStartCall: (behavior?: 'continue' | 'new') => void,
14
+ onHangup: () => void,
15
+ primaryColor: string = '#000000',
16
+ secondaryColor: string = '#111111',
17
+ onCallEnded?: (durationMs: number, startedAt: number) => void
18
+ ) {
19
+ this.onStartCall = onStartCall;
20
+ this.onHangup = onHangup;
21
+ this.primaryColor = primaryColor;
22
+ this.secondaryColor = secondaryColor;
23
+ this.onCallEnded = onCallEnded;
24
+ this.element = document.createElement('div');
25
+ this.element.style.height = '100%';
26
+ this.element.style.position = 'relative';
27
+
28
+ // Child classes implement specific init
29
+ // BUT calling initOverlay here is common
30
+ }
31
+
32
+ protected initOverlay() {
33
+ this.overlay = new VoiceOverlay(() => this.onHangup(), this.primaryColor, this.secondaryColor, this.onCallEnded);
34
+ this.element.appendChild(this.overlay.getElement());
35
+ }
36
+
37
+ public getElement(): HTMLDivElement {
38
+ return this.element;
39
+ }
40
+
41
+ public setCallActive(active: boolean) {
42
+ if (active) this.overlay.show();
43
+ else this.overlay.hide();
44
+ }
45
+
46
+ public setStatus(status: 'connecting' | 'connected' | 'disconnected' | 'error', errorMsg?: string) {
47
+ this.overlay.setStatus(status, errorMsg);
48
+ }
49
+
50
+ public setTranscription(text: string, isFinal: boolean) {
51
+ this.overlay.setTranscription(text, isFinal);
52
+ }
53
+
54
+ public setVideoTrack(track: MediaStreamTrack) {
55
+ this.overlay.setVideoTrack(track);
56
+ }
57
+ }
@@ -0,0 +1,78 @@
1
+ import { AvatarView } from '../components';
2
+ import { icons } from '../styles';
3
+ import { AbstractVoiceView } from './AbstractVoiceView';
4
+
5
+ export class AvatarOnlyView extends AbstractVoiceView {
6
+ private avatarView: AvatarView;
7
+ private startCallBtn: HTMLButtonElement;
8
+ private controlsContainer: HTMLDivElement;
9
+
10
+ constructor(
11
+ onStartCall: () => void,
12
+ onHangup: () => void,
13
+ primaryColor: string = '#000000',
14
+ secondaryColor: string = '#111111',
15
+ onCallEnded?: (durationMs: number, startedAt: number) => void
16
+ ) {
17
+ super(onStartCall, onHangup, primaryColor, secondaryColor, onCallEnded);
18
+
19
+ // Ensure flex layout
20
+ this.element.style.display = 'flex';
21
+ this.element.style.flexDirection = 'column';
22
+
23
+ // 1. Avatar View (Idle State matching Overlay Card)
24
+ this.avatarView = new AvatarView();
25
+ const avatarEl = this.avatarView.getElement();
26
+ // Match VoiceOverlay VideoContainer styles
27
+ avatarEl.style.width = '100%';
28
+ avatarEl.style.maxWidth = '300px';
29
+ avatarEl.style.aspectRatio = '1/1';
30
+ avatarEl.style.borderRadius = '16px';
31
+ avatarEl.style.overflow = 'hidden';
32
+ avatarEl.style.margin = 'auto'; // Center vertically and horizontally
33
+ avatarEl.style.position = 'relative'; // Ensure proper stacking
34
+
35
+ this.element.appendChild(avatarEl);
36
+
37
+ // 2. Start Call Controls
38
+ this.controlsContainer = document.createElement('div');
39
+ this.controlsContainer.className = 'widget-body';
40
+ this.controlsContainer.style.position = 'absolute';
41
+ this.controlsContainer.style.bottom = '20px';
42
+ this.controlsContainer.style.width = '100%';
43
+ this.controlsContainer.style.zIndex = '10';
44
+
45
+ const controls = document.createElement('div');
46
+ controls.className = 'controls';
47
+
48
+ this.startCallBtn = document.createElement('button');
49
+ this.startCallBtn.className = 'control-btn primary';
50
+ this.startCallBtn.innerHTML = `${icons.phone} <span>Start Call</span>`;
51
+ this.startCallBtn.onclick = () => this.onStartCall();
52
+
53
+ controls.appendChild(this.startCallBtn);
54
+ this.controlsContainer.appendChild(controls);
55
+ this.element.appendChild(this.controlsContainer);
56
+
57
+ // 3. Overlay
58
+ this.initOverlay();
59
+ this.overlay.setMode('avatar');
60
+ // REVERTED Full Screen: this.overlay.setFullScreen(true);
61
+ }
62
+
63
+ public getAvatarView(): AvatarView {
64
+ return this.avatarView;
65
+ }
66
+
67
+ public override setCallActive(active: boolean) {
68
+ if (active) {
69
+ this.overlay.setMode('avatar');
70
+ }
71
+ super.setCallActive(active);
72
+ if (active) {
73
+ this.controlsContainer.style.display = 'none';
74
+ } else {
75
+ this.controlsContainer.style.display = 'block';
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,66 @@
1
+ import { AbstractChatView } from './AbstractChatView';
2
+ import { icons } from '../styles';
3
+
4
+ export class ChatAvatarView extends AbstractChatView {
5
+
6
+ constructor(
7
+ onSendMessage: (msg: string) => void,
8
+ onStartCall: () => void,
9
+ onHangup: () => void,
10
+ primaryColor: string = '#000000',
11
+ secondaryColor: string = '#111111',
12
+ onCallEnded?: (durationMs: number, startedAt: number) => void
13
+ ) {
14
+ super(onSendMessage, onStartCall, onHangup, primaryColor, secondaryColor, onCallEnded);
15
+ // No top AvatarView anymore! Just standard chat setup.
16
+ this.setupChat();
17
+ this.overlay.setMode('avatar');
18
+ }
19
+
20
+ // Override to inject the unique Circular Video Button
21
+ protected override injectVoiceTrigger() {
22
+ const triggerBtn = document.createElement('button');
23
+ this.voiceTrigger = triggerBtn;
24
+ triggerBtn.className = 'voice-btn-simple';
25
+ // Custom styling for Avatar Mode
26
+ triggerBtn.style.overflow = 'hidden'; // Clip video
27
+ triggerBtn.style.padding = '0';
28
+ triggerBtn.style.border = '1.5px solid #e5e7eb'; // Clean border
29
+
30
+ // Video Loop (Simli Jenna or equiv)
31
+ triggerBtn.innerHTML = `
32
+ <video
33
+ src="https://www.simli.com/jenna.mp4"
34
+ autoplay loop muted pip="false" playsinline
35
+ style="width: 100%; height: 100%; object-fit: cover; border-radius: 50%;">
36
+ </video>
37
+ <div style="position: absolute; bottom: -2px; right: -2px; background: white; border-radius: 50%; padding: 2px; box-shadow: 0 1px 2px rgba(0,0,0,0.1);">
38
+ ${icons.audioLines.replace('width="24"', 'width="12"').replace('height="24"', 'height="12"')}
39
+ </div>
40
+ `;
41
+
42
+ // Ensure small icon style if replacement didn't work (regex safely)
43
+ const iconDiv = triggerBtn.querySelector('div');
44
+ if (iconDiv) {
45
+ const svg = iconDiv.querySelector('svg');
46
+ if (svg) {
47
+ svg.style.width = '12px';
48
+ svg.style.height = '12px';
49
+ }
50
+ }
51
+
52
+ triggerBtn.onclick = () => this.onStartCall();
53
+
54
+ // Synchronous injection
55
+ const inputArea = this.chatWindow.getElement().querySelector('.chat-input-area');
56
+ const sendBtn = inputArea?.querySelector('.chat-send-btn');
57
+
58
+ if (inputArea && sendBtn) {
59
+ inputArea.insertBefore(this.voiceTrigger, sendBtn);
60
+ } else {
61
+ console.warn('VaniraSDK: Failed to inject voice trigger', { inputArea, sendBtn });
62
+ }
63
+ }
64
+
65
+ // No getAvatarView method anymore. VaniraInternalProvider should check for capability or cast.
66
+ }
@@ -0,0 +1,28 @@
1
+ import { ChatWindow } from '../components';
2
+
3
+ export class ChatOnlyView {
4
+ private element: HTMLDivElement;
5
+ private chatWindow: ChatWindow;
6
+
7
+ constructor(
8
+ private onSendMessage: (msg: string) => void
9
+ ) {
10
+ this.element = document.createElement('div');
11
+ this.element.style.flex = '1';
12
+ this.element.style.width = '100%';
13
+ this.element.style.minHeight = '0'; // Crucial for flex scrolling
14
+ this.element.style.display = 'flex';
15
+ this.element.style.flexDirection = 'column';
16
+
17
+ this.chatWindow = new ChatWindow((msg) => this.onSendMessage(msg));
18
+ this.element.appendChild(this.chatWindow.getElement());
19
+ }
20
+
21
+ public getElement(): HTMLDivElement {
22
+ return this.element;
23
+ }
24
+
25
+ public getChatWindow(): ChatWindow {
26
+ return this.chatWindow;
27
+ }
28
+ }
@@ -0,0 +1,15 @@
1
+ import { AbstractChatView } from './AbstractChatView';
2
+
3
+ export class ChatVoiceView extends AbstractChatView {
4
+ constructor(
5
+ onSendMessage: (msg: string) => void,
6
+ onStartCall: () => void,
7
+ onHangup: () => void,
8
+ primaryColor: string = '#000000',
9
+ secondaryColor: string = '#111111',
10
+ onCallEnded?: (durationMs: number, startedAt: number) => void
11
+ ) {
12
+ super(onSendMessage, onStartCall, onHangup, primaryColor, secondaryColor, onCallEnded);
13
+ this.setupChat();
14
+ }
15
+ }
@@ -0,0 +1,25 @@
1
+ import { VoiceOrb } from '../components';
2
+ import { AbstractVoiceView } from './AbstractVoiceView';
3
+
4
+ export class VoiceOnlyView extends AbstractVoiceView {
5
+ private voiceOrb: VoiceOrb;
6
+
7
+ constructor(
8
+ onStartCall: (behavior?: 'continue' | 'new') => void,
9
+ onHangup: () => void,
10
+ primaryColor: string = '#000000',
11
+ secondaryColor: string = '#111111',
12
+ onCallEnded?: (durationMs: number, startedAt: number) => void
13
+ ) {
14
+ super(onStartCall, onHangup, primaryColor, secondaryColor, onCallEnded);
15
+
16
+ this.element.style.background = '#f9fafb';
17
+
18
+ this.voiceOrb = new VoiceOrb((behavior) => {
19
+ this.onStartCall(behavior);
20
+ });
21
+ this.element.appendChild(this.voiceOrb.getElement());
22
+
23
+ this.initOverlay();
24
+ }
25
+ }
@@ -0,0 +1,5 @@
1
+ export * from './VoiceOnlyView';
2
+ export * from './ChatOnlyView';
3
+ export * from './ChatVoiceView';
4
+ export * from './AvatarOnlyView';
5
+ export * from './ChatAvatarView';