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 @@
1
+ {"version":3,"file":"session-B3KIrj3_.mjs","sources":["../../app/composables/call/signaling.ts","../../app/composables/call/peer.ts","../../app/composables/call/peers.ts","../../app/composables/call/media.ts","../../app/composables/call/session.ts"],"sourcesContent":["import type {\n CallHandlers,\n CallMessageSchema,\n SignalData,\n TrackType,\n} from \"~/types\";\nimport { CallMessageType } from \"~/types\";\n\nexport type TrackTypes = { id: string; type: TrackType }[];\n\nexport interface SignalingHandlers {\n /** An SDP offer/answer or ICE candidate addressed to us. */\n onSignal(from: string, name: string, signal: SignalData & { sdp: string }): void;\n /** Someone entered the call. */\n onJoin(from: string, name: string): void;\n /** Someone described which of their streams are camera, screen, … */\n onTrackTypes(from: string, types: TrackTypes): void;\n /** Someone left the call. */\n onHangup(from: string): void;\n}\n\n/**\n * The call's messages over the host's pub/sub (`CallHandlers`), typed. Knows nothing about\n * WebRTC: it only turns calls into messages and messages into callbacks.\n */\nexport function createSignaling(\n handlers: Pick<CallHandlers, \"publisher\" | \"subscriber\" | \"unSubscriber\">,\n self: { id: string; name: () => string },\n) {\n let subscription: number | undefined;\n\n const publish = (message: CallMessageSchema) =>\n handlers.publisher(JSON.stringify(message));\n\n async function listen(on: SignalingHandlers) {\n subscription = await handlers.subscriber(async (message) => {\n const from = message.payload.from;\n if (from === self.id) return;\n\n switch (message.type) {\n case CallMessageType.Signal:\n if (message.payload.to === self.id && message.payload.signal)\n on.onSignal(from, message.payload.name, message.payload.signal);\n break;\n case CallMessageType.Join:\n on.onJoin(from, message.payload.name);\n break;\n case CallMessageType.TrackType:\n on.onTrackTypes(from, message.payload.types);\n break;\n case CallMessageType.Hangup:\n on.onHangup(from);\n break;\n }\n });\n }\n\n function stop() {\n if (subscription !== undefined) void handlers.unSubscriber(subscription);\n subscription = undefined;\n }\n\n return {\n listen,\n stop,\n join: () =>\n publish({\n type: CallMessageType.Join,\n payload: { from: self.id, name: self.name() },\n }),\n trackTypes: (types: TrackTypes) =>\n publish({\n type: CallMessageType.TrackType,\n payload: { from: self.id, types },\n }),\n signal: (to: string, signal: SignalData) =>\n publish({\n type: CallMessageType.Signal,\n payload: {\n from: self.id,\n to,\n name: self.name(),\n signal: signal as SignalData & { sdp: string },\n },\n }),\n /** Rings the other side of `channel`. */\n ring: (channel: string, avatar?: string) =>\n publish({\n type: CallMessageType.Call,\n payload: { from: self.id, name: self.name(), channel, avatar: avatar ?? \"\" },\n }),\n hangup: (channel: string) =>\n publish({\n type: CallMessageType.Hangup,\n payload: { from: self.id, channel },\n }),\n };\n}\n\nexport type Signaling = ReturnType<typeof createSignaling>;\n","import type { SignalData } from \"~/types\";\n\n/** How long to wait for ICE gathering before sending a description anyway (as simple-peer). */\nconst ICE_COMPLETE_TIMEOUT_MS = 5_000;\n\nexport interface PeerEvents {\n /** A message for the other side, to send over signalling. */\n onSignal(signal: SignalData): void;\n /** A new remote stream (once per stream, with its tracks already added). */\n onStream(stream: MediaStream): void;\n /** The connection is gone, from either side. Fires once. */\n onClose(): void;\n onError(error: Error): void;\n}\n\nexport interface PeerOptions extends PeerEvents {\n /** Whether this side makes the offers. Exactly one side of a pair must be the initiator. */\n initiator: boolean;\n stream: MediaStream;\n config: RTCConfiguration;\n}\n\n// Without trickle ICE, the candidates travel inside the SDP; don't advertise trickle support.\nconst withoutTrickle = (sdp: string) => sdp.replace(/a=ice-options:trickle\\s\\n/g, \"\");\n\nconst randomLabel = () =>\n Array.from(crypto.getRandomValues(new Uint8Array(20)), (b) =>\n b.toString(16).padStart(2, \"0\"),\n ).join(\"\");\n\n/**\n * One WebRTC connection to one remote participant, on the plain browser API.\n *\n * It speaks simple-peer's wire protocol (with `trickle: false`), so it interoperates with clients\n * still running the previous, simple-peer based build:\n * - full offers/answers with the ICE candidates inside the SDP;\n * - only the initiator makes offers; the other side asks with `{ type: \"renegotiate\" }` and, for\n * tracks the offer had no room for, `{ type: \"transceiverRequest\" }`, so offers never collide;\n * - a data channel whose closing tells either side that the other one left.\n */\nexport function createPeer(opts: PeerOptions) {\n const { initiator } = opts;\n const pc = new RTCPeerConnection(opts.config);\n\n let destroyed = false;\n let negotiating = false;\n let queuedNegotiation = false;\n let batched = false;\n let firstNegotiation = true;\n let channel: RTCDataChannel | null = null;\n const pendingCandidates: RTCIceCandidateInit[] = [];\n const knownStreams = new Set<string>();\n const requestedTransceivers = new WeakSet<RTCRtpTransceiver>();\n\n function destroy(error?: Error) {\n if (destroyed) return;\n destroyed = true;\n try {\n channel?.close();\n } catch {\n // already closed\n }\n pc.ontrack = pc.ondatachannel = pc.onconnectionstatechange = pc.onsignalingstatechange = null;\n pc.close();\n if (error) opts.onError(error);\n opts.onClose();\n }\n\n const fail = (code: string) => (err: unknown) =>\n destroy(Object.assign(err instanceof Error ? err : new Error(String(err)), { code }));\n\n function setupChannel(dc: RTCDataChannel) {\n channel = dc;\n // The other side destroying its connection closes the channel: that is how a leave is seen\n // without waiting for ICE to time out.\n dc.onclose = () => destroy();\n }\n\n function waitForIce() {\n if (pc.iceGatheringState === \"complete\") return Promise.resolve();\n return new Promise<void>((resolve) => {\n const timer = setTimeout(done, ICE_COMPLETE_TIMEOUT_MS);\n function done() {\n clearTimeout(timer);\n pc.removeEventListener(\"icegatheringstatechange\", check);\n resolve();\n }\n function check() {\n if (pc.iceGatheringState === \"complete\") done();\n }\n pc.addEventListener(\"icegatheringstatechange\", check);\n });\n }\n\n /** Sends our local description once its candidates are in. */\n async function sendDescription() {\n await waitForIce();\n if (destroyed || !pc.localDescription) return;\n opts.onSignal({ type: pc.localDescription.type, sdp: pc.localDescription.sdp });\n }\n\n async function createOffer() {\n if (destroyed) return;\n try {\n const offer = await pc.createOffer();\n await pc.setLocalDescription({ type: \"offer\", sdp: withoutTrickle(offer.sdp ?? \"\") });\n await sendDescription();\n } catch (err) {\n fail(\"ERR_CREATE_OFFER\")(err);\n }\n }\n\n async function createAnswer() {\n if (destroyed) return;\n try {\n const answer = await pc.createAnswer();\n await pc.setLocalDescription({ type: \"answer\", sdp: withoutTrickle(answer.sdp ?? \"\") });\n await sendDescription();\n if (!initiator) requestMissingTransceivers();\n } catch (err) {\n fail(\"ERR_CREATE_ANSWER\")(err);\n }\n }\n\n /** Our tracks the initiator's offer had no m-line for: ask it to add one. */\n function requestMissingTransceivers() {\n for (const transceiver of pc.getTransceivers()) {\n if (transceiver.mid || !transceiver.sender.track || requestedTransceivers.has(transceiver))\n continue;\n requestedTransceivers.add(transceiver);\n opts.onSignal({\n type: \"transceiverRequest\",\n transceiverRequest: { kind: transceiver.sender.track.kind },\n });\n }\n }\n\n function negotiate() {\n if (destroyed) return;\n if (negotiating) {\n queuedNegotiation = true;\n return;\n }\n negotiating = true;\n if (initiator) setTimeout(() => void createOffer(), 0);\n else opts.onSignal({ type: \"renegotiate\", renegotiate: true });\n }\n\n /** Batches changes made in one tick into a single negotiation. */\n function needsNegotiation() {\n if (batched) return;\n batched = true;\n queueMicrotask(() => {\n batched = false;\n // The first negotiation is the initiator's offer; the other side just answers it.\n if (initiator || !firstNegotiation) negotiate();\n firstNegotiation = false;\n });\n }\n\n pc.onsignalingstatechange = () => {\n if (destroyed || pc.signalingState !== \"stable\") return;\n negotiating = false;\n if (queuedNegotiation) {\n queuedNegotiation = false;\n needsNegotiation();\n }\n };\n\n pc.onconnectionstatechange = () => {\n if (pc.connectionState === \"failed\") fail(\"ERR_CONNECTION_FAILURE\")(new Error(\"Connection failed.\"));\n };\n\n pc.ontrack = (event) => {\n for (const stream of event.streams) {\n // One event per stream, even though each of its tracks arrives separately.\n if (knownStreams.has(stream.id)) continue;\n knownStreams.add(stream.id);\n queueMicrotask(() => !destroyed && opts.onStream(stream));\n }\n };\n\n if (initiator) setupChannel(pc.createDataChannel(randomLabel()));\n else pc.ondatachannel = (event) => setupChannel(event.channel);\n\n opts.stream.getTracks().forEach((track) => pc.addTrack(track, opts.stream));\n needsNegotiation();\n\n return {\n /** Applies a message from the other side. */\n signal(data: SignalData) {\n if (destroyed) return;\n if (data.renegotiate && initiator) needsNegotiation();\n if (data.transceiverRequest && initiator) {\n const { kind, init } = data.transceiverRequest;\n pc.addTransceiver(kind, init);\n needsNegotiation();\n }\n if (data.candidate) {\n if (pc.remoteDescription?.type) void pc.addIceCandidate(data.candidate).catch(() => {});\n else pendingCandidates.push(data.candidate);\n }\n if (data.sdp) {\n const type = data.type as RTCSdpType;\n pc.setRemoteDescription({ type, sdp: data.sdp })\n .then(() => {\n if (destroyed) return;\n pendingCandidates.splice(0).forEach((c) => void pc.addIceCandidate(c).catch(() => {}));\n if (type === \"offer\") void createAnswer();\n })\n .catch(fail(\"ERR_SET_REMOTE_DESCRIPTION\"));\n }\n },\n\n addTrack(track: MediaStreamTrack, stream: MediaStream) {\n if (destroyed) return;\n pc.addTrack(track, stream);\n needsNegotiation();\n },\n\n removeTrack(track: MediaStreamTrack) {\n if (destroyed) return;\n const sender = pc.getSenders().find((s) => s.track === track);\n if (!sender) return;\n pc.removeTrack(sender);\n needsNegotiation();\n },\n\n /** Swaps the outgoing video track in place (no renegotiation), or adds it if none is sent. */\n replaceVideoTrack(track: MediaStreamTrack, stream: MediaStream) {\n if (destroyed) return;\n const sender = pc.getSenders().find((s) => s.track?.kind === \"video\");\n if (sender) void sender.replaceTrack(track);\n else this.addTrack(track, stream);\n },\n\n /** Whether we currently send this track. */\n isSending(track: MediaStreamTrack) {\n return pc.getSenders().some((s) => s.track === track);\n },\n\n destroy: () => destroy(),\n };\n}\n\nexport type PeerConnection = ReturnType<typeof createPeer>;\n","import { markRaw, ref, watch, type Ref } from \"vue\";\nimport type { SignalData } from \"~/types\";\nimport { createPeer, type PeerConnection } from \"./peer\";\nimport type { Signaling, TrackTypes } from \"./signaling\";\n\nexport interface RemoteStream {\n name: string;\n stream: MediaStream;\n}\n\nexport interface PeerOptions {\n selfId: string;\n signaling: Signaling;\n localStream: Ref<MediaStream | null>;\n screenStream: Ref<MediaStream | null>;\n /** ICE servers and policy for each new connection, or null when none are usable yet. */\n rtcConfig: () => RTCConfiguration | null;\n log: (...args: unknown[]) => void;\n}\n\n/**\n * One connection per remote participant, plus the streams they send us.\n *\n * Who makes the offer is decided by comparing ids, so both sides agree without a round trip.\n * A peer can't be created before our own media exists; such requests wait until it does.\n */\nexport function usePeers(opts: PeerOptions) {\n const { selfId, signaling, localStream, screenStream, log } = opts;\n\n const peers = ref<Record<string, PeerConnection>>({});\n const remoteVideos = ref<Record<string, RemoteStream>>({});\n const remoteScreens = ref<Record<string, RemoteStream>>({});\n const remoteStreamTypes = ref<Record<string, TrackTypes>>({});\n /** Peers asked for before our media was ready. */\n const pending = ref<Record<string, { name: string }>>({});\n /** Signals that arrived for a peer that doesn't exist yet. */\n const pendingSignals: Record<string, SignalData> = {};\n /** Last SDP applied per peer, so a re-delivered message isn't applied twice. */\n const lastSdp: Record<string, string | undefined> = {};\n\n const isInitiator = (remoteId: string) => selfId > remoteId;\n const has = (remoteId: string) => !!peers.value[remoteId] || !!pending.value[remoteId];\n\n function remove(remoteId: string) {\n const peer = peers.value[remoteId];\n delete peers.value[remoteId];\n peer?.destroy();\n delete remoteVideos.value[remoteId];\n delete remoteScreens.value[remoteId];\n delete pending.value[remoteId];\n delete pendingSignals[remoteId];\n delete lastSdp[remoteId];\n }\n\n /** Whether `stream` is the remote's screen share, going by what they announced. */\n function isScreen(remoteId: string, stream: MediaStream) {\n const announced = remoteStreamTypes.value[remoteId] ?? [];\n const match = announced.find((t) => t.id === stream.id);\n if (match) return match.type === \"screen\";\n // Stream ids didn't survive the trip: fall back to \"they announced a screen\".\n return announced.some((t) => t.type === \"screen\");\n }\n\n function onRemoteStream(remoteId: string, name: string, stream: MediaStream) {\n log(\"stream from\", remoteId, stream.getTracks().map((t) => t.kind));\n const target =\n isScreen(remoteId, stream) && stream.getVideoTracks().length > 0\n ? remoteScreens\n : remoteVideos;\n target.value[remoteId] = { name, stream };\n\n // A stopped screen share leaves an empty stream behind; drop its tile.\n stream.addEventListener(\"removetrack\", () => {\n if (stream.getTracks().length > 0) return;\n if (target.value[remoteId]?.stream === stream) delete target.value[remoteId];\n });\n }\n\n function add(remoteId: string, name: string) {\n if (peers.value[remoteId]) return;\n\n if (!localStream.value) {\n log(\"local media not ready, deferring peer\", remoteId);\n pending.value[remoteId] = { name };\n return;\n }\n\n const config = opts.rtcConfig();\n if (!config) {\n log(\"no ICE servers available, cannot create peer for\", remoteId);\n return;\n }\n\n const initiator = isInitiator(remoteId);\n let peer: PeerConnection;\n try {\n peer = createPeer({\n initiator,\n stream: localStream.value,\n config,\n onSignal: (signal) => void signaling.signal(remoteId, signal),\n onStream: (stream) => onRemoteStream(remoteId, name, stream),\n onError(err) {\n console.error(\"[vue-chat] Peer error for\", remoteId, err);\n // Announce ourselves again so the pair can start over.\n void signaling.join();\n },\n onClose() {\n log(\"peer closed\", remoteId);\n // Only if it is still the current one: a replaced peer closing must not remove its successor.\n if (peers.value[remoteId] === peer) remove(remoteId);\n },\n });\n } catch (err) {\n console.error(\"[vue-chat] Could not create peer connection:\", err);\n return;\n }\n log(\"created peer\", remoteId, \"initiator:\", initiator);\n\n const screen = screenStream.value;\n screen?.getVideoTracks().forEach((track) => peer.addTrack(track, screen));\n\n peers.value[remoteId] = markRaw(peer);\n\n const queued = pendingSignals[remoteId];\n if (queued) {\n delete pendingSignals[remoteId];\n peer.signal(queued);\n }\n }\n\n /** Applies a signal from `remoteId`, creating the peer first if needed. */\n function signal(remoteId: string, name: string, data: SignalData & { sdp: string }) {\n if (data.sdp && lastSdp[remoteId] === data.sdp) return;\n if (!peers.value[remoteId]) add(remoteId, name);\n\n const peer = peers.value[remoteId];\n if (peer) {\n if (data.sdp) lastSdp[remoteId] = data.sdp;\n peer.signal(data);\n } else {\n log(\"peer still deferred, queueing signal for\", remoteId);\n pendingSignals[remoteId] = data;\n }\n }\n\n /** Sends a track to every connected peer, or takes it back. */\n function addTrackToAll(track: MediaStreamTrack, stream: MediaStream) {\n Object.values(peers.value).forEach((peer) => peer.addTrack(track, stream));\n }\n\n function removeTrackFromAll(track: MediaStreamTrack) {\n Object.values(peers.value).forEach((peer) => peer.removeTrack(track));\n }\n\n /** Swaps the outgoing camera track on every peer (after switching cameras). */\n function replaceVideoTrack(track: MediaStreamTrack, stream: MediaStream) {\n Object.values(peers.value).forEach((peer) => peer.replaceVideoTrack(track, stream));\n }\n\n function destroyAll() {\n const all = Object.values(peers.value);\n peers.value = {};\n all.forEach((peer) => peer.destroy());\n }\n\n // Create the peers that were waiting for our media.\n watch(\n localStream,\n (stream) => {\n if (!stream) return;\n const waiting = { ...pending.value };\n pending.value = {};\n for (const [remoteId, { name }] of Object.entries(waiting)) add(remoteId, name);\n },\n { immediate: true },\n );\n\n return {\n peers,\n remoteVideos,\n remoteScreens,\n remoteStreamTypes,\n isInitiator,\n has,\n add,\n remove,\n signal,\n addTrackToAll,\n removeTrackFromAll,\n replaceVideoTrack,\n destroyAll,\n };\n}\n","import { computed, ref } from \"vue\";\nimport type { TrackTypes } from \"./signaling\";\n\nexport interface MediaOptions {\n /** Shows an error; `key` is a `chat.call.errors.*` translation key. */\n notify: (key: string) => void;\n isMobile: () => boolean;\n}\n\n/** Why getUserMedia/getDisplayMedia failed, as a translation key. */\nfunction deviceError(err: unknown, kind: \"mic\" | \"camera\" | \"screen\", isMobile: boolean) {\n const name = (err as { name?: string })?.name;\n if (kind === \"screen\") {\n if (name === \"NotAllowedError\") return \"screenShareDenied\";\n if (name === \"NotFoundError\" || name === \"NotReadableError\") return \"screenAccessError\";\n return isMobile ? \"screenShareMobileMaybeUnavailable\" : \"screenShareStartFailed\";\n }\n const Kind = kind === \"mic\" ? \"Mic\" : \"Camera\";\n if (name === \"NotAllowedError\") return `${kind}Denied`;\n if (name === \"NotFoundError\") return `no${Kind}Found`;\n return `${kind}AccessError`;\n}\n\n/** This participant's camera, microphone and screen share. */\nexport function useCallMedia({ notify, isMobile }: MediaOptions) {\n const error = (key: string) => notify(`chat.call.errors.${key}`);\n\n const localStream = ref<MediaStream | null>(null);\n const screenStream = ref<MediaStream | null>(null);\n /** What each of our streams is, as announced to the others. */\n const trackTypes = ref<TrackTypes>([]);\n const videoTrack = ref<MediaStreamTrack | null>(null);\n const cameras = ref<MediaDeviceInfo[]>([]);\n\n const isAudioOn = ref(true);\n const isVideoOn = ref(true);\n const isScreenSharing = ref(false);\n const isFlashlightOn = ref(false);\n\n const permissions = ref<{\n camera: PermissionState | null;\n microphone: PermissionState | null;\n }>({ camera: null, microphone: null });\n\n const isFlashlightSupported = computed(() => {\n const capabilities = videoTrack.value?.getCapabilities?.() as\n | Record<string, unknown>\n | undefined;\n return !!capabilities && \"torch\" in capabilities;\n });\n\n async function checkPermissions() {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n cameras.value = devices.filter((d) => d.kind === \"videoinput\");\n if (!devices.some((d) => d.kind === \"audioinput\")) {\n error(\"noMicDevice\");\n isAudioOn.value = false;\n }\n if (!cameras.value.length) {\n error(\"noCameraDevice\");\n isVideoOn.value = false;\n }\n\n const query = (name: string) =>\n navigator.permissions.query({ name: name as PermissionName });\n permissions.value.camera = (await query(\"camera\")).state;\n permissions.value.microphone = (await query(\"microphone\")).state;\n if (permissions.value.camera === \"denied\") isVideoOn.value = false;\n if (permissions.value.microphone === \"denied\") isAudioOn.value = false;\n } catch (err) {\n console.error(\"[vue-chat] Error checking permissions:\", err);\n }\n }\n\n /** Gets camera and mic together, or whichever of the two is available. */\n async function start() {\n const { camera, microphone } = permissions.value;\n if (camera === \"denied\" || microphone === \"denied\") {\n if (camera === \"denied\") error(\"cameraDenied\");\n if (microphone === \"denied\") error(\"micDenied\");\n return;\n }\n\n try {\n localStream.value = await navigator.mediaDevices.getUserMedia({\n video: true,\n audio: true,\n });\n trackTypes.value = [\n ...trackTypes.value,\n { id: localStream.value.id, type: \"webcam_audio\" },\n ];\n } catch {\n const tracks: MediaStreamTrack[] = [];\n try {\n const audio = await navigator.mediaDevices.getUserMedia({ audio: true });\n trackTypes.value = [...trackTypes.value, { id: audio.id, type: \"audio\" }];\n tracks.push(...audio.getAudioTracks());\n } catch (err) {\n console.error(\"[vue-chat] Error accessing microphone:\", err);\n error(deviceError(err, \"mic\", isMobile()));\n isAudioOn.value = false;\n }\n try {\n const video = await navigator.mediaDevices.getUserMedia({ video: true });\n trackTypes.value = [...trackTypes.value, { id: video.id, type: \"webcam\" }];\n tracks.push(...video.getVideoTracks());\n } catch (err) {\n console.error(\"[vue-chat] Error accessing camera:\", err);\n error(deviceError(err, \"camera\", isMobile()));\n isVideoOn.value = false;\n }\n if (tracks.length) localStream.value = new MediaStream(tracks);\n }\n\n videoTrack.value = localStream.value?.getVideoTracks()[0] ?? null;\n }\n\n function toggleAudio() {\n isAudioOn.value = !isAudioOn.value;\n const track = localStream.value?.getAudioTracks()[0];\n if (track) track.enabled = isAudioOn.value;\n }\n\n function toggleVideo() {\n isVideoOn.value = !isVideoOn.value;\n const track = localStream.value?.getVideoTracks()[0];\n if (track) track.enabled = isVideoOn.value;\n }\n\n /** Swaps in another camera; returns the new track so peers can be updated. */\n async function switchCamera(deviceId: string) {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n video: { deviceId: { exact: deviceId } },\n audio: isAudioOn.value,\n });\n const track = stream.getVideoTracks()[0];\n if (!track || !localStream.value) return null;\n\n const old = localStream.value.getVideoTracks()[0];\n if (old) {\n localStream.value.removeTrack(old);\n old.stop();\n }\n localStream.value.addTrack(track);\n videoTrack.value = track;\n return track;\n } catch (err) {\n console.error(\"[vue-chat] Error switching camera:\", err);\n return null;\n }\n }\n\n /** Starts sharing the screen; returns the new stream, or null if there is none. */\n async function startScreenShare(onEnded: () => void) {\n if (screenStream.value) {\n // Resume a paused share.\n screenStream.value.getVideoTracks()[0]!.enabled = true;\n isScreenSharing.value = true;\n return null;\n }\n if (!navigator.mediaDevices?.getDisplayMedia) {\n error(\n isMobile()\n ? \"screenShareMobileUnsupported\"\n : \"screenShareBrowserUnsupported\",\n );\n return null;\n }\n\n try {\n const stream = await navigator.mediaDevices.getDisplayMedia({\n video: true,\n audio: false,\n });\n screenStream.value = stream;\n trackTypes.value = [...trackTypes.value, { id: stream.id, type: \"screen\" }];\n stream.getTracks().forEach((track) => (track.onended = onEnded));\n isScreenSharing.value = true;\n return stream;\n } catch (err) {\n console.error(\"[vue-chat] Screen share failed:\", err);\n error(deviceError(err, \"screen\", isMobile()));\n isScreenSharing.value = false;\n return null;\n }\n }\n\n /** Stops sharing; returns the stream that was shared so peers can drop it. */\n function stopScreenShare() {\n const stream = screenStream.value;\n if (!stream) return null;\n stream.getTracks().forEach((track) => {\n track.stop();\n track.enabled = false;\n });\n trackTypes.value = trackTypes.value.filter((t) => t.type !== \"screen\");\n screenStream.value = null;\n isScreenSharing.value = false;\n return stream;\n }\n\n async function toggleFlashlight() {\n const track = videoTrack.value;\n if (!track) {\n error(\"cameraUnavailable\");\n return;\n }\n if (!isFlashlightSupported.value) return;\n try {\n await track.applyConstraints({\n advanced: [{ torch: !isFlashlightOn.value } as MediaTrackConstraintSet],\n });\n isFlashlightOn.value = !isFlashlightOn.value;\n } catch (err) {\n console.error(\"[vue-chat] Flashlight toggle failed:\", err);\n error(\"flashlightToggleFailed\");\n }\n }\n\n /** Releases camera, mic and screen. */\n function stopAll() {\n if (isFlashlightOn.value && videoTrack.value) {\n void videoTrack.value\n .applyConstraints({ advanced: [{ torch: false } as MediaTrackConstraintSet] })\n .catch(() => {});\n }\n for (const stream of [localStream.value, screenStream.value]) {\n stream?.getTracks().forEach((track) => {\n track.stop();\n track.enabled = false;\n });\n }\n localStream.value = null;\n screenStream.value = null;\n videoTrack.value = null;\n }\n\n return {\n localStream,\n screenStream,\n trackTypes,\n videoTrack,\n cameras,\n isAudioOn,\n isVideoOn,\n isScreenSharing,\n isFlashlightOn,\n isFlashlightSupported,\n checkPermissions,\n start,\n toggleAudio,\n toggleVideo,\n switchCamera,\n startScreenShare,\n stopScreenShare,\n toggleFlashlight,\n stopAll,\n };\n}\n","import { computed, effectScope } from \"vue\";\nimport { nanoid } from \"nanoid\";\nimport type { CallHandlers } from \"~/types\";\nimport { createSignaling } from \"./signaling\";\nimport { usePeers } from \"./peers\";\nimport { useCallMedia } from \"./media\";\n\nexport interface CallSessionOptions {\n handlers: CallHandlers;\n channel: string;\n self: { id: string; name: () => string; avatar: () => Blob | undefined };\n /** Shows an error; `key` is a translation key. */\n notify: (key: string) => void;\n}\n\nconst toIceUrl = (url: string) =>\n url.startsWith(\"stun:\") || url.startsWith(\"turn:\") ? url : `turn:${url}`;\n\nconst isMobile = () =>\n /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ||\n !!window.matchMedia?.(\"(max-width: 768px)\").matches;\n\n/**\n * One call, from joining to hanging up: signalling, peer connections and local media. It lives in\n * the call store, not in a component, so the call carries on while no call view is mounted.\n */\nexport function createCallSession(opts: CallSessionOptions) {\n const { handlers, channel } = opts;\n // Watchers inside (e.g. deferred peers) belong to the call, not to whichever component\n // happened to start it.\n const scope = effectScope(true);\n\n const log = handlers.debug\n ? (...args: unknown[]) => console.log(\"[vue-chat:call]\", ...args)\n : () => {};\n\n // A fresh id per call, so rejoining never collides with our previous, stale peers.\n const selfId = `${opts.self.id}:${nanoid()}`;\n\n const session = scope.run(() => {\n const signaling = createSignaling(handlers, { id: selfId, name: opts.self.name });\n const media = useCallMedia({ notify: opts.notify, isMobile });\n const peers = usePeers({\n selfId,\n signaling,\n localStream: media.localStream,\n screenStream: media.screenStream,\n log,\n rtcConfig() {\n const policy = handlers.iceTransportPolicy ?? \"relay\";\n const cred = handlers.credential;\n const iceServers: RTCIceServer[] = cred?.urls?.length\n ? [{ urls: cred.urls.map(toIceUrl), username: cred.user, credential: cred.pass }]\n : [];\n // Relay-only without a relay can never connect.\n if (policy === \"relay\" && !iceServers.length) return null;\n return { iceTransportPolicy: policy, iceServers };\n },\n });\n\n const participantCount = computed(() => Object.keys(peers.peers.value).length + 1);\n const tileCount = computed(\n () =>\n (media.screenStream.value ? 1 : 0) +\n Object.keys(peers.remoteVideos.value).length +\n Object.keys(peers.remoteScreens.value).length,\n );\n\n return { signaling, media, peers, participantCount, tileCount };\n })!;\n const { signaling, media, peers } = session;\n\n let ended = false;\n\n async function ringOtherSide() {\n const blob = opts.self.avatar();\n let avatar: string | undefined;\n if (blob) {\n const bytes = new Uint8Array(await blob.arrayBuffer());\n avatar = `data:${blob.type};base64,${btoa(String.fromCharCode(...bytes))}`;\n }\n await signaling.ring(channel, avatar);\n }\n\n async function start() {\n await signaling.listen({\n onSignal: (from, name, signal) => peers.signal(from, name, signal),\n onJoin(from, name) {\n log(\"join from\", from, name);\n void signaling.trackTypes(media.trackTypes.value);\n const isNew = !peers.has(from);\n peers.add(from, name);\n // Only whoever is already in the call hears a newcomer's join. If the newcomer has to\n // make the offer, it doesn't know about us yet, so announce ourselves back to it.\n if (isNew && !peers.isInitiator(from)) void signaling.join();\n },\n onTrackTypes(from, types) {\n peers.remoteStreamTypes.value[from] = types;\n },\n onHangup(from) {\n log(\"hangup from\", from);\n peers.remove(from);\n },\n });\n\n await handlers.handleGenerateCred();\n await media.checkPermissions();\n await media.start();\n // Hung up while the camera prompt was open.\n if (ended) return media.stopAll();\n log(\"local media ready:\", media.localStream.value?.getTracks().map((t) => t.kind));\n\n void signaling.trackTypes(media.trackTypes.value);\n void ringOtherSide();\n void signaling.join();\n }\n\n function stopScreenShare() {\n const stream = media.stopScreenShare();\n stream?.getVideoTracks().forEach((track) => peers.removeTrackFromAll(track));\n }\n\n async function toggleScreenShare() {\n if (media.isScreenSharing.value) return stopScreenShare();\n const stream = await media.startScreenShare(stopScreenShare);\n if (!stream) return;\n // Tell the others this stream is a screen before it reaches them.\n await signaling.trackTypes(media.trackTypes.value);\n stream.getVideoTracks().forEach((track) => peers.addTrackToAll(track, stream));\n }\n\n /** Swaps cameras; returns the local stream so a view can refresh its preview. */\n async function switchCamera(deviceId: string) {\n const track = await media.switchCamera(deviceId);\n const stream = media.localStream.value;\n if (track && stream) peers.replaceVideoTrack(track, stream);\n return stream;\n }\n\n /** Leaves the call: tells the others, releases camera and mic, closes every connection. */\n function end() {\n if (ended) return;\n ended = true;\n void signaling.hangup(channel);\n signaling.stop();\n peers.destroyAll();\n media.stopAll();\n scope.stop();\n }\n\n return {\n ...session,\n channel,\n start,\n end,\n toggleScreenShare,\n switchCamera,\n };\n}\n\nexport type CallSession = ReturnType<typeof createCallSession>;\n"],"names":["signal","isMobile","signaling","media","peers"],"mappings":"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"}