### Access and Create Document Picture-in-Picture Window Source: https://context7.com/wicg/document-picture-in-picture/llms.txt Check if a PiP window is currently open using `documentPictureInPicture.window`. If no window exists, `requestWindow()` can be called to create a new one. This snippet demonstrates how to get an existing window or create a new one and then populate its body. ```javascript // Check if a PiP window is currently open function isPiPWindowOpen() { return documentPictureInPicture.window !== null; } // Get the current PiP window or open a new one async function getOrCreatePiPWindow() { // Check if there's already an open PiP window const existingWindow = documentPictureInPicture.window; if (existingWindow) { console.log('Using existing PiP window'); return existingWindow; } // No existing window, create a new one console.log('Creating new PiP window'); return await documentPictureInPicture.requestWindow({ width: 320, height: 240 }); } // Example usage document.querySelector('#pip-button').addEventListener('click', async () => { const pipWindow = await getOrCreatePiPWindow(); pipWindow.document.body.innerHTML = '

Hello PiP!

'; }); ``` -------------------------------- ### Configure and Open Document Picture-in-Picture Windows Source: https://context7.com/wicg/document-picture-in-picture/llms.txt Demonstrates various configurations for the requestWindow method, including setting dimensions, controlling the return-to-opener UI, and managing window placement. ```javascript // All available options for requestWindow() async function openConfiguredPiPWindow() { const options = { // Initial width of the PiP window viewport (in pixels) // Must be specified together with height, or both must be 0/omitted width: 400, // Initial height of the PiP window viewport (in pixels) height: 300, // When true, hints to the UA to hide the "return to tab" button // Useful for experiences where returning doesn't make sense disallowReturnToOpener: false, // When true, hints to the UA to use default position/size // instead of remembering previous PiP window position preferInitialWindowPlacement: false }; const pipWindow = await documentPictureInPicture.requestWindow(options); // Window inherits the opener's document mode (quirks/standards) console.log('Document mode:', pipWindow.document.compatMode); return pipWindow; } // Open with return-to-opener disabled (e.g., for standalone widgets) async function openStandaloneTimer() { const pipWindow = await documentPictureInPicture.requestWindow({ width: 200, height: 100, disallowReturnToOpener: true // Hide "back to tab" UI }); pipWindow.document.body.innerHTML = `
25:00
`; return pipWindow; } // Open with fresh positioning (ignore previous position) async function openFreshPiPWindow() { const pipWindow = await documentPictureInPicture.requestWindow({ width: 320, height: 240, preferInitialWindowPlacement: true // Don't remember last position }); return pipWindow; } ``` -------------------------------- ### Implement Video Conferencing in PiP Source: https://context7.com/wicg/document-picture-in-picture/llms.txt Creates a custom PiP window containing a video grid for remote streams, a local video preview, and interactive controls for muting and camera toggling. Requires an active local stream and a list of remote streams. ```javascript // Multi-stream video conferencing in PiP async function enterVideoConferencePiP(localStream, remoteStreams) { const pipWindow = await documentPictureInPicture.requestWindow({ width: 480, height: 360 }); // Add styles const style = pipWindow.document.createElement('style'); style.textContent = ` body { margin: 0; background: #1a1a1a; } .video-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 4px; height: calc(100% - 50px); } video { width: 100%; height: 100%; object-fit: cover; } .local-video { position: absolute; bottom: 60px; right: 10px; width: 120px; border: 2px solid #fff; border-radius: 8px; } .controls { position: fixed; bottom: 0; width: 100%; display: flex; justify-content: center; gap: 10px; padding: 10px; background: #2a2a2a; } .controls button { padding: 10px 20px; border-radius: 20px; border: none; cursor: pointer; } .mute-btn.muted { background: #ff4444; color: white; } `; pipWindow.document.head.appendChild(style); // Create video grid for remote participants const videoGrid = pipWindow.document.createElement('div'); videoGrid.className = 'video-grid'; remoteStreams.forEach((stream, index) => { const video = pipWindow.document.createElement('video'); video.srcObject = stream; video.autoplay = true; video.playsInline = true; videoGrid.appendChild(video); }); pipWindow.document.body.appendChild(videoGrid); // Local video preview (picture-in-picture within PiP) const localVideo = pipWindow.document.createElement('video'); localVideo.className = 'local-video'; localVideo.srcObject = localStream; localVideo.autoplay = true; localVideo.muted = true; // Prevent feedback localVideo.playsInline = true; pipWindow.document.body.appendChild(localVideo); // Control bar const controls = pipWindow.document.createElement('div'); controls.className = 'controls'; // Mute button const muteBtn = pipWindow.document.createElement('button'); muteBtn.className = 'mute-btn'; muteBtn.textContent = '🎀 Mute'; muteBtn.addEventListener('click', () => { const audioTrack = localStream.getAudioTracks()[0]; audioTrack.enabled = !audioTrack.enabled; muteBtn.classList.toggle('muted', !audioTrack.enabled); muteBtn.textContent = audioTrack.enabled ? '🎀 Mute' : 'πŸ”‡ Unmute'; }); // Camera toggle const cameraBtn = pipWindow.document.createElement('button'); cameraBtn.textContent = 'πŸ“Ή Camera'; cameraBtn.addEventListener('click', () => { const videoTrack = localStream.getVideoTracks()[0]; videoTrack.enabled = !videoTrack.enabled; cameraBtn.textContent = videoTrack.enabled ? 'πŸ“Ή Camera' : 'πŸ“· Camera Off'; }); // Leave call const leaveBtn = pipWindow.document.createElement('button'); leaveBtn.textContent = 'πŸ“ž Leave'; leaveBtn.style.background = '#ff4444'; leaveBtn.style.color = 'white'; leaveBtn.addEventListener('click', () => { window.focus(); pipWindow.close(); // Trigger leave call logic in main app window.dispatchEvent(new CustomEvent('leave-call')); }); controls.append(muteBtn, cameraBtn, leaveBtn); pipWindow.document.body.appendChild(controls); return pipWindow; } ``` -------------------------------- ### Move elements to and from PiP window Source: https://context7.com/wicg/document-picture-in-picture/llms.txt Demonstrates moving a video player element into a PiP window and returning it to the main document upon window closure. ```javascript // Complete example: Custom video player with PiP let pipWindow = null; async function enterVideoPlayerPiP() { const player = document.querySelector('#player'); const playerContainer = document.querySelector('#player-container'); // Open PiP window sized to the player pipWindow = await documentPictureInPicture.requestWindow({ width: player.clientWidth, height: player.clientHeight }); // Copy styles to PiP window const style = pipWindow.document.createElement('style'); style.textContent = ` body { margin: 0; overflow: hidden; } #player { width: 100%; height: 100%; } video { width: 100%; height: 100%; object-fit: contain; } .controls { position: absolute; bottom: 0; width: 100%; background: linear-gradient(transparent, rgba(0,0,0,0.7)); padding: 10px; box-sizing: border-box; } `; pipWindow.document.head.appendChild(style); // Move the player element to PiP window pipWindow.document.body.appendChild(player); // Add visual indicator in main page playerContainer.classList.add('pip-mode'); playerContainer.innerHTML = '

Player is in Picture-in-Picture mode

'; // Handle PiP window closing pipWindow.addEventListener('pagehide', () => { // Move player back to main document const pipPlayer = pipWindow.document.querySelector('#player'); playerContainer.classList.remove('pip-mode'); playerContainer.innerHTML = ''; playerContainer.appendChild(pipPlayer); pipWindow = null; }, { once: true }); } // HTML structure expected: //
//
// //
...
//
//
``` -------------------------------- ### Open a Document Picture-in-Picture Window Source: https://context7.com/wicg/document-picture-in-picture/llms.txt Use `documentPictureInPicture.requestWindow()` to open a new PiP window with specified dimensions. This method requires a user gesture and returns a Promise resolving to the new Window object. It handles copying stylesheets and includes error handling for unsupported features or disallowed operations. ```javascript async function openPictureInPicture() { try { // Request a PiP window with specified width and height const pipWindow = await documentPictureInPicture.requestWindow({ width: 400, height: 300, disallowReturnToOpener: false, preferInitialWindowPlacement: false }); // The pipWindow is a regular Window object with an empty document console.log('PiP window opened:', pipWindow); console.log('Document URL:', pipWindow.document.URL); // "about:blank" // Copy stylesheets from the main document to the PiP window [...document.styleSheets].forEach((styleSheet) => { try { const cssRules = [...styleSheet.cssRules].map((rule) => rule.cssText).join(''); const style = document.createElement('style'); style.textContent = cssRules; pipWindow.document.head.appendChild(style); } catch (e) { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = styleSheet.href; pipWindow.document.head.appendChild(link); } }); return pipWindow; } catch (error) { // Handle errors: NotSupportedError, NotAllowedError, or RangeError if (error.name === 'NotSupportedError') { console.error('Document PiP is not supported'); } else if (error.name === 'NotAllowedError') { console.error('Requires user gesture or top-level context'); } else if (error.name === 'RangeError') { console.error('Width and height must both be specified or both be zero'); } throw error; } } ``` -------------------------------- ### documentPictureInPicture.requestWindow() Source: https://context7.com/wicg/document-picture-in-picture/llms.txt Opens a new picture-in-picture window with a blank document. Requires a user gesture to execute. ```APIDOC ## documentPictureInPicture.requestWindow() ### Description Opens a new picture-in-picture window with a blank document that can be populated with arbitrary HTML elements. This method requires a user gesture to call. ### Parameters #### Request Body - **width** (number) - Optional - The width of the PiP window. - **height** (number) - Optional - The height of the PiP window. - **disallowReturnToOpener** (boolean) - Optional - Whether to prevent the return-to-opener button. - **preferInitialWindowPlacement** (boolean) - Optional - Whether to prefer initial window placement. ### Response #### Success Response (Promise) - **Window** (object) - Resolves to the new Window object representing the PiP window. ### Errors - **NotSupportedError** - Document PiP is not supported. - **NotAllowedError** - Requires user gesture or top-level context. - **RangeError** - Width and height must both be specified or both be zero. ``` -------------------------------- ### Enter Picture-in-Picture Mode Source: https://github.com/wicg/document-picture-in-picture/blob/main/README.md Initiates Picture-in-Picture mode for a video player. Requires the video element to be present in the DOM. Styles the container to indicate PiP mode and appends the player to the PiP window. ```javascript let pipWindow = null; async function enterPiP() { const player = document.querySelector("#player"); const pipOptions = { width: player.clientWidth, height: player.clientHeight, }; pipWindow = await documentPictureInPicture.requestWindow(pipOptions); // Style remaining container to imply the player is in PiP. const playerContainer = document.querySelector("#player-container"); playerContainer.classList.add("pip-mode"); // Add player to the PiP window. pipWindow.document.body.append(player); // Listen for the PiP closing event to put the video back. pipWindow.addEventListener("pagehide", onLeavePiP.bind(pipWindow), { once: true, }); } ``` -------------------------------- ### Handle PiP window creation events Source: https://context7.com/wicg/document-picture-in-picture/llms.txt Use the 'enter' event or 'onenter' property to detect when a PiP window is opened and access the window object. ```javascript // Listen for PiP window creation documentPictureInPicture.addEventListener('enter', (event) => { const pipWindow = event.window; console.log('PiP window opened with dimensions:', pipWindow.innerWidth, 'x', pipWindow.innerHeight); // Set up the PiP window content pipWindow.document.body.style.margin = '0'; pipWindow.document.body.style.backgroundColor = '#1a1a1a'; pipWindow.document.body.style.color = 'white'; // Add a close button to the PiP window const closeButton = pipWindow.document.createElement('button'); closeButton.textContent = 'Close PiP'; closeButton.style.cssText = 'position: fixed; top: 10px; right: 10px;'; closeButton.addEventListener('click', () => pipWindow.close()); pipWindow.document.body.appendChild(closeButton); }); // Alternative: using onenter property documentPictureInPicture.onenter = (event) => { console.log('PiP window ready:', event.window); }; ``` -------------------------------- ### Define HTML structure for PiP Source: https://github.com/wicg/document-picture-in-picture/blob/main/README.md Provides the basic markup for a video player container and a trigger button to initiate the Picture-in-Picture experience. ```html
``` -------------------------------- ### Manage PiP Window Lifecycle with PiPManager Source: https://context7.com/wicg/document-picture-in-picture/llms.txt This class handles opening, closing, and restoring elements from a Picture-in-Picture window. It tracks moved elements and ensures proper cleanup. ```javascript // Complete lifecycle management for PiP windows class PiPManager { constructor() { this.pipWindow = null; this.movedElements = new Map(); // Track moved elements and their origins } async open(options = {}) { // Close existing PiP window if open if (this.pipWindow && !this.pipWindow.closed) { this.pipWindow.close(); } this.pipWindow = await documentPictureInPicture.requestWindow({ width: options.width || 400, height: options.height || 300, ...options }); // Set up close handler this.pipWindow.addEventListener('pagehide', () => this.onClose(), { once: true }); // Handle opener window unload window.addEventListener('beforeunload', () => { if (this.pipWindow && !this.pipWindow.closed) { this.pipWindow.close(); } }, { once: true }); return this.pipWindow; } moveElement(element, placeholder = null) { if (!this.pipWindow || this.pipWindow.closed) { throw new Error('PiP window is not open'); } // Store original parent and position const originalParent = element.parentElement; const originalNextSibling = element.nextSibling; // Create placeholder if requested if (placeholder) { originalParent.insertBefore(placeholder, originalNextSibling); } // Track the moved element this.movedElements.set(element, { parent: originalParent, nextSibling: originalNextSibling, placeholder }); // Move to PiP window this.pipWindow.document.body.appendChild(element); } onClose() { // Restore all moved elements to their original positions this.movedElements.forEach((origin, element) => { // Remove placeholder if exists if (origin.placeholder && origin.placeholder.parentElement) { origin.placeholder.remove(); } // Move element back if (origin.parent) { if (origin.nextSibling) { origin.parent.insertBefore(element, origin.nextSibling); } else { origin.parent.appendChild(element); } } }); this.movedElements.clear(); this.pipWindow = null; // Dispatch custom event for app to handle window.dispatchEvent(new CustomEvent('pip-closed')); } close() { if (this.pipWindow && !this.pipWindow.closed) { this.pipWindow.close(); } } get isOpen() { return this.pipWindow !== null && !this.pipWindow.closed; } } // Usage const pipManager = new PiPManager(); document.querySelector('#enter-pip').addEventListener('click', async () => { await pipManager.open({ width: 400, height: 300 }); const player = document.querySelector('#player'); const placeholder = document.createElement('div'); placeholder.className = 'pip-placeholder'; placeholder.textContent = 'Video is in Picture-in-Picture'; pipManager.moveElement(player, placeholder); }); window.addEventListener('pip-closed', () => { console.log('PiP window was closed, elements restored'); }); ``` -------------------------------- ### documentPictureInPicture.window Source: https://context7.com/wicg/document-picture-in-picture/llms.txt A read-only property to check if a PiP window is currently open. ```APIDOC ## documentPictureInPicture.window ### Description A read-only property that returns the current PiP window if one is open and not closed, otherwise returns null. ### Response - **Window | null** - The current PiP window object or null if no window is open. ``` -------------------------------- ### Add Interactive Controls to PiP Window Source: https://context7.com/wicg/document-picture-in-picture/llms.txt Injects custom DOM elements into the PiP window to control video playback, volume, and navigation. Requires access to the pipWindow and video element references. ```javascript // Add custom interactive controls to a PiP video player function addPiPControls(pipWindow, video) { const controlsContainer = pipWindow.document.createElement('div'); controlsContainer.className = 'pip-controls'; controlsContainer.style.cssText = ` position: fixed; bottom: 0; left: 0; right: 0; display: flex; gap: 10px; padding: 10px; background: rgba(0,0,0,0.8); `; // Play/Pause button const playPauseBtn = pipWindow.document.createElement('button'); playPauseBtn.textContent = video.paused ? '▢️' : '⏸️'; playPauseBtn.addEventListener('click', () => { if (video.paused) { video.play(); playPauseBtn.textContent = '⏸️'; } else { video.pause(); playPauseBtn.textContent = '▢️'; } }); // Mute button const muteBtn = pipWindow.document.createElement('button'); muteBtn.textContent = video.muted ? 'πŸ”‡' : 'πŸ”Š'; muteBtn.addEventListener('click', () => { video.muted = !video.muted; muteBtn.textContent = video.muted ? 'πŸ”‡' : 'πŸ”Š'; }); // Progress bar const progressBar = pipWindow.document.createElement('input'); progressBar.type = 'range'; progressBar.min = 0; progressBar.max = 100; progressBar.value = 0; progressBar.style.flexGrow = '1'; video.addEventListener('timeupdate', () => { progressBar.value = (video.currentTime / video.duration) * 100; }); progressBar.addEventListener('input', () => { video.currentTime = (progressBar.value / 100) * video.duration; }); // Return to tab button const returnBtn = pipWindow.document.createElement('button'); returnBtn.textContent = '↩️ Return to tab'; returnBtn.addEventListener('click', () => { window.focus(); // Focus the opener window pipWindow.close(); // Optionally close the PiP window }); controlsContainer.append(playPauseBtn, muteBtn, progressBar, returnBtn); pipWindow.document.body.appendChild(controlsContainer); } ``` -------------------------------- ### Resize PiP Window Programmatically Source: https://context7.com/wicg/document-picture-in-picture/llms.txt Uses resizeTo and resizeBy methods to adjust window dimensions. These operations must be triggered by a user gesture within the PiP window. ```javascript // Add resize controls to PiP window (requires user gesture) function addResizeControls(pipWindow) { const resizeContainer = pipWindow.document.createElement('div'); resizeContainer.style.cssText = ` position: fixed; top: 5px; right: 5px; display: flex; gap: 5px; `; // Expand button const expandBtn = pipWindow.document.createElement('button'); expandBtn.textContent = '⬆️ Expand'; expandBtn.addEventListener('click', () => { // Increase window size by 50px in each direction pipWindow.resizeBy(50, 50); console.log('New size:', pipWindow.innerWidth, 'x', pipWindow.innerHeight); }); // Shrink button const shrinkBtn = pipWindow.document.createElement('button'); shrinkBtn.textContent = '⬇️ Shrink'; shrinkBtn.addEventListener('click', () => { // Decrease window size by 50px in each direction pipWindow.resizeBy(-50, -50); }); // Preset size button const presetBtn = pipWindow.document.createElement('button'); presetBtn.textContent = 'πŸ“ Standard'; presetBtn.addEventListener('click', () => { // Set to a specific size (16:9 aspect ratio) pipWindow.resizeTo(480, 270); }); resizeContainer.append(expandBtn, shrinkBtn, presetBtn); pipWindow.document.body.appendChild(resizeContainer); } ``` -------------------------------- ### Apply CSS for Picture-in-Picture Mode Source: https://context7.com/wicg/document-picture-in-picture/llms.txt Uses the display-mode media feature to apply styles specifically when content is rendered in a PiP window. Useful for hiding non-essential UI and optimizing layout for smaller viewports. ```css /* Styles applied only in PiP mode */ @media all and (display-mode: picture-in-picture) { /* Remove margins for compact display */ body { margin: 0; padding: 0; overflow: hidden; } /* Smaller fonts for reduced viewport */ h1 { font-size: 0.8em; margin: 5px; } /* Larger click targets for PiP controls */ button { min-width: 44px; min-height: 44px; font-size: 1.2em; } /* Hide non-essential elements */ .sidebar, .comments, .recommendations { display: none; } /* Make video fill the window */ video { width: 100%; height: 100%; object-fit: contain; } /* Compact control bar */ .controls { position: fixed; bottom: 0; left: 0; right: 0; padding: 5px; background: linear-gradient(transparent, rgba(0,0,0,0.8)); } } ``` -------------------------------- ### Add Event Listeners to PiP Window Controls Source: https://github.com/wicg/document-picture-in-picture/blob/main/README.md Creates and appends a custom button to the Picture-in-Picture window that toggles the mute state of a video element. This requires the PiP window to be open. ```javascript const pipVideo = pipWindow.document.querySelector("#video"); const pipMuteButton = pipWindow.document.createElement("button"); pipMuteButton.textContent = "Toggle mute"; pipMuteButton.addEventListener("click", () => { pipVideo.muted = !pipVideo.muted; }); pipWindow.document.body.append(pipMuteButton); ``` -------------------------------- ### Programmatically Resize PiP Window Source: https://github.com/wicg/document-picture-in-picture/blob/main/README.md Adds a button to the Picture-in-Picture window that, when clicked, resizes the PiP window by a specified amount. This functionality requires a user gesture within the PiP window. ```javascript const expandButton = pipWindow.document.createElement('button'); expandButton.textContent = 'Expand PiP Window'; expandButton.addEventListener('click', () => { // Expand the PiP window's width by 20px and height by 30px. pipWindow.resizeBy(20, 30); }); pipWindow.document.body.append(expandButton); ``` -------------------------------- ### Access and Modify PiP Window Elements Source: https://github.com/wicg/document-picture-in-picture/blob/main/README.md Selects an element within the Picture-in-Picture window and modifies its properties. Ensure the PiP window is open and the element exists. ```javascript const pipVideo = pipWindow.document.querySelector("#video"); pipVideo.loop = true; ``` -------------------------------- ### Handle PiP Window Closing and Element Return Source: https://github.com/wicg/document-picture-in-picture/blob/main/README.md This function is called when the Picture-in-Picture window closes. It removes PiP-specific styling from the main container and appends the video player back to the main document. It also resets the global `pipWindow` variable. ```javascript // Called when the PiP window has closed. function onLeavePiP() { if (this !== pipWindow) { return; } // Remove PiP styling from the container. const playerContainer = document.querySelector("#player-container"); playerContainer.classList.remove("pip-mode"); // Add the player back to the main window. const pipPlayer = pipWindow.document.querySelector("#player"); playerContainer.append(pipPlayer); pipWindow = null; } ``` -------------------------------- ### Add a Contributor Source: https://github.com/wicg/document-picture-in-picture/blob/main/CONTRIBUTING.md Use this syntax in a pull request comment to add a contributor other than yourself. ```text +@github_username ``` -------------------------------- ### Remove a Contributor Source: https://github.com/wicg/document-picture-in-picture/blob/main/CONTRIBUTING.md Use this syntax in a pull request comment to remove a contributor, including yourself if needed. ```text -@github_username ``` -------------------------------- ### Close Picture-in-Picture Window Programmatically Source: https://github.com/wicg/document-picture-in-picture/blob/main/README.md Closes the Picture-in-Picture window. This action will trigger the 'pagehide' event listener, allowing for cleanup and returning elements to the main document. ```javascript // This will close the PiP window and trigger our existing onLeavePiP() // listener. pipWindow.close(); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.