diff --git a/apps/client/src/components/lookout/LookoutRecorder.tsx b/apps/client/src/components/lookout/LookoutRecorder.tsx index 0cb98f0..32c3d96 100644 --- a/apps/client/src/components/lookout/LookoutRecorder.tsx +++ b/apps/client/src/components/lookout/LookoutRecorder.tsx @@ -437,54 +437,6 @@ function SessionSelector({ onSelectSession, onNewSession, onClose }: { ); } -/** - * Request the screen-share stream *now*, from within a user gesture, and make - * the Lookout SDK reuse it. - * - * WebKit/Safari rejects getDisplayMedia unless the call itself originates - * directly from a user gesture — it does not honour the transient-activation - * window Chrome allows. But the SDK calls getDisplayMedia from a mount effect, - * which only runs after we've created a session over the network — long past - * the gesture. So instead of blocking on session creation, we fire - * getDisplayMedia here on the click (kicking off the OS picker immediately, - * while the session request flies in parallel) and briefly shim - * navigator.mediaDevices.getDisplayMedia so the SDK's later call resolves to - * the stream we already requested. The shim is one-shot and self-restoring. - * - * Returns a cleanup() that restores the original getDisplayMedia and stops the - * stream — call it if recording never actually starts (e.g. session creation - * failed) so we don't leak a live screen-share or a dangling override. - */ -function primeScreenShare(): () => void { - const md = navigator.mediaDevices; - const real = md.getDisplayMedia.bind(md); - - // Plain { video: true } so the call never rejects with a TypeError on older - // Safari (which chokes on nested frameRate constraints). The SDK downscales - // captured frames itself, so we don't lose any output fidelity here. - const streamPromise = real({ video: true, audio: false }); - // The SDK may never consume this (cancelled flow) — swallow so a rejected - // picker doesn't surface as an unhandled promise rejection. - streamPromise.catch(() => {}); - - let restored = false; - const restore = () => { - if (restored) return; - restored = true; - if (md.getDisplayMedia === shim) md.getDisplayMedia = real; - }; - const shim: MediaDevices["getDisplayMedia"] = () => { - restore(); - return streamPromise; - }; - md.getDisplayMedia = shim; - - return () => { - restore(); - streamPromise.then((s) => s.getTracks().forEach((t) => t.stop())).catch(() => {}); - }; -} - export default function LookoutRecorder() { const auth = useAuth(true); @@ -504,14 +456,6 @@ export default function LookoutRecorder() { const [browserError, setBrowserError] = useState(null); const [confirmingBrowser, setConfirmingBrowser] = useState(false); const initialized = useRef(false); - // Cleanup for an in-gesture screen-share primed by primeScreenShare(): stops - // the stream and restores getDisplayMedia if the SDK never consumed it. - const primeCleanupRef = useRef<(() => void) | null>(null); - - const releasePendingStream = useCallback(() => { - primeCleanupRef.current?.(); - primeCleanupRef.current = null; - }, []); const checkLookoutDrafts = useCallback(async () => { try { @@ -545,12 +489,10 @@ export default function LookoutRecorder() { const res = await api.timelapse.createRecordingSession({}); if (!res.ok) { setInitError(res.message); - releasePendingStream(); return; } if (!res.data.draftId) { setInitError("Server returned no draft ID. Please restart the server."); - releasePendingStream(); return; } const cfg: LookoutSessionConfig = { @@ -570,7 +512,6 @@ export default function LookoutRecorder() { onReady(cfg); } catch (err) { setInitError(err instanceof Error ? err.message : "Failed to create recording session"); - releasePendingStream(); } finally { setIsCreating(false); } @@ -599,21 +540,6 @@ export default function LookoutRecorder() { } setConfirmingBrowser(false); - // For Browser (screen) mode, request the screen-share *synchronously* here - // in the click — Safari requires getDisplayMedia to fire from the gesture - // itself. primeScreenShare pops the OS picker now and hands the stream to - // the SDK when it asks; meanwhile session creation runs in the background. - if (selectedMode === "screen") { - try { - primeCleanupRef.current = primeScreenShare(); - } catch (err) { - const e = err instanceof Error ? err : new Error(String(err)); - setBrowserError(e.message || "Failed to start screen sharing."); - setSelectedMode("desktop"); - return; - } - } - if (config) { startWithConfig(config); return; @@ -676,6 +602,8 @@ export default function LookoutRecorder() { } if (desktopLaunched && config) { + const deepLink = `lookout://session?token=${config.lookoutToken}`; + return (
@@ -696,6 +624,12 @@ export default function LookoutRecorder() { > Get Lookout +

+ Lookout opening but not working? Try entering this deep link{" "} + + {deepLink} + +