# How Fluxer Implements the Application Menu and Platform-Specific Menu Items

> Discover how Fluxer dynamically builds its cross-platform application menu at runtime using Electron. Learn how it conditionally renders macOS specific items and handles Windows Linux menus from a unified template.

- Repository: [Fluxer/fluxer](https://github.com/fluxerapp/fluxer)
- Tags: internals
- Published: 2026-03-17

---

**Fluxer builds its cross-platform application menu dynamically at runtime using Electron's `Menu.buildFromTemplate` in [`fluxer_desktop/src/main/Menu.tsx`](https://github.com/fluxerapp/fluxer/blob/main/fluxer_desktop/src/main/Menu.tsx), detecting the platform and build channel to conditionally render macOS-specific items like the App menu and standard Edit roles while handling Windows/Linux through a unified template array.**

Fluxer is an Electron-based desktop application that requires native-looking menu bars across macOS, Windows, and Linux. The application menu implementation lives in the [`fluxer_desktop/src/main/Menu.tsx`](https://github.com/fluxerapp/fluxer/blob/main/fluxer_desktop/src/main/Menu.tsx) module, where it constructs a platform-aware menu template that adapts labels, roles, and accelerators based on the operating system and build channel.

## Runtime Platform Detection

The menu construction begins by detecting the execution environment. In [`fluxer_desktop/src/main/Menu.tsx`](https://github.com/fluxerapp/fluxer/blob/main/fluxer_desktop/src/main/Menu.tsx), the implementation checks `process.platform === 'darwin'` to determine if the app is running on macOS, and evaluates `BUILD_CHANNEL === 'canary'` to adjust the application name dynamically.

```typescript
// fluxer_desktop/src/main/Menu.tsx
const isCanary = BUILD_CHANNEL === 'canary';
const appName = isCanary ? 'Fluxer Canary' : 'Fluxer';
const isMac = process.platform === 'darwin';

const template: Array<MenuItemConstructorOptions> = [];

```

These variables control which menu sections are appended to the template array and how labels are generated throughout the menu structure.

## Constructing the Menu Template

The `createApplicationMenu()` function assembles an ordered array of `MenuItemConstructorOptions` objects, pushing platform-specific sections based on the `isMac` boolean.

### The macOS App Menu

On macOS, Fluxer inserts a dedicated application menu as the first item in the template. This section calls `app.setName(appName)` to ensure the menu displays the correct branding (stable or Canary), then includes standard macOS roles like `about`, `services`, `hide`, and `quit`.

```typescript
if (isMac) {
  app.setName(appName);
  template.push({
    label: appName,
    submenu: [
      { role: 'about', label: `About ${appName}` },
      { type: 'separator' },
      {
        label: 'Preferences...',
        accelerator: 'Cmd+,',
        click: () => {
          const mainWindow = getMainWindow();
          mainWindow?.webContents.send('open-settings');
        },
      },
      { type: 'separator' },
      { role: 'services' },
      { type: 'separator' },
      { role: 'hide' },
      { role: 'hideOthers' },
      { role: 'unhide' },
      { type: 'separator' },
      { role: 'quit' }
    ],
  });
}

```

### Cross-Platform File Menu

The **File** menu adapts based on the platform. On macOS, it contains only a `close` role to match Apple Human Interface Guidelines. On Windows and Linux, the File menu includes explicit **Preferences** and **Quit** entries since those platforms lack the dedicated App menu.

```typescript
// File menu structure (simplified)
template.push({
  label: 'File',
  submenu: isMac 
    ? [{ role: 'close' }]
    : [
        {
          label: 'Preferences',
          accelerator: 'Ctrl+,',
          click: () => getMainWindow()?.webContents.send('open-settings')
        },
        { type: 'separator' },
        { role: 'quit' }
      ]
});

```

### Edit Menu with Platform Roles

The **Edit** menu provides standard text editing actions across all platforms. macOS receives additional roles including `pasteAndMatchStyle`, `delete`, and `selectAll`, along with the `Speech` submenu for dictation support.

```typescript
template.push({
  label: 'Edit',
  submenu: [
    { role: 'undo' },
    { role: 'redo' },
    { type: 'separator' },
    { role: 'cut' },
    { role: 'copy' },
    { role: 'paste' },
    ...(isMac ? [
      { role: 'pasteAndMatchStyle' },
      { role: 'delete' },
      { role: 'selectAll' },
      { type: 'separator' },
      { label: 'Speech', submenu: [{ role: 'startSpeaking' }, { role: 'stopSpeaking' }] }
    ] : [
      { role: 'delete' },
      { type: 'separator' },
      { role: 'selectAll' }
    ])
  ]
});

```

### View Menu and IPC Communication

The **View** menu handles zoom controls and developer tools. Zoom actions dispatch IPC messages to the renderer process using `getMainWindow().webContents.send()`, allowing the UI to update zoom levels without reloading.

```typescript
{
  label: 'View',
  submenu: [
    { role: 'reload' },
    { role: 'forceReload' },
    { role: 'toggleDevTools' },
    { type: 'separator' },
    {
      label: 'Zoom In',
      accelerator: 'CmdOrCtrl+Plus',
      click: () => getMainWindow()?.webContents.send('zoom-in')
    },
    {
      label: 'Zoom Out',
      accelerator: 'CmdOrCtrl+-',
      click: () => getMainWindow()?.webContents.send('zoom-out')
    },
    {
      label: 'Reset Zoom',
      accelerator: 'CmdOrCtrl+0',
      click: () => getMainWindow()?.webContents.send('zoom-reset')
    },
    { type: 'separator' },
    { role: 'togglefullscreen' }
  ]
}

```

The **Window** menu (macOS) and **Help** menu (all platforms) follow similar patterns, with the Help menu using `shell.openExternal` to open the website and GitHub repository in the system browser.

## Registering the Menu

After assembling the template array, the function builds the menu instance and registers it as the global application menu. This occurs in [`fluxer_desktop/src/main/index.tsx`](https://github.com/fluxerapp/fluxer/blob/main/fluxer_desktop/src/main/index.tsx) during the app's `whenReady` lifecycle event.

```typescript
// fluxer_desktop/src/main/Menu.tsx
export function createApplicationMenu(): void {
  // ... template construction ...
  const menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);
}

```

The main entry point invokes this function immediately after the Electron app reaches the ready state:

```typescript
// fluxer_desktop/src/main/index.tsx
app.whenReady().then(async () => {
  try {
    createApplicationMenu();
  } catch (error) {
    log.error('[Init] Failed to create application menu:', error);
  }
  createWindow();
});

```

## Summary

- **Fluxer** generates its application menu at runtime in [`fluxer_desktop/src/main/Menu.tsx`](https://github.com/fluxerapp/fluxer/blob/main/fluxer_desktop/src/main/Menu.tsx) using Electron's `Menu.buildFromTemplate`.
- **Platform detection** relies on `process.platform === 'darwin'` to conditionally render macOS-specific items like the App menu and extended Edit roles.
- **Build channel awareness** changes the application name dynamically between "Fluxer" and "Fluxer Canary" throughout menu labels.
- **IPC communication** bridges the main and renderer processes for zoom controls and preferences, using `webContents.send()` from menu click handlers.
- **Lifecycle integration** occurs in [`fluxer_desktop/src/main/index.tsx`](https://github.com/fluxerapp/fluxer/blob/main/fluxer_desktop/src/main/index.tsx), ensuring the menu is built before the main window creation.

## Frequently Asked Questions

### How does Fluxer handle the macOS App menu differently from Windows?

On macOS, Fluxer creates a dedicated application menu as the first item containing `About`, `Preferences`, `Services`, `Hide`, and `Quit` roles, following Apple's Human Interface Guidelines. Windows and Linux omit this menu entirely, moving **Preferences** and **Quit** into the **File** menu instead.

### Where does Fluxer detect the platform to build the correct menu?

Platform detection occurs at the start of `createApplicationMenu()` in [`fluxer_desktop/src/main/Menu.tsx`](https://github.com/fluxerapp/fluxer/blob/main/fluxer_desktop/src/main/Menu.tsx) using `const isMac = process.platform === 'darwin'`. This boolean determines which menu sections are pushed into the template array.

### How do menu items communicate with the renderer process?

Menu items use Electron's IPC mechanism by calling `getMainWindow().webContents.send()` with specific channel names like `'open-settings'`, `'zoom-in'`, or `'zoom-reset'`. The renderer process listens for these messages to update UI state without page reloads.

### What determines whether the menu shows "Fluxer" or "Fluxer Canary"?

The `BUILD_CHANNEL` environment variable checked at `Menu.tsx:L24` sets the `appName` variable to either `"Fluxer"` or `"Fluxer Canary"`. This variable propagates to menu labels, window titles, and the `app.setName()` call on macOS.