# How TUUI Integrates the System Tray: Menu Creation and Window Management

> Discover how TUUI integrates the system tray for seamless menu creation and window management. Explore the createTray function and its advanced features for a better user experience.

- Repository: [AIQL/tuui](https://github.com/ai-ql/tuui)
- Tags: how-to-guide
- Published: 2026-02-23

---

**TL;DR:** TUUI implements system tray integration in the main process through the `createTray()` function in [`src/main/tray.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/tray.ts), which configures an Electron `Tray` instance with optional context menus, click handlers for window visibility, and dynamic positioning relative to the tray icon bounds.

TUUI, an Electron-based application framework maintained in the ai-ql/tuui repository, provides a flexible system tray integration that supports both traditional context menu behavior and floating window mode. The implementation centers on configurable **TrayOptions** that control menu creation, window alignment, and click interactions. This article examines the complete architecture from configuration defaults in [`src/main/utils/Constants.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/utils/Constants.ts) through the window positioning algorithms in [`src/main/tray.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/tray.ts).

## Architecture Overview

The system tray integration relies on four coordinated components:

- **`Constants.DEFAULT_TRAY_OPTIONS`**: Defines baseline configuration including `enabled`, `trayWindow`, `menu`, and `margin` settings in [`src/main/utils/Constants.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/utils/Constants.ts) (lines 58-65).
- **`MainRunner`**: Merges user-defined options with defaults, instantiates the main `BrowserWindow`, and conditionally invokes `createTray()` when `trayOptions.enabled` is true (lines 24-27 and 77-84 of [`src/main/MainRunner.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/MainRunner.ts)).
- **`createTray(window, options)`**: Core factory function in [`src/main/tray.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/tray.ts) (lines 7-66) that constructs the `Tray` instance, attaches event handlers, and optionally builds the context menu.
- **Window management helpers**: Utility functions `hideWindow`, `showWindow`, `toggleWindow`, and `alignWindow` (lines 69-121 of [`src/main/tray.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/tray.ts)) handle visibility state and geometric positioning.

The initialization flow proceeds sequentially: `MainRunner.createMainWindow()` constructs the `BrowserWindow`, checks `trayOptions.enabled`, and delegates tray setup to `createTray(mainWindow, trayOptions)`. When `trayWindow` mode is active, `alignWindow()` calculates the window's screen position relative to the tray icon bounds.

## Core Implementation Details

### Default Tray Configuration

Default behavior is centralized in [`src/main/utils/Constants.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/utils/Constants.ts):

```typescript
static DEFAULT_TRAY_OPTIONS: TrayOptions = {
  enabled: false,
  trayWindow: false,
  menu: false,
  tooltip: Constants.APP_NAME,
  margin: { x: 0, y: 0 },
  showAtStartup: false
}

```

These values serve as fallbacks when `MainRunner` merges configuration objects (lines 77-84). The `trayWindow` property determines whether the tray operates in floating-window mode (click toggles visibility) or traditional menu mode (right-click displays options).

### Tray Initialization and Event Wiring

The `createTray()` function in [`src/main/tray.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/tray.ts) implements the core logic:

```typescript
export function createTray(window: BrowserWindow, options) {
  trayOptions = options || Constants.DEFAULT_TRAY_OPTIONS

  // Floating window mode disables context menus
  if (trayOptions.trayWindow) {
    trayOptions.menu = false
  }

  tray = new Tray(Constants.ASSETS_PATH.icon)
  tray.setToolTip(trayOptions.tooltip)

  if (trayOptions.menu) {
    // Classic mode: right-click menu with dev tools and exit options
    tray.on('click', () => debounce(() => toggleWindow(window), 100))

    const contextMenu = Menu.buildFromTemplate([
      { label: 'Open Dev Tools', click: () => window.webContents.openDevTools() },
      { label: 'Force Reload',   click: () => window.webContents.reloadIgnoringCache() },
      {
        label: 'Clear Storage',
        click: () => {
          const sess = session.fromPartition(Constants.PARTITION_NAME)
          sess.clearStorageData({ storages: ['cookies','cachestorage','localstorage','indexdb','serviceworkers'] })
          window.webContents.reloadIgnoringCache()
        }
      },
      { label: 'Exit', click: () => app.quit() }
    ])
    tray.setContextMenu(contextMenu)
  } else {
    // Tray-window mode: both click and right-click toggle visibility
    tray.on('right-click', () => debounce(() => toggleWindow(window)))
    tray.on('click',        () => debounce(() => toggleWindow(window)))
  }

  alignWindow(window)
  return tray
}

```

Key implementation details include:

- **Icon sourcing**: Loaded from `Constants.ASSETS_PATH.icon` defined in the constants module.
- **Debounced handlers**: Click events use `debounce()` to prevent rapid-fire toggling.
- **Mode exclusivity**: When `trayWindow` is true, the code explicitly sets `trayOptions.menu = false` to prevent menu conflicts.

### Window Visibility Management

Helper functions manage the floating window's visibility state (lines 69-92):

```typescript
export function hideWindow(window) { 
  window.hide() 
}

export function toggleWindow(window) {
  if (window.isVisible()) hideWindow(window)
  else showWindow(window)
}

export function showWindow(window) {
  window.show()
  alignWindow(window)  // Recalculate position on every show
}

```

The `showWindow` function ensures the window re-aligns to the current tray position each time it becomes visible, accommodating changes in screen layout or tray location.

### Dynamic Positioning Algorithm

Window positioning is handled by `alignWindow()` and `calculateWindowPosition()` (lines 94-121):

```typescript
export function alignWindow(window) {
  if (!trayOptions.trayWindow) return
  const b = window.getBounds()
  const position = calculateWindowPosition(b)
  window.setBounds({ width: b.width, height: b.height, x: position.x, y: position.y })
}

```

The `calculateWindowPosition` function utilizes Electron's `screen` API to:

1. Obtain the primary display's work area dimensions.
2. Read the tray icon's bounds via `tray.getBounds()`.
3. Determine if the icon resides in the lower screen half to adjust vertical placement.
4. Apply optional `margin` offsets from `TrayOptions`.
5. Clamp coordinates to `Math.max(0, Math.min(...))` constraints to keep the window within visible screen bounds.

## Customizing TUUI's System Tray Behavior

To override default behavior, create a custom initialization module:

```typescript
import { BrowserWindow } from 'electron'
import { createTray } from './tray'
import Constants from './utils/Constants'

const customOptions = {
  enabled: true,
  trayWindow: true,          // Enable floating window mode
  menu: false,
  tooltip: 'Custom TUUI Instance',
  margin: { x: 10, y: 12 }, // Offset from tray icon
  showAtStartup: true       // Display immediately on launch
}

export function initCustomTray(mainWin: BrowserWindow) {
  const tray = createTray(mainWin, customOptions)
  // Runtime modifications are supported:
  tray.setToolTip('Application Active')
}

```

When `showAtStartup: true`, `MainRunner` (lines 30-33) automatically invokes `showWindow(mainWindow)` after creation, displaying the aligned window immediately.

## Summary

- **Configuration hierarchy**: Default `TrayOptions` in [`src/main/utils/Constants.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/utils/Constants.ts) are merged with user settings in `MainRunner` before passing to `createTray()`.
- **Dual mode support**: The implementation supports **context menu mode** (`menu: true`) with right-click templates or **tray-window mode** (`trayWindow: true`) with click-to-toggle visibility.
- **Positioning safety**: The `alignWindow()` function calculates geometry relative to tray icon bounds and constrains results to visible screen areas using `Math` clamps.
- **Event architecture**: All click handlers use debouncing and delegate to `toggleWindow()`, which coordinates between `showWindow` (triggering re-alignment) and `hideWindow`.

## Frequently Asked Questions

### How do I switch from a context menu to a floating window in TUUI?

Set `trayWindow: true` in your `TrayOptions` configuration. According to the source code in [`src/main/tray.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/tray.ts), this automatically disables the context menu (`trayOptions.menu = false`) and registers both left and right click events to call `toggleWindow()`, creating a floating window that appears near the tray icon.

### Where does TUUI store the default system tray settings?

Default settings are defined as `Constants.DEFAULT_TRAY_OPTIONS` in [`src/main/utils/Constants.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/utils/Constants.ts) (lines 58-65). These include `enabled: false`, `trayWindow: false`, `menu: false`, and default `margin` values of `{ x: 0, y: 0 }`.

### How does TUUI prevent the tray window from appearing off-screen?

The `calculateWindowPosition` function (called within `alignWindow` in [`src/main/tray.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/tray.ts)) uses Electron's `screen` API to determine the primary display bounds. It compares the tray icon's position against screen dimensions, applies configurable `margin` offsets, and clamps the final X/Y coordinates using `Math.max(0, Math.min(...))` to ensure the window remains within visible boundaries.

### Can I modify the context menu items in TUUI's system tray?

Yes. When `menu: true` is set, the `createTray()` function builds the menu using `Menu.buildFromTemplate()` with hardcoded labels like "Open Dev Tools" and "Clear Storage". To customize these, you would need to fork or modify [`src/main/tray.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/tray.ts) and alter the array passed to `Menu.buildFromTemplate()` before `tray.setContextMenu(contextMenu)` is called.