vue-parley 4.0.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 (202) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +276 -0
  3. package/dist/chat.css +3223 -0
  4. package/dist/chunks/Call-CTIujxDi.cjs +818 -0
  5. package/dist/chunks/Call-CTIujxDi.cjs.map +1 -0
  6. package/dist/chunks/Call-Dwuyd-Ut.mjs +818 -0
  7. package/dist/chunks/Call-Dwuyd-Ut.mjs.map +1 -0
  8. package/dist/chunks/index-D50Yn7q4.cjs +7031 -0
  9. package/dist/chunks/index-D50Yn7q4.cjs.map +1 -0
  10. package/dist/chunks/index-JU_JZkSC.mjs +7010 -0
  11. package/dist/chunks/index-JU_JZkSC.mjs.map +1 -0
  12. package/dist/chunks/session-B3KIrj3_.mjs +711 -0
  13. package/dist/chunks/session-B3KIrj3_.mjs.map +1 -0
  14. package/dist/chunks/session-BOfkV8PJ.cjs +711 -0
  15. package/dist/chunks/session-BOfkV8PJ.cjs.map +1 -0
  16. package/dist/fakes.cjs +335 -0
  17. package/dist/fakes.cjs.map +1 -0
  18. package/dist/fakes.mjs +335 -0
  19. package/dist/fakes.mjs.map +1 -0
  20. package/dist/index.cjs +24 -0
  21. package/dist/index.cjs.map +1 -0
  22. package/dist/index.mjs +24 -0
  23. package/dist/index.mjs.map +1 -0
  24. package/dist/types/app/components/ChatPage.vue.d.ts +60 -0
  25. package/dist/types/app/components/ChatPage.vue.d.ts.map +1 -0
  26. package/dist/types/app/components/call/Call.vue.d.ts +8 -0
  27. package/dist/types/app/components/call/Call.vue.d.ts.map +1 -0
  28. package/dist/types/app/components/chat/ChatBubble.vue.d.ts +64 -0
  29. package/dist/types/app/components/chat/ChatBubble.vue.d.ts.map +1 -0
  30. package/dist/types/app/components/chat/ChatInput.vue.d.ts +60 -0
  31. package/dist/types/app/components/chat/ChatInput.vue.d.ts.map +1 -0
  32. package/dist/types/app/components/chat/ChatMessages.vue.d.ts +45 -0
  33. package/dist/types/app/components/chat/ChatMessages.vue.d.ts.map +1 -0
  34. package/dist/types/app/components/chat/ChatPageBar.vue.d.ts +50 -0
  35. package/dist/types/app/components/chat/ChatPageBar.vue.d.ts.map +1 -0
  36. package/dist/types/app/components/chat/ChatProfileOverview.vue.d.ts +11 -0
  37. package/dist/types/app/components/chat/ChatProfileOverview.vue.d.ts.map +1 -0
  38. package/dist/types/app/components/chat/ChatView.vue.d.ts +244 -0
  39. package/dist/types/app/components/chat/ChatView.vue.d.ts.map +1 -0
  40. package/dist/types/app/components/chat/chat-bubbles/BubbleDivider.vue.d.ts +7 -0
  41. package/dist/types/app/components/chat/chat-bubbles/BubbleDivider.vue.d.ts.map +1 -0
  42. package/dist/types/app/components/chat/chat-bubbles/BubbleImages.vue.d.ts +13 -0
  43. package/dist/types/app/components/chat/chat-bubbles/BubbleImages.vue.d.ts.map +1 -0
  44. package/dist/types/app/components/chat/chat-bubbles/BubbleMeta.vue.d.ts +11 -0
  45. package/dist/types/app/components/chat/chat-bubbles/BubbleMeta.vue.d.ts.map +1 -0
  46. package/dist/types/app/components/chat/chat-bubbles/BubbleOptions.vue.d.ts +16 -0
  47. package/dist/types/app/components/chat/chat-bubbles/BubbleOptions.vue.d.ts.map +1 -0
  48. package/dist/types/app/components/chat/chat-bubbles/BubbleRequest.vue.d.ts +8 -0
  49. package/dist/types/app/components/chat/chat-bubbles/BubbleRequest.vue.d.ts.map +1 -0
  50. package/dist/types/app/components/chat/chat-bubbles/BubbleSelectionMark.vue.d.ts +7 -0
  51. package/dist/types/app/components/chat/chat-bubbles/BubbleSelectionMark.vue.d.ts.map +1 -0
  52. package/dist/types/app/components/chat/chat-bubbles/BubbleStatus.vue.d.ts +7 -0
  53. package/dist/types/app/components/chat/chat-bubbles/BubbleStatus.vue.d.ts.map +1 -0
  54. package/dist/types/app/components/chat/chat-bubbles/BubbleVideo.vue.d.ts +25 -0
  55. package/dist/types/app/components/chat/chat-bubbles/BubbleVideo.vue.d.ts.map +1 -0
  56. package/dist/types/app/components/chat/chat-bubbles/FileDisplay.vue.d.ts +13 -0
  57. package/dist/types/app/components/chat/chat-bubbles/FileDisplay.vue.d.ts.map +1 -0
  58. package/dist/types/app/components/chat/chat-bubbles/ImageGroupDisplay.vue.d.ts +14 -0
  59. package/dist/types/app/components/chat/chat-bubbles/ImageGroupDisplay.vue.d.ts.map +1 -0
  60. package/dist/types/app/components/chat/chat-bubbles/ReplyPreview.vue.d.ts +10 -0
  61. package/dist/types/app/components/chat/chat-bubbles/ReplyPreview.vue.d.ts.map +1 -0
  62. package/dist/types/app/components/chat/chat-bubbles/UploadProgressOverlay.vue.d.ts +10 -0
  63. package/dist/types/app/components/chat/chat-bubbles/UploadProgressOverlay.vue.d.ts.map +1 -0
  64. package/dist/types/app/components/chat/chat-bubbles/VoiceDisplay.vue.d.ts +12 -0
  65. package/dist/types/app/components/chat/chat-bubbles/VoiceDisplay.vue.d.ts.map +1 -0
  66. package/dist/types/app/components/chat/chat-input/AttachementFileDisplay.vue.d.ts +12 -0
  67. package/dist/types/app/components/chat/chat-input/AttachementFileDisplay.vue.d.ts.map +1 -0
  68. package/dist/types/app/components/chat/chat-input/InputActionBar.vue.d.ts +13 -0
  69. package/dist/types/app/components/chat/chat-input/InputActionBar.vue.d.ts.map +1 -0
  70. package/dist/types/app/components/chat/chat-input/InputAttachement.vue.d.ts +18 -0
  71. package/dist/types/app/components/chat/chat-input/InputAttachement.vue.d.ts.map +1 -0
  72. package/dist/types/app/components/chat/chat-input/PermissionPopup.vue.d.ts +8 -0
  73. package/dist/types/app/components/chat/chat-input/PermissionPopup.vue.d.ts.map +1 -0
  74. package/dist/types/app/components/chat/chat-input/RecordButton.vue.d.ts +27 -0
  75. package/dist/types/app/components/chat/chat-input/RecordButton.vue.d.ts.map +1 -0
  76. package/dist/types/app/components/chat/chat-input/RecordingStatus.vue.d.ts +12 -0
  77. package/dist/types/app/components/chat/chat-input/RecordingStatus.vue.d.ts.map +1 -0
  78. package/dist/types/app/components/chat/chat-input/VideoRecordDisplay.vue.d.ts +26 -0
  79. package/dist/types/app/components/chat/chat-input/VideoRecordDisplay.vue.d.ts.map +1 -0
  80. package/dist/types/app/components/chat/contact/ChatContactDisplay.vue.d.ts +8 -0
  81. package/dist/types/app/components/chat/contact/ChatContactDisplay.vue.d.ts.map +1 -0
  82. package/dist/types/app/components/chat/contact/ChatList.vue.d.ts +5 -0
  83. package/dist/types/app/components/chat/contact/ChatList.vue.d.ts.map +1 -0
  84. package/dist/types/app/components/chat/contact/ChatListSearch.vue.d.ts +16 -0
  85. package/dist/types/app/components/chat/contact/ChatListSearch.vue.d.ts.map +1 -0
  86. package/dist/types/app/components/chat/contact/ContactAvatar.vue.d.ts +10 -0
  87. package/dist/types/app/components/chat/contact/ContactAvatar.vue.d.ts.map +1 -0
  88. package/dist/types/app/components/chat/contact/ContactSkeleton.vue.d.ts +3 -0
  89. package/dist/types/app/components/chat/contact/ContactSkeleton.vue.d.ts.map +1 -0
  90. package/dist/types/app/components/chat/messages/FloatingDateHeader.vue.d.ts +9 -0
  91. package/dist/types/app/components/chat/messages/FloatingDateHeader.vue.d.ts.map +1 -0
  92. package/dist/types/app/components/chat/messages/MessagesSkeleton.vue.d.ts +8 -0
  93. package/dist/types/app/components/chat/messages/MessagesSkeleton.vue.d.ts.map +1 -0
  94. package/dist/types/app/components/chat/messages/ScrollToBottomButton.vue.d.ts +11 -0
  95. package/dist/types/app/components/chat/messages/ScrollToBottomButton.vue.d.ts.map +1 -0
  96. package/dist/types/app/components/chat/profile/FileDisplay.vue.d.ts +9 -0
  97. package/dist/types/app/components/chat/profile/FileDisplay.vue.d.ts.map +1 -0
  98. package/dist/types/app/components/general/ConfirmModal.vue.d.ts +21 -0
  99. package/dist/types/app/components/general/ConfirmModal.vue.d.ts.map +1 -0
  100. package/dist/types/app/components/general/FileFormatDisplay.vue.d.ts +16 -0
  101. package/dist/types/app/components/general/FileFormatDisplay.vue.d.ts.map +1 -0
  102. package/dist/types/app/components/general/IconButton.vue.d.ts +13 -0
  103. package/dist/types/app/components/general/IconButton.vue.d.ts.map +1 -0
  104. package/dist/types/app/components/general/LoadingStatus.vue.d.ts +16 -0
  105. package/dist/types/app/components/general/LoadingStatus.vue.d.ts.map +1 -0
  106. package/dist/types/app/components/general/MediaImage.vue.d.ts +30 -0
  107. package/dist/types/app/components/general/MediaImage.vue.d.ts.map +1 -0
  108. package/dist/types/app/components/general/NoDataDisplay.vue.d.ts +7 -0
  109. package/dist/types/app/components/general/NoDataDisplay.vue.d.ts.map +1 -0
  110. package/dist/types/app/components/general/ResponsiveDialog.vue.d.ts +28 -0
  111. package/dist/types/app/components/general/ResponsiveDialog.vue.d.ts.map +1 -0
  112. package/dist/types/app/components/general/SafeEmojiText.vue.d.ts +10 -0
  113. package/dist/types/app/components/general/SafeEmojiText.vue.d.ts.map +1 -0
  114. package/dist/types/app/components/global/BEmojiPicker.vue.d.ts +8 -0
  115. package/dist/types/app/components/global/BEmojiPicker.vue.d.ts.map +1 -0
  116. package/dist/types/app/components/global/BIcon.vue.d.ts +11 -0
  117. package/dist/types/app/components/global/BIcon.vue.d.ts.map +1 -0
  118. package/dist/types/app/components/global/BVirtualVerticalList.vue.d.ts +30 -0
  119. package/dist/types/app/components/global/BVirtualVerticalList.vue.d.ts.map +1 -0
  120. package/dist/types/app/composables/call/media.d.ts +204 -0
  121. package/dist/types/app/composables/call/media.d.ts.map +1 -0
  122. package/dist/types/app/composables/call/peer.d.ts +39 -0
  123. package/dist/types/app/composables/call/peer.d.ts.map +1 -0
  124. package/dist/types/app/composables/call/peers.d.ts +54 -0
  125. package/dist/types/app/composables/call/peers.d.ts.map +1 -0
  126. package/dist/types/app/composables/call/session.d.ts +284 -0
  127. package/dist/types/app/composables/call/session.d.ts.map +1 -0
  128. package/dist/types/app/composables/call/signaling.d.ts +36 -0
  129. package/dist/types/app/composables/call/signaling.d.ts.map +1 -0
  130. package/dist/types/app/composables/call/useCallUi.d.ts +27 -0
  131. package/dist/types/app/composables/call/useCallUi.d.ts.map +1 -0
  132. package/dist/types/app/composables/chat/useChatRecording.d.ts +75 -0
  133. package/dist/types/app/composables/chat/useChatRecording.d.ts.map +1 -0
  134. package/dist/types/app/composables/chat/useInputDraft.d.ts +14 -0
  135. package/dist/types/app/composables/chat/useInputDraft.d.ts.map +1 -0
  136. package/dist/types/app/composables/useAppPermissions.d.ts +25 -0
  137. package/dist/types/app/composables/useAppPermissions.d.ts.map +1 -0
  138. package/dist/types/app/composables/useAppToast.d.ts +13 -0
  139. package/dist/types/app/composables/useAppToast.d.ts.map +1 -0
  140. package/dist/types/app/composables/useAttachmentPicker.d.ts +18 -0
  141. package/dist/types/app/composables/useAttachmentPicker.d.ts.map +1 -0
  142. package/dist/types/app/composables/useCall.d.ts +91 -0
  143. package/dist/types/app/composables/useCall.d.ts.map +1 -0
  144. package/dist/types/app/composables/useChatMessageList.d.ts +18 -0
  145. package/dist/types/app/composables/useChatMessageList.d.ts.map +1 -0
  146. package/dist/types/app/composables/useDate.d.ts +7 -0
  147. package/dist/types/app/composables/useDate.d.ts.map +1 -0
  148. package/dist/types/app/composables/useDevice.d.ts +32 -0
  149. package/dist/types/app/composables/useDevice.d.ts.map +1 -0
  150. package/dist/types/app/composables/useFlippedVirtualScroll.d.ts +22 -0
  151. package/dist/types/app/composables/useFlippedVirtualScroll.d.ts.map +1 -0
  152. package/dist/types/app/composables/useHostI18n.d.ts +15 -0
  153. package/dist/types/app/composables/useHostI18n.d.ts.map +1 -0
  154. package/dist/types/app/composables/useLocalI18n.d.ts +5 -0
  155. package/dist/types/app/composables/useLocalI18n.d.ts.map +1 -0
  156. package/dist/types/app/composables/useLongPress.d.ts +6 -0
  157. package/dist/types/app/composables/useLongPress.d.ts.map +1 -0
  158. package/dist/types/app/composables/useRichTextEditor.d.ts +12 -0
  159. package/dist/types/app/composables/useRichTextEditor.d.ts.map +1 -0
  160. package/dist/types/app/directives/loading.d.ts +6 -0
  161. package/dist/types/app/directives/loading.d.ts.map +1 -0
  162. package/dist/types/app/index.d.ts +31 -0
  163. package/dist/types/app/index.d.ts.map +1 -0
  164. package/dist/types/app/plugin.d.ts +28 -0
  165. package/dist/types/app/plugin.d.ts.map +1 -0
  166. package/dist/types/app/provider/callProvider.d.ts +7 -0
  167. package/dist/types/app/provider/callProvider.d.ts.map +1 -0
  168. package/dist/types/app/stores/callStore.d.ts +1626 -0
  169. package/dist/types/app/stores/callStore.d.ts.map +1 -0
  170. package/dist/types/app/stores/chatStore.d.ts +126 -0
  171. package/dist/types/app/stores/chatStore.d.ts.map +1 -0
  172. package/dist/types/app/stores/mediaStore.d.ts +18 -0
  173. package/dist/types/app/stores/mediaStore.d.ts.map +1 -0
  174. package/dist/types/app/stores/messageStores.d.ts +2460 -0
  175. package/dist/types/app/stores/messageStores.d.ts.map +1 -0
  176. package/dist/types/app/stores/profileStore.d.ts +102 -0
  177. package/dist/types/app/stores/profileStore.d.ts.map +1 -0
  178. package/dist/types/app/types/components/chat-input.d.ts +4 -0
  179. package/dist/types/app/types/components/chat-input.d.ts.map +1 -0
  180. package/dist/types/app/types/components/menu-options.d.ts +7 -0
  181. package/dist/types/app/types/components/menu-options.d.ts.map +1 -0
  182. package/dist/types/app/types/components/modal.d.ts +17 -0
  183. package/dist/types/app/types/components/modal.d.ts.map +1 -0
  184. package/dist/types/app/types/index.d.ts +257 -0
  185. package/dist/types/app/types/index.d.ts.map +1 -0
  186. package/dist/types/app/utils/emojiParser.d.ts +5 -0
  187. package/dist/types/app/utils/emojiParser.d.ts.map +1 -0
  188. package/dist/types/app/utils/format.d.ts +4 -0
  189. package/dist/types/app/utils/format.d.ts.map +1 -0
  190. package/dist/types/fakes/backend.d.ts +25 -0
  191. package/dist/types/fakes/backend.d.ts.map +1 -0
  192. package/dist/types/fakes/call.d.ts +7 -0
  193. package/dist/types/fakes/call.d.ts.map +1 -0
  194. package/dist/types/fakes/data.d.ts +13 -0
  195. package/dist/types/fakes/data.d.ts.map +1 -0
  196. package/dist/types/fakes/index.d.ts +7 -0
  197. package/dist/types/fakes/index.d.ts.map +1 -0
  198. package/dist/types/fakes.d.ts +2 -0
  199. package/dist/types/i18n/locales/index.d.ts +3557 -0
  200. package/dist/types/i18n/locales/index.d.ts.map +1 -0
  201. package/dist/types/index.d.ts +2 -0
  202. package/package.json +98 -0
@@ -0,0 +1,711 @@
1
+ import { ref, watch, markRaw, computed, effectScope } from "vue";
2
+ import { nanoid } from "nanoid";
3
+ import { C as CallMessageType } from "./index-JU_JZkSC.mjs";
4
+ function createSignaling(handlers, self) {
5
+ let subscription;
6
+ const publish = (message) => handlers.publisher(JSON.stringify(message));
7
+ async function listen(on) {
8
+ subscription = await handlers.subscriber(async (message) => {
9
+ const from = message.payload.from;
10
+ if (from === self.id) return;
11
+ switch (message.type) {
12
+ case CallMessageType.Signal:
13
+ if (message.payload.to === self.id && message.payload.signal)
14
+ on.onSignal(from, message.payload.name, message.payload.signal);
15
+ break;
16
+ case CallMessageType.Join:
17
+ on.onJoin(from, message.payload.name);
18
+ break;
19
+ case CallMessageType.TrackType:
20
+ on.onTrackTypes(from, message.payload.types);
21
+ break;
22
+ case CallMessageType.Hangup:
23
+ on.onHangup(from);
24
+ break;
25
+ }
26
+ });
27
+ }
28
+ function stop() {
29
+ if (subscription !== void 0) void handlers.unSubscriber(subscription);
30
+ subscription = void 0;
31
+ }
32
+ return {
33
+ listen,
34
+ stop,
35
+ join: () => publish({
36
+ type: CallMessageType.Join,
37
+ payload: { from: self.id, name: self.name() }
38
+ }),
39
+ trackTypes: (types) => publish({
40
+ type: CallMessageType.TrackType,
41
+ payload: { from: self.id, types }
42
+ }),
43
+ signal: (to, signal) => publish({
44
+ type: CallMessageType.Signal,
45
+ payload: {
46
+ from: self.id,
47
+ to,
48
+ name: self.name(),
49
+ signal
50
+ }
51
+ }),
52
+ /** Rings the other side of `channel`. */
53
+ ring: (channel, avatar) => publish({
54
+ type: CallMessageType.Call,
55
+ payload: { from: self.id, name: self.name(), channel, avatar: avatar ?? "" }
56
+ }),
57
+ hangup: (channel) => publish({
58
+ type: CallMessageType.Hangup,
59
+ payload: { from: self.id, channel }
60
+ })
61
+ };
62
+ }
63
+ const ICE_COMPLETE_TIMEOUT_MS = 5e3;
64
+ const withoutTrickle = (sdp) => sdp.replace(/a=ice-options:trickle\s\n/g, "");
65
+ const randomLabel = () => Array.from(
66
+ crypto.getRandomValues(new Uint8Array(20)),
67
+ (b) => b.toString(16).padStart(2, "0")
68
+ ).join("");
69
+ function createPeer(opts) {
70
+ const { initiator } = opts;
71
+ const pc = new RTCPeerConnection(opts.config);
72
+ let destroyed = false;
73
+ let negotiating = false;
74
+ let queuedNegotiation = false;
75
+ let batched = false;
76
+ let firstNegotiation = true;
77
+ let channel = null;
78
+ const pendingCandidates = [];
79
+ const knownStreams = /* @__PURE__ */ new Set();
80
+ const requestedTransceivers = /* @__PURE__ */ new WeakSet();
81
+ function destroy(error) {
82
+ if (destroyed) return;
83
+ destroyed = true;
84
+ try {
85
+ channel == null ? void 0 : channel.close();
86
+ } catch {
87
+ }
88
+ pc.ontrack = pc.ondatachannel = pc.onconnectionstatechange = pc.onsignalingstatechange = null;
89
+ pc.close();
90
+ if (error) opts.onError(error);
91
+ opts.onClose();
92
+ }
93
+ const fail = (code) => (err) => destroy(Object.assign(err instanceof Error ? err : new Error(String(err)), { code }));
94
+ function setupChannel(dc) {
95
+ channel = dc;
96
+ dc.onclose = () => destroy();
97
+ }
98
+ function waitForIce() {
99
+ if (pc.iceGatheringState === "complete") return Promise.resolve();
100
+ return new Promise((resolve) => {
101
+ const timer = setTimeout(done, ICE_COMPLETE_TIMEOUT_MS);
102
+ function done() {
103
+ clearTimeout(timer);
104
+ pc.removeEventListener("icegatheringstatechange", check);
105
+ resolve();
106
+ }
107
+ function check() {
108
+ if (pc.iceGatheringState === "complete") done();
109
+ }
110
+ pc.addEventListener("icegatheringstatechange", check);
111
+ });
112
+ }
113
+ async function sendDescription() {
114
+ await waitForIce();
115
+ if (destroyed || !pc.localDescription) return;
116
+ opts.onSignal({ type: pc.localDescription.type, sdp: pc.localDescription.sdp });
117
+ }
118
+ async function createOffer() {
119
+ if (destroyed) return;
120
+ try {
121
+ const offer = await pc.createOffer();
122
+ await pc.setLocalDescription({ type: "offer", sdp: withoutTrickle(offer.sdp ?? "") });
123
+ await sendDescription();
124
+ } catch (err) {
125
+ fail("ERR_CREATE_OFFER")(err);
126
+ }
127
+ }
128
+ async function createAnswer() {
129
+ if (destroyed) return;
130
+ try {
131
+ const answer = await pc.createAnswer();
132
+ await pc.setLocalDescription({ type: "answer", sdp: withoutTrickle(answer.sdp ?? "") });
133
+ await sendDescription();
134
+ if (!initiator) requestMissingTransceivers();
135
+ } catch (err) {
136
+ fail("ERR_CREATE_ANSWER")(err);
137
+ }
138
+ }
139
+ function requestMissingTransceivers() {
140
+ for (const transceiver of pc.getTransceivers()) {
141
+ if (transceiver.mid || !transceiver.sender.track || requestedTransceivers.has(transceiver))
142
+ continue;
143
+ requestedTransceivers.add(transceiver);
144
+ opts.onSignal({
145
+ type: "transceiverRequest",
146
+ transceiverRequest: { kind: transceiver.sender.track.kind }
147
+ });
148
+ }
149
+ }
150
+ function negotiate() {
151
+ if (destroyed) return;
152
+ if (negotiating) {
153
+ queuedNegotiation = true;
154
+ return;
155
+ }
156
+ negotiating = true;
157
+ if (initiator) setTimeout(() => void createOffer(), 0);
158
+ else opts.onSignal({ type: "renegotiate", renegotiate: true });
159
+ }
160
+ function needsNegotiation() {
161
+ if (batched) return;
162
+ batched = true;
163
+ queueMicrotask(() => {
164
+ batched = false;
165
+ if (initiator || !firstNegotiation) negotiate();
166
+ firstNegotiation = false;
167
+ });
168
+ }
169
+ pc.onsignalingstatechange = () => {
170
+ if (destroyed || pc.signalingState !== "stable") return;
171
+ negotiating = false;
172
+ if (queuedNegotiation) {
173
+ queuedNegotiation = false;
174
+ needsNegotiation();
175
+ }
176
+ };
177
+ pc.onconnectionstatechange = () => {
178
+ if (pc.connectionState === "failed") fail("ERR_CONNECTION_FAILURE")(new Error("Connection failed."));
179
+ };
180
+ pc.ontrack = (event) => {
181
+ for (const stream of event.streams) {
182
+ if (knownStreams.has(stream.id)) continue;
183
+ knownStreams.add(stream.id);
184
+ queueMicrotask(() => !destroyed && opts.onStream(stream));
185
+ }
186
+ };
187
+ if (initiator) setupChannel(pc.createDataChannel(randomLabel()));
188
+ else pc.ondatachannel = (event) => setupChannel(event.channel);
189
+ opts.stream.getTracks().forEach((track) => pc.addTrack(track, opts.stream));
190
+ needsNegotiation();
191
+ return {
192
+ /** Applies a message from the other side. */
193
+ signal(data) {
194
+ var _a;
195
+ if (destroyed) return;
196
+ if (data.renegotiate && initiator) needsNegotiation();
197
+ if (data.transceiverRequest && initiator) {
198
+ const { kind, init } = data.transceiverRequest;
199
+ pc.addTransceiver(kind, init);
200
+ needsNegotiation();
201
+ }
202
+ if (data.candidate) {
203
+ if ((_a = pc.remoteDescription) == null ? void 0 : _a.type) void pc.addIceCandidate(data.candidate).catch(() => {
204
+ });
205
+ else pendingCandidates.push(data.candidate);
206
+ }
207
+ if (data.sdp) {
208
+ const type = data.type;
209
+ pc.setRemoteDescription({ type, sdp: data.sdp }).then(() => {
210
+ if (destroyed) return;
211
+ pendingCandidates.splice(0).forEach((c) => void pc.addIceCandidate(c).catch(() => {
212
+ }));
213
+ if (type === "offer") void createAnswer();
214
+ }).catch(fail("ERR_SET_REMOTE_DESCRIPTION"));
215
+ }
216
+ },
217
+ addTrack(track, stream) {
218
+ if (destroyed) return;
219
+ pc.addTrack(track, stream);
220
+ needsNegotiation();
221
+ },
222
+ removeTrack(track) {
223
+ if (destroyed) return;
224
+ const sender = pc.getSenders().find((s) => s.track === track);
225
+ if (!sender) return;
226
+ pc.removeTrack(sender);
227
+ needsNegotiation();
228
+ },
229
+ /** Swaps the outgoing video track in place (no renegotiation), or adds it if none is sent. */
230
+ replaceVideoTrack(track, stream) {
231
+ if (destroyed) return;
232
+ const sender = pc.getSenders().find((s) => {
233
+ var _a;
234
+ return ((_a = s.track) == null ? void 0 : _a.kind) === "video";
235
+ });
236
+ if (sender) void sender.replaceTrack(track);
237
+ else this.addTrack(track, stream);
238
+ },
239
+ /** Whether we currently send this track. */
240
+ isSending(track) {
241
+ return pc.getSenders().some((s) => s.track === track);
242
+ },
243
+ destroy: () => destroy()
244
+ };
245
+ }
246
+ function usePeers(opts) {
247
+ const { selfId, signaling, localStream, screenStream, log } = opts;
248
+ const peers = ref({});
249
+ const remoteVideos = ref({});
250
+ const remoteScreens = ref({});
251
+ const remoteStreamTypes = ref({});
252
+ const pending = ref({});
253
+ const pendingSignals = {};
254
+ const lastSdp = {};
255
+ const isInitiator = (remoteId) => selfId > remoteId;
256
+ const has = (remoteId) => !!peers.value[remoteId] || !!pending.value[remoteId];
257
+ function remove(remoteId) {
258
+ const peer = peers.value[remoteId];
259
+ delete peers.value[remoteId];
260
+ peer == null ? void 0 : peer.destroy();
261
+ delete remoteVideos.value[remoteId];
262
+ delete remoteScreens.value[remoteId];
263
+ delete pending.value[remoteId];
264
+ delete pendingSignals[remoteId];
265
+ delete lastSdp[remoteId];
266
+ }
267
+ function isScreen(remoteId, stream) {
268
+ const announced = remoteStreamTypes.value[remoteId] ?? [];
269
+ const match = announced.find((t) => t.id === stream.id);
270
+ if (match) return match.type === "screen";
271
+ return announced.some((t) => t.type === "screen");
272
+ }
273
+ function onRemoteStream(remoteId, name, stream) {
274
+ log("stream from", remoteId, stream.getTracks().map((t) => t.kind));
275
+ const target = isScreen(remoteId, stream) && stream.getVideoTracks().length > 0 ? remoteScreens : remoteVideos;
276
+ target.value[remoteId] = { name, stream };
277
+ stream.addEventListener("removetrack", () => {
278
+ var _a;
279
+ if (stream.getTracks().length > 0) return;
280
+ if (((_a = target.value[remoteId]) == null ? void 0 : _a.stream) === stream) delete target.value[remoteId];
281
+ });
282
+ }
283
+ function add(remoteId, name) {
284
+ if (peers.value[remoteId]) return;
285
+ if (!localStream.value) {
286
+ log("local media not ready, deferring peer", remoteId);
287
+ pending.value[remoteId] = { name };
288
+ return;
289
+ }
290
+ const config = opts.rtcConfig();
291
+ if (!config) {
292
+ log("no ICE servers available, cannot create peer for", remoteId);
293
+ return;
294
+ }
295
+ const initiator = isInitiator(remoteId);
296
+ let peer;
297
+ try {
298
+ peer = createPeer({
299
+ initiator,
300
+ stream: localStream.value,
301
+ config,
302
+ onSignal: (signal2) => void signaling.signal(remoteId, signal2),
303
+ onStream: (stream) => onRemoteStream(remoteId, name, stream),
304
+ onError(err) {
305
+ console.error("[vue-chat] Peer error for", remoteId, err);
306
+ void signaling.join();
307
+ },
308
+ onClose() {
309
+ log("peer closed", remoteId);
310
+ if (peers.value[remoteId] === peer) remove(remoteId);
311
+ }
312
+ });
313
+ } catch (err) {
314
+ console.error("[vue-chat] Could not create peer connection:", err);
315
+ return;
316
+ }
317
+ log("created peer", remoteId, "initiator:", initiator);
318
+ const screen = screenStream.value;
319
+ screen == null ? void 0 : screen.getVideoTracks().forEach((track) => peer.addTrack(track, screen));
320
+ peers.value[remoteId] = markRaw(peer);
321
+ const queued = pendingSignals[remoteId];
322
+ if (queued) {
323
+ delete pendingSignals[remoteId];
324
+ peer.signal(queued);
325
+ }
326
+ }
327
+ function signal(remoteId, name, data) {
328
+ if (data.sdp && lastSdp[remoteId] === data.sdp) return;
329
+ if (!peers.value[remoteId]) add(remoteId, name);
330
+ const peer = peers.value[remoteId];
331
+ if (peer) {
332
+ if (data.sdp) lastSdp[remoteId] = data.sdp;
333
+ peer.signal(data);
334
+ } else {
335
+ log("peer still deferred, queueing signal for", remoteId);
336
+ pendingSignals[remoteId] = data;
337
+ }
338
+ }
339
+ function addTrackToAll(track, stream) {
340
+ Object.values(peers.value).forEach((peer) => peer.addTrack(track, stream));
341
+ }
342
+ function removeTrackFromAll(track) {
343
+ Object.values(peers.value).forEach((peer) => peer.removeTrack(track));
344
+ }
345
+ function replaceVideoTrack(track, stream) {
346
+ Object.values(peers.value).forEach((peer) => peer.replaceVideoTrack(track, stream));
347
+ }
348
+ function destroyAll() {
349
+ const all = Object.values(peers.value);
350
+ peers.value = {};
351
+ all.forEach((peer) => peer.destroy());
352
+ }
353
+ watch(
354
+ localStream,
355
+ (stream) => {
356
+ if (!stream) return;
357
+ const waiting = { ...pending.value };
358
+ pending.value = {};
359
+ for (const [remoteId, { name }] of Object.entries(waiting)) add(remoteId, name);
360
+ },
361
+ { immediate: true }
362
+ );
363
+ return {
364
+ peers,
365
+ remoteVideos,
366
+ remoteScreens,
367
+ remoteStreamTypes,
368
+ isInitiator,
369
+ has,
370
+ add,
371
+ remove,
372
+ signal,
373
+ addTrackToAll,
374
+ removeTrackFromAll,
375
+ replaceVideoTrack,
376
+ destroyAll
377
+ };
378
+ }
379
+ function deviceError(err, kind, isMobile2) {
380
+ const name = err == null ? void 0 : err.name;
381
+ if (kind === "screen") {
382
+ if (name === "NotAllowedError") return "screenShareDenied";
383
+ if (name === "NotFoundError" || name === "NotReadableError") return "screenAccessError";
384
+ return isMobile2 ? "screenShareMobileMaybeUnavailable" : "screenShareStartFailed";
385
+ }
386
+ const Kind = kind === "mic" ? "Mic" : "Camera";
387
+ if (name === "NotAllowedError") return `${kind}Denied`;
388
+ if (name === "NotFoundError") return `no${Kind}Found`;
389
+ return `${kind}AccessError`;
390
+ }
391
+ function useCallMedia({ notify, isMobile: isMobile2 }) {
392
+ const error = (key) => notify(`chat.call.errors.${key}`);
393
+ const localStream = ref(null);
394
+ const screenStream = ref(null);
395
+ const trackTypes = ref([]);
396
+ const videoTrack = ref(null);
397
+ const cameras = ref([]);
398
+ const isAudioOn = ref(true);
399
+ const isVideoOn = ref(true);
400
+ const isScreenSharing = ref(false);
401
+ const isFlashlightOn = ref(false);
402
+ const permissions = ref({ camera: null, microphone: null });
403
+ const isFlashlightSupported = computed(() => {
404
+ var _a, _b;
405
+ const capabilities = (_b = (_a = videoTrack.value) == null ? void 0 : _a.getCapabilities) == null ? void 0 : _b.call(_a);
406
+ return !!capabilities && "torch" in capabilities;
407
+ });
408
+ async function checkPermissions() {
409
+ try {
410
+ const devices = await navigator.mediaDevices.enumerateDevices();
411
+ cameras.value = devices.filter((d) => d.kind === "videoinput");
412
+ if (!devices.some((d) => d.kind === "audioinput")) {
413
+ error("noMicDevice");
414
+ isAudioOn.value = false;
415
+ }
416
+ if (!cameras.value.length) {
417
+ error("noCameraDevice");
418
+ isVideoOn.value = false;
419
+ }
420
+ const query = (name) => navigator.permissions.query({ name });
421
+ permissions.value.camera = (await query("camera")).state;
422
+ permissions.value.microphone = (await query("microphone")).state;
423
+ if (permissions.value.camera === "denied") isVideoOn.value = false;
424
+ if (permissions.value.microphone === "denied") isAudioOn.value = false;
425
+ } catch (err) {
426
+ console.error("[vue-chat] Error checking permissions:", err);
427
+ }
428
+ }
429
+ async function start() {
430
+ var _a;
431
+ const { camera, microphone } = permissions.value;
432
+ if (camera === "denied" || microphone === "denied") {
433
+ if (camera === "denied") error("cameraDenied");
434
+ if (microphone === "denied") error("micDenied");
435
+ return;
436
+ }
437
+ try {
438
+ localStream.value = await navigator.mediaDevices.getUserMedia({
439
+ video: true,
440
+ audio: true
441
+ });
442
+ trackTypes.value = [
443
+ ...trackTypes.value,
444
+ { id: localStream.value.id, type: "webcam_audio" }
445
+ ];
446
+ } catch {
447
+ const tracks = [];
448
+ try {
449
+ const audio = await navigator.mediaDevices.getUserMedia({ audio: true });
450
+ trackTypes.value = [...trackTypes.value, { id: audio.id, type: "audio" }];
451
+ tracks.push(...audio.getAudioTracks());
452
+ } catch (err) {
453
+ console.error("[vue-chat] Error accessing microphone:", err);
454
+ error(deviceError(err, "mic", isMobile2()));
455
+ isAudioOn.value = false;
456
+ }
457
+ try {
458
+ const video = await navigator.mediaDevices.getUserMedia({ video: true });
459
+ trackTypes.value = [...trackTypes.value, { id: video.id, type: "webcam" }];
460
+ tracks.push(...video.getVideoTracks());
461
+ } catch (err) {
462
+ console.error("[vue-chat] Error accessing camera:", err);
463
+ error(deviceError(err, "camera", isMobile2()));
464
+ isVideoOn.value = false;
465
+ }
466
+ if (tracks.length) localStream.value = new MediaStream(tracks);
467
+ }
468
+ videoTrack.value = ((_a = localStream.value) == null ? void 0 : _a.getVideoTracks()[0]) ?? null;
469
+ }
470
+ function toggleAudio() {
471
+ var _a;
472
+ isAudioOn.value = !isAudioOn.value;
473
+ const track = (_a = localStream.value) == null ? void 0 : _a.getAudioTracks()[0];
474
+ if (track) track.enabled = isAudioOn.value;
475
+ }
476
+ function toggleVideo() {
477
+ var _a;
478
+ isVideoOn.value = !isVideoOn.value;
479
+ const track = (_a = localStream.value) == null ? void 0 : _a.getVideoTracks()[0];
480
+ if (track) track.enabled = isVideoOn.value;
481
+ }
482
+ async function switchCamera(deviceId) {
483
+ try {
484
+ const stream = await navigator.mediaDevices.getUserMedia({
485
+ video: { deviceId: { exact: deviceId } },
486
+ audio: isAudioOn.value
487
+ });
488
+ const track = stream.getVideoTracks()[0];
489
+ if (!track || !localStream.value) return null;
490
+ const old = localStream.value.getVideoTracks()[0];
491
+ if (old) {
492
+ localStream.value.removeTrack(old);
493
+ old.stop();
494
+ }
495
+ localStream.value.addTrack(track);
496
+ videoTrack.value = track;
497
+ return track;
498
+ } catch (err) {
499
+ console.error("[vue-chat] Error switching camera:", err);
500
+ return null;
501
+ }
502
+ }
503
+ async function startScreenShare(onEnded) {
504
+ var _a;
505
+ if (screenStream.value) {
506
+ screenStream.value.getVideoTracks()[0].enabled = true;
507
+ isScreenSharing.value = true;
508
+ return null;
509
+ }
510
+ if (!((_a = navigator.mediaDevices) == null ? void 0 : _a.getDisplayMedia)) {
511
+ error(
512
+ isMobile2() ? "screenShareMobileUnsupported" : "screenShareBrowserUnsupported"
513
+ );
514
+ return null;
515
+ }
516
+ try {
517
+ const stream = await navigator.mediaDevices.getDisplayMedia({
518
+ video: true,
519
+ audio: false
520
+ });
521
+ screenStream.value = stream;
522
+ trackTypes.value = [...trackTypes.value, { id: stream.id, type: "screen" }];
523
+ stream.getTracks().forEach((track) => track.onended = onEnded);
524
+ isScreenSharing.value = true;
525
+ return stream;
526
+ } catch (err) {
527
+ console.error("[vue-chat] Screen share failed:", err);
528
+ error(deviceError(err, "screen", isMobile2()));
529
+ isScreenSharing.value = false;
530
+ return null;
531
+ }
532
+ }
533
+ function stopScreenShare() {
534
+ const stream = screenStream.value;
535
+ if (!stream) return null;
536
+ stream.getTracks().forEach((track) => {
537
+ track.stop();
538
+ track.enabled = false;
539
+ });
540
+ trackTypes.value = trackTypes.value.filter((t) => t.type !== "screen");
541
+ screenStream.value = null;
542
+ isScreenSharing.value = false;
543
+ return stream;
544
+ }
545
+ async function toggleFlashlight() {
546
+ const track = videoTrack.value;
547
+ if (!track) {
548
+ error("cameraUnavailable");
549
+ return;
550
+ }
551
+ if (!isFlashlightSupported.value) return;
552
+ try {
553
+ await track.applyConstraints({
554
+ advanced: [{ torch: !isFlashlightOn.value }]
555
+ });
556
+ isFlashlightOn.value = !isFlashlightOn.value;
557
+ } catch (err) {
558
+ console.error("[vue-chat] Flashlight toggle failed:", err);
559
+ error("flashlightToggleFailed");
560
+ }
561
+ }
562
+ function stopAll() {
563
+ if (isFlashlightOn.value && videoTrack.value) {
564
+ void videoTrack.value.applyConstraints({ advanced: [{ torch: false }] }).catch(() => {
565
+ });
566
+ }
567
+ for (const stream of [localStream.value, screenStream.value]) {
568
+ stream == null ? void 0 : stream.getTracks().forEach((track) => {
569
+ track.stop();
570
+ track.enabled = false;
571
+ });
572
+ }
573
+ localStream.value = null;
574
+ screenStream.value = null;
575
+ videoTrack.value = null;
576
+ }
577
+ return {
578
+ localStream,
579
+ screenStream,
580
+ trackTypes,
581
+ videoTrack,
582
+ cameras,
583
+ isAudioOn,
584
+ isVideoOn,
585
+ isScreenSharing,
586
+ isFlashlightOn,
587
+ isFlashlightSupported,
588
+ checkPermissions,
589
+ start,
590
+ toggleAudio,
591
+ toggleVideo,
592
+ switchCamera,
593
+ startScreenShare,
594
+ stopScreenShare,
595
+ toggleFlashlight,
596
+ stopAll
597
+ };
598
+ }
599
+ const toIceUrl = (url) => url.startsWith("stun:") || url.startsWith("turn:") ? url : `turn:${url}`;
600
+ const isMobile = () => {
601
+ var _a;
602
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || !!((_a = window.matchMedia) == null ? void 0 : _a.call(window, "(max-width: 768px)").matches);
603
+ };
604
+ function createCallSession(opts) {
605
+ const { handlers, channel } = opts;
606
+ const scope = effectScope(true);
607
+ const log = handlers.debug ? (...args) => console.log("[vue-chat:call]", ...args) : () => {
608
+ };
609
+ const selfId = `${opts.self.id}:${nanoid()}`;
610
+ const session = scope.run(() => {
611
+ const signaling2 = createSignaling(handlers, { id: selfId, name: opts.self.name });
612
+ const media2 = useCallMedia({ notify: opts.notify, isMobile });
613
+ const peers2 = usePeers({
614
+ selfId,
615
+ signaling: signaling2,
616
+ localStream: media2.localStream,
617
+ screenStream: media2.screenStream,
618
+ log,
619
+ rtcConfig() {
620
+ var _a;
621
+ const policy = handlers.iceTransportPolicy ?? "relay";
622
+ const cred = handlers.credential;
623
+ const iceServers = ((_a = cred == null ? void 0 : cred.urls) == null ? void 0 : _a.length) ? [{ urls: cred.urls.map(toIceUrl), username: cred.user, credential: cred.pass }] : [];
624
+ if (policy === "relay" && !iceServers.length) return null;
625
+ return { iceTransportPolicy: policy, iceServers };
626
+ }
627
+ });
628
+ const participantCount = computed(() => Object.keys(peers2.peers.value).length + 1);
629
+ const tileCount = computed(
630
+ () => (media2.screenStream.value ? 1 : 0) + Object.keys(peers2.remoteVideos.value).length + Object.keys(peers2.remoteScreens.value).length
631
+ );
632
+ return { signaling: signaling2, media: media2, peers: peers2, participantCount, tileCount };
633
+ });
634
+ const { signaling, media, peers } = session;
635
+ let ended = false;
636
+ async function ringOtherSide() {
637
+ const blob = opts.self.avatar();
638
+ let avatar;
639
+ if (blob) {
640
+ const bytes = new Uint8Array(await blob.arrayBuffer());
641
+ avatar = `data:${blob.type};base64,${btoa(String.fromCharCode(...bytes))}`;
642
+ }
643
+ await signaling.ring(channel, avatar);
644
+ }
645
+ async function start() {
646
+ var _a;
647
+ await signaling.listen({
648
+ onSignal: (from, name, signal) => peers.signal(from, name, signal),
649
+ onJoin(from, name) {
650
+ log("join from", from, name);
651
+ void signaling.trackTypes(media.trackTypes.value);
652
+ const isNew = !peers.has(from);
653
+ peers.add(from, name);
654
+ if (isNew && !peers.isInitiator(from)) void signaling.join();
655
+ },
656
+ onTrackTypes(from, types) {
657
+ peers.remoteStreamTypes.value[from] = types;
658
+ },
659
+ onHangup(from) {
660
+ log("hangup from", from);
661
+ peers.remove(from);
662
+ }
663
+ });
664
+ await handlers.handleGenerateCred();
665
+ await media.checkPermissions();
666
+ await media.start();
667
+ if (ended) return media.stopAll();
668
+ log("local media ready:", (_a = media.localStream.value) == null ? void 0 : _a.getTracks().map((t) => t.kind));
669
+ void signaling.trackTypes(media.trackTypes.value);
670
+ void ringOtherSide();
671
+ void signaling.join();
672
+ }
673
+ function stopScreenShare() {
674
+ const stream = media.stopScreenShare();
675
+ stream == null ? void 0 : stream.getVideoTracks().forEach((track) => peers.removeTrackFromAll(track));
676
+ }
677
+ async function toggleScreenShare() {
678
+ if (media.isScreenSharing.value) return stopScreenShare();
679
+ const stream = await media.startScreenShare(stopScreenShare);
680
+ if (!stream) return;
681
+ await signaling.trackTypes(media.trackTypes.value);
682
+ stream.getVideoTracks().forEach((track) => peers.addTrackToAll(track, stream));
683
+ }
684
+ async function switchCamera(deviceId) {
685
+ const track = await media.switchCamera(deviceId);
686
+ const stream = media.localStream.value;
687
+ if (track && stream) peers.replaceVideoTrack(track, stream);
688
+ return stream;
689
+ }
690
+ function end() {
691
+ if (ended) return;
692
+ ended = true;
693
+ void signaling.hangup(channel);
694
+ signaling.stop();
695
+ peers.destroyAll();
696
+ media.stopAll();
697
+ scope.stop();
698
+ }
699
+ return {
700
+ ...session,
701
+ channel,
702
+ start,
703
+ end,
704
+ toggleScreenShare,
705
+ switchCamera
706
+ };
707
+ }
708
+ export {
709
+ createCallSession
710
+ };
711
+ //# sourceMappingURL=session-B3KIrj3_.mjs.map