diff --git a/CHANGELOG.md b/CHANGELOG.md index bf93da8ff..2a8cee389 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ Programa is a fork of [cmux](https://github.com/manaflow-ai/cmux); for history p - The library-validation and unsigned-executable-memory entitlements. ### Fixed +- The Window menu's Fill, Center, Move & Resize, Full Screen Tile and Move to Display items, and their shortcuts, work again. The main window had been marked not movable, which makes macOS disable all of them. - Port chips in the sidebar now show up again for terminals restored after relaunching Programa. The restored terminals kept their running shell, but port scanning never learned about them. - A window closed with the red button no longer comes back on the next launch. Since 0.5.0 a closed window was kept alive for Dock reopen and then saved and restored like a visible one, so every restart opened an extra window of stale workspaces. Closed windows now stay closed across a relaunch and their shells are ended at startup. - The agent status badge in a sidebar row stays readable in a narrow sidebar; the tab title is cut short instead. While the badge shows, a spinner character at the start of a program-set title (such as Claude Code's "◐" or "✳") is hidden, since the badge already shows the state. diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index df29cdfa4..6c3bfbb84 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -4757,8 +4757,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser window.title = "" window.titleVisibility = .hidden window.titlebarAppearsTransparent = true + // Keep the window movable: AppKit validates Window > Fill / Center / + // Move & Resize / Move to Display (and their shortcuts) against + // `isMovable`. Native titlebar drag stays off because + // MainWindowHostingView returns false from `mouseDownCanMoveWindow`; + // explicit drag surfaces use `performDrag` instead. window.isMovableByWindowBackground = false - window.isMovable = false let restoredFrame = resolvedWindowFrame(from: sessionWindowSnapshot) if let restoredFrame { window.setFrame(restoredFrame, display: false) @@ -8135,16 +8139,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser if !window.titlebarAppearsTransparent { window.titlebarAppearsTransparent = true } - // Keep window immovable; the sidebar's WindowDragHandleView handles - // drag-to-move via performDrag with temporary movable override. - // isMovableByWindowBackground=true breaks tab reordering, and - // isMovable=true blocks clicks on sidebar buttons in minimal mode. + // isMovableByWindowBackground=true breaks sidebar tab reordering, so + // drag-to-move goes through WindowDragHandleView's performDrag. The + // window itself stays movable (see createMainWindow) so the Window menu + // tiling items validate; MainWindowHostingView's mouseDownCanMoveWindow + // override keeps clicks on sidebar buttons from becoming native drags. if window.isMovableByWindowBackground { window.isMovableByWindowBackground = false } - if window.isMovable { - window.isMovable = false - } if !window.styleMask.contains(.fullSizeContentView) { window.styleMask.insert(.fullSizeContentView) } diff --git a/Sources/MainWindowHostingView.swift b/Sources/MainWindowHostingView.swift index e05b1f992..e7ea4eaf7 100644 --- a/Sources/MainWindowHostingView.swift +++ b/Sources/MainWindowHostingView.swift @@ -19,6 +19,12 @@ final class MainWindowHostingView: NSHostingView, MainWi override var safeAreaRect: NSRect { bounds } override var safeAreaLayoutGuide: NSLayoutGuide { zeroSafeAreaLayoutGuide } + /// The window stays `isMovable` so AppKit enables the Window menu tiling + /// items, but SwiftUI content under the transparent titlebar must not turn + /// a click into a native window drag (sidebar buttons in minimal mode). + /// Explicit drag surfaces call `performDrag` themselves. + override var mouseDownCanMoveWindow: Bool { false } + required init(rootView: Content) { super.init(rootView: rootView) // Tell SwiftUI to ignore safe areas outright, rather than relying only on