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

events.mddocs/

Event System

Real-time bidirectional communication system between frontend and backend with support for custom events, built-in system events, and flexible targeting options.

Capabilities

Event Listening

Listen for events from the backend or other parts of the application.

/**
 * Listen for an event and call handler each time it occurs
 * @param event - Event name to listen for
 * @param handler - Function to call when event occurs
 * @param options - Optional targeting and configuration
 * @returns Promise resolving to function that removes the listener
 */
function listen<T>(
  event: EventName, 
  handler: EventCallback<T>, 
  options?: Options
): Promise<UnlistenFn>;

/**
 * Listen for an event and call handler only once
 * @param event - Event name to listen for
 * @param handler - Function to call when event occurs
 * @param options - Optional targeting and configuration
 * @returns Promise resolving to function that removes the listener
 */
function once<T>(
  event: EventName, 
  handler: EventCallback<T>, 
  options?: Options
): Promise<UnlistenFn>;

type EventCallback<T> = (event: Event<T>) => void;
type UnlistenFn = () => void;
type EventName = string;

interface Options {
  target?: EventTarget;
}

Event Emission

Send events to other parts of the application or the backend.

/**
 * Emit an event to all listeners
 * @param event - Event name to emit
 * @param payload - Optional data to send with event
 */
function emit<T>(event: string, payload?: T): Promise<void>;

/**
 * Emit an event to a specific target
 * @param target - Target to send event to
 * @param event - Event name to emit
 * @param payload - Optional data to send with event
 */
function emitTo<T>(
  target: EventTarget | string, 
  event: string, 
  payload?: T
): Promise<void>;

Event Objects

Structure of events received by handlers.

/**
 * Event object passed to event handlers
 */
interface Event<T> {
  /** Event name */
  event: string;
  /** Unique event ID */
  id: number;
  /** Event payload data */
  payload: T;
}

Event Targeting

Control which components receive events.

/**
 * Event targeting options
 */
type EventTarget = 
  | { Any: null }
  | { AnyLabel: { label: string } }
  | { App: null }
  | { Window: { label: string } }
  | { Webview: { label: string } }
  | { WebviewWindow: { label: string } };

Built-in System Events

Predefined events automatically emitted by Tauri for system state changes.

/**
 * Built-in Tauri events
 */
enum TauriEvent {
  // Window events
  WINDOW_RESIZED = 'tauri://resize',
  WINDOW_MOVED = 'tauri://move',
  WINDOW_CLOSE_REQUESTED = 'tauri://close-requested',
  WINDOW_DESTROYED = 'tauri://destroyed',
  WINDOW_FOCUS = 'tauri://focus',
  WINDOW_BLUR = 'tauri://blur',
  WINDOW_SCALE_FACTOR_CHANGED = 'tauri://scale-change',
  WINDOW_THEME_CHANGED = 'tauri://theme-changed',
  WINDOW_FILE_DROP = 'tauri://file-drop',
  WINDOW_FILE_DROP_HOVER = 'tauri://file-drop-hover',
  WINDOW_FILE_DROP_CANCELLED = 'tauri://file-drop-cancelled',
  
  // Webview events
  WEBVIEW_CREATED = 'tauri://webview-created',
  
  // Menu events
  MENU = 'tauri://menu',
  
  // Tray events
  TRAY_CLICK = 'tauri://tray-click',
  TRAY_DOUBLE_CLICK = 'tauri://tray-double-click',
  TRAY_RIGHT_CLICK = 'tauri://tray-right-click',
  TRAY_ENTER = 'tauri://tray-enter',
  TRAY_LEAVE = 'tauri://tray-leave',
  TRAY_MOVE = 'tauri://tray-move'
}

File Drop Events

Events for handling file drag-and-drop operations.

/**
 * File drop event types
 */
type DragDropEvent = 
  | { type: 'drop'; paths: string[]; position: PhysicalPosition }
  | { type: 'hover'; paths: string[]; position: PhysicalPosition }
  | { type: 'cancel' };

Usage Examples

Basic Event Listening

import { listen, once } from '@tauri-apps/api/event';

// Listen for custom events from backend
const unlisten = await listen<string>('backend-message', (event) => {
  console.log('Received message:', event.payload);
  console.log('Event ID:', event.id);
});

// Listen for event only once
await once<{ status: string }>('initialization-complete', (event) => {
  console.log('App initialized with status:', event.payload.status);
});

// Remove listener when done
unlisten();

System Event Handling

import { listen, TauriEvent } from '@tauri-apps/api/event';

// Handle window resize
await listen(TauriEvent.WINDOW_RESIZED, (event) => {
  const { width, height } = event.payload;
  console.log(`Window resized to ${width}x${height}`);
  // Adjust UI layout
});

// Handle window focus/blur
await listen(TauriEvent.WINDOW_FOCUS, () => {
  document.body.classList.add('focused');
});

await listen(TauriEvent.WINDOW_BLUR, () => {
  document.body.classList.remove('focused');
});

// Handle theme changes
await listen(TauriEvent.WINDOW_THEME_CHANGED, (event) => {
  const theme = event.payload.theme; // 'light' | 'dark'
  document.documentElement.setAttribute('data-theme', theme);
});

File Drop Handling

import { listen, TauriEvent } from '@tauri-apps/api/event';

// Handle file drops
await listen<DragDropEvent>(TauriEvent.WINDOW_FILE_DROP, (event) => {
  if (event.payload.type === 'drop') {
    const files = event.payload.paths;
    console.log('Files dropped:', files);
    processDroppedFiles(files);
  }
});

// Handle drag hover
await listen<DragDropEvent>(TauriEvent.WINDOW_FILE_DROP_HOVER, (event) => {
  if (event.payload.type === 'hover') {
    document.body.classList.add('drag-over');
    showDropZone();
  }
});

// Handle drag cancel
await listen<DragDropEvent>(TauriEvent.WINDOW_FILE_DROP_CANCELLED, () => {
  document.body.classList.remove('drag-over');
  hideDropZone();
});

function processDroppedFiles(files: string[]) {
  files.forEach(async (file) => {
    if (file.endsWith('.json')) {
      // Process JSON files
      const content = await readTextFile(file);
      console.log('JSON content:', JSON.parse(content));
    } else if (file.match(/\.(jpg|png|gif)$/i)) {
      // Process image files
      const image = await Image.fromPath(file);
      displayImage(image);
    }
  });
}

Custom Event Communication

import { emit, emitTo, listen } from '@tauri-apps/api/event';

// Frontend to backend communication
await emit('user-action', { 
  type: 'button-click', 
  buttonId: 'save-btn' 
});

// Send data to backend for processing
await emit('process-data', {
  data: [1, 2, 3, 4, 5],
  algorithm: 'quicksort'
});

// Listen for backend responses
await listen<{ result: number[] }>('data-processed', (event) => {
  console.log('Processed data:', event.payload.result);
});

// Inter-window communication
await emitTo('settings-window', 'config-updated', {
  theme: 'dark',
  language: 'en'
});

Targeted Event Emission

import { emitTo } from '@tauri-apps/api/event';

// Send to specific window
await emitTo(
  { Window: { label: 'main-window' } },
  'refresh-data',
  { timestamp: Date.now() }
);

// Send to specific webview
await emitTo(
  { Webview: { label: 'content-view' } },
  'scroll-to-top',
  null
);

// Send to all windows with specific label pattern
await emitTo(
  { AnyLabel: { label: 'editor-*' } },
  'save-all',
  { force: true }
);

// Send to entire application
await emitTo(
  { App: null },
  'global-setting-changed',
  { setting: 'theme', value: 'dark' }
);

Event-Driven Architecture

import { listen, emit } from '@tauri-apps/api/event';

class EventBus {
  private listeners = new Map<string, Function[]>();
  
  async subscribe<T>(event: string, callback: (payload: T) => void) {
    const unlistenFn = await listen<T>(event, (e) => callback(e.payload));
    
    if (!this.listeners.has(event)) {
      this.listeners.set(event, []);
    }
    this.listeners.get(event)!.push(unlistenFn);
    
    return unlistenFn;
  }
  
  async publish<T>(event: string, payload: T) {
    await emit(event, payload);
  }
  
  cleanup() {
    this.listeners.forEach(unlisteners => {
      unlisteners.forEach(unlisten => unlisten());
    });
    this.listeners.clear();
  }
}

// Usage
const eventBus = new EventBus();

// Subscribe to events
await eventBus.subscribe<string>('user-login', (username) => {
  console.log(`User ${username} logged in`);
  updateUI();
});

await eventBus.subscribe<{ error: string }>('api-error', (data) => {
  showErrorNotification(data.error);
});

// Publish events
await eventBus.publish('user-login', 'john_doe');
await eventBus.publish('api-error', { error: 'Network timeout' });

Menu Event Integration

import { listen } from '@tauri-apps/api/event';

// Handle menu item clicks
await listen<{ menuItemId: string }>('tauri://menu', (event) => {
  const menuId = event.payload.menuItemId;
  
  switch (menuId) {
    case 'file-new':
      createNewFile();
      break;
    case 'file-open':
      openFileDialog();
      break;
    case 'edit-copy':
      copyToClipboard();
      break;
    case 'view-toggle-sidebar':
      toggleSidebar();
      break;
    default:
      console.log('Unknown menu item:', menuId);
  }
});

Tray Icon Events

import { listen } from '@tauri-apps/api/event';

// Handle tray icon interactions
await listen('tauri://tray-click', (event) => {
  console.log('Tray clicked:', event.payload);
  toggleMainWindow();
});

await listen('tauri://tray-double-click', () => {
  showMainWindow();
});

await listen('tauri://tray-right-click', () => {
  // Context menu automatically shown
  console.log('Tray right-clicked');
});

Error Handling and Cleanup

import { listen } from '@tauri-apps/api/event';

class ComponentWithEvents {
  private unlisteners: (() => void)[] = [];
  
  async initialize() {
    // Register multiple event listeners
    const unlisten1 = await listen('event1', this.handleEvent1.bind(this));
    const unlisten2 = await listen('event2', this.handleEvent2.bind(this));
    const unlisten3 = await listen('event3', this.handleEvent3.bind(this));
    
    this.unlisteners.push(unlisten1, unlisten2, unlisten3);
  }
  
  private handleEvent1(event: Event<any>) {
    try {
      // Handle event
    } catch (error) {
      console.error('Error handling event1:', error);
    }
  }
  
  private handleEvent2(event: Event<any>) {
    // Handle event
  }
  
  private handleEvent3(event: Event<any>) {
    // Handle event
  }
  
  cleanup() {
    // Remove all listeners when component is destroyed
    this.unlisteners.forEach(unlisten => unlisten());
    this.unlisteners = [];
  }
}

// Usage
const component = new ComponentWithEvents();
await component.initialize();

// Later, when component is no longer needed
component.cleanup();

Performance Considerations

import { listen, emit } from '@tauri-apps/api/event';

// Debounce high-frequency events
function debounce<T extends (...args: any[]) => void>(
  func: T, 
  wait: number
): T {
  let timeout: NodeJS.Timeout;
  return ((...args: any[]) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(this, args), wait);
  }) as T;
}

// Handle resize events efficiently
const debouncedResize = debounce((size: { width: number; height: number }) => {
  console.log('Window resized to:', size);
  // Expensive layout recalculation
}, 250);

await listen(TauriEvent.WINDOW_RESIZED, (event) => {
  debouncedResize(event.payload);
});

// Batch multiple events
let eventQueue: any[] = [];
const processQueue = () => {
  if (eventQueue.length > 0) {
    console.log('Processing', eventQueue.length, 'events');
    // Process all queued events at once
    eventQueue = [];
  }
};

await listen('high-frequency-event', (event) => {
  eventQueue.push(event.payload);
});

// Process queue periodically
setInterval(processQueue, 100);

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