### Install Jitsi Meet API Library Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/README.md Use npm install to get the library dependencies. Run npm run build to compile the sources. ```bash npm install npm run build ``` -------------------------------- ### Install jitsi-meet-tokens Package Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/doc/tokens.md Install the jitsi-meet-tokens package to enable token authentication. Ensure jitsi-meet version 779 or higher for automatic configuration. ```bash apt-get install jitsi-meet-tokens ``` -------------------------------- ### Install lib-jitsi-meet using npm pack Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/CLAUDE.md Recommended method for local development. First, package lib-jitsi-meet, then install the generated tarball into jitsi-meet. ```bash # In lib-jitsi-meet directory npm pack # In jitsi-meet directory npm install file:///path/to/lib-jitsi-meet-.tgz --force && make ``` -------------------------------- ### Install lib-jitsi-meet using npm link Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/CLAUDE.md Simpler method for local development, but may not work for mobile builds. Links the local lib-jitsi-meet to jitsi-meet's node_modules. ```bash # In lib-jitsi-meet directory npm link # In jitsi-meet directory npm link lib-jitsi-meet # Rebuild after changes cd node_modules/lib-jitsi-meet && npm run build ``` -------------------------------- ### Run Development Build with Watch Mode Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/CLAUDE.md Starts a development build process that automatically recompiles on file changes. Ideal for active development. ```bash npm run watch ``` -------------------------------- ### Initialize lib-jitsi-meet Source: https://context7.com/jitsi/lib-jitsi-meet/llms.txt Call JitsiMeetJS.init(options) once before using any other API. It starts device monitoring, statistics, and optional RTCStats logging. Configure options like audioLevelsInterval, disableAudioLevels, disableThirdPartyRequests, enableWindowOnErrorHandler, enableAnalyticsLogging, externalStorage, and feature flags. ```typescript import JitsiMeetJS from 'lib-jitsi-meet'; JitsiMeetJS.init({ // Audio level polling interval in ms (default: 200) audioLevelsInterval: 200, // Disable audio level collection entirely disableAudioLevels: false, // Prevent the library from making third-party analytics calls disableThirdPartyRequests: false, // Enable window.onerror handler enableWindowOnErrorHandler: true, // Enable analytics event logging (set false to disable analytics entirely) enableAnalyticsLogging: true, // Override localStorage with a custom Storage implementation externalStorage: window.sessionStorage, // Feature flags flags: { runInLiteMode: false, // Lite mode: receive audio-only by default ssrcRewritingEnabled: false // SSRC rewriting support }, analytics: { // Store logs in RTCStats for debugging rtcstatsStoreLogs: true, rtcstatsLogFlushSizeBytes: 1024 * 50 } }); // Check browser support before proceeding if (!JitsiMeetJS.isWebRtcSupported()) { console.error('WebRTC is not supported in this browser'); } // Set log level (TRACE, DEBUG, INFO, WARN, ERROR) JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.WARN); ``` -------------------------------- ### Full Rebuild Command Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/CLAUDE.md Command to perform a full rebuild of the project, including installing dependencies. ```bash npm install lib-jitsi-meet --force && make ``` -------------------------------- ### Conventional Commits Example Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/CLAUDE.md Example commit messages following the Conventional Commits specification with mandatory module scopes. ```bash feat(RTC): add new WebRTC functionality fix(xmpp): resolve Jingle negotiation issue docs(JitsiConnection): update connection API documentation ``` -------------------------------- ### JitsiConnection with Tenant Configuration Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/doc/tokens.md Example of how to configure JitsiConnection with a specific tenant when the JWT 'sub' claim includes tenant information. This ensures the connection is directed to the correct MUC service. ```javascript const connection = new JitsiMeetJS.JitsiConnection( '${applicationId}', '${token}', { serviceUrl: 'wss://server.net/tenant/xmpp-websocket', hosts: { domain: 'muc.server.net', muc: 'conference1.tenant.muc.server.net', }, }, ); ``` -------------------------------- ### Library Only Rebuild Command Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/CLAUDE.md Command to install dependencies and deploy only the library, without a full project rebuild. ```bash npm install lib-jitsi-meet --force && make deploy-lib-jitsi-meet ``` -------------------------------- ### Initialize and Manage JitsiConference Session Source: https://context7.com/jitsi/lib-jitsi-meet/llms.txt Initializes a JitsiConference, sets up event listeners for various conference events, adds local audio and video tracks, configures receiver constraints, and joins the conference. Use this for the main conference setup and interaction. ```typescript import JitsiMeetJS from 'lib-jitsi-meet'; const { events: { conference: ConfEvents } } = JitsiMeetJS; // Assume `connection` is established and `audioTrack`, `videoTrack` are local tracks const conference = connection.initJitsiConference('myroom', { config: { p2p: { enabled: true, backToP2PDelay: 5 }, channelLastN: -1, // receive all streams enableNoAudioDetection: true, enableNoisyMicDetection: true, enableTalkWhileMuted: true, videoQuality: { preferredCodec: 'VP9', codecPreferenceOrder: ['VP9', 'VP8', 'H264'] } } }); // Track management conference.addEventListener(ConfEvents.CONFERENCE_JOINED, () => { console.log('Joined conference:', conference.getName()); console.log('My ID:', conference.myUserId()); conference.addTrack(audioTrack).then(() => console.log('Audio added')); conference.addTrack(videoTrack).then(() => console.log('Video added')); // Set display name in presence conference.setDisplayName('Alice'); // Set receiver constraints (limit received video quality) conference.setReceiverConstraints({ defaultConstraints: { maxHeight: 360 }, lastN: 5 }); }); conference.addEventListener(ConfEvents.USER_JOINED, (id, participant) => { console.log(`${participant.getDisplayName()} joined (${id})`); }); conference.addEventListener(ConfEvents.USER_LEFT, (id) => { console.log('Participant left:', id); }); conference.addEventListener(ConfEvents.TRACK_ADDED, (track) => { if (track.isLocal()) return; // skip local tracks const remoteTrack = track; if (remoteTrack.getType() === 'video') { const videoEl = document.createElement('video'); videoEl.autoplay = true; document.body.appendChild(videoEl); remoteTrack.attach(videoEl); } }); conference.addEventListener(ConfEvents.TRACK_REMOVED, (track) => { track.detach(); }); conference.addEventListener(ConfEvents.DOMINANT_SPEAKER_CHANGED, (id) => { console.log('Dominant speaker:', id); }); conference.addEventListener(ConfEvents.CONFERENCE_FAILED, (errorType) => { if (errorType === JitsiMeetJS.errors.conference.ICE_FAILED) { console.error('ICE connection failed'); } }); // Join the room conference.join(); // --- Track replacement (e.g., camera switch) --- const [newCameraTrack] = await JitsiMeetJS.createLocalTracks({ devices: ['video'] }); await conference.replaceTrack(videoTrack, newCameraTrack); // --- Muting --- // JitsiLocalTrack.mute()/unmute() changes the track state audioTrack.mute(); videoTrack.unmute(); // --- Messaging --- conference.sendTextMessage('Hello everyone!'); conference.sendCommand('raise-hand', { attributes: { raised: 'true' } }); // --- Leaving --- await conference.leave(); await connection.disconnect(); ``` -------------------------------- ### Manage Media Tracks in JitsiConference Source: https://context7.com/jitsi/lib-jitsi-meet/llms.txt Demonstrates adding, removing, and replacing media tracks within an active JitsiConference. Use these methods for dynamic media stream management, such as starting screen sharing or switching cameras. Remember to dispose of tracks when they are no longer needed. ```typescript // Add a screen share track alongside the existing camera track const [desktopTrack] = await JitsiMeetJS.createLocalTracks({ devices: ['desktop'] }); await conference.addTrack(desktopTrack); // Participant on the other side receives TRACK_ADDED event // conference.on(ConfEvents.TRACK_ADDED, handler) ← fires for both local & remote // Remove the screen share await conference.removeTrack(desktopTrack); destopTrack.dispose(); // Replace camera with a different camera device const [frontCamera] = await JitsiMeetJS.createLocalTracks({ devices: ['video'], cameraDeviceId: 'front-camera-device-id' }); const currentCamera = conference.getLocalVideoTrack(); await conference.replaceTrack(currentCamera, frontCamera); currentCamera.dispose(); // Get all local tracks const allLocal = conference.getLocalTracks(); // all media types const audioTracks = conference.getLocalTracks('audio'); // audio only const videoTracks = conference.getLocalTracks('video'); // video only // Get specific local tracks const localAudio = conference.getLocalAudioTrack(); const localVideo = conference.getLocalVideoTrack(); ``` -------------------------------- ### Jitsi Meet Conference URL with Token Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/doc/tokens.md To start a Jitsi Meet conference with a token, append the JWT as a URL parameter. Every user joining must provide this token. ```url https://example.com/angrywhalesgrowhigh?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ ``` -------------------------------- ### JitsiMeetJS.joinConference Source: https://context7.com/jitsi/lib-jitsi-meet/llms.txt A high-level convenience method to establish a connection and join a conference in a single call, optimized for JaaS (8x8.vc). It simplifies the process by handling connection setup and conference joining with sensible defaults. ```APIDOC ## JitsiMeetJS.joinConference — High-level one-call conference join (JaaS) `joinConference(roomName, appId, token, options)` is a convenience method that handles connection establishment and conference join in a single call, with built-in defaults for JaaS (8x8.vc). ### Parameters - **roomName** (string): The name of the room to join. - **appId** (string): The JaaS application ID (starts with 'vpaas-magic-cookie'). - **token** (string): The JWT token for authentication. - **options** (object): Optional configuration for joining the conference. - **tracks** (array): An array of local audio and video tracks to add to the conference. - **jaas** (object): JaaS specific options. - **release** (string): Optional backend release identifier. - **useStaging** (boolean): Whether to use the staging environment. - **conferenceOptions** (object): Options for the JitsiConference instance. - **config** (object): Configuration object for the conference (e.g., p2p settings, channel last N, start muted). ### Request Example ```typescript import JitsiMeetJS from 'lib-jitsi-meet'; JitsiMeetJS.init({ enableAnalyticsLogging: false }); // Create local tracks first const [audioTrack, videoTrack] = await JitsiMeetJS.createLocalTracks({ devices: ['audio', 'video'], resolution: '720' }) as JitsiLocalTrack[]; // Join a JaaS conference const conference = await JitsiMeetJS.joinConference( 'my-room-name', 'vpaas-magic-cookie-abc123', // JaaS appId (starts with 'vpaas-magic-cookie') 'eyJhbGciOiJSUzI1N...', // JWT token { tracks: [audioTrack, videoTrack], jaas: { release: '1234', // backend release (optional) useStaging: false }, conferenceOptions: { config: { p2p: { enabled: true }, channelLastN: 20, startAudioMuted: 9, startVideoMuted: 9 } } } ); // conference is a JitsiConference instance, already joined and tracks added conference.addEventListener( JitsiMeetJS.events.conference.USER_JOINED, (id, participant) => console.log('User joined:', participant.getDisplayName()) ); // Leave and clean up // await conference.dispose(); // leaves + disconnects ``` ### Response - **conference** (JitsiConference): A JitsiConference instance representing the joined conference. ``` -------------------------------- ### Include Meet Host Config in Prosody Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/doc/tokens.md Ensure your Prosody configuration includes the meet host configuration by adding this line to */etc/prosody/prosody.cfg.lua*. ```lua Include "conf.d/*.cfg.lua" ``` -------------------------------- ### JitsiMeetJS.init Source: https://context7.com/jitsi/lib-jitsi-meet/llms.txt Initializes the lib-jitsi-meet library. This function must be called once before any other API calls. It sets up device monitoring, statistics, and optional logging. ```APIDOC ## JitsiMeetJS.init — Initialize the library `JitsiMeetJS.init(options)` must be called once before any other API is used. It starts device monitoring, statistics, and optional RTCStats logging. ```typescript import JitsiMeetJS from 'lib-jitsi-meet'; JitsiMeetJS.init({ // Audio level polling interval in ms (default: 200) audioLevelsInterval: 200, // Disable audio level collection entirely disableAudioLevels: false, // Prevent the library from making third-party analytics calls disableThirdPartyRequests: false, // Enable window.onerror handler enableWindowOnErrorHandler: true, // Enable analytics event logging (set false to disable analytics entirely) enableAnalyticsLogging: true, // Override localStorage with a custom Storage implementation externalStorage: window.sessionStorage, // Feature flags flags: { runInLiteMode: false, // Lite mode: receive audio-only by default ssrcRewritingEnabled: false // SSRC rewriting support }, analytics: { // Store logs in RTCStats for debugging rtcstatsStoreLogs: true, rtcstatsLogFlushSizeBytes: 1024 * 50 } }); // Check browser support before proceeding if (!JitsiMeetJS.isWebRtcSupported()) { console.error('WebRTC is not supported in this browser'); } // Set log level (TRACE, DEBUG, INFO, WARN, ERROR) JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.WARN); ``` ``` -------------------------------- ### Public Key Validation JWT Payload Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/doc/tokens.md Example JWT payload for public key validation, including optional 'context' for user display information. ```json { "context": { "user": { "avatar": "https:/gravatar.com/avatar/abc123", "name": "John Doe", "email": "jdoe@example.com", "id": "abcd:a1b2c3-d4e5f6-0abc1-23de-abcdef01fedcba" }, "group": "a123-123-456-789" }, "aud": "jitsi", "iss": "my_client", "sub": "meet.jit.si", "room": "*", "exp": 1500006923 } ``` -------------------------------- ### Create Local Media Tracks Source: https://context7.com/jitsi/lib-jitsi-meet/llms.txt Use JitsiMeetJS.createLocalTracks(options) to call getUserMedia and obtain JitsiLocalTrack instances for audio, video, or screen sharing. Handle potential errors like PERMISSION_DENIED or NOT_FOUND. Attach video tracks to HTML elements and manage audio tracks. ```typescript import JitsiMeetJS from 'lib-jitsi-meet'; JitsiMeetJS.init({}); // Request both audio and video tracks JitsiMeetJS.createLocalTracks({ devices: ['audio', 'video'], resolution: '720', // '240'|'360'|'480'|'720'|'1080'|'4k' cameraDeviceId: 'specific-camera-id', // from enumerateDevices (optional) micDeviceId: 'specific-mic-id', // from enumerateDevices (optional) }) .then((tracks: JitsiLocalTrack[]) => { for (const track of tracks) { if (track.getType() === 'video') { const videoEl = document.getElementById('localVideo') as HTMLVideoElement; track.attach(videoEl); } if (track.getType() === 'audio') { // Local audio – do not attach to avoid echo; just hold the reference console.log('Audio track acquired:', track.getDeviceId()); } } }) .catch(error => { if (error.name === JitsiMeetJS.errors.track.PERMISSION_DENIED) { console.error('Camera/microphone permission denied'); } else if (error.name === JitsiMeetJS.errors.track.NOT_FOUND) { console.error('No camera or microphone found'); } else { console.error('Track creation failed:', error); } }); // Request screen share JitsiMeetJS.createLocalTracks({ devices: ['desktop'] }) .then(([screenTrack]) => { console.log('Screen share track:', screenTrack.getVideoType()); // 'desktop' }); ``` -------------------------------- ### Lint Jitsi Meet API Library Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/README.md Execute npm run lint to check the code for style and potential errors. ```bash npm run lint ``` -------------------------------- ### Run Unit Tests for Jitsi Meet API Library Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/README.md Use npm test to execute the unit tests for the library. ```bash npm test ``` -------------------------------- ### Prosody Configuration for Public Key Validation Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/doc/tokens.md Configure your Jitsi Meet virtual host for token authentication using public key validation. This involves setting `authentication`, `app_id`, and `asap_key_server`. ```lua VirtualHost "jitmeet.example.com" authentication = "token"; app_id = "example_app_id"; -- application identifier asap_key_server = "https://keyserver.example.com/asap"; -- URL for public keyserver storing keys by kid allow_empty_token = false; -- tokens are verified only if they are supplied ``` -------------------------------- ### Enumerate and Manage Media Devices with JitsiMeetJS Source: https://context7.com/jitsi/lib-jitsi-meet/llms.txt Use JitsiMeetJS.mediaDevices to enumerate cameras, microphones, and audio output devices. Listen for device list and permission changes. Check and switch audio output devices if supported. ```typescript const { mediaDevices, events: { mediaDevices: DevEvents } } = JitsiMeetJS; // Enumerate all connected devices mediaDevices.enumerateDevices((devices: MediaDeviceInfo[]) => { const cameras = devices.filter(d => d.kind === 'videoinput'); const mics = devices.filter(d => d.kind === 'audioinput'); const outputs = devices.filter(d => d.kind === 'audiooutput'); console.log('Cameras:', cameras.map(d => d.label)); console.log('Mics:', mics.map(d => d.label)); console.log('Outputs:', outputs.map(d => d.label)); }); // React to device list changes (device plugged in / unplugged) mediaDevices.addEventListener(DevEvents.DEVICE_LIST_CHANGED, (devices: MediaDeviceInfo[]) => { console.log('Device list changed, now have', devices.length, 'devices'); }); // React to permission state changes mediaDevices.addEventListener(DevEvents.PERMISSIONS_CHANGED, (permissions) => { console.log('Camera permission:', permissions['video']); // true | false console.log('Mic permission:', permissions['audio']); }); // Check if camera permission is granted const hasCamera = await mediaDevices.isDevicePermissionGranted('video'); const hasMic = await mediaDevices.isDevicePermissionGranted('audio'); // Check if audio output device can be changed (Chrome/Edge only) if (mediaDevices.isDeviceChangeAvailable('output')) { const currentOutput = mediaDevices.getAudioOutputDevice(); console.log('Current output device:', currentOutput); // Switch audio output (e.g., to speaker from earpiece) await mediaDevices.setAudioOutputDevice('specific-audiooutput-device-id'); } // Check if multiple simultaneous audio inputs are supported console.log('Multiple audio inputs:', mediaDevices.isMultipleAudioInputSupported()); ``` -------------------------------- ### Run Full Build with npm Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/CLAUDE.md Executes a full build including Webpack UMD bundle and TypeScript compilation. Use for production builds. ```bash npm run build ``` -------------------------------- ### Configure Prosody Plugin Paths Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/doc/tokens.md Update the `plugin_paths` in your Prosody configuration to include the Jitsi Meet Prosody plugins directory. ```lua plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" } ``` -------------------------------- ### JitsiConference Lifecycle and Basic Operations Source: https://context7.com/jitsi/lib-jitsi-meet/llms.txt Demonstrates how to initialize, join, manage tracks, send messages, and leave a Jitsi conference. ```APIDOC ## JitsiConference — Conference session lifecycle `JitsiConference` represents an active conference room. It is obtained via `connection.initJitsiConference()` or `JitsiMeetJS.joinConference()`. ```typescript import JitsiMeetJS from 'lib-jitsi-meet'; const { events: { conference: ConfEvents } } = JitsiMeetJS; // Assume `connection` is established and `audioTrack`, `videoTrack` are local tracks const conference = connection.initJitsiConference('myroom', { config: { p2p: { enabled: true, backToP2PDelay: 5 }, channelLastN: -1, // receive all streams enableNoAudioDetection: true, enableNoisyMicDetection: true, enableTalkWhileMuted: true, videoQuality: { preferredCodec: 'VP9', codecPreferenceOrder: ['VP9', 'VP8', 'H264'] } } }); // Track management conference.addEventListener(ConfEvents.CONFERENCE_JOINED, () => { console.log('Joined conference:', conference.getName()); console.log('My ID:', conference.myUserId()); conference.addTrack(audioTrack).then(() => console.log('Audio added')); conference.addTrack(videoTrack).then(() => console.log('Video added')); // Set display name in presence conference.setDisplayName('Alice'); // Set receiver constraints (limit received video quality) conference.setReceiverConstraints({ defaultConstraints: { maxHeight: 360 }, lastN: 5 }); }); conference.addEventListener(ConfEvents.USER_JOINED, (id, participant) => { console.log(`${participant.getDisplayName()} joined (${id})`); }); conference.addEventListener(ConfEvents.USER_LEFT, (id) => { console.log('Participant left:', id); }); conference.addEventListener(ConfEvents.TRACK_ADDED, (track) => { if (track.isLocal()) return; // skip local tracks const remoteTrack = track; if (remoteTrack.getType() === 'video') { const videoEl = document.createElement('video'); videoEl.autoplay = true; document.body.appendChild(videoEl); remoteTrack.attach(videoEl); } }); conference.addEventListener(ConfEvents.TRACK_REMOVED, (track) => { track.detach(); }); conference.addEventListener(ConfEvents.DOMINANT_SPEAKER_CHANGED, (id) => { console.log('Dominant speaker:', id); }); conference.addEventListener(ConfEvents.CONFERENCE_FAILED, (errorType) => { if (errorType === JitsiMeetJS.errors.conference.ICE_FAILED) { console.error('ICE connection failed'); } }); // Join the room conference.join(); // --- Track replacement (e.g., camera switch) --- const [newCameraTrack] = await JitsiMeetJS.createLocalTracks({ devices: ['video'] }); await conference.replaceTrack(videoTrack, newCameraTrack); // --- Muting --- // JitsiLocalTrack.mute()/unmute() changes the track state audioTrack.mute(); videoTrack.unmute(); // --- Messaging --- conference.sendTextMessage('Hello everyone!'); conference.sendCommand('raise-hand', { attributes: { raised: 'true' } }); // --- Leaving --- await conference.leave(); await connection.disconnect(); ``` ``` -------------------------------- ### Generate TypeScript Documentation with npm Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/CLAUDE.md Generates API documentation from TypeScript source files using TypeDoc. Run to update project documentation. ```bash npm run typedoc ``` -------------------------------- ### JitsiMeetJS.createLocalTracks Source: https://context7.com/jitsi/lib-jitsi-meet/llms.txt Captures local audio and video streams using `getUserMedia`. Returns `JitsiLocalTrack` instances for the requested device types. ```APIDOC ## JitsiMeetJS.createLocalTracks — Capture local audio/video `createLocalTracks(options)` calls `getUserMedia` and returns `JitsiLocalTrack` instances for the requested device types. ```typescript import JitsiMeetJS from 'lib-jitsi-meet'; JitsiMeetJS.init({}); // Request both audio and video tracks JitsiMeetJS.createLocalTracks({ devices: ['audio', 'video'], resolution: '720', // '240'|'360'|'480'|'720'|'1080'|'4k' cameraDeviceId: 'specific-camera-id', // from enumerateDevices (optional) micDeviceId: 'specific-mic-id', // from enumerateDevices (optional) }) .then((tracks: JitsiLocalTrack[]) => { for (const track of tracks) { if (track.getType() === 'video') { const videoEl = document.getElementById('localVideo') as HTMLVideoElement; track.attach(videoEl); } if (track.getType() === 'audio') { // Local audio – do not attach to avoid echo; just hold the reference console.log('Audio track acquired:', track.getDeviceId()); } } }) .catch(error => { if (error.name === JitsiMeetJS.errors.track.PERMISSION_DENIED) { console.error('Camera/microphone permission denied'); } else if (error.name === JitsiMeetJS.errors.track.NOT_FOUND) { console.error('No camera or microphone found'); } else { console.error('Track creation failed:', error); } }); // Request screen share JitsiMeetJS.createLocalTracks({ devices: ['desktop'] }) .then(([screenTrack]) => { console.log('Screen share track:', screenTrack.getVideoType()); // 'desktop' }); ``` ``` -------------------------------- ### Build UMD Bundle with npm Source: https://github.com/jitsi/lib-jitsi-meet/blob/master/CLAUDE.md Builds only the UMD bundle, suitable for inclusion in browser