Skip to content

Add Window, Help and About menus to the desktop app - #863

Merged
alexeyzimarev merged 3 commits into
mainfrom
capacitor/agent-bf339e4951cc4e
Sep 10, 2026
Merged

alexeyzimarev merged 3 commits into
mainfrom
capacitor/agent-bf339e4951cc4e

Conversation

@alexeyzimarev

Copy link
Copy Markdown
Member

Closes #861 — AI-2687

What & why

The desktop app's menu bar is Avalonia's fallback app menu alone, and its About opens "About Avalonia". This adds a Window menu (Minimize, Zoom, Toggle Full Screen, Bring All to Front, and Kurrent Capacitor ⌘0 to show the main window), a Help menu (the docs, and GitHub releases as the changelog), and About Kurrent Capacitor opening the standard macOS panel. Each window carries its own Window and Help menus, and AppKitMenus registers them with AppKit whenever a window becomes key, which is what makes macOS add Move to a display, the tiling items, the window list and the Help search field.

Where to look

While no window of ours is key (the main window hidden to the tray, or minimized), Avalonia shows only the app menu, so Window and Help are gone until a window is focused again.

Verification

  • dotnet run --project test/Capacitor.App.Tests.Unit: 1652 passed, 0 failed.
  • dotnet build src/Capacitor.App --no-incremental: 0 warnings.
  • Dev build on macOS: the Window menu showed Fill, Center, Move & Resize, Full Screen Tile and the window list; Help showed the search field; About opened the macOS panel.

Every window carries its own menus because Avalonia makes the key window's menu the menu bar, so while none of ours is key only the app menu shows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-10T15:14:21.744854Z 7aa01a1 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add Window, Help, and About menus to the desktop app

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Adds per-window Window and Help menus with standard desktop actions and shortcuts.
• Registers active menus with AppKit and opens the native macOS About panel.
• Covers menu layout, window behavior, links, and attachment with unit tests.
Diagram

sequenceDiagram
    participant App
    participant Bar as AppMenuBar
    participant Win as Avalonia Window
    participant Bridge as AppKitMenus
    participant Mac as macOS AppKit
    participant Links as LinkPolicy
    App->>Bar: Build app menu
    App->>Bar: Install window handler
    Win->>Bar: Window opened
    Bar->>Win: Attach Window and Help
    Win->>Bridge: Window activated
    Bridge->>Mac: Register native menus
    Bar->>Links: Open help URL
    Bar->>Bridge: Show About
    Bridge->>Mac: Open About panel
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use Avalonia menus only
  • ➕ Avoids direct Objective-C interop.
  • ➕ Keeps menu behavior entirely cross-platform.,
  • ➖ Cannot populate AppKit windowsMenu and helpMenu slots.
  • ➖ Loses native window additions, window listing, and Help search.
  • ➖ Retains Avalonia's fallback About behavior without additional handling.
2. Adopt a full AppKit binding package
  • ➕ Provides typed native APIs instead of raw objc_msgSend calls.
  • ➕ Could support broader macOS integration in future changes.
  • ➖ Adds dependency and packaging overhead for two narrow native operations.
  • ➖ May increase application size and platform-specific maintenance.

Recommendation: Keep the PR's narrow AppKit bridge. Avalonia alone cannot provide the required native Window and Help behavior, while a full binding dependency is disproportionate for menu registration and the standard About panel. If native macOS integration expands materially, replacing the raw bridge with typed bindings should be reconsidered.

Files changed (5) +377 / -1

Enhancement (3) +143 / -1
App.axaml.csInitialize application and per-window desktop menus +8/-1

Initialize application and per-window desktop menus

• Installs the custom application menu during initialization to prevent Avalonia's fallback About item. Registers per-window menu handling before startup can synchronously display the first window.

src/Capacitor.App/App.axaml.cs

AppKitMenus.csBridge Avalonia menus to native AppKit slots +57/-0

Bridge Avalonia menus to native AppKit slots

• Adds a macOS-only Objective-C interop bridge that registers Window and Help submenus with NSApplication. It also opens the standard AppKit About panel using bundle metadata.

src/Capacitor.App/Views/AppKitMenus.cs

AppMenuBar.csBuild and attach Window, Help, and About menus +78/-0

Build and attach Window, Help, and About menus

• Creates per-window menus for minimizing, zooming, full-screen toggling, showing the main window, and bringing visible windows forward. Adds documentation and changelog links, standard shortcuts, resize-aware enablement, and AppKit adoption on activation.

src/Capacitor.App/Views/AppMenuBar.cs

Tests (1) +219 / -0
AppMenuBarTests.csVerify desktop menu layout and actions +219/-0

Verify desktop menu layout and actions

• Tests menu ordering, conditional items, shortcuts, window-state transitions, resize restrictions, activation order, help links, About behavior, and idempotent attachment. Avalonia-dependent cases run through the shared UI session.

test/Capacitor.App.Tests.Unit/AppMenuBarTests.cs

Documentation (1) +15 / -0
CHANGES.mdDocument desktop menu architecture and macOS constraints +15/-0

Document desktop menu architecture and macOS constraints

• Explains why every window owns Window and Help menus, how AppKit registration restores native additions, and why the app supplies its own About item. It also records lifecycle and bundle-version limitations.

docs/CHANGES.md

@qodo-code-review

qodo-code-review Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Menu test comment narrates coverage ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
AppMenuBarTests has an XML comment that only lists the menu layout, actions, and links covered by
the class. Because the test names already expose that scope, the duplicate narration adds no
constraint and can drift when the suite changes.
Code

test/Capacitor.App.Tests.Unit/AppMenuBarTests.cs[R8-9]

+/// The Window and Help menus every window carries: their layout, what each item does to the
+/// window it was built for, and the exact pages Help opens.
Relevance

●●● Strong

Recent precedents accepted removing comments that duplicate test names or merely narrate coverage.

PR-#834
PR-#831

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Compliance rule 2762993 permits comments only when they document non-obvious, behavior-critical
constraints. The added comment merely enumerates the test class's subject matter and coverage.

Rule 2762993: Restrict comments to documenting non-obvious, behavior‑critical constraints
test/Capacitor.App.Tests.Unit/AppMenuBarTests.cs[8-9]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The XML comment on `AppMenuBarTests` merely summarizes test coverage rather than documenting a non-obvious, behavior-critical constraint.

## Fix Focus Areas
- test/Capacitor.App.Tests.Unit/AppMenuBarTests.cs[8-9]

## Recommended Fix
Remove the class-level XML comment. The individual test names already communicate the covered menu behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Startup errors offer a broken command ✓ Resolved 🐞 Bug ☼ Reliability
Description
OnFrameworkInitializationCompleted exposes ShowMainWindow whenever _coordinator is merely
non-null, even after startup has marked that coordinator quit-in-progress and disposed its graph. If
the main-window factory throws after assignment, the subsequently created startup-error window
receives this item, and invoking it retries the failed factory outside StartAsync's catch.
Code

src/Capacitor.App/App.axaml.cs[206]

+            new AppMenuBar(new ShellUrlOpener(), () => desktop.Windows, () => _coordinator is { } c ? c.ShowMainWindow : null).Install();
Relevance

●●● Strong

Recent startup-teardown findings were accepted; stale disposed state exposing unsafe actions matches
established reliability concerns.

PR-#831
PR-#446

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new callback at line 206 considers only whether _coordinator exists. Startup failures
explicitly set QuitInProgress but retain that coordinator, then call HandleStartupFailureAsync,
which disposes the live startup graph before showing a new error window; the global window-open
handler therefore builds that error window's menu from the stale coordinator.

src/Capacitor.App/App.axaml.cs[203-207]
src/Capacitor.App/App.axaml.cs[258-276]
src/Capacitor.App/App.axaml.cs[860-899]
src/Capacitor.App/App.axaml.cs[927-930]
src/Capacitor.App/Views/AppMenuBar.cs[20-29]
src/Capacitor.App/Views/AppMenuBar.cs[48-51]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Startup-error windows can receive a main-window command when `_coordinator` remains non-null after its window factory or later startup work fails. Invoking that command can retry failed or disposed startup state outside the original exception handler.

## Fix Focus Areas
- src/Capacitor.App/App.axaml.cs[205-206]
- src/Capacitor.App/App.axaml.cs[258-276]

## Recommended Fix
Only return the coordinator's `ShowMainWindow` action while the coordinator remains usable, such as by requiring `QuitInProgress` to be false. Add coverage proving a startup-error window does not include the main-window item after the failure path marks the coordinator unusable.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 64 rules
✅ Cross-repo context — repo relationships
Review mode: ⚖️ Balanced: This adds platform-specific native menu integration and multiple runtime interaction paths, including window state, activation, AppKit interop, and startup behavior, so a careful single-pass review is warranted.

Grey Divider

Tip of the day
💡 Did you know, you can turn these tips off under Display preferences

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread test/Capacitor.App.Tests.Unit/AppMenuBarTests.cs Outdated
Comment thread src/Capacitor.App/App.axaml.cs Outdated
alexeyzimarev and others added 2 commits September 10, 2026 17:47
A failed startup keeps its latched coordinator and then opens an error window; offering the item there would re-run the window factory over the torn-down graph.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every op is queued before the toggle, so it can settle before the assertion runs and leave a settled state last; the ubuntu leg failed there under load.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Window, Help and About menus to the desktop app

1 participant