Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
---

Expand Down
Original file line number Diff line number Diff line change
@@ -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
25 changes: 23 additions & 2 deletions apps/app/src/panels/live.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 }: {
Expand Down Expand Up @@ -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;
Expand Down