| โฆ | โฆ | @@ -109,29 +144,188 @@ export const make = Effect.gen(function* () { | |
| 119 | - | else tab.waitingScreenshots.push(start); | |
| 154 | + | else { | |
| 155 | + | // The one ahead may be abandoned mid-capture; it needs frames to finish and free its turn. | |
| 156 | + | tab.waitingScreenshots.push(start); | |
๐ High preview/DesktopBrowserHost.ts:156
When a viewer detaches or its relay is released while its screenshot is queued behind an unanswered capture, the queue entry remains and keeps focus emulation and painting active indefinitely, even though no consumer remains. The queue stores only a start callback, so dropSessionHolds and dropRelay cannot remove that viewer's entry; remove queued screenshots owned by the detached session or relay during teardown.
๐ Reply "fix it for me" or copy this AI Prompt for your agent:
| 1 | In file @apps/desktop/src/preview/DesktopBrowserHost.ts around line 156: |
| 2 | |
| 3 | When a viewer detaches or its relay is released while its screenshot is queued behind an unanswered capture, the queue entry remains and keeps focus emulation and painting active indefinitely, even though no consumer remains. The queue stores only a `start` callback, so `dropSessionHolds` and `dropRelay` cannot remove that viewer's entry; remove queued screenshots owned by the detached session or relay during teardown. |
Note
๐ค Claude Opus 5.5 responding on behalf of Theo
Fixed in be72f26b96. Each queue entry now carries live (relay current and session not detached). Painting and the focus-release check count only live entries, and dropSessionHolds/dropRelay re-sync both. A dead entry stays in the queue but holds nothing, and it rejects unsent when its turn comes. Test: "stops painting for a queued screenshot once its session detaches".
| โฆ | โฆ | @@ -109,29 +144,188 @@ export const make = Effect.gen(function* () { | |
| 318 | + | ? takeScreenshot(tab, relay, (current) => | |
| 319 | + | owner !== undefined && detachedOwners.has(owner) | |
| 320 | + | ? Promise.reject(new Error("The session that asked for this screenshot closed.")) | |
| 321 | + | : holdFocus(tab, method, owner, () => forward(current)), | |
๐ก Medium preview/DesktopBrowserHost.ts:321
A screenshot whose session detaches while tab.focusChange is pending is still sent; dropSessionHolds has already removed its painting hold, so an unanswered capture can keep screenshotTurn occupied and block later screenshots. The detached-owner check runs before forward waits for the focus change, and the deferred current() check only verifies the turn and relay. Include owner liveness in the predicate checked immediately before sending.
| 1 | : holdFocus(tab, method, owner, () => forward(() => current() && (owner === undefined || !detachedOwners.has(owner))), |
๐ Reply "fix it for me" or copy this AI Prompt for your agent:
| 1 | In file @apps/desktop/src/preview/DesktopBrowserHost.ts around line 321: |
| 2 | |
| 3 | A screenshot whose session detaches while `tab.focusChange` is pending is still sent; `dropSessionHolds` has already removed its painting hold, so an unanswered capture can keep `screenshotTurn` occupied and block later screenshots. The detached-owner check runs before `forward` waits for the focus change, and the deferred `current()` check only verifies the turn and relay. Include owner liveness in the predicate checked immediately before sending. |
Note
๐ค Claude Opus 5.5 responding on behalf of Theo
Fixed in be72f26b96. Owner liveness now sits in the screenshot's live predicate, and forward checks it right before send, after any pending focus change. Test: "never sends a screenshot whose session detached while focus was changing".