fix(macos): refresh menu sessions without resizing
This commit is contained in:
@@ -70,6 +70,7 @@
|
|||||||
- Remote SSH tunnels now get health checks; Debug → Ports highlights unhealthy tunnels and offers Reset SSH tunnel.
|
- Remote SSH tunnels now get health checks; Debug → Ports highlights unhealthy tunnels and offers Reset SSH tunnel.
|
||||||
- Menu bar session/node sections no longer reflow while open, keeping hover highlights aligned.
|
- Menu bar session/node sections no longer reflow while open, keeping hover highlights aligned.
|
||||||
- Menu hover highlights now span the full width (including submenu arrows).
|
- Menu hover highlights now span the full width (including submenu arrows).
|
||||||
|
- Menu session rows now refresh while open without width changes (no more stuck “Loading sessions…”).
|
||||||
|
|
||||||
### Nodes & Canvas
|
### Nodes & Canvas
|
||||||
- Debug status overlay gated and toggleable on macOS/iOS/Android nodes.
|
- Debug status overlay gated and toggleable on macOS/iOS/Android nodes.
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ final class MenuSessionsInjector: NSObject, NSMenuDelegate {
|
|||||||
private var nodesLoadTask: Task<Void, Never>?
|
private var nodesLoadTask: Task<Void, Never>?
|
||||||
private var isMenuOpen = false
|
private var isMenuOpen = false
|
||||||
private var lastKnownMenuWidth: CGFloat?
|
private var lastKnownMenuWidth: CGFloat?
|
||||||
|
private var menuOpenWidth: CGFloat?
|
||||||
|
|
||||||
private var cachedSnapshot: SessionStoreSnapshot?
|
private var cachedSnapshot: SessionStoreSnapshot?
|
||||||
private var cachedErrorText: String?
|
private var cachedErrorText: String?
|
||||||
@@ -48,27 +49,38 @@ final class MenuSessionsInjector: NSObject, NSMenuDelegate {
|
|||||||
func menuWillOpen(_ menu: NSMenu) {
|
func menuWillOpen(_ menu: NSMenu) {
|
||||||
self.originalDelegate?.menuWillOpen?(menu)
|
self.originalDelegate?.menuWillOpen?(menu)
|
||||||
self.isMenuOpen = true
|
self.isMenuOpen = true
|
||||||
|
self.menuOpenWidth = self.currentMenuWidth(for: menu)
|
||||||
|
|
||||||
self.inject(into: menu)
|
self.inject(into: menu)
|
||||||
self.injectNodes(into: menu)
|
self.injectNodes(into: menu)
|
||||||
|
|
||||||
// Refresh in background for the next open (but do not re-inject while open).
|
// Refresh in background for the next open; keep width stable while open.
|
||||||
self.loadTask?.cancel()
|
self.loadTask?.cancel()
|
||||||
self.loadTask = Task { [weak self] in
|
self.loadTask = Task { [weak self] in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
await self.refreshCache(force: false)
|
await self.refreshCache(force: false)
|
||||||
|
await MainActor.run {
|
||||||
|
guard self.isMenuOpen else { return }
|
||||||
|
self.inject(into: menu)
|
||||||
|
self.injectNodes(into: menu)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.nodesLoadTask?.cancel()
|
self.nodesLoadTask?.cancel()
|
||||||
self.nodesLoadTask = Task { [weak self] in
|
self.nodesLoadTask = Task { [weak self] in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
await self.nodesStore.refresh()
|
await self.nodesStore.refresh()
|
||||||
|
await MainActor.run {
|
||||||
|
guard self.isMenuOpen else { return }
|
||||||
|
self.injectNodes(into: menu)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func menuDidClose(_ menu: NSMenu) {
|
func menuDidClose(_ menu: NSMenu) {
|
||||||
self.originalDelegate?.menuDidClose?(menu)
|
self.originalDelegate?.menuDidClose?(menu)
|
||||||
self.isMenuOpen = false
|
self.isMenuOpen = false
|
||||||
|
self.menuOpenWidth = nil
|
||||||
self.loadTask?.cancel()
|
self.loadTask?.cancel()
|
||||||
self.nodesLoadTask?.cancel()
|
self.nodesLoadTask?.cancel()
|
||||||
}
|
}
|
||||||
@@ -736,6 +748,9 @@ final class MenuSessionsInjector: NSObject, NSMenuDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func initialWidth(for menu: NSMenu) -> CGFloat {
|
private func initialWidth(for menu: NSMenu) -> CGFloat {
|
||||||
|
if let openWidth = self.menuOpenWidth {
|
||||||
|
return max(300, openWidth)
|
||||||
|
}
|
||||||
let candidates: [CGFloat] = [
|
let candidates: [CGFloat] = [
|
||||||
menu.size.width,
|
menu.size.width,
|
||||||
menu.minimumWidth,
|
menu.minimumWidth,
|
||||||
@@ -785,9 +800,21 @@ final class MenuSessionsInjector: NSObject, NSMenuDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func captureMenuWidthIfAvailable(from view: NSView) {
|
private func captureMenuWidthIfAvailable(from view: NSView) {
|
||||||
|
guard !self.isMenuOpen else { return }
|
||||||
guard let width = view.window?.contentView?.bounds.width, width > 0 else { return }
|
guard let width = view.window?.contentView?.bounds.width, width > 0 else { return }
|
||||||
self.lastKnownMenuWidth = max(300, width)
|
self.lastKnownMenuWidth = max(300, width)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func currentMenuWidth(for menu: NSMenu) -> CGFloat {
|
||||||
|
let candidates: [CGFloat] = [
|
||||||
|
menu.size.width,
|
||||||
|
menu.minimumWidth,
|
||||||
|
self.lastKnownMenuWidth ?? 0,
|
||||||
|
self.fallbackWidth,
|
||||||
|
]
|
||||||
|
let resolved = candidates.max() ?? self.fallbackWidth
|
||||||
|
return max(300, resolved)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|||||||
Reference in New Issue
Block a user