diff --git a/.lane/memory/apps/app/src/panels/live.tsx/01M5YZZPJW4GZEWHECPRQ0KG88-status-is-polled-on-a-timer.md b/.lane/memory/apps/app/src/panels/live.tsx/01M5YZZPJW4GZEWHECPRQ0KG88-status-is-polled-on-a-timer.md index 59265ea7..f57b1941 100644 --- a/.lane/memory/apps/app/src/panels/live.tsx/01M5YZZPJW4GZEWHECPRQ0KG88-status-is-polled-on-a-timer.md +++ b/.lane/memory/apps/app/src/panels/live.tsx/01M5YZZPJW4GZEWHECPRQ0KG88-status-is-polled-on-a-timer.md @@ -4,9 +4,9 @@ anchor: function Live created: 2026-10-27T15:34:41Z norm: '1' sig: acb4d23736381074 -body_hash: 7514a01d203af649 -raw_hash: b1b33c8835350ead -vouched: 2026-10-27T16:32:18Z +body_hash: 79b1035eae7641e9 +raw_hash: af47a64ee8f1e395 +vouched: 2026-10-27T18:07:12Z lines: 13-83 --- diff --git a/.lane/memory/apps/app/src/panels/live.tsx/01M5Z8Q15TZEXP328G0QCEEMMH-the-native-view-draws-above.md b/.lane/memory/apps/app/src/panels/live.tsx/01M5Z8Q15TZEXP328G0QCEEMMH-the-native-view-draws-above.md new file mode 100644 index 00000000..a82a301e --- /dev/null +++ b/.lane/memory/apps/app/src/panels/live.tsx/01M5Z8Q15TZEXP328G0QCEEMMH-the-native-view-draws-above.md @@ -0,0 +1,12 @@ +--- +id: 01M5Z8Q15TZEXP328G0QCEEMMH +anchor: function isCovered +created: 2026-10-27T18:07:15Z +norm: '1' +sig: 930a89ecef95ee2d +body_hash: 99b0e113d13ed2ba +raw_hash: ea6708870bc6da14 +lines: 19-27 +--- + +the native view draws above the page, so anything the page shows over the pane must hide it diff --git a/apps/app/src/panels/live.tsx b/apps/app/src/panels/live.tsx index 108b394b..624ba313 100644 --- a/apps/app/src/panels/live.tsx +++ b/apps/app/src/panels/live.tsx @@ -9,6 +9,23 @@ import type { Content } from "../layout/storage"; /** How often a still pane asks whether its window is still there. */ const CHECK = 500; +/** An empty box keeps the capture running while nothing is drawn. */ +const HIDDEN = new DOMRect(); + +/** + * The page can't draw over the native view, so it hides under any modal, since its backdrop dims + * the whole window, and under a menu, list or popover that reaches the pane. + */ +function isCovered(rect: DOMRect): boolean { + if (document.querySelector('[data-slot="dialog-overlay"]')) return true; + const overlays = document.querySelectorAll('[role="menu"], [role="listbox"], [role="dialog"]'); + return [...overlays].some((overlay) => { + const other = overlay.getBoundingClientRect(); + if (other.right <= rect.left || other.left >= rect.right) return false; + return other.bottom > rect.top && other.top < rect.bottom; + }); +} + /** Another app's window, drawn natively over this pane at its own frame rate. */ export function Live( { id, window, app, active, update }: { @@ -45,16 +62,20 @@ export function Live( const live = desktop?.live; const element = ref.current; if (!live || !element || !active) return; + const box = () => { + const rect = element.getBoundingClientRect(); + return isCovered(rect) ? HIDDEN : rect; + }; const send = () => { const request = relink.current; relink.current = undefined; - live(id, window, element.getBoundingClientRect(), request).then(report).catch(() => {}); + live(id, window, box(), request).then(report).catch(() => {}); }; // Pane drags and window resizes move the box without a React render, so follow it per frame. let last = ""; let frame = requestAnimationFrame(function follow() { frame = requestAnimationFrame(follow); - const rect = element.getBoundingClientRect(); + const rect = box(); const key = `${rect.x},${rect.y},${rect.width},${rect.height}`; if (key === last) return; last = key;