# Tauri Syntax Menu

> >

- **Type:** Skill
- **Install:** `agentstack add skill-impertio-studio-tauri-2-claude-skill-package-tauri-syntax-menu`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Impertio-Studio](https://agentstack.voostack.com/s/impertio-studio)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Impertio-Studio](https://github.com/Impertio-Studio)
- **Source:** https://github.com/Impertio-Studio/Tauri-2-Claude-Skill-Package/tree/main/skills/source/tauri-syntax/tauri-syntax-menu
- **Website:** https://github.com/OpenAEC-Foundation

## Install

```sh
agentstack add skill-impertio-studio-tauri-2-claude-skill-package-tauri-syntax-menu
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# tauri-syntax-menu

## Quick Reference

### Menu Item Types (Rust)

| Type | Builder | Purpose |
|------|---------|---------|
| `MenuItem` | `MenuItemBuilder` | Basic clickable text item |
| `CheckMenuItem` | `CheckMenuItemBuilder` | Toggleable checkbox item |
| `IconMenuItem` | `IconMenuItemBuilder` | Item with icon |
| `PredefinedMenuItem` | -- | OS-native standard items |
| `Submenu` | `SubmenuBuilder` | Nested menu container |

### PredefinedMenuItem Types (both Rust and JS)

Platform-standard items: `About`, `Hide`, `HideOthers`, `ShowAll`, `CloseWindow`, `Quit`, `Copy`, `Cut`, `Paste`, `SelectAll`, `Undo`, `Redo`, `Minimize`, `Zoom`, `Separator`, `Fullscreen`, `Services`, `BringAllToFront`.

### SubmenuBuilder Convenience Methods (Rust)

| Method | Creates |
|--------|---------|
| `.text(id, text)` | MenuItem with ID and text |
| `.separator()` | Menu separator line |
| `.quit()` | PredefinedMenuItem::Quit |
| `.undo()` | PredefinedMenuItem::Undo |
| `.redo()` | PredefinedMenuItem::Redo |
| `.cut()` | PredefinedMenuItem::Cut |
| `.copy()` | PredefinedMenuItem::Copy |
| `.paste()` | PredefinedMenuItem::Paste |
| `.select_all()` | PredefinedMenuItem::SelectAll |

### TrayIconBuilder Key Methods (Rust)

| Method | Signature | Description |
|--------|-----------|-------------|
| `new()` | `() -> Self` | Create builder |
| `with_id(id)` | `(impl Into) -> Self` | Set custom ID |
| `icon(image)` | `(Image) -> Self` | Set tray icon |
| `tooltip(text)` | `(impl Into) -> Self` | Set tooltip |
| `title(text)` | `(impl Into) -> Self` | Set title |
| `menu(menu)` | `(&Menu) -> Self` | Attach context menu |
| `show_menu_on_left_click(bool)` | `(bool) -> Self` | Show menu on left click (default: true) |
| `icon_as_template(bool)` | `(bool) -> Self` | macOS template icon |
| `on_menu_event(F)` | `(F) -> Self` | Menu click handler |
| `on_tray_icon_event(F)` | `(F) -> Self` | Tray icon click handler |
| `build(manager)` | `(impl Manager) -> Result` | Build tray icon |

### JS Menu Classes

| Class | Import | Description |
|-------|--------|-------------|
| `Menu` | `@tauri-apps/api/menu` | Menu container |
| `MenuItem` | `@tauri-apps/api/menu` | Clickable text item |
| `Submenu` | `@tauri-apps/api/menu` | Nested menu |
| `CheckMenuItem` | `@tauri-apps/api/menu` | Toggleable item |
| `PredefinedMenuItem` | `@tauri-apps/api/menu` | OS-standard item |
| `TrayIcon` | `@tauri-apps/api/tray` | System tray icon |

### JS TrayIcon Event Types

| Event Type | Description |
|------------|-------------|
| `Click` | Tray icon clicked |
| `DoubleClick` | Tray icon double-clicked |
| `Enter` | Cursor entered tray icon |
| `Move` | Cursor moved over tray icon |
| `Leave` | Cursor left tray icon |

### JS TrayIcon Mouse Buttons

`Left`, `Right`, `Middle`

### Critical Warnings

**NEVER** call `Builder::on_menu_event()` in Tauri 2 -- it was removed. Use `App::on_menu_event()` or the builder `.on_menu_event()` method instead.

**NEVER** use Tauri v1 menu type names (`CustomMenuItem`, `SystemTray`, `MenuItem` for predefined items) -- they are renamed in v2.

**ALWAYS** call `.build()` on `MenuBuilder` and `SubmenuBuilder` -- forgetting `.build()` produces a builder, not a usable menu.

**ALWAYS** match menu event IDs as strings using `event.id().as_ref()` in Rust -- menu item IDs are compared as string references, not typed enums.

**ALWAYS** use `Menu.new()` (not `new Menu()`) in JavaScript -- menu items are created via async factory methods.

---

## Essential Patterns

### Pattern 1: Application Menu (Rust)

```rust
use tauri::menu::{MenuBuilder, SubmenuBuilder, PredefinedMenuItem};

tauri::Builder::default()
    .menu(|app| {
        let file_menu = SubmenuBuilder::new(app, "File")
            .text("new", "New")
            .text("open", "Open")
            .separator()
            .quit()
            .build()?;

        let edit_menu = SubmenuBuilder::new(app, "Edit")
            .undo()
            .redo()
            .separator()
            .cut()
            .copy()
            .paste()
            .select_all()
            .build()?;

        MenuBuilder::new(app)
            .item(&file_menu)
            .item(&edit_menu)
            .build()
    })
    .on_menu_event(|app, event| {
        match event.id().as_ref() {
            "new" => println!("New file"),
            "open" => println!("Open file"),
            _ => {}
        }
    })
    .run(tauri::generate_context!())
    .expect("error running app");
```

### Pattern 2: Application Menu (JavaScript)

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

const menu = await Menu.new({
  items: [
    await Submenu.new({
      text: 'File',
      items: [
        await MenuItem.new({
          text: 'Open',
          accelerator: 'CmdOrCtrl+O',
          action: () => { console.log('Open clicked'); },
        }),
        await MenuItem.new({
          text: 'Save',
          accelerator: 'CmdOrCtrl+S',
          action: () => { console.log('Save clicked'); },
        }),
        await PredefinedMenuItem.new({ item: 'Separator' }),
        await PredefinedMenuItem.new({ item: 'Quit' }),
      ],
    }),
    await Submenu.new({
      text: 'View',
      items: [
        await CheckMenuItem.new({
          text: 'Dark Mode',
          checked: false,
          action: (item) => { console.log('Toggled'); },
        }),
      ],
    }),
  ],
});

await menu.setAsAppMenu();
```

### Pattern 3: Context Menu (JavaScript)

```typescript
// Show context menu at cursor position
await menu.popup();

// Show context menu at specific position
await menu.popup({ x: 100, y: 200 });
```

### Pattern 4: System Tray (Rust)

```rust
use tauri::tray::TrayIconBuilder;
use tauri::menu::MenuBuilder;
use tauri::image::Image;

tauri::Builder::default()
    .setup(|app| {
        let menu = MenuBuilder::new(app)
            .text("show", "Show Window")
            .text("hide", "Hide Window")
            .separator()
            .text("quit", "Quit")
            .build()?;

        let _tray = TrayIconBuilder::new()
            .icon(Image::from_path("icons/tray.png")?)
            .tooltip("My Tauri App")
            .menu(&menu)
            .show_menu_on_left_click(true)
            .on_menu_event(|app, event| {
                match event.id().as_ref() {
                    "show" => {
                        if let Some(w) = app.get_webview_window("main") {
                            w.show().unwrap();
                        }
                    }
                    "quit" => app.exit(0),
                    _ => {}
                }
            })
            .on_tray_icon_event(|tray, event| {
                println!("Tray event: {:?}", event);
            })
            .build(app)?;

        Ok(())
    })
    .run(tauri::generate_context!())
    .expect("error running app");
```

### Pattern 5: System Tray (JavaScript)

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

const menu = await Menu.new({
  items: [
    await MenuItem.new({ text: 'Show', action: () => showWindow() }),
    await MenuItem.new({ text: 'Quit', action: () => exit(0) }),
  ],
});

const tray = await TrayIcon.new({
  icon: 'icons/tray-icon.png',
  tooltip: 'My App',
  menu,
  action: (event) => {
    if (event.type === 'Click') {
      console.log('Tray clicked with', event.button);
    }
  },
});

// Update at runtime
await tray.setTooltip('Updated tooltip');
await tray.setIcon('icons/new-icon.png');
await tray.setVisible(false);
```

### Pattern 6: Menu Item Management (JavaScript)

```typescript
// Dynamic menu manipulation
await menu.append(await MenuItem.new({ text: 'New Item', action: () => {} }));
await menu.prepend(await MenuItem.new({ text: 'First Item', action: () => {} }));
await menu.insert(1, await MenuItem.new({ text: 'At Index 1', action: () => {} }));
await menu.remove('item-id');
await menu.removeAt(0);

const item = await menu.get('item-id');
const allItems = await menu.items();
```

---

## Menu Event Handling (Rust)

```rust
// On the builder
.on_menu_event(|app, event| {
    match event.id().as_ref() {
        "new" => println!("New file"),
        "open" => println!("Open file"),
        _ => {}
    }
})
```

The `event.id()` returns a `MenuId`. Use `.as_ref()` to get a `&str` for pattern matching.

---

## Tray Configuration in tauri.conf.json

```json
{
  "app": {
    "trayIcon": {
      "iconPath": "icons/icon.png",
      "iconAsTemplate": true
    }
  }
}
```

---

## Permissions

Menu operations require `core:menu:default` and tray operations require `core:tray:default` in the capability file.

```json
{
  "permissions": [
    "core:menu:default",
    "core:tray:default"
  ]
}
```

---

## v1 to v2 Migration Names

| Tauri v1 | Tauri v2 |
|----------|----------|
| `Menu` | `MenuBuilder` |
| `CustomMenuItem` | `MenuItemBuilder` |
| `Submenu` | `SubmenuBuilder` |
| `MenuItem` (predefined) | `PredefinedMenuItem` |
| `SystemTray` | `TrayIconBuilder` |
| `SystemTrayEvent` | `TrayIconEvent` |

---

## Reference Links

- [references/methods.md](references/methods.md) -- Complete API signatures for Menu, Tray, and all item types
- [references/examples.md](references/examples.md) -- Working code examples for menu and tray patterns
- [references/anti-patterns.md](references/anti-patterns.md) -- What NOT to do with menus and tray, with WHY explanations

### Official Sources

- https://v2.tauri.app/develop/menu/
- https://v2.tauri.app/develop/system-tray/
- https://v2.tauri.app/reference/javascript/api/namespacemenu/
- https://v2.tauri.app/reference/javascript/api/namespacetray/

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Impertio-Studio](https://github.com/Impertio-Studio)
- **Source:** [Impertio-Studio/Tauri-2-Claude-Skill-Package](https://github.com/Impertio-Studio/Tauri-2-Claude-Skill-Package)
- **License:** MIT
- **Homepage:** https://github.com/OpenAEC-Foundation

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-impertio-studio-tauri-2-claude-skill-package-tauri-syntax-menu
- Seller: https://agentstack.voostack.com/s/impertio-studio
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
