import { IconCameraOff } from "@tabler/icons-react"; import { invoke } from "@tauri-apps/api/core"; import { emit, listen } from "@tauri-apps/api/event"; import { getCurrentWindow } from "@tauri-apps/api/window"; import { useEffect, useRef, useState } from "react"; type BubbleSize = "small" | "medium"; /** * Draggable, circular camera bubble — a PURE RENDERER. * * # Why we usually don't call getUserMedia here * * Tauri v2's macOS backend runs every webview window inside a single * WebKit process. WebKit enforces a documented single-page * capture-exclusion policy: when one page calls `getDisplayMedia` or * `getUserMedia`, all capture sources in OTHER pages in the same * process are MUTED — the track stays `readyState="live"` but frames * stop arriving (see WebKit bugs 179363, 237359, 212040, 238456; * changeset 271154). Earlier attempts worked around this with onmute * listeners, watchdogs, luma probes, cooldowns, and * destroy-and-respawn dances — none held up reliably because the * underlying behavior is intentional in WebKit. * * The robust fix for browser/window capture is architectural: the POPOVER * owns the camera (it also owns the display-capture session, so "same page" * applies), and streams video to this overlay. Two transport paths are * supported: * * 1. **WebRTC loopback (preferred)** — the popover runs an * `RTCPeerConnection`, adds the camera video track, creates an * offer, and shuttles SDP + ICE through Tauri events. This * bubble creates a receiving `RTCPeerConnection`, gets the * track via `ontrack`, and plays it in a `