feat(v3/darwin): prepare AppKit layer for macOS 27 compatibility#5760
feat(v3/darwin): prepare AppKit layer for macOS 27 compatibility#5760taliesin-ai wants to merge 5 commits into
Conversation
macOS 27 deprecates NSResponder mouse overrides, synthesised mouse NSEvents and NSEvent monitors as input paths (TN3212); they also miss Sidecar/touch-synthesised input. It further hides menu-item images by default, tightens the default window corner radius, and warns against managing grouped glass surfaces as loose siblings. - Replace the app-global NSEvent left-mouse monitors with a per-window NSGestureRecognizer observer that forwards mouseDown/mouseUp to the window delegate (title-bar drag and JS-initiated drag are unchanged). The observer never claims the gesture, so webview event delivery is unaffected. - Replace the system-tray pre-click NSEvent monitor with the same observer pattern on the status-item button, and the synthesised mouseDown: in showMenu with performClick:. - Opt menu-item images back in explicitly via preferredImageVisibility (runtime-guarded; no-op on macOS <= 26 where the selector is absent). - Host grouped Liquid Glass surfaces in an NSGlassEffectContainerView (with spacing) instead of the non-functional groupIdentifier KVC. - Rescue the webview before stripping glass views in windowRemoveVisualEffects and retain it across reparenting: the view hierarchy holds the only retain, so re-applying glass previously deallocated the webview (latent use-after-free). - Adopt NSViewCornerConfiguration containerConcentric for frameless windows when available (macOS 27), keeping the 8.0 radius fallback.
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Validated against the macOS 27.0 SDK (Xcode 27.0 beta 2, 27A5209h): - NSView.cornerConfiguration is readonly and subclass-overridable, not settable as previously assumed. Frameless windows now use a WailsFramelessContentView that returns a container-concentric configuration and applies the system-resolved effectiveCornerRadii to its backing layer in viewDidChangeEffectiveCornerRadii, falling back to the historic 8.0 when the radii resolve to nothing. Inert on <= 26. - NSMenuItem.preferredImageVisibility and NSGlassEffectContainerView usages confirmed to match the 27 SDK headers exactly; no changes. - SDK 27 deprecation triage: the only warnings in the darwin layer are 10.14-era (NSFilenamesPboardType, legacy NSVisualEffectMaterial values in the pre-26 fallback, NSStatusItem setAttributedTitle:); macOS 27 deprecates nothing new in our usage. NSEvent monitors and NSResponder mouse overrides carry no formal API_DEPRECATED in the 27 SDK - the migration pressure is TN3212 guidance plus Sidecar/touch routing.
Visual parity verification: Liquid Glass is unchanged (pixel-identical)Concern raised: the glass refactor (container grouping + webview rescue) must not lose or alter the existing Liquid Glass effect. Method: built the Results:
Note on the grouped path: on macOS 26.3 the Remaining (unchanged): the same visual pass on an actual macOS 27 seed once a VM/box exists, where the system-wide transparency slider range also needs checking per the spec. Branch:
Master:
|
Review feedback: the gesture-recognizer migration and performClick: change should not alter macOS 26 behaviour. Wrap every input-path change in an @available(macOS 27.0, *) check so <= 26 keeps its exact original code: - App-global NSEvent left-mouse monitors: restored on <= 26; the per-window WailsWindowMouseGestureObserver is attached only on 27+. - System tray: the pre-click NSEvent monitor is restored on <= 26; the status-item gesture observer is used only on 27+. Controller keeps both eventMonitor and gestureObserver; teardown releases whichever is set. - showMenu: the synthesised mouseDown: NSEvent path is restored on <= 26; performClick: is used only on 27+. - Liquid Glass grouping: the NSGlassEffectContainerView branch runs only on 27+; <= 26 keeps the historic bare glass view (the old groupID KVC was a no-op anyway, so no behaviour is lost). Menu-item preferredImageVisibility and the frameless corner configuration were already inert on <= 26 (guarded by respondsToSelector / an overridden readonly property the OS never calls), so they are unchanged. Verified on macOS 26.3 (Tart VM): injected a title-bar drag with CGEvent on a frameless invisible-title-bar window built from this branch; the window frame tracked the cursor delta exactly, confirming the <= 26 monitor path still drives dragging.
…orktree-macos27-appkit-prep
Fix: macOS 27 input changes are now guarded; macOS ≤26 behaviour is byte-for-byte unchangedGood catch on the review — the earlier revision replaced the macOS 26
Empirical proof drag still works on macOS 26.3Built a frameless invisible-title-bar window from this branch, ran it in a macOS 26.3 Tart VM, and injected a real title-bar drag with Rebuilt clean against both the 26.5 and 27.0 SDKs after the change; |




What
Implements the P0 items of the macOS 27 "Golden Gate" AppKit compatibility spec for the v3 native macOS layer. Goal: no regressions in window chrome, native menus, or Liquid Glass when users upgrade to macOS 27 (beta 2 now, GA expected fall 2026).
Update: all 27-only API usage has now been validated against the real macOS 27.0 SDK (Xcode 27.0 beta 2, 27A5209h) — see P0-5 below.
P0-1 Gesture recognizer migration (TN3212)
Audit result: the native layer has no
mouseDown:/mouseDragged:/mouseUp:NSResponder overrides and nonextEventMatching:loops. The deprecated input patterns it did have:NSEventlocal monitors driving invisible-title-bar drag and the JSdrag()event cache → replaced with a per-windowWailsWindowMouseGestureObserver(anNSGestureRecognizerthat forwardsmouseDown:/mouseUp:to the window delegate and never claims the gesture, so webview event delivery is untouched). Gesture recognizers are the only input path that receives Sidecar/touch-synthesised events on macOS 27.NSEventmonitor → same observer pattern attached to the status-item button (rightMouseDown:included).showMenusynthesised a fakemouseDown:NSEvent → now[statusItem.button performClick:nil]. (Likely relevant to [v3][macOS 27] Tray menu fails to open/render on click #5752 — tray menu reported broken on 27; the synthetic-event path is exactly what 27 stopped honouring. Verified no regression on 26 via VM + golwg; on-27 confirmation still pending.)API_DEPRECATEDon monitors/responder overrides — the migration pressure is TN3212 guidance + Sidecar routing, so no compiler warnings would have flagged this.P0-2 Menu icon visibility
All icon-bearing menu items funnel through
setMenuItemBitmap; it now setspreferredImageVisibilityto visible (runtime-guarded — the selector doesn't exist on ≤26, where images are always shown). Confirmed against the 27 SDK headers:NSMenuItemImageVisibilityVisible = 1,API_AVAILABLE(macos(27.0))— matches the implementation exactly.P0-3 Liquid Glass
NSGlassEffectViewpath already embeds the webview in the glasscontentView— the sibling-behind-content anti-pattern only exists in theNSVisualEffectViewfallback used on ≤15, where the warning doesn't apply. No refactor needed there.groupIdentifier/groupNameKVC was a silent no-op (no such properties exist onNSGlassEffectView). Glass windows with aGroupIDare now hosted in anNSGlassEffectContainerView(withGroupSpacing→spacing). Confirmed against the 27 SDK: container API is unchanged from 26 (contentView+spacing); 27 only addsNSGlassEffectView.effectIsInteractive, which we don't need for parity.windowRemoveVisualEffectsremoved the glass view while the webview lived inside it — the hierarchy holds the only retain on the webview (webViewproperty isassign), so re-applying glass deallocated it. The webview is now rescued back to the content view first, and retained across reparenting.P0-4 Corner radius / concentricity
Reworked after checking the real 27 SDK:
NSView.cornerConfigurationis readonly and subclass-overridable, not settable. Frameless windows now useWailsFramelessContentView, which returns a container-concentric configuration (NSViewCornerRadius.containerConcentricRadiusviaconfigurationWithRadius:) and applies the system-resolvedeffectiveCornerRadiito its backing layer inviewDidChangeEffectiveCornerRadii, falling back to the historic8.0if the radii resolve to zero. On ≤26 none of this machinery is invoked and the hardcoded8.0stands. Glass corner radius was already only set when explicitly configured, so unconfigured windows inherit the new system default automatically.P0-5 Build / smoke test
v3/pkg/...+go vet+go test ./pkg/application/against both SDK 26.5 (Xcode 26.5) and macOS 27.0 SDK (Xcode 27.0 beta 2, 27A5209h).NSFilenamesPboardTypein drag-and-drop, legacyNSVisualEffectMaterialvalues in the pre-26 fallback,NSStatusItem.setAttributedTitle:) — candidates for an unrelated cleanup PR.performClick:change.P1 audit (non-blocking, all N/A today)
NSControl.Events: no hand-rolled event dispatch on customNSControlsubclasses exists.NSToolbarItemGroupRole/NSSegmentedControlRole(.tabs): noNSToolbar/NSSegmentedControltab navigation in the runtime.Spec open questions
Remaining before GA
performClick:fixes [v3][macOS 27] Tray menu fails to open/render on click #5752 on 27 — have been header-validated but never executed on a 27 runtime.go run ./examples/liquid-glass), tray click menus, glass visuals.