CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tauri-apps--api

TypeScript/JavaScript API bindings for Tauri applications providing comprehensive desktop app functionality

Pending
Overview
Eval results
Files

menu-system.mddocs/

Menu System

The menu system provides comprehensive native menu functionality including menu bars, context menus, and system tray menus. It supports different menu item types with platform-specific behavior optimizations.

Capabilities

Menu Creation and Management

Create and manage application menus with support for hierarchical structures.

/**
 * Main menu class for creating menu bars and context menus
 */
class Menu {
  /**
   * Create a new menu
   * @param opts - Menu creation options
   * @returns Promise resolving to Menu instance
   */
  static new(opts?: MenuOptions): Promise<Menu>;
  
  /**
   * Create a default application menu
   * @returns Promise resolving to Menu instance with platform defaults
   */
  static default(): Promise<Menu>;
  
  /**
   * Add menu items to the end of this menu
   * @param items - Single item or array of items to add
   */
  append<T extends MenuItemType>(items: T | T[]): Promise<void>;
  
  /**
   * Add menu items to the beginning of this menu
   * @param items - Single item or array of items to add
   */
  prepend<T extends MenuItemType>(items: T | T[]): Promise<void>;
  
  /**
   * Insert menu items at specific position
   * @param items - Single item or array of items to insert
   * @param position - Zero-based position to insert at
   */
  insert<T extends MenuItemType>(items: T | T[], position: number): Promise<void>;
  
  /**
   * Remove a menu item from this menu
   * @param item - Menu item instance to remove
   */
  remove(item: MenuItemInstance): Promise<void>;
  
  /**
   * Remove menu item at specific position
   * @param position - Zero-based position to remove
   * @returns Removed menu item or null
   */
  removeAt(position: number): Promise<MenuItemInstance | null>;
  
  /**
   * Get all menu items in this menu
   * @returns Array of menu item instances
   */
  items(): Promise<MenuItemInstance[]>;
  
  /**
   * Find menu item by ID
   * @param id - Menu item identifier
   * @returns Menu item instance or null if not found
   */
  get(id: string): Promise<MenuItemInstance | null>;
  
  /**
   * Show this menu as a context menu
   * @param at - Position to show menu (relative to window)
   * @param window - Target window (defaults to current)
   */
  popup(at?: Position, window?: Window): Promise<void>;
  
  /**
   * Set this menu as the application-wide menu
   * @returns Previous app menu or null
   */
  setAsAppMenu(): Promise<Menu | null>;
  
  /**
   * Set this menu as a window-specific menu (Windows/Linux only)
   * @param window - Target window (defaults to current)
   * @returns Previous window menu or null
   */
  setAsWindowMenu(window?: Window): Promise<Menu | null>;
}

interface MenuOptions {
  /** Menu identifier */
  id?: string;
  /** Initial menu items */
  items?: MenuItemType[];
}

Usage Examples:

import { Menu, MenuItem, Submenu } from '@tauri-apps/api/menu';

// Create basic menu
const menu = await Menu.new({
  items: [
    { text: 'File', items: [
      { text: 'New', action: () => console.log('New file') },
      { text: 'Open', action: () => console.log('Open file') },
      { text: '---' }, // Separator
      { text: 'Exit', action: () => console.log('Exit') }
    ]},
    { text: 'Edit', items: [
      { text: 'Cut', accelerator: 'CmdOrCtrl+X' },
      { text: 'Copy', accelerator: 'CmdOrCtrl+C' },
      { text: 'Paste', accelerator: 'CmdOrCtrl+V' }
    ]}
  ]
});

// Set as application menu
await menu.setAsAppMenu();

// Create context menu
const contextMenu = await Menu.new({
  items: [
    { text: 'Copy', action: () => navigator.clipboard.writeText('data') },
    { text: 'Paste', action: async () => {
      const text = await navigator.clipboard.readText();
      console.log('Pasted:', text);
    }}
  ]
});

// Show context menu at mouse position
document.addEventListener('contextmenu', async (e) => {
  e.preventDefault();
  await contextMenu.popup({ x: e.clientX, y: e.clientY });
});

Menu Item Types

Different types of menu items for various use cases.

/**
 * Standard menu item with text and action
 */
class MenuItem {
  static new(opts: MenuItemOptions): Promise<MenuItem>;
  
  text(): Promise<string>;
  setText(text: string): Promise<void>;
  isEnabled(): Promise<boolean>;
  setEnabled(enabled: boolean): Promise<void>;
}

interface MenuItemOptions {
  /** Menu item identifier */
  id?: string;
  /** Display text */
  text: string;
  /** Whether item is enabled */
  enabled?: boolean;
  /** Keyboard accelerator (e.g., 'CmdOrCtrl+N') */
  accelerator?: string;
  /** Action to execute when clicked */
  action?: () => void;
}

/**
 * Menu item with checkbox state
 */
class CheckMenuItem {
  static new(opts: CheckMenuItemOptions): Promise<CheckMenuItem>;
  
  isChecked(): Promise<boolean>;
  setChecked(checked: boolean): Promise<void>;
}

interface CheckMenuItemOptions extends MenuItemOptions {
  /** Initial checked state */
  checked?: boolean;
}

/**
 * Menu item with icon
 */
class IconMenuItem {
  static new(opts: IconMenuItemOptions): Promise<IconMenuItem>;
  
  setIcon(icon: Image | string): Promise<void>;
}

interface IconMenuItemOptions extends MenuItemOptions {
  /** Menu item icon */
  icon: Image | string;
}

/**
 * Submenu containing other menu items
 */
class Submenu {
  static new(opts: SubmenuOptions): Promise<Submenu>;
  
  append<T extends MenuItemType>(items: T | T[]): Promise<void>;
  prepend<T extends MenuItemType>(items: T | T[]): Promise<void>;
  insert<T extends MenuItemType>(items: T | T[], position: number): Promise<void>;
  items(): Promise<MenuItemInstance[]>;
}

interface SubmenuOptions {
  /** Submenu identifier */
  id?: string;
  /** Submenu text */
  text: string;
  /** Whether submenu is enabled */
  enabled?: boolean;
  /** Child menu items */
  items?: MenuItemType[];
}

/**
 * Platform-specific predefined menu items
 */
class PredefinedMenuItem {
  static new(opts: PredefinedMenuItemOptions): Promise<PredefinedMenuItem>;
}

interface PredefinedMenuItemOptions {
  /** Predefined item type */
  item: PredefinedMenuItemType;
  /** Override default text */
  text?: string;
}

type PredefinedMenuItemType = 
  | 'Copy'
  | 'Cut' 
  | 'Paste'
  | 'SelectAll'
  | 'Undo'
  | 'Redo'
  | 'Minimize'
  | 'Hide'
  | 'HideOthers'
  | 'ShowAll'
  | 'CloseWindow'
  | 'Quit'
  | 'About'
  | '---'; // Separator

Usage Examples:

import { MenuItem, CheckMenuItem, IconMenuItem, Submenu, PredefinedMenuItem } from '@tauri-apps/api/menu';

// Standard menu item
const newItem = await MenuItem.new({
  text: 'New Document',
  accelerator: 'CmdOrCtrl+N',
  action: () => createNewDocument()
});

// Checkbox menu item
const darkModeItem = await CheckMenuItem.new({
  text: 'Dark Mode',
  checked: true,
  action: () => toggleDarkMode()
});

// Icon menu item
const saveItem = await IconMenuItem.new({
  text: 'Save',
  icon: '/icons/save.png',
  accelerator: 'CmdOrCtrl+S',
  action: () => saveDocument()
});

// Submenu
const fileSubmenu = await Submenu.new({
  text: 'File',
  items: [
    { text: 'New', action: () => console.log('New') },
    { text: 'Open', action: () => console.log('Open') },
    { text: '---' }, // Separator
    { text: 'Recent', items: [
      { text: 'file1.txt' },
      { text: 'file2.txt' }
    ]}
  ]
});

// Predefined menu items
const copyItem = await PredefinedMenuItem.new({ item: 'Copy' });
const separator = await PredefinedMenuItem.new({ item: '---' });
const quitItem = await PredefinedMenuItem.new({ 
  item: 'Quit',
  text: 'Exit Application' // Override default text
});

Dynamic Menu Management

Modify menus at runtime based on application state.

import { Menu, MenuItem } from '@tauri-apps/api/menu';

// Create dynamic menu that updates based on state
async function createDynamicMenu() {
  const menu = await Menu.new();
  
  // Add initial items
  await menu.append([
    { text: 'File', items: [] },
    { text: 'Edit', items: [] }
  ]);
  
  return menu;
}

// Update menu based on application state
async function updateMenuForState(menu: Menu, hasOpenFile: boolean) {
  // Get File submenu
  const fileSubmenu = await menu.get('file-menu') as Submenu;
  
  if (hasOpenFile) {
    // Add file-specific items
    await fileSubmenu.append([
      { text: 'Save', accelerator: 'CmdOrCtrl+S' },
      { text: 'Save As...', accelerator: 'CmdOrCtrl+Shift+S' },
      { text: 'Close', accelerator: 'CmdOrCtrl+W' }
    ]);
  } else {
    // Remove file-specific items
    const items = await fileSubmenu.items();
    for (const item of items) {
      if (['Save', 'Save As...', 'Close'].includes(await item.text())) {
        await fileSubmenu.remove(item);
      }
    }
  }
}

// Enable/disable menu items
async function setMenuItemState(menu: Menu, itemId: string, enabled: boolean) {
  const item = await menu.get(itemId);
  if (item && 'setEnabled' in item) {
    await item.setEnabled(enabled);
  }
}

Platform-Specific Behavior

Handle platform differences in menu systems.

import { Menu, Submenu } from '@tauri-apps/api/menu';

async function createPlatformOptimizedMenu() {
  const menu = await Menu.new();
  
  // macOS typically has app name as first menu
  if (navigator.platform.includes('Mac')) {
    const appSubmenu = await Submenu.new({
      text: 'MyApp',
      items: [
        { item: 'About' },
        { item: '---' },
        { text: 'Preferences', accelerator: 'Cmd+,' },
        { item: '---' },
        { item: 'Hide' },
        { item: 'HideOthers' },
        { item: 'ShowAll' },
        { item: '---' },
        { item: 'Quit' }
      ]
    });
    await menu.append(appSubmenu);
  }
  
  // File menu
  const fileSubmenu = await Submenu.new({
    text: 'File',
    items: [
      { text: 'New', accelerator: 'CmdOrCtrl+N' },
      { text: 'Open', accelerator: 'CmdOrCtrl+O' },
      { item: '---' },
      { text: 'Save', accelerator: 'CmdOrCtrl+S' }
    ]
  });
  await menu.append(fileSubmenu);
  
  // Window menu (common on macOS)
  if (navigator.platform.includes('Mac')) {
    const windowSubmenu = await Submenu.new({
      text: 'Window',
      items: [
        { item: 'Minimize' },
        { item: 'CloseWindow' }
      ]
    });
    await menu.append(windowSubmenu);
  }
  
  return menu;
}

Menu Events and Actions

Handle menu item clicks and state changes.

import { Menu, CheckMenuItem } from '@tauri-apps/api/menu';

// Create menu with event handlers
async function createMenuWithEvents() {
  const menu = await Menu.new({
    items: [
      {
        text: 'View',
        items: [
          {
            text: 'Show Sidebar',
            checked: true,
            action: async () => {
              const item = await menu.get('sidebar-toggle') as CheckMenuItem;
              const isChecked = await item.isChecked();
              
              // Toggle sidebar visibility
              const sidebar = document.getElementById('sidebar');
              if (sidebar) {
                sidebar.style.display = isChecked ? 'block' : 'none';
              }
              
              console.log('Sidebar:', isChecked ? 'shown' : 'hidden');
            }
          },
          {
            text: 'Zoom In',
            accelerator: 'CmdOrCtrl+Plus',
            action: () => {
              document.body.style.zoom = String(parseFloat(document.body.style.zoom || '1') + 0.1);
            }
          },
          {
            text: 'Zoom Out', 
            accelerator: 'CmdOrCtrl+-',
            action: () => {
              document.body.style.zoom = String(Math.max(0.5, parseFloat(document.body.style.zoom || '1') - 0.1));
            }
          },
          {
            text: 'Reset Zoom',
            accelerator: 'CmdOrCtrl+0',
            action: () => {
              document.body.style.zoom = '1';
            }
          }
        ]
      }
    ]
  });
  
  return menu;
}

Type Definitions

type MenuItemType = 
  | MenuItemOptions
  | CheckMenuItemOptions  
  | IconMenuItemOptions
  | SubmenuOptions
  | PredefinedMenuItemOptions;

type MenuItemInstance = 
  | MenuItem
  | CheckMenuItem
  | IconMenuItem  
  | Submenu
  | PredefinedMenuItem;

type Position = LogicalPosition | PhysicalPosition | { x: number; y: number };

Error Handling

Menu operations can fail due to platform limitations or invalid parameters:

import { Menu, MenuItem } from '@tauri-apps/api/menu';

try {
  const menu = await Menu.new();
  await menu.append({ text: 'Test Item' });
  await menu.setAsAppMenu();
} catch (error) {
  // Common error scenarios:
  // - Invalid menu structure
  // - Platform doesn't support operation
  // - Menu item not found
  // - Permission denied
  console.error('Menu operation failed:', error);
}

Best Practices

Performance

  • Cache menu instances rather than recreating them
  • Use get() to find menu items instead of iterating through items()
  • Batch menu modifications when possible

User Experience

  • Use standard accelerators (Ctrl+C, Ctrl+V, etc.)
  • Follow platform conventions for menu organization
  • Provide visual feedback for checkable items
  • Use separators to group related functionality

Platform Integration

  • Use Menu.default() as a starting point for standard menus
  • Test menu behavior on all target platforms
  • Handle platform-specific limitations gracefully
  • Use predefined menu items when available for better integration

Install with Tessl CLI

npx tessl i tessl/npm-tauri-apps--api

docs

app-lifecycle.md

core-ipc.md

events.md

index.md

menu-system.md

system-integration.md

testing.md

utilities.md

window-management.md

tile.json