macOS menu bar
Build the native macOS menu bar declaratively from Compose — SF Symbols, keyboard shortcuts, badges, submenus, checkboxes, radio buttons.
The macOS menu bar (the strip at the top of the screen, not in your window) is the OS's signature surface. menu-macos lets you build it the way you build your UI — as a Compose tree, with state-driven items, SF Symbols, native keyboard shortcuts, badges, and submenus.
TL;DR
NativeMenuBar { … }composable installs and tears down a realNSMenu.- Items support SF Symbols, system images, keyboard shortcuts, badges, checkboxes, radio groups.
- Reactive: mutate state, the native menu updates in place.
Install
dependencies {
implementation("dev.nucleusframework:nucleus.menu-macos:<version>")
}Pulls in core-runtime and sf-symbols constants transitively.
Quickstart
import dev.nucleusframework.menu.macos.*
import dev.nucleusframework.sfsymbols.*
@Composable
fun App() {
NativeMenuBar {
menu("File") {
item(
"New",
shortcut = NativeKeyShortcut("n"),
icon = NsMenuItemImage.SystemSymbol(SFSymbolObjectsAndTools.DOCUMENT_BADGE_PLUS),
) { newDocument() }
item(
"Open…",
shortcut = NativeKeyShortcut("o"),
icon = NsMenuItemImage.SystemSymbol(SFSymbolObjectsAndTools.FOLDER),
) { open() }
separator()
item(
"Quit",
shortcut = NativeKeyShortcut("q"),
) { exitProcess(0) }
}
menu("Help", role = MenuRole.Help) {
item("Documentation") { openDocs() }
}
}
// … window content
}How it works
NativeMenuBar snapshots the active NSApp.mainMenu on first composition, then installs a new NSMenu built from your DSL. When it leaves the composition the original menu is restored. The menu tree participates in Compose recomposition: change a label, the native item relabels; toggle an item's state, the checkmark animates in.
Reference
Menus and items
NativeMenuBar {
menu("Edit") {
item("Undo", shortcut = NativeKeyShortcut("z")) { undo() }
item("Redo", shortcut = NativeKeyShortcut("z", shift = true)) { redo() }
separator()
submenu("Find") {
item("Find…", shortcut = NativeKeyShortcut("f")) { find() }
item("Find Next", shortcut = NativeKeyShortcut("g")) { findNext() }
}
}
}SF Symbols
icon = NsMenuItemImage.SystemSymbol(SFSymbolGeneral.GEAR)The sf-symbols module exposes every category — SFSymbolGeneral, SFSymbolPower, SFSymbolHealth, etc. — as typed constants so you cannot misspell a name.
Checkable / radio items
var darkMode by remember { mutableStateOf(false) }
NativeMenuBar {
menu("View") {
item(
"Dark mode",
state = if (darkMode) NsMenuItemState.On else NsMenuItemState.Off,
) { darkMode = !darkMode }
}
}Badges
item("Inbox", badge = NsMenuItemBadge.Counter(unreadCount)) { openInbox() }Roles
MenuRole.Window and MenuRole.Help tag menus that AppKit recognises (Window list, Spotlight search inside Help). Plain menus default to MenuRole.None.
Notes
- The composable installs the menu while it is mounted — host it from your application root, not from a window content scope, so the menu survives window swaps.
- Use
NativeKeyShortcut("a", command = true, shift = true, …)for full modifier control.
System info
Read-only inventory of the host — OS, CPU, memory, disks, GPUs, batteries, network, processes, users — through one Kotlin API.
FreeDesktop icon names
Typed Kotlin constants for the FreeDesktop Icon Naming Specification — used by notification-linux, launcher-linux, and any Linux UI that resolves icons by theme.