or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

app-lifecycle.mdcore-ipc.mdevents.mdindex.mdmenu-system.mdsystem-integration.mdtesting.mdutilities.mdwindow-management.md

events.mddocs/

0

# Event System

1

2

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

3

4

## Capabilities

5

6

### Event Listening

7

8

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

9

10

```typescript { .api }

11

/**

12

* Listen for an event and call handler each time it occurs

13

* @param event - Event name to listen for

14

* @param handler - Function to call when event occurs

15

* @param options - Optional targeting and configuration

16

* @returns Promise resolving to function that removes the listener

17

*/

18

function listen<T>(

19

event: EventName,

20

handler: EventCallback<T>,

21

options?: Options

22

): Promise<UnlistenFn>;

23

24

/**

25

* Listen for an event and call handler only once

26

* @param event - Event name to listen for

27

* @param handler - Function to call when event occurs

28

* @param options - Optional targeting and configuration

29

* @returns Promise resolving to function that removes the listener

30

*/

31

function once<T>(

32

event: EventName,

33

handler: EventCallback<T>,

34

options?: Options

35

): Promise<UnlistenFn>;

36

37

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

38

type UnlistenFn = () => void;

39

type EventName = string;

40

41

interface Options {

42

target?: EventTarget;

43

}

44

```

45

46

### Event Emission

47

48

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

49

50

```typescript { .api }

51

/**

52

* Emit an event to all listeners

53

* @param event - Event name to emit

54

* @param payload - Optional data to send with event

55

*/

56

function emit<T>(event: string, payload?: T): Promise<void>;

57

58

/**

59

* Emit an event to a specific target

60

* @param target - Target to send event to

61

* @param event - Event name to emit

62

* @param payload - Optional data to send with event

63

*/

64

function emitTo<T>(

65

target: EventTarget | string,

66

event: string,

67

payload?: T

68

): Promise<void>;

69

```

70

71

### Event Objects

72

73

Structure of events received by handlers.

74

75

```typescript { .api }

76

/**

77

* Event object passed to event handlers

78

*/

79

interface Event<T> {

80

/** Event name */

81

event: string;

82

/** Unique event ID */

83

id: number;

84

/** Event payload data */

85

payload: T;

86

}

87

```

88

89

### Event Targeting

90

91

Control which components receive events.

92

93

```typescript { .api }

94

/**

95

* Event targeting options

96

*/

97

type EventTarget =

98

| { Any: null }

99

| { AnyLabel: { label: string } }

100

| { App: null }

101

| { Window: { label: string } }

102

| { Webview: { label: string } }

103

| { WebviewWindow: { label: string } };

104

```

105

106

### Built-in System Events

107

108

Predefined events automatically emitted by Tauri for system state changes.

109

110

```typescript { .api }

111

/**

112

* Built-in Tauri events

113

*/

114

enum TauriEvent {

115

// Window events

116

WINDOW_RESIZED = 'tauri://resize',

117

WINDOW_MOVED = 'tauri://move',

118

WINDOW_CLOSE_REQUESTED = 'tauri://close-requested',

119

WINDOW_DESTROYED = 'tauri://destroyed',

120

WINDOW_FOCUS = 'tauri://focus',

121

WINDOW_BLUR = 'tauri://blur',

122

WINDOW_SCALE_FACTOR_CHANGED = 'tauri://scale-change',

123

WINDOW_THEME_CHANGED = 'tauri://theme-changed',

124

WINDOW_FILE_DROP = 'tauri://file-drop',

125

WINDOW_FILE_DROP_HOVER = 'tauri://file-drop-hover',

126

WINDOW_FILE_DROP_CANCELLED = 'tauri://file-drop-cancelled',

127

128

// Webview events

129

WEBVIEW_CREATED = 'tauri://webview-created',

130

131

// Menu events

132

MENU = 'tauri://menu',

133

134

// Tray events

135

TRAY_CLICK = 'tauri://tray-click',

136

TRAY_DOUBLE_CLICK = 'tauri://tray-double-click',

137

TRAY_RIGHT_CLICK = 'tauri://tray-right-click',

138

TRAY_ENTER = 'tauri://tray-enter',

139

TRAY_LEAVE = 'tauri://tray-leave',

140

TRAY_MOVE = 'tauri://tray-move'

141

}

142

```

143

144

### File Drop Events

145

146

Events for handling file drag-and-drop operations.

147

148

```typescript { .api }

149

/**

150

* File drop event types

151

*/

152

type DragDropEvent =

153

| { type: 'drop'; paths: string[]; position: PhysicalPosition }

154

| { type: 'hover'; paths: string[]; position: PhysicalPosition }

155

| { type: 'cancel' };

156

```

157

158

## Usage Examples

159

160

### Basic Event Listening

161

162

```typescript

163

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

164

165

// Listen for custom events from backend

166

const unlisten = await listen<string>('backend-message', (event) => {

167

console.log('Received message:', event.payload);

168

console.log('Event ID:', event.id);

169

});

170

171

// Listen for event only once

172

await once<{ status: string }>('initialization-complete', (event) => {

173

console.log('App initialized with status:', event.payload.status);

174

});

175

176

// Remove listener when done

177

unlisten();

178

```

179

180

### System Event Handling

181

182

```typescript

183

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

184

185

// Handle window resize

186

await listen(TauriEvent.WINDOW_RESIZED, (event) => {

187

const { width, height } = event.payload;

188

console.log(`Window resized to ${width}x${height}`);

189

// Adjust UI layout

190

});

191

192

// Handle window focus/blur

193

await listen(TauriEvent.WINDOW_FOCUS, () => {

194

document.body.classList.add('focused');

195

});

196

197

await listen(TauriEvent.WINDOW_BLUR, () => {

198

document.body.classList.remove('focused');

199

});

200

201

// Handle theme changes

202

await listen(TauriEvent.WINDOW_THEME_CHANGED, (event) => {

203

const theme = event.payload.theme; // 'light' | 'dark'

204

document.documentElement.setAttribute('data-theme', theme);

205

});

206

```

207

208

### File Drop Handling

209

210

```typescript

211

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

212

213

// Handle file drops

214

await listen<DragDropEvent>(TauriEvent.WINDOW_FILE_DROP, (event) => {

215

if (event.payload.type === 'drop') {

216

const files = event.payload.paths;

217

console.log('Files dropped:', files);

218

processDroppedFiles(files);

219

}

220

});

221

222

// Handle drag hover

223

await listen<DragDropEvent>(TauriEvent.WINDOW_FILE_DROP_HOVER, (event) => {

224

if (event.payload.type === 'hover') {

225

document.body.classList.add('drag-over');

226

showDropZone();

227

}

228

});

229

230

// Handle drag cancel

231

await listen<DragDropEvent>(TauriEvent.WINDOW_FILE_DROP_CANCELLED, () => {

232

document.body.classList.remove('drag-over');

233

hideDropZone();

234

});

235

236

function processDroppedFiles(files: string[]) {

237

files.forEach(async (file) => {

238

if (file.endsWith('.json')) {

239

// Process JSON files

240

const content = await readTextFile(file);

241

console.log('JSON content:', JSON.parse(content));

242

} else if (file.match(/\.(jpg|png|gif)$/i)) {

243

// Process image files

244

const image = await Image.fromPath(file);

245

displayImage(image);

246

}

247

});

248

}

249

```

250

251

### Custom Event Communication

252

253

```typescript

254

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

255

256

// Frontend to backend communication

257

await emit('user-action', {

258

type: 'button-click',

259

buttonId: 'save-btn'

260

});

261

262

// Send data to backend for processing

263

await emit('process-data', {

264

data: [1, 2, 3, 4, 5],

265

algorithm: 'quicksort'

266

});

267

268

// Listen for backend responses

269

await listen<{ result: number[] }>('data-processed', (event) => {

270

console.log('Processed data:', event.payload.result);

271

});

272

273

// Inter-window communication

274

await emitTo('settings-window', 'config-updated', {

275

theme: 'dark',

276

language: 'en'

277

});

278

```

279

280

### Targeted Event Emission

281

282

```typescript

283

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

284

285

// Send to specific window

286

await emitTo(

287

{ Window: { label: 'main-window' } },

288

'refresh-data',

289

{ timestamp: Date.now() }

290

);

291

292

// Send to specific webview

293

await emitTo(

294

{ Webview: { label: 'content-view' } },

295

'scroll-to-top',

296

null

297

);

298

299

// Send to all windows with specific label pattern

300

await emitTo(

301

{ AnyLabel: { label: 'editor-*' } },

302

'save-all',

303

{ force: true }

304

);

305

306

// Send to entire application

307

await emitTo(

308

{ App: null },

309

'global-setting-changed',

310

{ setting: 'theme', value: 'dark' }

311

);

312

```

313

314

### Event-Driven Architecture

315

316

```typescript

317

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

318

319

class EventBus {

320

private listeners = new Map<string, Function[]>();

321

322

async subscribe<T>(event: string, callback: (payload: T) => void) {

323

const unlistenFn = await listen<T>(event, (e) => callback(e.payload));

324

325

if (!this.listeners.has(event)) {

326

this.listeners.set(event, []);

327

}

328

this.listeners.get(event)!.push(unlistenFn);

329

330

return unlistenFn;

331

}

332

333

async publish<T>(event: string, payload: T) {

334

await emit(event, payload);

335

}

336

337

cleanup() {

338

this.listeners.forEach(unlisteners => {

339

unlisteners.forEach(unlisten => unlisten());

340

});

341

this.listeners.clear();

342

}

343

}

344

345

// Usage

346

const eventBus = new EventBus();

347

348

// Subscribe to events

349

await eventBus.subscribe<string>('user-login', (username) => {

350

console.log(`User ${username} logged in`);

351

updateUI();

352

});

353

354

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

355

showErrorNotification(data.error);

356

});

357

358

// Publish events

359

await eventBus.publish('user-login', 'john_doe');

360

await eventBus.publish('api-error', { error: 'Network timeout' });

361

```

362

363

### Menu Event Integration

364

365

```typescript

366

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

367

368

// Handle menu item clicks

369

await listen<{ menuItemId: string }>('tauri://menu', (event) => {

370

const menuId = event.payload.menuItemId;

371

372

switch (menuId) {

373

case 'file-new':

374

createNewFile();

375

break;

376

case 'file-open':

377

openFileDialog();

378

break;

379

case 'edit-copy':

380

copyToClipboard();

381

break;

382

case 'view-toggle-sidebar':

383

toggleSidebar();

384

break;

385

default:

386

console.log('Unknown menu item:', menuId);

387

}

388

});

389

```

390

391

### Tray Icon Events

392

393

```typescript

394

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

395

396

// Handle tray icon interactions

397

await listen('tauri://tray-click', (event) => {

398

console.log('Tray clicked:', event.payload);

399

toggleMainWindow();

400

});

401

402

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

403

showMainWindow();

404

});

405

406

await listen('tauri://tray-right-click', () => {

407

// Context menu automatically shown

408

console.log('Tray right-clicked');

409

});

410

```

411

412

### Error Handling and Cleanup

413

414

```typescript

415

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

416

417

class ComponentWithEvents {

418

private unlisteners: (() => void)[] = [];

419

420

async initialize() {

421

// Register multiple event listeners

422

const unlisten1 = await listen('event1', this.handleEvent1.bind(this));

423

const unlisten2 = await listen('event2', this.handleEvent2.bind(this));

424

const unlisten3 = await listen('event3', this.handleEvent3.bind(this));

425

426

this.unlisteners.push(unlisten1, unlisten2, unlisten3);

427

}

428

429

private handleEvent1(event: Event<any>) {

430

try {

431

// Handle event

432

} catch (error) {

433

console.error('Error handling event1:', error);

434

}

435

}

436

437

private handleEvent2(event: Event<any>) {

438

// Handle event

439

}

440

441

private handleEvent3(event: Event<any>) {

442

// Handle event

443

}

444

445

cleanup() {

446

// Remove all listeners when component is destroyed

447

this.unlisteners.forEach(unlisten => unlisten());

448

this.unlisteners = [];

449

}

450

}

451

452

// Usage

453

const component = new ComponentWithEvents();

454

await component.initialize();

455

456

// Later, when component is no longer needed

457

component.cleanup();

458

```

459

460

### Performance Considerations

461

462

```typescript

463

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

464

465

// Debounce high-frequency events

466

function debounce<T extends (...args: any[]) => void>(

467

func: T,

468

wait: number

469

): T {

470

let timeout: NodeJS.Timeout;

471

return ((...args: any[]) => {

472

clearTimeout(timeout);

473

timeout = setTimeout(() => func.apply(this, args), wait);

474

}) as T;

475

}

476

477

// Handle resize events efficiently

478

const debouncedResize = debounce((size: { width: number; height: number }) => {

479

console.log('Window resized to:', size);

480

// Expensive layout recalculation

481

}, 250);

482

483

await listen(TauriEvent.WINDOW_RESIZED, (event) => {

484

debouncedResize(event.payload);

485

});

486

487

// Batch multiple events

488

let eventQueue: any[] = [];

489

const processQueue = () => {

490

if (eventQueue.length > 0) {

491

console.log('Processing', eventQueue.length, 'events');

492

// Process all queued events at once

493

eventQueue = [];

494

}

495

};

496

497

await listen('high-frequency-event', (event) => {

498

eventQueue.push(event.payload);

499

});

500

501

// Process queue periodically

502

setInterval(processQueue, 100);

503

```