From 44a05a88aad347916fd2447bf29b637d553238b7 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 6 Aug 2026 13:03:45 -0700 Subject: [PATCH] macos: discard debounced selection notification Discard the selection notification payload before debouncing accessibility changes. The debouncer previously retained the notification and its surface object, keeping a closed tab's view and PTY alive after the undo timeout. --- .../Ghostty/Surface View/SurfaceView_AppKit.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift b/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift index 0ce244873..fe2a7730a 100644 --- a/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift +++ b/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift @@ -294,11 +294,20 @@ extension Ghostty { accessibilitySelectionCancellable = NotificationCenter.default // The publisher retains its object, so filtering with a weak capture // avoids a cycle between self and the stored cancellable. + // But we also need to be careful to do the map below (see + // comment below) .publisher(for: .ghosttySelectionDidChange) .filter { [weak self] notification in guard let self else { return false } return notification.object as AnyObject? === self } + .map { + // Debounce retains its latest upstream value. In this + // case its a Notification, which retains its object, + // which is a surface. So this creates a retain cycle. + // This discards the notification before debounce. + _ in () + } .debounce(for: .milliseconds(100), scheduler: DispatchQueue.main) .sink { [weak self] _ in guard let self else { return }