From 5fc2903222153f1a6974e7650066a005e3d21538 Mon Sep 17 00:00:00 2001 From: Rajin Date: Tue, 6 Oct 2026 16:06:51 +1100 Subject: [PATCH] fix(release): stop calling DEBUG-only focus and log helpers from release code debugDesiredFocusState() and cmuxDebugLog exist only in DEBUG, so the Mac Release build failed to compile. Expose a read-only isFocusDesired inside the device-terminal-focus fence (#817) and guard the route-candidate log. --- .../Sources/CmuxTerminal/Surface/TerminalSurface.swift | 3 +++ SUPERMUX-TOUCHPOINTS.md | 2 +- .../Supermux/Devices/SupermuxDeviceTerminalActions.swift | 6 +++--- Sources/Supermux/Devices/SupermuxRouteCandidateSync.swift | 2 ++ 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface.swift b/Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface.swift index 0727b50cdc..6413e63d83 100644 --- a/Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface.swift +++ b/Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface.swift @@ -415,6 +415,9 @@ public final class TerminalSurface: Identifiable, ObservableObject { /// on the main thread). A device mirror sends it to the Mac that runs the /// terminal. public var onFocusStateChange: ((Bool) -> Void)? + /// Read-only ``desiredFocusState`` for release builds, where the DEBUG-only + /// `debugDesiredFocusState()` does not exist. + public var isFocusDesired: Bool { desiredFocusState } // SUPERMUX:end device-terminal-focus /// Whether this model still owns its logical surface-registry entry. diff --git a/SUPERMUX-TOUCHPOINTS.md b/SUPERMUX-TOUCHPOINTS.md index 13d783c6fb..55d4a64b5d 100644 --- a/SUPERMUX-TOUCHPOINTS.md +++ b/SUPERMUX-TOUCHPOINTS.md @@ -815,7 +815,7 @@ Rules for adding a touchpoint: | 814 | `Sources/Workspace.swift` | `device-terminal-focus` | In `makeRemoteTmuxPanePanel`, sets the new surface's `onFocusStateChange` (#817) to `SupermuxDeviceTerminalActions.focusChanged`, which sends a device mirror pane's settled focus to the owning Mac (`focus_in` / `focus_out`) | | 815 | `Sources/Panels/TerminalPanel.swift` | `device-terminal-actions` | In `performBindingAction`, `SupermuxDeviceTerminalActions.perform(action, on: surface, locally:)` runs `clear_screen`/`reset` on another Mac's terminal too (the `surface.clear_history` socket path); `nil` keeps upstream's path | | 816 | `Sources/GhosttyTerminalView.swift` | `device-terminal-actions` | Two fences. In `keyDown`, right after the surface is ready, a device mirror pane (`ioMode == .manualMirror`) with Cmd or Ctrl held asks `handleDeviceTerminalKey`; the second fence adds that helper next to `ghosttyConsumeMenuAction`: Ctrl+V with only an image on the clipboard runs `paste(nil)` (the Cmd+V upload), and a key bound to `clear_screen`/`reset` is consumed and runs `SupermuxDeviceTerminalActions.perform` (local clear + the owning Mac's) | -| 817 | `Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface.swift` | `device-terminal-focus` | `desiredFocusState` gets a `didSet` that calls the new `public var onFocusStateChange: ((Bool) -> Void)?` when the value changes (upstream: a plain stored `var desiredFocusState: Bool = false`) | +| 817 | `Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface.swift` | `device-terminal-focus` | `desiredFocusState` gets a `didSet` that calls the new `public var onFocusStateChange: ((Bool) -> Void)?` when the value changes, plus a read-only `public var isFocusDesired` for release builds (upstream: a plain stored `var desiredFocusState: Bool = false`) | | 818 | `cmuxTests/SupermuxMobileAuthorizationTests.swift` | `device-terminal-actions-authz` | `classificationCoversWorkspacePaneAndMacWideMethods` expects `terminal.action` to be terminal-scoped (it names `workspace_id` and `terminal_id`, like `terminal.select`) | | 830 | `Sources/AppDelegate.swift` | `remote-host-mode` | Remote Host Mode (`Sources/Supermux/RemoteHost/SupermuxRemoteHostMode.swift`). Five fenced sites: the top of `applicationShouldHandleReopen` (`showsWindowsOnReopen()`: reopening a headless host shows its windows), the end of `syncApplicationPresentationPreferences` after `syncMenuBarExtraVisibility` (`syncToSettings()`: the setting turning on hides every main window, off shows them), the show block near the end of `createMainWindow` (an `if keepsNewMainWindowHidden(window) { window.orderOut(nil) } else` prefix to upstream's `if !shouldActivate || …` so a new window stays hidden while headless), the top of `handleMainTerminalWindowCloseRequest` (the close button / `performClose` hides the window: `return false`), and `closeWindowWithConfirmation` after its `isMainTerminalWindow` guard (Close Window hides with no dialog) | | 831 | `Sources/App/MenuBarExtraController.swift` | `remote-host-mode` | Four fenced sites: a stored `supermuxRemoteHostItems` (`SupermuxRemoteHostModeMenuItems`), installed right after `showMainWindowItem` in `buildMenu()` and refreshed after its `isHidden` line in `refreshUI(snapshot:)` (Show/Hide Supermux and Turn Off Remote Host Mode; hides upstream's Show cmux while the mode is on), `MenuBarExtraSettings.shouldInstallMenuBarExtra` returns true while the mode is on, and `MenuBarOnlySettings.activationPolicy` returns `.accessory` while the mode is on (no Dock icon) | diff --git a/Sources/Supermux/Devices/SupermuxDeviceTerminalActions.swift b/Sources/Supermux/Devices/SupermuxDeviceTerminalActions.swift index b5d05593b3..82ab61858b 100644 --- a/Sources/Supermux/Devices/SupermuxDeviceTerminalActions.swift +++ b/Sources/Supermux/Devices/SupermuxDeviceTerminalActions.swift @@ -104,7 +104,7 @@ enum SupermuxDeviceTerminalActions { focusSettles[panelID] = nil guard let surface, deviceTerminal(for: surface) != nil else { return } let onScreen = SupermuxTerminalSizingVisibility.isOnScreen(surface) - let focused = surface.debugDesiredFocusState() && onScreen + let focused = surface.isFocusDesired && onScreen applyGhosttyFocus(surface, onScreen: onScreen) // Nothing sent yet counts as unfocused, so a pane built hidden // never takes focus from whoever uses that terminal on its Mac. @@ -118,11 +118,11 @@ enum SupermuxDeviceTerminalActions { private static func applyGhosttyFocus(_ surface: TerminalSurface, onScreen: Bool) { let panelID = surface.id if !onScreen { - guard surface.debugDesiredFocusState(), + guard surface.isFocusDesired, let live = surface.liveSurfaceForGhosttyAccess(reason: "supermux.mirrorFocus.offScreen") else { return } ghostty_surface_set_focus(live, false) unfocusedOffScreen.insert(panelID) - } else if unfocusedOffScreen.remove(panelID) != nil, surface.debugDesiredFocusState() { + } else if unfocusedOffScreen.remove(panelID) != nil, surface.isFocusDesired { // `force`: cmux's record already says focused, so only the // runtime surface (and its display id) is brought back. surface.setFocus(true, force: true) diff --git a/Sources/Supermux/Devices/SupermuxRouteCandidateSync.swift b/Sources/Supermux/Devices/SupermuxRouteCandidateSync.swift index 1050c67ba0..dfa70485e2 100644 --- a/Sources/Supermux/Devices/SupermuxRouteCandidateSync.swift +++ b/Sources/Supermux/Devices/SupermuxRouteCandidateSync.swift @@ -89,9 +89,11 @@ final class SupermuxRouteCandidateSync { let outcome = await ask(device) schedules[instance]?.finished(outcome.answer, at: Date()) if outcome == .stored { storedCount += 1 } + #if DEBUG if outcome != .stored, outcome != .unsupported { cmuxDebugLog("supermux.route candidates from \(instance.deviceID.prefix(8)): \(outcome.rawValue)") } + #endif return outcome }