fix(macos): convert Main.storyboard using programmatic AppKit APIs#2836
Merged
Conversation
tido64
commented
Jul 3, 2026
Comment on lines
+76
to
+93
| return NSMenuItem(submenu: title, items: [ | ||
| NSMenuItem(title: "About \(title)", action: #selector(NSApplication.orderFrontStandardAboutPanel(_:))), | ||
| .separator(), | ||
| NSMenuItem(title: "Preferences…", keyEquivalent: ","), | ||
| .separator(), | ||
| NSMenuItem(submenu: servicesMenu), | ||
| .separator(), | ||
| NSMenuItem(title: "Hide \(title)", action: #selector(NSApplication.hide(_:)), keyEquivalent: "h"), | ||
| NSMenuItem( | ||
| title: "Hide Others", | ||
| action: #selector(NSApplication.hideOtherApplications(_:)), | ||
| keyEquivalent: "h", | ||
| keyEquivalentModifierMask: [.option, .command] | ||
| ), | ||
| NSMenuItem(title: "Show All", action: #selector(NSApplication.unhideAllApplications(_:))), | ||
| .separator(), | ||
| NSMenuItem(title: "Quit \(title)", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"), | ||
| ]) |
Comment on lines
+97
to
+116
| NSMenuItem(submenu: "File", items: [ | ||
| NSMenuItem(title: "New", action: #selector(NSDocumentController.newDocument(_:)), keyEquivalent: "n"), | ||
| NSMenuItem(title: "Open…", action: #selector(NSDocumentController.openDocument(_:)), keyEquivalent: "o"), | ||
| NSMenuItem(submenu: "Open Recent", items: [ | ||
| NSMenuItem(title: "Clear Menu", action: #selector(NSDocumentController.clearRecentDocuments(_:))), | ||
| ]), | ||
| .separator(), | ||
| NSMenuItem(title: "Close", action: #selector(NSWindow.performClose(_:)), keyEquivalent: "w"), | ||
| NSMenuItem(title: "Save…", action: #selector(NSDocument.save(_:)), keyEquivalent: "s"), | ||
| NSMenuItem(title: "Save As…", action: #selector(NSDocument.saveAs(_:)), keyEquivalent: "S"), | ||
| NSMenuItem(title: "Revert to Saved", action: #selector(NSDocument.revertToSaved(_:)), keyEquivalent: "r"), | ||
| .separator(), | ||
| NSMenuItem( | ||
| title: "Page Setup…", | ||
| action: #selector(NSDocument.runPageLayout(_:)), | ||
| keyEquivalent: "P", | ||
| keyEquivalentModifierMask: [.shift, .command] | ||
| ), | ||
| NSMenuItem(title: "Print…", action: #selector(NSView.printView(_:)), keyEquivalent: "p"), | ||
| ]) |
Comment on lines
+120
to
+239
| NSMenuItem(submenu: "Edit", items: [ | ||
| NSMenuItem(title: "Undo", action: Selector(("undo:")), keyEquivalent: "z"), | ||
| NSMenuItem(title: "Redo", action: Selector(("redo:")), keyEquivalent: "Z"), | ||
| .separator(), | ||
| NSMenuItem(title: "Cut", action: #selector(NSText.cut(_:)), keyEquivalent: "x"), | ||
| NSMenuItem(title: "Copy", action: #selector(NSText.copy(_:)), keyEquivalent: "c"), | ||
| NSMenuItem(title: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v"), | ||
| NSMenuItem( | ||
| title: "Paste and Match Style", | ||
| action: #selector(NSTextView.pasteAsPlainText(_:)), | ||
| keyEquivalent: "V", | ||
| keyEquivalentModifierMask: [.option, .command] | ||
| ), | ||
| NSMenuItem(title: "Delete", action: #selector(NSText.delete(_:))), | ||
| NSMenuItem(title: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a"), | ||
| .separator(), | ||
| NSMenuItem(submenu: "Find", items: [ | ||
| NSMenuItem( | ||
| title: "Find…", | ||
| action: #selector(NSTextView.performFindPanelAction(_:)), | ||
| keyEquivalent: "f", | ||
| tag: 1 | ||
| ), | ||
| NSMenuItem( | ||
| title: "Find and Replace…", | ||
| action: #selector(NSTextView.performFindPanelAction(_:)), | ||
| keyEquivalent: "f", | ||
| keyEquivalentModifierMask: [.option, .command], | ||
| tag: 12 | ||
| ), | ||
| NSMenuItem( | ||
| title: "Find Next", | ||
| action: #selector(NSTextView.performFindPanelAction(_:)), | ||
| keyEquivalent: "g", | ||
| tag: 2 | ||
| ), | ||
| NSMenuItem( | ||
| title: "Find Previous", | ||
| action: #selector(NSTextView.performFindPanelAction(_:)), | ||
| keyEquivalent: "G", | ||
| tag: 3 | ||
| ), | ||
| NSMenuItem( | ||
| title: "Use Selection for Find", | ||
| action: #selector(NSTextView.performFindPanelAction(_:)), | ||
| keyEquivalent: "e", | ||
| tag: 7 | ||
| ), | ||
| NSMenuItem( | ||
| title: "Jump to Selection", | ||
| action: #selector(NSTextView.centerSelectionInVisibleArea(_:)), | ||
| keyEquivalent: "j" | ||
| ), | ||
| ]), | ||
| NSMenuItem(submenu: "Spelling and Grammar", items: [ | ||
| NSMenuItem( | ||
| title: "Show Spelling and Grammar", | ||
| action: #selector(NSText.showGuessPanel(_:)), | ||
| keyEquivalent: ":" | ||
| ), | ||
| NSMenuItem( | ||
| title: "Check Document Now", | ||
| action: #selector(NSText.checkSpelling(_:)), | ||
| keyEquivalent: ";" | ||
| ), | ||
| .separator(), | ||
| NSMenuItem( | ||
| title: "Check Spelling While Typing", | ||
| action: #selector(NSTextView.toggleContinuousSpellChecking(_:)) | ||
| ), | ||
| NSMenuItem( | ||
| title: "Check Grammar With Spelling", | ||
| action: #selector(NSTextView.toggleGrammarChecking(_:)) | ||
| ), | ||
| NSMenuItem( | ||
| title: "Correct Spelling Automatically", | ||
| action: #selector(NSTextView.toggleAutomaticSpellingCorrection(_:)) | ||
| ), | ||
| ]), | ||
| NSMenuItem(submenu: "Substitutions", items: [ | ||
| NSMenuItem( | ||
| title: "Show Substitutions", | ||
| action: #selector(NSTextView.orderFrontSubstitutionsPanel(_:)) | ||
| ), | ||
| .separator(), | ||
| NSMenuItem( | ||
| title: "Smart Copy/Paste", | ||
| action: #selector(NSTextView.toggleSmartInsertDelete(_:)) | ||
| ), | ||
| NSMenuItem( | ||
| title: "Smart Quotes", | ||
| action: #selector(NSTextView.toggleAutomaticQuoteSubstitution(_:)) | ||
| ), | ||
| NSMenuItem( | ||
| title: "Smart Dashes", | ||
| action: #selector(NSTextView.toggleAutomaticDashSubstitution(_:)) | ||
| ), | ||
| NSMenuItem( | ||
| title: "Smart Links", | ||
| action: #selector(NSTextView.toggleAutomaticLinkDetection(_:)) | ||
| ), | ||
| NSMenuItem( | ||
| title: "Data Detectors", | ||
| action: #selector(NSTextView.toggleAutomaticDataDetection(_:)) | ||
| ), | ||
| NSMenuItem( | ||
| title: "Text Replacement", | ||
| action: #selector(NSTextView.toggleAutomaticTextReplacement(_:)) | ||
| ), | ||
| ]), | ||
| NSMenuItem(submenu: "Transformations", items: [ | ||
| NSMenuItem(title: "Make Upper Case", action: #selector(NSTextView.uppercaseWord(_:))), | ||
| NSMenuItem(title: "Make Lower Case", action: #selector(NSTextView.lowercaseWord(_:))), | ||
| NSMenuItem(title: "Capitalize", action: #selector(NSTextView.capitalizeWord(_:))), | ||
| ]), | ||
| NSMenuItem(submenu: "Speech", items: [ | ||
| NSMenuItem(title: "Start Speaking", action: #selector(NSTextView.startSpeaking(_:))), | ||
| NSMenuItem(title: "Stop Speaking", action: #selector(NSTextView.stopSpeaking(_:))), | ||
| ]), | ||
| ]) |
Comment on lines
+243
to
+267
| NSMenuItem(submenu: "View", items: [ | ||
| NSMenuItem( | ||
| title: "Show Toolbar", | ||
| action: #selector(NSWindow.toggleToolbarShown(_:)), | ||
| keyEquivalent: "t", | ||
| keyEquivalentModifierMask: [.option, .command] | ||
| ), | ||
| NSMenuItem( | ||
| title: "Customize Toolbar…", | ||
| action: #selector(NSWindow.runToolbarCustomizationPalette(_:)) | ||
| ), | ||
| .separator(), | ||
| NSMenuItem( | ||
| title: "Show Sidebar", | ||
| action: #selector(NSSplitViewController.toggleSidebar(_:)), | ||
| keyEquivalent: "s", | ||
| keyEquivalentModifierMask: [.control, .command] | ||
| ), | ||
| NSMenuItem( | ||
| title: "Enter Full Screen", | ||
| action: #selector(NSWindow.toggleFullScreen(_:)), | ||
| keyEquivalent: "f", | ||
| keyEquivalentModifierMask: [.control, .command] | ||
| ), | ||
| ]) |
Comment on lines
+271
to
+298
| let rememberLastComponentMenuItem = NSMenuItem( | ||
| title: "Remember Last Opened Component", | ||
| action: #selector(onRememberLastComponentSelected(_:)), | ||
| target: self | ||
| ) | ||
| rememberLastComponentMenuItem.isEnabled = false | ||
| self.rememberLastComponentMenuItem = rememberLastComponentMenuItem | ||
|
|
||
| let reactMenu = NSMenu(title: "React", items: [ | ||
| NSMenuItem( | ||
| title: "Load Embedded JS Bundle", | ||
| action: #selector(onLoadEmbeddedBundleSelected(_:)), | ||
| target: self | ||
| ), | ||
| NSMenuItem( | ||
| title: "Load From Dev Server", | ||
| action: #selector(onLoadFromDevServerSelected(_:)), | ||
| target: self | ||
| ), | ||
| rememberLastComponentMenuItem, | ||
| .separator(), | ||
| ]) | ||
| reactMenu.autoenablesItems = false | ||
| self.reactMenu = reactMenu | ||
|
|
||
| let reactMenuItem = NSMenuItem(submenu: reactMenu) | ||
| reactMenuItem.isHidden = true | ||
| return reactMenuItem |
Comment on lines
+302
to
+311
| let windowMenu = NSMenu(title: "Window", items: [ | ||
| NSMenuItem(title: "Minimize", action: #selector(NSWindow.performMiniaturize(_:)), keyEquivalent: "m"), | ||
| NSMenuItem(title: "Zoom", action: #selector(NSWindow.performZoom(_:))), | ||
| .separator(), | ||
| NSMenuItem(title: "Bring All to Front", action: #selector(NSApplication.arrangeInFront(_:))), | ||
| ]) | ||
|
|
||
| NSApp.windowsMenu = windowMenu | ||
|
|
||
| return NSMenuItem(submenu: windowMenu) |
Comment on lines
+315
to
+321
| let helpMenu = NSMenu(title: "Help", items: [ | ||
| NSMenuItem(title: "\(title) Help", action: #selector(NSApplication.showHelp(_:)), keyEquivalent: "?"), | ||
| ]) | ||
|
|
||
| NSApp.helpMenu = helpMenu | ||
|
|
||
| return NSMenuItem(submenu: helpMenu) |
2298886 to
c761fd2
Compare
Collaborator
|
I think I have a WIP in RNM where I changed the remote to SwiftUI with much of the same fratutes. I can dig it up? |
Member
Author
What are "much of the same features"? And how much code is there? We have hooks in the |
c761fd2 to
ff0b9a8
Compare
Saadnajmi
approved these changes
Jul 7, 2026
Saadnajmi
added a commit
to Saadnajmi/react-native-test-app
that referenced
this pull request
Jul 7, 2026
Replace the programmatic AppKit menu bar (added in microsoft#2836) with the SwiftUI App lifecycle. SwiftUI provides the standard App/File/Edit/View/Window/Help menus for free, so the ~320 lines of makeMainMenu/make*MenuItem in WindowController.swift are deleted. The dynamic "React" menu is now declarative: ReactCommands renders a CommandMenu("React") driven by an observable AppModel. Publishing components/componentsEnabled/rememberLastComponent rebuilds the menu, so the manual NSMenuItem add/remove bookkeeping is gone. An AppDelegate is kept via @NSApplicationDelegateAdaptor for RN setup and lifecycle notifications. - AppDelegate.swift: @main App struct + adaptor + AppModel - ReactCommands.swift: dynamic React menu (Cmd-Shift-1..9 component shortcuts) - ViewController.swift: SwiftUI content hosting + placeholder - WindowController.swift: deleted - project.pbxproj / pack.test.mts: swap WindowController.swift for ReactCommands.swift Both ENABLE_SINGLE_APP_MODE and multi-app behavior are preserved. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Saadnajmi
added a commit
to Saadnajmi/react-native-test-app
that referenced
this pull request
Jul 7, 2026
Replace the programmatic AppKit menu bar (added in microsoft#2836) with the SwiftUI App lifecycle. SwiftUI provides the standard App/File/Edit/View/Window/Help menus for free, so the ~320 lines of makeMainMenu/make*MenuItem in WindowController.swift are deleted. The dynamic "React" menu is now declarative: ReactCommands renders a CommandMenu("React") driven by an observable AppModel. Publishing components/componentsEnabled/rememberLastComponent rebuilds the menu, so the manual NSMenuItem add/remove bookkeeping is gone. An AppDelegate is kept via @NSApplicationDelegateAdaptor for RN setup and lifecycle notifications. - AppDelegate.swift: @main App struct + adaptor + AppModel - ReactCommands.swift: dynamic React menu (Cmd-Shift-1..9 component shortcuts) - ViewController.swift: SwiftUI content hosting + placeholder - WindowController.swift: deleted - project.pbxproj / pack.test.mts: swap WindowController.swift for ReactCommands.swift Both ENABLE_SINGLE_APP_MODE and multi-app behavior are preserved. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.







Description
Resolves #111.
Platforms affected
Test plan
Screenshots
More below…