UNPKG

@livekit/components-core

Version:
1,456 lines (1,423 loc) 79.4 kB
var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it); // src/constants.ts var cssPrefix = "lk"; // src/track-reference/track-reference.types.ts function isTrackReference(trackReference) { if (typeof trackReference === "undefined") { return false; } return isTrackReferenceSubscribed(trackReference) || isTrackReferencePublished(trackReference); } function isTrackReferenceSubscribed(trackReference) { var _a; if (!trackReference) { return false; } return trackReference.hasOwnProperty("participant") && trackReference.hasOwnProperty("source") && trackReference.hasOwnProperty("track") && typeof ((_a = trackReference.publication) == null ? void 0 : _a.track) !== "undefined"; } function isTrackReferencePublished(trackReference) { if (!trackReference) { return false; } return trackReference.hasOwnProperty("participant") && trackReference.hasOwnProperty("source") && trackReference.hasOwnProperty("publication") && typeof trackReference.publication !== "undefined"; } function isTrackReferencePlaceholder(trackReference) { if (!trackReference) { return false; } return trackReference.hasOwnProperty("participant") && trackReference.hasOwnProperty("source") && typeof trackReference.publication === "undefined"; } // src/track-reference/track-reference.utils.ts function getTrackReferenceId(trackReference) { if (typeof trackReference === "string" || typeof trackReference === "number") { return `${trackReference}`; } else if (isTrackReferencePlaceholder(trackReference)) { return `${trackReference.participant.identity}_${trackReference.source}_placeholder`; } else if (isTrackReference(trackReference)) { return `${trackReference.participant.identity}_${trackReference.publication.source}_${trackReference.publication.trackSid}`; } else { throw new Error(`Can't generate a id for the given track reference: ${trackReference}`); } } function getTrackReferenceSource(trackReference) { if (isTrackReference(trackReference)) { return trackReference.publication.source; } else { return trackReference.source; } } function isEqualTrackRef(a, b) { if (a === void 0 || b === void 0) { return false; } if (isTrackReference(a) && isTrackReference(b)) { return a.publication.trackSid === b.publication.trackSid; } else { return getTrackReferenceId(a) === getTrackReferenceId(b); } } function isTrackReferencePinned(trackReference, pinState) { if (typeof pinState === "undefined") { return false; } if (isTrackReference(trackReference)) { return pinState.some( (pinnedTrackReference) => pinnedTrackReference.participant.identity === trackReference.participant.identity && isTrackReference(pinnedTrackReference) && pinnedTrackReference.publication.trackSid === trackReference.publication.trackSid ); } else if (isTrackReferencePlaceholder(trackReference)) { return pinState.some( (pinnedTrackReference) => pinnedTrackReference.participant.identity === trackReference.participant.identity && isTrackReferencePlaceholder(pinnedTrackReference) && pinnedTrackReference.source === trackReference.source ); } else { return false; } } function isPlaceholderReplacement(currentTrackRef, nextTrackRef) { return isTrackReferencePlaceholder(currentTrackRef) && isTrackReference(nextTrackRef) && nextTrackRef.participant.identity === currentTrackRef.participant.identity && nextTrackRef.source === currentTrackRef.source; } // src/utils.ts function isLocal(p) { return p.isLocal; } function isRemote(p) { return !p.isLocal; } var attachIfSubscribed = (publication, element) => { if (!publication) return; const { isSubscribed, track } = publication; if (element && track) { if (isSubscribed) { track.attach(element); } else { track.detach(element); } } }; function isParticipantTrackReferencePinned(trackRef, pinState) { if (pinState === void 0) { return false; } return pinState.some((pinnedTrackRef) => isEqualTrackRef(pinnedTrackRef, trackRef)); } function getScrollBarWidth() { const inner = document.createElement("p"); inner.style.width = "100%"; inner.style.height = "200px"; const outer = document.createElement("div"); outer.style.position = "absolute"; outer.style.top = "0px"; outer.style.left = "0px"; outer.style.visibility = "hidden"; outer.style.width = "200px"; outer.style.height = "150px"; outer.style.overflow = "hidden"; outer.appendChild(inner); document.body.appendChild(outer); const w1 = inner.offsetWidth; outer.style.overflow = "scroll"; let w2 = inner.offsetWidth; if (w1 === w2) { w2 = outer.clientWidth; } document.body.removeChild(outer); const scrollBarWidth = w1 - w2; return scrollBarWidth; } // src/helper/detectMobileBrowser.ts function isWeb() { return typeof document !== "undefined"; } function isMobileBrowser() { return isWeb() ? /Mobi/i.test(window.navigator.userAgent) : false; } // src/helper/url-regex.ts function createUrlRegExp(options) { options = __spreadValues({}, options); const protocol = `(?:(?:[a-z]+:)?//)?`; const auth = "(?:\\S+(?::\\S*)?@)?"; const ip = new RegExp( "(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}", "g" ).source; const host = "(?:(?:[a-z\\u00a1-\\uffff0-9][-_]*)*[a-z\\u00a1-\\uffff0-9]+)"; const domain = "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*"; const tld = `(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))\\.?`; const port = "(?::\\d{2,5})?"; const path = '(?:[/?#][^\\s"]*)?'; const regex2 = `(?:${protocol}|www\\.)${auth}(?:localhost|${ip}|${host}${domain}${tld})${port}${path}`; return options.exact ? new RegExp(`(?:^${regex2}$)`, "i") : new RegExp(regex2, "ig"); } // src/helper/emailRegex.ts var regex = "[^\\.\\s@:](?:[^\\s@:]*[^\\s@:\\.])?@[^\\.\\s@]+(?:\\.[^\\.\\s@]+)*"; function createEmailRegExp({ exact } = {}) { return exact ? new RegExp(`^${regex}$`) : new RegExp(regex, "g"); } // src/helper/floating-menu.ts import { autoUpdate, computePosition, flip, offset, shift } from "@floating-ui/dom"; function computeMenuPosition(button, menu, onUpdate) { const cleanup = autoUpdate(button, menu, () => __async(null, null, function* () { const { x, y } = yield computePosition(button, menu, { placement: "top", middleware: [offset(6), flip(), shift({ padding: 5 })] }); onUpdate == null ? void 0 : onUpdate(x, y); })); return cleanup; } function wasClickOutside(insideElement, event) { const isOutside = !insideElement.contains(event.target); return isOutside; } // src/helper/tokenizer.ts var createDefaultGrammar = () => { return { email: createEmailRegExp(), url: createUrlRegExp({}) }; }; function tokenize(input, grammar) { const matches = Object.entries(grammar).map( ([type, rx], weight) => Array.from(input.matchAll(rx)).map(({ index, 0: content }) => ({ type, weight, content, index: index != null ? index : 0 })) ).flat().sort((a, b) => { const d = a.index - b.index; return d !== 0 ? d : a.weight - b.weight; }).filter(({ index }, i, arr) => { if (i === 0) return true; const prev = arr[i - 1]; return prev.index + prev.content.length <= index; }); const tokens = []; let pos = 0; for (const { type, content, index } of matches) { if (index > pos) tokens.push(input.substring(pos, index)); tokens.push({ type, content }); pos = index + content.length; } if (input.length > pos) tokens.push(input.substring(pos)); return tokens; } // src/helper/eventGroups.ts import { ParticipantEvent, RoomEvent } from "livekit-client"; var allRemoteParticipantRoomEvents = [ RoomEvent.ConnectionStateChanged, RoomEvent.RoomMetadataChanged, RoomEvent.ActiveSpeakersChanged, RoomEvent.ConnectionQualityChanged, RoomEvent.ParticipantConnected, RoomEvent.ParticipantDisconnected, RoomEvent.ParticipantPermissionsChanged, RoomEvent.ParticipantMetadataChanged, RoomEvent.ParticipantNameChanged, RoomEvent.ParticipantAttributesChanged, RoomEvent.TrackMuted, RoomEvent.TrackUnmuted, RoomEvent.TrackPublished, RoomEvent.TrackUnpublished, RoomEvent.TrackStreamStateChanged, RoomEvent.TrackSubscriptionFailed, RoomEvent.TrackSubscriptionPermissionChanged, RoomEvent.TrackSubscriptionStatusChanged ]; var allParticipantRoomEvents = [ ...allRemoteParticipantRoomEvents, RoomEvent.LocalTrackPublished, RoomEvent.LocalTrackUnpublished ]; var participantTrackEvents = [ ParticipantEvent.TrackPublished, ParticipantEvent.TrackUnpublished, ParticipantEvent.TrackMuted, ParticipantEvent.TrackUnmuted, ParticipantEvent.TrackStreamStateChanged, ParticipantEvent.TrackSubscribed, ParticipantEvent.TrackUnsubscribed, ParticipantEvent.TrackSubscriptionPermissionChanged, ParticipantEvent.TrackSubscriptionFailed, ParticipantEvent.LocalTrackPublished, ParticipantEvent.LocalTrackUnpublished ]; var allRemoteParticipantEvents = [ ParticipantEvent.ConnectionQualityChanged, ParticipantEvent.IsSpeakingChanged, ParticipantEvent.ParticipantMetadataChanged, ParticipantEvent.ParticipantPermissionsChanged, ParticipantEvent.TrackMuted, ParticipantEvent.TrackUnmuted, ParticipantEvent.TrackPublished, ParticipantEvent.TrackUnpublished, ParticipantEvent.TrackStreamStateChanged, ParticipantEvent.TrackSubscriptionFailed, ParticipantEvent.TrackSubscriptionPermissionChanged, ParticipantEvent.TrackSubscriptionStatusChanged ]; var allParticipantEvents = [ ...allRemoteParticipantEvents, ParticipantEvent.LocalTrackPublished, ParticipantEvent.LocalTrackUnpublished ]; // src/logger.ts import { setLogLevel as setClientSdkLogLevel, setLogExtension as setClientSdkLogExtension, LogLevel as LogLevelEnum } from "livekit-client"; import loglevel from "loglevel"; var log = loglevel.getLogger("lk-components-js"); log.setDefaultLevel("WARN"); function setLogLevel(level, options = {}) { var _a; log.setLevel(level); setClientSdkLogLevel((_a = options.liveKitClientLogLevel) != null ? _a : level); } function setLogExtension(extension, options = {}) { var _a; const originalFactory = log.methodFactory; log.methodFactory = (methodName, configLevel, loggerName) => { const rawMethod = originalFactory(methodName, configLevel, loggerName); const logLevel = LogLevelEnum[methodName]; const needLog = logLevel >= configLevel && logLevel < LogLevelEnum.silent; return (msg, context) => { if (context) rawMethod(msg, context); else rawMethod(msg); if (needLog) { extension(logLevel, msg, context); } }; }; log.setLevel(log.getLevel()); setClientSdkLogExtension((_a = options.liveKitClientLogExtension) != null ? _a : extension); } // src/helper/grid-layouts.ts var GRID_LAYOUTS = [ { columns: 1, rows: 1 }, { columns: 1, rows: 2, orientation: "portrait" }, { columns: 2, rows: 1, orientation: "landscape" }, { columns: 2, rows: 2, minWidth: 560 }, { columns: 3, rows: 3, minWidth: 700 }, { columns: 4, rows: 4, minWidth: 960 }, { columns: 5, rows: 5, minWidth: 1100 } ]; function selectGridLayout(layoutDefinitions, participantCount, width, height) { if (layoutDefinitions.length < 1) { throw new Error("At least one grid layout definition must be provided."); } const layouts = expandAndSortLayoutDefinitions(layoutDefinitions); if (width <= 0 || height <= 0) { return layouts[0]; } let currentLayoutIndex = 0; const containerOrientation = width / height > 1 ? "landscape" : "portrait"; let layout = layouts.find((layout_, index, allLayouts) => { currentLayoutIndex = index; const isBiggerLayoutAvailable = allLayouts.findIndex((l, i) => { const fitsOrientation = !l.orientation || l.orientation === containerOrientation; const layoutIsBiggerThanCurrent = i > index; const layoutFitsSameAmountOfParticipants = l.maxTiles === layout_.maxTiles; return layoutIsBiggerThanCurrent && layoutFitsSameAmountOfParticipants && fitsOrientation; }) !== -1; return layout_.maxTiles >= participantCount && !isBiggerLayoutAvailable; }); if (layout === void 0) { layout = layouts[layouts.length - 1]; if (layout) { log.warn( `No layout found for: participantCount: ${participantCount}, width/height: ${width}/${height} fallback to biggest available layout (${layout}).` ); } else { throw new Error(`No layout or fallback layout found.`); } } if (width < layout.minWidth || height < layout.minHeight) { if (currentLayoutIndex > 0) { const smallerLayout = layouts[currentLayoutIndex - 1]; layout = selectGridLayout( layouts.slice(0, currentLayoutIndex), smallerLayout.maxTiles, width, height ); } } return layout; } function expandAndSortLayoutDefinitions(layouts) { return [...layouts].map((layout) => { var _a, _b; return { name: `${layout.columns}x${layout.rows}`, columns: layout.columns, rows: layout.rows, maxTiles: layout.columns * layout.rows, minWidth: (_a = layout.minWidth) != null ? _a : 0, minHeight: (_b = layout.minHeight) != null ? _b : 0, orientation: layout.orientation }; }).sort((a, b) => { if (a.maxTiles !== b.maxTiles) { return a.maxTiles - b.maxTiles; } else if (a.minWidth !== 0 || b.minWidth !== 0) { return a.minWidth - b.minWidth; } else if (a.minHeight !== 0 || b.minHeight !== 0) { return a.minHeight - b.minHeight; } else { return 0; } }); } // src/helper/set-helper.ts function setDifference(setA, setB) { const _difference = new Set(setA); for (const elem of setB) { _difference.delete(elem); } return _difference; } // src/helper/featureDetection.ts function supportsScreenSharing() { return typeof navigator !== "undefined" && navigator.mediaDevices && !!navigator.mediaDevices.getDisplayMedia; } // src/helper/transcriptions.ts function getActiveTranscriptionSegments(segments, syncTimes, maxAge = 0) { return segments.filter((segment) => { var _a; const hasTrackSync = !!syncTimes.rtpTimestamp; const currentTrackTime = (_a = syncTimes.rtpTimestamp) != null ? _a : performance.timeOrigin + performance.now(); const displayStartTime = hasTrackSync ? Math.max(segment.receivedAtMediaTimestamp, segment.startTime) : segment.receivedAt; const segmentDuration = maxAge + segment.endTime - segment.startTime; return currentTrackTime >= displayStartTime && currentTrackTime <= displayStartTime + segmentDuration; }); } function addMediaTimestampToTranscription(segment, timestamps) { var _a; return __spreadProps(__spreadValues({}, segment), { receivedAtMediaTimestamp: (_a = timestamps.rtpTimestamp) != null ? _a : 0, receivedAt: timestamps.timestamp }); } function dedupeSegments(prevSegments, newSegments, windowSize) { return [...prevSegments, ...newSegments].reduceRight((acc, segment) => { if (!acc.find((val) => val.id === segment.id)) { acc.unshift(segment); } return acc; }, []).slice(0 - windowSize); } function didActiveSegmentsChange(prevActive, newActive) { if (newActive.length !== prevActive.length) { return true; } return !newActive.every((newSegment) => { return prevActive.find( (prevSegment) => prevSegment.id === newSegment.id && prevSegment.text === newSegment.text && prevSegment.final === newSegment.final && prevSegment.language === newSegment.language && prevSegment.startTime === newSegment.startTime && prevSegment.endTime === newSegment.endTime ); }); } // src/helper/participant-attributes.ts var ParticipantAgentAttributes = /* @__PURE__ */ ((ParticipantAgentAttributes2) => { ParticipantAgentAttributes2["AgentState"] = "lk.agent.state"; ParticipantAgentAttributes2["PublishOnBehalf"] = "lk.publish_on_behalf"; ParticipantAgentAttributes2["TranscriptionFinal"] = "lk.transcription_final"; ParticipantAgentAttributes2["TranscriptionSegmentId"] = "lk.segment_id"; ParticipantAgentAttributes2["TranscribedTrackId"] = "lk.transcribed_track_id"; ParticipantAgentAttributes2["Expression"] = "lk.expression"; return ParticipantAgentAttributes2; })(ParticipantAgentAttributes || {}); // src/types.ts var PIN_DEFAULT_STATE = []; var WIDGET_DEFAULT_STATE = { showChat: false, unreadMessages: 0, showSettings: false }; function isSourceWitOptions(source) { return typeof source === "object"; } function isSourcesWithOptions(sources) { return Array.isArray(sources) && sources.filter(isSourceWitOptions).length > 0; } // src/sorting/sort-track-bundles.ts import { Track as Track2 } from "livekit-client"; // src/sorting/base-sort-functions.ts import { Track } from "livekit-client"; function sortParticipantsByAudioLevel(a, b) { return b.audioLevel - a.audioLevel; } function sortParticipantsByIsSpeaking(a, b) { if (a.isSpeaking === b.isSpeaking) { return 0; } else { return a.isSpeaking ? -1 : 1; } } function sortParticipantsByLastSpokenAT(a, b) { var _a, _b, _c, _d; if (a.lastSpokeAt !== void 0 || b.lastSpokeAt !== void 0) { return ((_b = (_a = b.lastSpokeAt) == null ? void 0 : _a.getTime()) != null ? _b : 0) - ((_d = (_c = a.lastSpokeAt) == null ? void 0 : _c.getTime()) != null ? _d : 0); } else { return 0; } } function sortParticipantsByJoinedAt(a, b) { var _a, _b, _c, _d; return ((_b = (_a = a.joinedAt) == null ? void 0 : _a.getTime()) != null ? _b : 0) - ((_d = (_c = b.joinedAt) == null ? void 0 : _c.getTime()) != null ? _d : 0); } function sortTrackReferencesByType(a, b) { if (isTrackReference(a)) { if (isTrackReference(b)) { return 0; } else { return -1; } } else if (isTrackReference(b)) { return 1; } else { return 0; } } function sortTrackRefsByIsCameraEnabled(a, b) { const aVideo = a.participant.isCameraEnabled; const bVideo = b.participant.isCameraEnabled; if (aVideo !== bVideo) { if (aVideo) { return -1; } else { return 1; } } else { return 0; } } // src/sorting/sort-track-bundles.ts function sortTrackReferences(tracks) { const localTracks = []; const screenShareTracks = []; const cameraTracks = []; const undefinedTracks = []; tracks.forEach((trackRef) => { if (trackRef.participant.isLocal && trackRef.source === Track2.Source.Camera) { localTracks.push(trackRef); } else if (trackRef.source === Track2.Source.ScreenShare) { screenShareTracks.push(trackRef); } else if (trackRef.source === Track2.Source.Camera) { cameraTracks.push(trackRef); } else { undefinedTracks.push(trackRef); } }); const sortedScreenShareTracks = sortScreenShareTracks(screenShareTracks); const sortedCameraTracks = sortCameraTracks(cameraTracks); return [...localTracks, ...sortedScreenShareTracks, ...sortedCameraTracks, ...undefinedTracks]; } function sortScreenShareTracks(screenShareTracks) { const localScreenShares = []; const remoteScreenShares = []; screenShareTracks.forEach((trackRef) => { if (trackRef.participant.isLocal) { localScreenShares.push(trackRef); } else { remoteScreenShares.push(trackRef); } }); localScreenShares.sort((a, b) => sortParticipantsByJoinedAt(a.participant, b.participant)); remoteScreenShares.sort((a, b) => sortParticipantsByJoinedAt(a.participant, b.participant)); const sortedScreenShareTrackRefs = [...remoteScreenShares, ...localScreenShares]; return sortedScreenShareTrackRefs; } function sortCameraTracks(cameraTrackReferences) { const localCameraTracks = []; const remoteCameraTracks = []; cameraTrackReferences.forEach((trackRef) => { if (trackRef.participant.isLocal) { localCameraTracks.push(trackRef); } else { remoteCameraTracks.push(trackRef); } }); remoteCameraTracks.sort((a, b) => { if (a.participant.isSpeaking && b.participant.isSpeaking) { return sortParticipantsByAudioLevel(a.participant, b.participant); } if (a.participant.isSpeaking !== b.participant.isSpeaking) { return sortParticipantsByIsSpeaking(a.participant, b.participant); } if (a.participant.lastSpokeAt !== b.participant.lastSpokeAt) { return sortParticipantsByLastSpokenAT(a.participant, b.participant); } if (isTrackReference(a) !== isTrackReference(b)) { return sortTrackReferencesByType(a, b); } if (a.participant.isCameraEnabled !== b.participant.isCameraEnabled) { return sortTrackRefsByIsCameraEnabled(a, b); } return sortParticipantsByJoinedAt(a.participant, b.participant); }); return [...localCameraTracks, ...remoteCameraTracks]; } // src/sorting/sort-participants.ts function sortParticipants(participants) { const sortedParticipants = [...participants]; sortedParticipants.sort((a, b) => { if (a.isSpeaking && b.isSpeaking) { return sortParticipantsByAudioLevel(a, b); } if (a.isSpeaking !== b.isSpeaking) { return sortParticipantsByIsSpeaking(a, b); } if (a.lastSpokeAt !== b.lastSpokeAt) { return sortParticipantsByLastSpokenAT(a, b); } const aVideo = a.videoTrackPublications.size > 0; const bVideo = b.videoTrackPublications.size > 0; if (aVideo !== bVideo) { if (aVideo) { return -1; } else { return 1; } } return sortParticipantsByJoinedAt(a, b); }); const localParticipant = sortedParticipants.find((p) => p.isLocal); if (localParticipant) { const localIdx = sortedParticipants.indexOf(localParticipant); if (localIdx >= 0) { sortedParticipants.splice(localIdx, 1); if (sortedParticipants.length > 0) { sortedParticipants.splice(0, 0, localParticipant); } else { sortedParticipants.push(localParticipant); } } } return sortedParticipants; } // src/helper/array-helper.ts function chunk(input, size) { return input.reduce( (arr, item, idx) => { return idx % size === 0 ? [...arr, [item]] : [...arr.slice(0, -1), [...arr.slice(-1)[0], item]]; }, [] ); } function zip(a1, a2) { const resultLength = Math.max(a1.length, a2.length); return new Array(resultLength).fill([]).map((_val, idx) => [a1[idx], a2[idx]]); } function differenceBy(a1, a2, by) { return a1.filter((c) => !a2.map((v) => by(v)).includes(by(c))); } // src/track-reference/test-utils.ts import { Participant, RemoteTrackPublication, Track as Track3 } from "livekit-client"; import { TrackInfo } from "@livekit/protocol"; function flatTrackReferenceArray(list) { return list.map((item) => { if (typeof item === "string" || typeof item === "number") { return `${item}`; } else { return getTrackReferenceId(item); } }); } // src/sorting/tile-array-update.ts function visualPageChange(state, next) { return { dropped: differenceBy(state, next, getTrackReferenceId), added: differenceBy(next, state, getTrackReferenceId) }; } function listNeedsUpdating(changes) { return changes.added.length !== 0 || changes.dropped.length !== 0; } function findIndex(trackReference, trackReferences) { const indexToReplace = trackReferences.findIndex( (trackReference_) => getTrackReferenceId(trackReference_) === getTrackReferenceId(trackReference) ); if (indexToReplace === -1) { throw new Error( `Element not part of the array: ${getTrackReferenceId( trackReference )} not in ${flatTrackReferenceArray(trackReferences)}` ); } return indexToReplace; } function swapItems(moveForward, moveBack, trackReferences) { const indexToReplace = findIndex(moveForward, trackReferences); const indexReplaceWith = findIndex(moveBack, trackReferences); trackReferences.splice(indexToReplace, 1, moveBack); trackReferences.splice(indexReplaceWith, 1, moveForward); return trackReferences; } function dropItem(itemToDrop, list) { const indexOfElementToDrop = findIndex(itemToDrop, list); list.splice(indexOfElementToDrop, 1); return list; } function addItem(itemToAdd, list) { return [...list, itemToAdd]; } function divideIntoPages(list, maxElementsOnPage) { const pages = chunk(list, maxElementsOnPage); return pages; } function updatePages(currentList, nextList, maxItemsOnPage) { let updatedList = refreshList(currentList, nextList); if (updatedList.length < nextList.length) { const addedItems = differenceBy(nextList, updatedList, getTrackReferenceId); updatedList = [...updatedList, ...addedItems]; } const currentPages = divideIntoPages(updatedList, maxItemsOnPage); const nextPages = divideIntoPages(nextList, maxItemsOnPage); zip(currentPages, nextPages).forEach(([currentPage, nextPage], pageIndex) => { if (currentPage && nextPage) { const updatedPage = divideIntoPages(updatedList, maxItemsOnPage)[pageIndex]; const changes = visualPageChange(updatedPage, nextPage); if (listNeedsUpdating(changes)) { log.debug( `Detected visual changes on page: ${pageIndex}, current: ${flatTrackReferenceArray( currentPage )}, next: ${flatTrackReferenceArray(nextPage)}`, { changes } ); if (changes.added.length === changes.dropped.length) { zip(changes.added, changes.dropped).forEach(([added, dropped]) => { if (added && dropped) { updatedList = swapItems(added, dropped, updatedList); } else { throw new Error( `For a swap action we need a addition and a removal one is missing: ${added}, ${dropped}` ); } }); } if (changes.added.length === 0 && changes.dropped.length > 0) { changes.dropped.forEach((item) => { updatedList = dropItem(item, updatedList); }); } if (changes.added.length > 0 && changes.dropped.length === 0) { changes.added.forEach((item) => { updatedList = addItem(item, updatedList); }); } } } }); if (updatedList.length > nextList.length) { const missingItems = differenceBy(updatedList, nextList, getTrackReferenceId); updatedList = updatedList.filter( (item) => !missingItems.map(getTrackReferenceId).includes(getTrackReferenceId(item)) ); } return updatedList; } function refreshList(currentList, nextList) { return currentList.map((currentItem) => { const updateForCurrentItem = nextList.find( (newItem_) => ( // If the IDs match or .. getTrackReferenceId(currentItem) === getTrackReferenceId(newItem_) || // ... if the current item is a placeholder and the new item is the track reference can replace it. typeof currentItem !== "number" && isTrackReferencePlaceholder(currentItem) && isTrackReference(newItem_) && isPlaceholderReplacement(currentItem, newItem_) ) ); return updateForCurrentItem != null ? updateForCurrentItem : currentItem; }); } // src/components/mediaToggle.ts import { Track as Track7 } from "livekit-client"; import { Subject, map as map4, startWith as startWith4 } from "rxjs"; // src/observables/participant.ts import { ParticipantEvent as ParticipantEvent2, RoomEvent as RoomEvent3, Track as Track6 } from "livekit-client"; import { Observable as Observable2, map as map3, startWith as startWith3, switchMap } from "rxjs"; // src/components/mediaTrack.ts import { Track as Track4 } from "livekit-client"; import { map, startWith } from "rxjs"; // src/styles-interface/class-prefixer.ts function prefixClass(unprefixedClassName) { return `${cssPrefix}-${unprefixedClassName}`; } // src/components/mediaTrack.ts function setupMediaTrack(trackIdentifier) { const initialPub = getTrackByIdentifier(trackIdentifier); const trackObserver = observeParticipantMedia(trackIdentifier.participant).pipe( map(() => { return getTrackByIdentifier(trackIdentifier); }), startWith(initialPub) ); const className = prefixClass( trackIdentifier.source === Track4.Source.Camera || trackIdentifier.source === Track4.Source.ScreenShare ? "participant-media-video" : "participant-media-audio" ); return { className, trackObserver }; } function getTrackByIdentifier(options) { if (isTrackReference(options)) { return options.publication; } else { const { source, name, participant } = options; if (source && name) { return participant.getTrackPublications().find((pub) => pub.source === source && pub.trackName === name); } else if (name) { return participant.getTrackPublicationByName(name); } else if (source) { return participant.getTrackPublication(source); } else { throw new Error("At least one of source and name needs to be defined"); } } } // src/observables/room.ts import { Room, RoomEvent as RoomEvent2, Track as Track5 } from "livekit-client"; import { concat, filter, map as map2, Observable, startWith as startWith2 } from "rxjs"; function observeRoomEvents(room, ...events) { const observable = new Observable((subscribe) => { const onRoomUpdate = () => { subscribe.next(room); }; events.forEach((evt) => { room.on(evt, onRoomUpdate); }); const unsubscribe = () => { events.forEach((evt) => { room.off(evt, onRoomUpdate); }); }; return unsubscribe; }).pipe(startWith2(room)); return observable; } function roomEventSelector(room, event) { const observable = new Observable((subscribe) => { const update = (...params) => { subscribe.next(params); }; room.on(event, update); const unsubscribe = () => { room.off(event, update); }; return unsubscribe; }); return observable; } function roomObserver(room) { const observable = observeRoomEvents( room, RoomEvent2.ParticipantConnected, RoomEvent2.ParticipantDisconnected, RoomEvent2.ActiveSpeakersChanged, RoomEvent2.TrackSubscribed, RoomEvent2.TrackUnsubscribed, RoomEvent2.LocalTrackPublished, RoomEvent2.LocalTrackUnpublished, RoomEvent2.AudioPlaybackStatusChanged, RoomEvent2.ConnectionStateChanged ); return observable; } function connectionStateObserver(room) { return roomEventSelector(room, RoomEvent2.ConnectionStateChanged).pipe( map2(([connectionState]) => connectionState), startWith2(room.state) ); } function screenShareObserver(room) { let screenShareSubscriber; const observers = []; const observable = new Observable((subscriber) => { screenShareSubscriber = subscriber; return () => { observers.forEach((observer) => { observer.unsubscribe(); }); }; }); const screenShareTracks = []; const handleSub = (publication, participant) => { if (publication.source !== Track5.Source.ScreenShare && publication.source !== Track5.Source.ScreenShareAudio) { return; } let trackMap = screenShareTracks.find((tr) => tr.participant.identity === participant.identity); const getScreenShareTracks = (participant2) => { return participant2.getTrackPublications().filter( (track) => (track.source === Track5.Source.ScreenShare || track.source === Track5.Source.ScreenShareAudio) && track.track ); }; if (!trackMap) { trackMap = { participant, tracks: getScreenShareTracks(participant) }; } else { const index = screenShareTracks.indexOf(trackMap); screenShareTracks.splice(index, 1); trackMap.tracks = getScreenShareTracks(participant); } if (trackMap.tracks.length > 0) { screenShareTracks.push(trackMap); } screenShareSubscriber.next(screenShareTracks); }; observers.push( roomEventSelector(room, RoomEvent2.TrackSubscribed).subscribe( ([, ...args]) => handleSub(...args) ) ); observers.push( roomEventSelector(room, RoomEvent2.TrackUnsubscribed).subscribe( ([, ...args]) => handleSub(...args) ) ); observers.push( roomEventSelector(room, RoomEvent2.LocalTrackPublished).subscribe((args) => handleSub(...args)) ); observers.push( roomEventSelector(room, RoomEvent2.LocalTrackUnpublished).subscribe((args) => { handleSub(...args); }) ); observers.push( roomEventSelector(room, RoomEvent2.TrackMuted).subscribe((args) => { handleSub(...args); }) ); observers.push( roomEventSelector(room, RoomEvent2.TrackUnmuted).subscribe((args) => { handleSub(...args); }) ); setTimeout(() => { for (const p of room.remoteParticipants.values()) { p.getTrackPublications().forEach((track) => { handleSub(track, p); }); } }, 1); return observable; } function roomInfoObserver(room) { const observer = observeRoomEvents( room, RoomEvent2.RoomMetadataChanged, RoomEvent2.ConnectionStateChanged ).pipe( map2((r) => { return { name: r.name, metadata: r.metadata }; }) ); return observer; } function activeSpeakerObserver(room) { return roomEventSelector(room, RoomEvent2.ActiveSpeakersChanged).pipe( map2(([speakers]) => speakers) ); } function createMediaDeviceObserver(kind, onError, requestPermissions = true) { const initialDevices$ = new Observable((subscriber) => { Room.getLocalDevices(kind, requestPermissions).then((devices) => { subscriber.next(devices); subscriber.complete(); }).catch((e) => { onError == null ? void 0 : onError(e); subscriber.next([]); subscriber.complete(); }); }); const deviceChanges$ = new Observable((subscriber) => { var _a; const onDeviceChange = () => __async(null, null, function* () { try { const newDevices = yield Room.getLocalDevices(kind, requestPermissions); subscriber.next(newDevices); } catch (e) { onError == null ? void 0 : onError(e); } }); if (typeof window !== "undefined") { if (!window.isSecureContext) { throw new Error( `Accessing media devices is available only in secure contexts (HTTPS and localhost), in some or all supporting browsers. See: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/mediaDevices` ); } (_a = navigator == null ? void 0 : navigator.mediaDevices) == null ? void 0 : _a.addEventListener("devicechange", onDeviceChange); } return () => { var _a2; (_a2 = navigator == null ? void 0 : navigator.mediaDevices) == null ? void 0 : _a2.removeEventListener("devicechange", onDeviceChange); }; }); return concat(initialDevices$, deviceChanges$); } function createDataObserver(room) { return roomEventSelector(room, RoomEvent2.DataReceived); } function createChatObserver(room) { return roomEventSelector(room, RoomEvent2.ChatMessage); } function roomAudioPlaybackAllowedObservable(room) { const observable = observeRoomEvents(room, RoomEvent2.AudioPlaybackStatusChanged).pipe( map2((room2) => { return { canPlayAudio: room2.canPlaybackAudio }; }) ); return observable; } function roomVideoPlaybackAllowedObservable(room) { const observable = observeRoomEvents(room, RoomEvent2.VideoPlaybackStatusChanged).pipe( map2((room2) => { return { canPlayVideo: room2.canPlaybackVideo }; }) ); return observable; } function createActiveDeviceObservable(room, kind) { return roomEventSelector(room, RoomEvent2.ActiveDeviceChanged).pipe( filter(([kindOfDevice]) => kindOfDevice === kind), map2(([kind2, deviceId]) => { log.debug("activeDeviceObservable | RoomEvent.ActiveDeviceChanged", { kind: kind2, deviceId }); return deviceId; }) ); } function encryptionStatusObservable(room, participant) { return roomEventSelector(room, RoomEvent2.ParticipantEncryptionStatusChanged).pipe( filter( ([, p]) => (participant == null ? void 0 : participant.identity) === (p == null ? void 0 : p.identity) || !p && (participant == null ? void 0 : participant.identity) === room.localParticipant.identity ), map2(([encrypted]) => encrypted), startWith2( (participant == null ? void 0 : participant.isLocal) ? participant.isE2EEEnabled : !!(participant == null ? void 0 : participant.isEncrypted) ) ); } function recordingStatusObservable(room) { return roomEventSelector(room, RoomEvent2.RecordingStatusChanged).pipe( map2(([recording]) => recording), startWith2(room.isRecording) ); } // src/observables/participant.ts function observeParticipantEvents(participant, ...events) { const observable = new Observable2((subscribe) => { const onParticipantUpdate = () => { subscribe.next(participant); }; events.forEach((evt) => { participant.on(evt, onParticipantUpdate); }); const unsubscribe = () => { events.forEach((evt) => { participant.off(evt, onParticipantUpdate); }); }; return unsubscribe; }).pipe(startWith3(participant)); return observable; } function observeParticipantMedia(participant) { const participantObserver = observeParticipantEvents( participant, ParticipantEvent2.TrackMuted, ParticipantEvent2.TrackUnmuted, ParticipantEvent2.ParticipantPermissionsChanged, // ParticipantEvent.IsSpeakingChanged, ParticipantEvent2.TrackPublished, ParticipantEvent2.TrackUnpublished, ParticipantEvent2.LocalTrackPublished, ParticipantEvent2.LocalTrackUnpublished, ParticipantEvent2.MediaDevicesError, ParticipantEvent2.TrackSubscriptionStatusChanged // ParticipantEvent.ConnectionQualityChanged, ).pipe( map3((p) => { const { isMicrophoneEnabled, isCameraEnabled, isScreenShareEnabled } = p; const microphoneTrack = p.getTrackPublication(Track6.Source.Microphone); const cameraTrack = p.getTrackPublication(Track6.Source.Camera); const participantMedia = { isCameraEnabled, isMicrophoneEnabled, isScreenShareEnabled, cameraTrack, microphoneTrack, participant: p }; return participantMedia; }) ); return participantObserver; } function createTrackObserver(participant, options) { return observeParticipantMedia(participant).pipe( map3(() => { return { publication: getTrackByIdentifier(options) }; }) ); } function participantInfoObserver(participant) { if (!participant) { return void 0; } const observer = observeParticipantEvents( participant, ParticipantEvent2.ParticipantMetadataChanged, ParticipantEvent2.ParticipantNameChanged ).pipe( map3(({ name, identity, metadata }) => { return { name, identity, metadata }; }), startWith3({ name: participant.name, identity: participant.identity, metadata: participant.metadata }) ); return observer; } function createConnectionQualityObserver(participant) { const observer = participantEventSelector( participant, ParticipantEvent2.ConnectionQualityChanged ).pipe( map3(([quality]) => quality), startWith3(participant.connectionQuality) ); return observer; } function participantEventSelector(participant, event) { const observable = new Observable2((subscribe) => { const update = (...params) => { subscribe.next(params); }; participant.on(event, update); const unsubscribe = () => { participant.off(event, update); }; return unsubscribe; }); return observable; } function mutedObserver(trackRef) { var _a, _b, _c, _d; return observeParticipantEvents( trackRef.participant, ParticipantEvent2.TrackMuted, ParticipantEvent2.TrackUnmuted, ParticipantEvent2.TrackSubscribed, ParticipantEvent2.TrackUnsubscribed, ParticipantEvent2.LocalTrackPublished, ParticipantEvent2.LocalTrackUnpublished ).pipe( map3((participant) => { var _a2, _b2; const pub = (_a2 = trackRef.publication) != null ? _a2 : participant.getTrackPublication(trackRef.source); return (_b2 = pub == null ? void 0 : pub.isMuted) != null ? _b2 : true; }), startWith3( (_d = (_c = (_a = trackRef.publication) == null ? void 0 : _a.isMuted) != null ? _c : (_b = trackRef.participant.getTrackPublication(trackRef.source)) == null ? void 0 : _b.isMuted) != null ? _d : true ) ); } function createIsSpeakingObserver(participant) { return participantEventSelector(participant, ParticipantEvent2.IsSpeakingChanged).pipe( map3(([isSpeaking]) => isSpeaking) ); } function connectedParticipantsObserver(room, options = {}) { var _a; let subscriber; const observable = new Observable2((sub) => { subscriber = sub; return () => listener.unsubscribe(); }).pipe(startWith3(Array.from(room.remoteParticipants.values()))); const additionalRoomEvents = (_a = options.additionalRoomEvents) != null ? _a : allParticipantRoomEvents; const roomEvents = Array.from( /* @__PURE__ */ new Set([ RoomEvent3.ParticipantConnected, RoomEvent3.ParticipantDisconnected, RoomEvent3.ConnectionStateChanged, ...additionalRoomEvents ]) ); const listener = observeRoomEvents(room, ...roomEvents).subscribe( (r) => subscriber == null ? void 0 : subscriber.next(Array.from(r.remoteParticipants.values())) ); if (room.remoteParticipants.size > 0) { subscriber == null ? void 0 : subscriber.next(Array.from(room.remoteParticipants.values())); } return observable; } function connectedParticipantObserver(room, identity, options = {}) { var _a; const additionalEvents = (_a = options.additionalEvents) != null ? _a : allParticipantEvents; const observable = observeRoomEvents( room, RoomEvent3.ParticipantConnected, RoomEvent3.ParticipantDisconnected, RoomEvent3.ConnectionStateChanged ).pipe( switchMap((r) => { const participant = r.getParticipantByIdentity(identity); if (participant) { return observeParticipantEvents(participant, ...additionalEvents); } else { return new Observable2((subscribe) => subscribe.next(void 0)); } }), startWith3(room.getParticipantByIdentity(identity)) ); return observable; } function participantPermissionObserver(participant) { const observer = participantEventSelector( participant, ParticipantEvent2.ParticipantPermissionsChanged ).pipe( map3(() => participant.permissions), startWith3(participant.permissions) ); return observer; } function participantByIdentifierObserver(room, { kind, identity }, options = {}) { var _a; const additionalEvents = (_a = options.additionalEvents) != null ? _a : allParticipantEvents; const matchesIdentifier = (participant) => { let isMatch = true; if (kind) { isMatch = isMatch && participant.kind === kind; } if (identity) { isMatch = isMatch && participant.identity === identity; } return isMatch; }; const observable = observeRoomEvents( room, RoomEvent3.ParticipantConnected, RoomEvent3.ParticipantDisconnected, RoomEvent3.ConnectionStateChanged ).pipe( switchMap((r) => { const participant = Array.from(r.remoteParticipants.values()).find( (p) => matchesIdentifier(p) ); if (participant) { return observeParticipantEvents(participant, ...additionalEvents); } else { return new Observable2((subscribe) => subscribe.next(void 0)); } }), startWith3(Array.from(room.remoteParticipants.values()).find((p) => matchesIdentifier(p))) ); return observable; } function participantAttributesObserver(participant) { if (typeof participant === "undefined") { return new Observable2(); } return participantEventSelector(participant, ParticipantEvent2.AttributesChanged).pipe( map3(([changedAttributes]) => { return { changed: changedAttributes, attributes: participant.attributes }; }), startWith3({ changed: participant.attributes, attributes: participant.attributes }) ); } // src/components/mediaToggle.ts function setupMediaToggle(source, room, options, publishOptions, onError) { const { localParticipant } = room; const getSourceEnabled = (source2, localParticipant2) => { let isEnabled = false; switch (source2) { case Track7.Source.Camera: isEnabled = localParticipant2.isCameraEnabled; break; case Track7.Source.Microphone: isEnabled = localParticipant2.isMicrophoneEnabled; break; case Track7.Source.ScreenShare: isEnabled = localParticipant2.isScreenShareEnabled; break; default: break; } return isEnabled; }; const enabledObserver = observeParticipantMedia(localParticipant).pipe( map4((media) => { return getSourceEnabled(source, media.participant); }), startWith4(getSourceEnabled(source, localParticipant)) ); const pendingSubject = new Subject(); const toggle = (forceState, captureOptions) => __async(null, null, function* () { try { captureOptions != null ? captureOptions : captureOptions = options; pendingSubject.next(true); switch (source) { case Track7.Source.Camera: yield localParticipant.setCameraEnabled( forceState != null ? forceState : !localParticipant.isCameraEnabled, captureOptions, publishOptions ); return localParticipant.isCameraEnabled; case Track7.Source.Microphone: yield localParticipant.setMicrophoneEnabled( forceState != null ? forceState : !localParticipant.isMicrophoneEnabled, captureOptions, publishOptions ); return localParticipant.isMicrophoneEnabled; case Track7.Source.ScreenShare: yield localParticipant.setScreenShareEnabled( forceState != null ? forceState : !localParticipant.isScreenShareEnabled, captureOptions, publishOptions ); return localParticipant.isScreenShareEnabled; default: throw new TypeError("Tried to toggle unsupported source"); } } catch (e) { if (onError && e instanceof Error) { onError == null ? void 0 : onError(e); return void 0; } else { throw e; } } finally { pendingSubject.next(false); } }); const className = prefixClass("button"); return { className, toggle, enabledObserver, pendingObserver: pendingSubject.asObservable() }; } function setupManualToggle() { let state = false; const enabledSubject = new Subject(); const pendingSubject = new Subject(); const toggle = (forceState) => __async(null, null, function* () { pendingSubject.next(true); state = forceState != null ? forceState : !state; enabledSubject.next(state); pendingSubject.next(false); }); const className = prefixClass("button"); return { className, toggle, enabledObserver: enabledSubject.asObservable(), pendingObserver: pendingSubject.asObservable() }; } // src/components/mediaDeviceSelect.ts import { Track as Track8, getBrowser } from "livekit-client"; import { BehaviorSubject } from "rxjs"; function setupDeviceSelector(kind, room, localTrack) { const activeDeviceSubject = new BehaviorSubject(void 0); const activeDeviceObservable = createActiveDeviceObservable(room, kind); const setActiveMediaDevice = (_0, ..._1) => __async(null, [_0, ..._1], function* (id, options = {}) { var _a, _b, _c; if (localTrack) { yield localTrack.setDeviceId(options.exact ? { exact: id } : id); const actualId = yield localTrack.getDeviceId(false); activeDeviceSubject.next( id === "default" && localTrack.mediaStreamTrack.label.startsWith("Default") ? id : actualId ); } else if (room) { const browser = getBrowser(); if (kind === "audiooutput" && ((browser == null ? void 0 : browser.name) === "Safari" || (browser == null ?