From 150d163fa1f91c2f0b2161e4570e1a9c5ccaa731 Mon Sep 17 00:00:00 2001 From: Peter Pistorius Date: Wed, 29 Apr 2026 12:16:12 +0200 Subject: [PATCH] Refresh tile previews on every overlay show MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cached CGImage from the previous close was being painted as hasRenderedFrame=true, which short-circuited snapshot() on the next show — so the user saw a stale frame until the live stream's first significant-change frame arrived (or forever, for idle windows). Drop the short-circuit so snapshot() always captures a fresh image, kick off snapshot+stream in parallel with the show animation instead of waiting 210ms, and stop gating the display path on the dirty-rect threshold (keep it only for the idle-dot signal) so the tile keeps tracking the source as frames arrive. Co-Authored-By: Claude Opus 4.7 (1M context) --- .changeset/26563231.md | 5 +++++ Sources/cmdcmd/Overlay.swift | 15 ++++++--------- Sources/cmdcmd/Tile.swift | 6 ++---- 3 files changed, 13 insertions(+), 13 deletions(-) create mode 100644 .changeset/26563231.md diff --git a/.changeset/26563231.md b/.changeset/26563231.md new file mode 100644 index 0000000..d4b9ede --- /dev/null +++ b/.changeset/26563231.md @@ -0,0 +1,5 @@ +--- +bump: patch +--- + +Refresh tile previews on every overlay show so live updates aren't masked by the previous capture diff --git a/Sources/cmdcmd/Overlay.swift b/Sources/cmdcmd/Overlay.swift index 4f83358..8b79a2c 100644 --- a/Sources/cmdcmd/Overlay.swift +++ b/Sources/cmdcmd/Overlay.swift @@ -492,15 +492,12 @@ private static func windowMostlyOn(displayBounds: CGRect, window: SCWindow) -> B updateSelection() } let live = config.livePreviewsEnabled - let delay: TimeInterval = config.animations ? Self.pickDuration + 0.05 : 0 - DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [ordered] in - Task { - await withTaskGroup(of: Void.self) { group in - for t in ordered { - group.addTask { - await t.snapshot() - if live { await t.start() } - } + Task { + await withTaskGroup(of: Void.self) { group in + for t in ordered { + group.addTask { + await t.snapshot() + if live { await t.start() } } } } diff --git a/Sources/cmdcmd/Tile.swift b/Sources/cmdcmd/Tile.swift index 6738496..ddb8cd7 100644 --- a/Sources/cmdcmd/Tile.swift +++ b/Sources/cmdcmd/Tile.swift @@ -154,7 +154,6 @@ final class Tile: NSObject, SCStreamOutput, SCStreamDelegate { CATransaction.setDisableActions(true) inner.contents = cached CATransaction.commit() - self.hasRenderedFrame = true } } @@ -320,7 +319,7 @@ final class Tile: NSObject, SCStreamOutput, SCStreamDelegate { } func snapshot() async { - if cancelled || hasRenderedFrame || hasRenderedLiveFrame { return } + if cancelled || hasRenderedLiveFrame { return } let filter = SCContentFilter(desktopIndependentWindow: scWindow) let config = captureConfig(maxDim: Tile.thumbMaxDim) do { @@ -334,6 +333,7 @@ final class Tile: NSObject, SCStreamOutput, SCStreamDelegate { self.content.contents = image CATransaction.commit() self.hasRenderedFrame = true + self.lastSignificantChangeAt = CFAbsoluteTimeGetCurrent() } } catch { Log.write("tile snapshot failed wid=\(scWindow.windowID): \(error)") @@ -435,8 +435,6 @@ final class Tile: NSObject, SCStreamOutput, SCStreamDelegate { } } - guard significantChange || !hasRenderedLiveFrame else { return } - self.lastPixelBuffer = pixelBuffer if suppressFrames { return }