Skip to content

Menu API ​

The menu namespace provides methods to query host menu structures, execute main and context menu commands, and display native or custom popup menus.

See Standard command GUIDs for SDK-defined addresses and Targets and completion for execution route constraints. The addresses are identical for SDK calls and fb2k.invoke.

Close the self-drawn menu if one is open; its menu:dismiss carries reason.

ParameterTypeRequiredDescription
reasonstringNoReason reported in menu:dismiss. Default: "api".

Returns

success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.

js
const result = await fb2k.invoke('menu.close', { reason: 'api' });

Get the context menu for the tracks mode selects, as a tree.

ParameterTypeRequiredDescription
modestringNoTracks to build the menu for: handles uses handles, playlist the active playlist, nowPlaying the playing track and selection the active playlist selection. Any other value, auto included, takes the first of handles, the playing track, the selection and the active playlist that is available. Given handles of which none is usable fail with INVALID_PARAMS in every mode, auto included, rather than falling back to other tracks; nowPlaying with nothing playing and selection with nothing selected fail with NO_ACTIVE_ITEM. Default: "auto".
localestringNoLocale for displayLabel: auto keeps the host's labels; a zh or en tag translates common labels. Default: "auto".
i18nbooleanNofalse turns the label translation off. Default: true.
withAvailabilitybooleanNoAdd command counts to submenus. Default: true.
handlesany[]NoTracks for mode: "handles" and auto: each a path, optionally ending in |subsong:N, or an object { path, subsong }. Paths the security policy refuses and entries of other types are skipped. Each path is checked on disk, so prefer selection, playlist or nowPlaying for tracks the host already lists.

Returns

FieldTypeDescription
mode"handles" | "playlist" | "nowPlaying" | "selection"The mode used; auto resolves to one of the others.
localestringThe locale used.
i18nbooleanThe i18n used.
withAvailabilitybooleanThe withAvailability used.
itemsMenuTreeNode[]The top-level nodes.
items[].type"separator" | "submenu" | "command"Node kind.
items[].labelstringLabel as the host reports it (localized on a translated build).
items[].displayLabelstringlabel translated for locale; the same as label when translation is off or has no entry for it.
items[].pathstringSlash-separated labels from the top of the menu.
items[].displayPathstringpath made of display labels.
items[].flagsintegerRaw display flags. Their bits depend on where the node came from: SDK menu flags from the menu tree and the flat list, Win32 menu state from the Win32 menus. Absent on a submenu of the Win32 menus.
items[].childrenMenuTreeNode[]The children, in menu order; only on a submenu.
items[].availabilityMenuAvailabilityCommand counts below this submenu; only with withAvailability, and not on submenus nested inside the Win32 menus.
items[].availability.totalCommandsintegerCommands anywhere below the submenu.
items[].availability.availableCommandsintegerCommands that are enabled.
items[].availability.disabledCommandsintegerCommands that are disabled.
items[].availability.allAvailablebooleanWhether every command is enabled; also true when there are none.
items[].enabledbooleanWhether the command can run; only on a command.
items[].availablebooleanThe same as enabled; only on a command.
items[].checkedbooleanWhether the command shows a check mark (a radio mark counts too); only on a command.
items[].radioCheckedbooleanWhether the check mark is a radio mark; only on a command.
items[].hiddenbooleanWhether the host would not draw the command; only on a command.
items[].commandIdintegerMenu item id. For a context-menu command, menu.runContextCommandById runs it. The id is valid only for the menu it came from, so do not store it. Absent on the flat list.
items[].guidstringCommand GUID, the stable address to pass to menu.runMainMenuCommand or menu.runContextCommand; absent when it could not be resolved.
items[].subGuidstringGUID of a dynamic child command, passed together with guid.
items[].source"mainmenu_static" | "mainmenu_dynamic" | "contextmenu_static" | "contextmenu_dynamic" | "hmenu_fallback"Main-menu sources reflect the provider's node kind, even when a dynamic leaf has no usable subGuid. Context-menu sources use subGuid presence. Win32 menu commands use hmenu_fallback.
items[].executablebooleanFor main menus, whether the command has a unique complete execution address, independently of enabled. For context menus, whether a command GUID is present.
items[].unaddressableReason"noStableIdentifier" | "ambiguousMatch" | "missingDynamicIdentifier" | "dynamicParent" | "incompleteHierarchy"Why the command cannot be run; present when executable is false.
items[].addressCandidatesMenuAddressCandidate[]Diagnostic candidates for an ambiguous main-menu node; never select the first automatically.
items[].addressCandidates[].pathstringFull observed label path.
items[].addressCandidates[].guidstringOwning command GUID.
items[].addressCandidates[].subGuidstringDynamic leaf GUID, when present.
items[].addressCandidates[].groupGuidstringImmediate registered parent group GUID; empty when unavailable.
items[].fallbackbooleantrue on a command of the flat list.
targetKeystringA fingerprint of the tracks the menu was built for, to pass back as expectTarget. Absent for mode: "playlist". Opaque, and valid only while the host process runs.

success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.

js
const res = await fb2k.invoke('menu.getContextMenu', { mode: 'selection' });
if (res.success === false) throw new Error(res.error);
const { items } = res;

Get the main menu as a tree. The host builds it from its menu tree, falls back to the Win32 menus (source: "v1-hmenu"), and as a last resort returns a flat command list (fallback: "flat-mainmenu-commands"). A source that cannot build a tree permits fallback; unreadable state in a built tree fails with OPERATION_FAILED instead of returning guessed availability. Root filtering keeps full command paths. Addressability is independent of enabled.

ParameterTypeRequiredDescription
rootstringNoLabel or slash-separated path of a submenu to return instead of the whole menu. When nothing matches, the whole menu is returned with rootMatched: false. Default: "".
localestringNoLocale for displayLabel: auto keeps the host's labels; a zh or en tag translates common labels. Default: "auto".
i18nbooleanNofalse turns the label translation off. Default: true.
withAvailabilitybooleanNoAdd command counts to submenus. Default: true.

Returns

FieldTypeDescription
rootstringLabel of the submenu returned; empty for the whole menu.
requestedRootstringThe root that was asked for.
rootMatchedbooleanWhether root was found; true when none was asked for, except on the flat list, which is always false.
localestringThe locale used.
i18nbooleanThe i18n used.
withAvailabilitybooleanThe withAvailability used.
itemsMenuTreeNode[]The top-level nodes.
items[].type"separator" | "submenu" | "command"Node kind.
items[].labelstringLabel as the host reports it (localized on a translated build).
items[].displayLabelstringlabel translated for locale; the same as label when translation is off or has no entry for it.
items[].pathstringSlash-separated labels from the top of the menu.
items[].displayPathstringpath made of display labels.
items[].flagsintegerRaw display flags. Their bits depend on where the node came from: SDK menu flags from the menu tree and the flat list, Win32 menu state from the Win32 menus. Absent on a submenu of the Win32 menus.
items[].childrenMenuTreeNode[]The children, in menu order; only on a submenu.
items[].availabilityMenuAvailabilityCommand counts below this submenu; only with withAvailability, and not on submenus nested inside the Win32 menus.
items[].availability.totalCommandsintegerCommands anywhere below the submenu.
items[].availability.availableCommandsintegerCommands that are enabled.
items[].availability.disabledCommandsintegerCommands that are disabled.
items[].availability.allAvailablebooleanWhether every command is enabled; also true when there are none.
items[].enabledbooleanWhether the command can run; only on a command.
items[].availablebooleanThe same as enabled; only on a command.
items[].checkedbooleanWhether the command shows a check mark (a radio mark counts too); only on a command.
items[].radioCheckedbooleanWhether the check mark is a radio mark; only on a command.
items[].hiddenbooleanWhether the host would not draw the command; only on a command.
items[].commandIdintegerMenu item id. For a context-menu command, menu.runContextCommandById runs it. The id is valid only for the menu it came from, so do not store it. Absent on the flat list.
items[].guidstringCommand GUID, the stable address to pass to menu.runMainMenuCommand or menu.runContextCommand; absent when it could not be resolved.
items[].subGuidstringGUID of a dynamic child command, passed together with guid.
items[].source"mainmenu_static" | "mainmenu_dynamic" | "contextmenu_static" | "contextmenu_dynamic" | "hmenu_fallback"Main-menu sources reflect the provider's node kind, even when a dynamic leaf has no usable subGuid. Context-menu sources use subGuid presence. Win32 menu commands use hmenu_fallback.
items[].executablebooleanFor main menus, whether the command has a unique complete execution address, independently of enabled. For context menus, whether a command GUID is present.
items[].unaddressableReason"noStableIdentifier" | "ambiguousMatch" | "missingDynamicIdentifier" | "dynamicParent" | "incompleteHierarchy"Why the command cannot be run; present when executable is false.
items[].addressCandidatesMenuAddressCandidate[]Diagnostic candidates for an ambiguous main-menu node; never select the first automatically.
items[].addressCandidates[].pathstringFull observed label path.
items[].addressCandidates[].guidstringOwning command GUID.
items[].addressCandidates[].subGuidstringDynamic leaf GUID, when present.
items[].addressCandidates[].groupGuidstringImmediate registered parent group GUID; empty when unavailable.
items[].fallbackbooleantrue on a command of the flat list.
source"v1-hmenu"v1-hmenu when the tree was built from the Win32 menus.
fallback"flat-mainmenu-commands"flat-mainmenu-commands when only the flat command list could be built.

success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.

Each command leaf carries a source. In the primary menu tree, a leaf with a subGuid reports mainmenu_dynamic, while one without reports mainmenu_static. When falling back to Win32 menu traversal, the response sets source: "v1-hmenu" and leaves report hmenu_fallback. The flat command list fallback (fallback: "flat-mainmenu-commands") marks items with fallback: true, preserves flags, and omits commandId.

js
const res = await fb2k.invoke('menu.getMainMenu', { root: 'View' });
if (res.success === false) throw new Error(res.error);
const { items } = res;

Run a context-menu command on the playing track, or on the active playlist selection when nothing is playing; fails with NO_ACTIVE_ITEM when there is neither. A name that matches no command fails with MENU_COMMAND_NOT_FOUND, a GUID that no command owns with NOT_FOUND. The call answers when the command returns: one that shows a modal dialog answers only after the dialog closes.

ParameterTypeRequiredDescription
commandstringYesThe command: a GUID or a command name. Must not be empty.
subGuidstringNoGUID of a dynamic child such as a rating value or a converter preset; without it the owning container is targeted, which does nothing. Empty is the same as absent.

Returns

FieldTypeDescription
guidstringGUID of the command that ran; absent when it was reached by name and has no GUID.
itemCountintegerHow many tracks the command ran on.
executionConfirmedbooleanfalse when the command has no GUID and was handed to a host entry point that reports nothing back, so whether it ran is unknown.
completion"completed" | "partial" | "cancelled" | "failed" | "unknown"Always unknown: foobar2000 does not report whether the command finished or whether a dialog it opened was cancelled.

success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.

executionConfirmed: false indicates that the command was handed to a host entry point that provides no return status, preventing completion observation. This applies only to registrations lacking a stable GUID.

js
// Track properties; the target is resolved when the call runs.
await fb2k.invoke('menu.runContextCommand', {
    command: '{6F441057-1D18-4A58-9AC4-8F409CDA7DFD}',
});

// node is the dynamic leaf the user chose from menu.getContextMenu().
async function runChosenDynamicContextCommand(node) {
    if (node.type !== 'command' || !node.guid || !node.subGuid) return;
    return fb2k.invoke('menu.runContextCommand', {
        command: node.guid,
        subGuid: node.subGuid,
    });
}

Run the context-menu item whose commandId a menu.getContextMenu call reported. The menu is rebuilt from mode and handles, so pass the handles that call used and the mode it reported, not auto again. An id is a position in the rebuilt menu, not a name: an id past its end fails with NOT_FOUND, but when the tracks, the selection, the playing track or the installed components changed since, the same id can run another item without failing. Pass expectPath and expectTarget together to rule that out: the host then runs the item only when the rebuilt menu matches what that call reported, and otherwise fails without running anything, with details.reason set: NOT_FOUND (targetChanged, commandChanged), MENU_MATCH_AMBIGUOUS (ambiguousPath), MENU_ITEM_DISABLED (disabled), or NOT_SUPPORTED (unsupportedTargetMode) for mode: "playlist". Reopen the menu after such a failure. The call answers when the command returns: one that shows a modal dialog answers only after the dialog closes.

ParameterTypeRequiredDescription
idintegerYesThe commandId of a context-menu node. Between 0 and 4294967295 inclusive.
modestringNoThe mode the menu was built with; see menu.getContextMenu. Default: "auto".
handlesany[]NoThe handles the menu was built with; see menu.getContextMenu.
expectPathstringNoThe node's path as menu.getContextMenu reported it. Give it with expectTarget: the item at id must have this path, and no other node may share it. Must not be empty.
expectTargetstringNoThe targetKey that menu.getContextMenu reported. Give it with expectPath: the tracks resolved now must give the same key. Must not be empty.

Returns

FieldTypeDescription
completion"completed" | "partial" | "cancelled" | "failed" | "unknown"Always unknown: foobar2000 does not report whether the command finished or whether a dialog it opened was cancelled.

success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.

js
await fb2k.invoke('menu.runContextCommandById', { id: 3 });

Menu Item Index Scope and Validation

commandId represents a dynamic entry index within a specific rebuilt menu tree. If the track selection or player state changes between calls, the same index may resolve to a different command. When calling runContextCommandById, pass expectPath and expectTarget to enforce pre-execution consistency validation; see Command Targets and Completion Results.

Run a main-menu command addressed by GUID, leaf name or slash-separated path. A disabled command fails with MENU_ITEM_DISABLED. Paths match every ancestor; a wrong prefix never falls back to a leaf name. Ambiguous identities fail with MENU_MATCH_AMBIGUOUS and details.candidates; a missing name/path is MENU_COMMAND_NOT_FOUND, a missing GUID is NOT_FOUND. Addressing a dynamic leaf by GUID requires both GUIDs. Unreadable state fails with OPERATION_FAILED. The call answers when the command returns: one that shows a modal dialog, such as renaming a playlist, answers only after the dialog closes.

ParameterTypeRequiredDescription
commandstringYesThe command: a GUID such as {11213A01-...}, a leaf name, or a slash-separated path. Prefer the GUID, the one form that does not depend on the host's language. Must not be empty.
subGuidstringNoGUID of a dynamic child of the command; only with the GUID form. Empty is the same as absent.

Returns

FieldTypeDescription
guidstringGUID of the command that ran, including calls resolved from names or paths. Older hosts can omit it for those forms.
dynamicbooleanWhether it was a dynamic child command.
subGuidstringGUID of the dynamic child that ran.
source"v2-tree" | "index"How a name or path was resolved: v2-tree by the menu tree, index by the command list. Absent for the GUID form.
completion"completed" | "partial" | "cancelled" | "failed" | "unknown"Always unknown: foobar2000 does not report whether the command finished or whether a dialog it opened was cancelled.

success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.

command accepts a GUID, a leaf command name, or a slash-separated path. Prefer GUIDs, which remain stable across localized hosts. Localized foobar2000 installations report translated labels, causing English names or paths to fail resolution.

Name and path forms are matched exactly per segment. When a path or name matches multiple commands, the call fails with MENU_MATCH_AMBIGUOUS and lists candidate commands rather than executing an arbitrary match.

Failure is reported as success: false with a code:

codeMeaning
MENU_ITEM_DISABLEDCommand exists but is currently greyed out
MENU_MATCH_AMBIGUOUSName matched several commands; see details.candidates
MENU_COMMAND_NOT_FOUNDNo command matched
NOT_FOUNDNo command owns the GUID
js
// Preferences: an SDK-defined GUID independent of the host's language.
const result = await fb2k.invoke('menu.runMainMenuCommand', {
    command: '{11213A01-9F36-4E69-A1BB-7A72F418DE3A}',
});

// node is the dynamic leaf the user chose from menu.getMainMenu().
async function runChosenDynamicMainCommand(node) {
    if (node.type !== 'command' || !node.guid || !node.subGuid) return;
    return fb2k.invoke('menu.runMainMenuCommand', {
        command: node.guid,
        subGuid: node.subGuid,
    });
}

Show a self-drawn menu. The choice arrives as menu:select and a dismissal as menu:dismiss; changing a rating, slider or segmented row reports menu:valueChanged and leaves the menu open. Too many rows, too deep nesting, too many segments or too much style text fail with INVALID_PARAMS and details.

ParameterTypeRequiredDescription
itemsany[]NoMenu rows; the SDK's MenuPopupItem describes their fields. A row icon over 32 KiB is dropped.
xintegerNoScreen x of the anchor in physical pixels; absent or negative uses the mouse pointer.
yintegerNoScreen y of the anchor in physical pixels; absent or negative uses the mouse pointer.
windowModelstringNocontentSized draws each panel in a window sized to its content; any other value uses one full-screen overlay window.
cssstringNoStyle sheet applied to the menu on top of the built-in styles, at most 256 KiB.
cssReplacebooleanNoReplace the built-in styles with css instead of adding to them.
backdropstringNoWindow backdrop: acrylic (the default), mica, mica-alt or none. Other values keep the default.
backdropDarkModebooleanNoDark tint for the backdrop; the default is true.
closeAnimationMsintegerNoLength of the closing animation in milliseconds, clamped to 0–1000; the default 0 closes at once.

Returns

FieldTypeDescription
menuIdstringId of the menu; menu:select, menu:dismiss and menu:valueChanged carry it.

success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.

js
const res = await fb2k.invoke('menu.show', {
    items: [
        { id: 'play', label: 'Play' },
        { type: 'separator' },
        { id: 'props', label: 'Properties' },
    ],
});
if (res.success === false) throw new Error(res.error);
const { menuId } = res;

Show the host's native context menu at the mouse pointer for the tracks mode selects. The menu opens just after the call returns.

ParameterTypeRequiredDescription
modestringNoTracks to build the menu for; see menu.getContextMenu. Default: "auto".
handlesany[]NoTracks for mode: "handles" and auto; see menu.getContextMenu.
xintegerNoAccepted for compatibility and ignored: the menu always opens at the mouse pointer.
yintegerNoAccepted for compatibility and ignored: the menu always opens at the mouse pointer.

Returns

success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.

js
await fb2k.invoke('menu.showNativePopup', { mode: 'nowPlaying' });

Native Popup Mechanism

Native menus open asynchronously via a Win32 timer mechanism, preventing UI deadlocks during bridge event callbacks. The popup coordinates anchor directly to the current system cursor position, preventing coordinate offsets across high-DPI and mixed-DPI multi-monitor environments.

Owner-family behavior and limits ​

  • menu.getContextMenu, menu.runContextCommandById, and menu.showNativePopup use mode to select handles, nowPlaying, selection, or playlist context. auto evaluates those sources in sequence, ending with active playlist context. In handles mode, every path is validated for media access before creating track handles. menu.showNativePopup uses screen cursor coordinates and returns before the native menu is displayed.
  • menu.show displays a custom HTML overlay after resource validation. menu.close dismisses the active overlay; internal menu.__* endpoints are not part of the public API surface.

Contract supplements ​

The sections below document parameter validation and return schemas established during the strict parameter audit without altering existing semantics.

Contract supplement: menu.getContextMenu ​

ParameterTypeRequiredDefaultDescription
handlesarrayNo[]Track paths, or { path, subsong } objects, used as the menu context
i18nbooleanNotrueTranslates item labels into displayLabel
localestringNoautoTranslation locale; auto keeps the host's own labels
modestringNoautoOne of auto, selection, playlist, nowPlaying, or handles
withAvailabilitybooleanNotrueIncludes per-submenu availability counters

Return fields ​

FieldTypeOptional
errorstringYes
successbooleanNo
i18njsonNo
itemsjsonNo
localejsonNo
modejsonNo
withAvailabilityjsonNo

Semantics: omitted optional parameters use handler defaults. mode: 'handles' additionally requires a non-empty handles array.

js
const res = await fb2k.invoke('menu.getContextMenu', { mode: 'nowPlaying' });
if (res.success === false) throw new Error(res.error);
const { items } = res;

Contract supplement: menu.runContextCommandById ​

ParameterTypeRequiredDefaultDescription
idintegerYes—Command id from the built context menu. A missing id fails with id is required, a non-integer with id must be an integer, and a negative value with id is out of range, all as INVALID_PARAMS
modestringNoautoOne of selection, playlist, nowPlaying, handles, or auto
handlesarrayNo[]Track paths, or { path, subsong } objects, used as the menu context

Return fields ​

FieldTypeOptional
errorstringYes
successbooleanNo

Semantics: omitted optional parameters use handler defaults. mode: 'handles' additionally requires a non-empty handles array.

js
await fb2k.invoke('menu.runContextCommandById', { id: 3, mode: 'selection' });