Створення нативного меню та системного трею для десктоп-додатків

Клієнт просить «зробити як у Telegram» — нативне меню та іконку в треї. Ми вже реалізували це на Electron і Tauri для десятка проєктів, але кожного разу стикаємося з нюансами платформної інтеграції. Наприклад, на macOS іконка трею без `Template Image` перетворюється на чорний квадрат, а на Windows к

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Створення нативного меню та системного трею для десктоп-додатків
Середній
~2-3 дні

Наші компетенції:

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1419
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    984
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1248
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    984
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    998

Клієнт просить «зробити як у Telegram» — нативне меню та іконку в треї. Ми вже реалізували це на Electron і Tauri для десятка проєктів, але кожного разу стикаємося з нюансами платформної інтеграції. Наприклад, на macOS іконка трею без Template Image перетворюється на чорний квадрат, а на Windows контекстне меню може не реагувати на лівий клік. У цій статті розберемо, як уникнути цих проблем і зробити застосунок «своїм» на кожній системі.

Нативне меню та системний трей — елементи, які перетворюють веб-обгортку на повноцінний десктоп-застосунок. Правильна реалізація робить застосунок «своїм» на кожній платформі; неправильна — виглядає як веб-сторінка в рамці. Ми беремо на себе розробку «під ключ»: від прототипу до публікації в магазинах застосунків. Вартість розраховується індивідуально, а економія часу на налагодження становить до 60% порівняно з самостійною реалізацією.

Як налаштувати нативне меню для macOS?

Меню будується з MenuItem об'єктів і встановлюється через Menu.setApplicationMenu. Нижче — повний приклад з урахуванням macOS (перший пункт — ім'я застосунку) та Windows.

// main/menu.js const { Menu, MenuItem, app, shell } = require('electron'); function createAppMenu(mainWindow) { const isMac = process.platform === 'darwin'; const template = [ ...(isMac ? [{ label: app.name, submenu: [ { role: 'about' }, { type: 'separator' }, { role: 'services' }, { type: 'separator' }, { role: 'hide' }, { role: 'hideOthers' }, { role: 'unhide' }, { type: 'separator' }, { role: 'quit' } ] }] : []), { label: 'Файл', submenu: [ { label: 'Новий', accelerator: 'CmdOrCtrl+N', click: () => mainWindow.webContents.send('menu:new') }, { label: 'Відкрити...', accelerator: 'CmdOrCtrl+O', click: async () => { const { dialog } = require('electron'); const result = await dialog.showOpenDialog(mainWindow, { filters: [{ name: 'Documents', extensions: ['json', 'txt'] }] }); if (!result.canceled) mainWindow.webContents.send('menu:open', result.filePaths[0]); }}, { type: 'separator' }, isMac ? { role: 'close' } : { role: 'quit' } ] }, { label: 'Правка', submenu: [ { role: 'undo' }, { role: 'redo' }, { type: 'separator' }, { role: 'cut' }, { role: 'copy' }, { role: 'paste' }, { role: 'selectAll' } ] }, { label: 'Вид', submenu: [ { role: 'reload' }, { type: 'separator' }, { role: 'resetZoom' }, { role: 'zoomIn' }, { role: 'zoomOut' }, { type: 'separator' }, { role: 'togglefullscreen' } ] }, { label: 'Довідка', role: 'help', submenu: [ { label: 'Документація', click: () => shell.openExternal('https://www.electronjs.org/docs/latest/api/menu') }, { label: `Версія ${app.getVersion()}`, enabled: false } ] } ]; const menu = Menu.buildFromTemplate(template); Menu.setApplicationMenu(menu); return menu; } module.exports = { createAppMenu }; 

Чому системний трей потребує особливого підходу на macOS?

На macOS іконка трею повинна бути Template Image — PNG 16×16 з темними пікселями на прозорому фоні. Система сама інвертує колір під темну/світлу тему. На Windows достатньо звичайної іконки. Ми автоматизуємо збірку іконок для всіх платформ. Процес займає до 1 дня та економить до 8 годин ручного налаштування.

// main/tray.js const { Tray, Menu, nativeImage, app } = require('electron'); const path = require('path'); let tray = null; function createTray(mainWindow) { const iconPath = process.platform === 'darwin' ? path.join(__dirname, '../resources/tray-icon-mac.png') : path.join(__dirname, '../resources/tray-icon.png'); tray = new Tray(nativeImage.createFromPath(iconPath)); tray.setToolTip('My Application'); const contextMenu = Menu.buildFromTemplate([ { label: 'Відкрити', click: () => { mainWindow.show(); mainWindow.focus(); } }, { label: 'Статус: активний', enabled: false, id: 'status-item' }, { type: 'separator' }, { label: 'Налаштування', click: () => { mainWindow.show(); mainWindow.webContents.send('navigate', '/settings'); } }, { type: 'separator' }, { label: 'Вийти', click: () => { app.isQuitting = true; app.quit(); } } ]); tray.setContextMenu(contextMenu); tray.on('click', () => { mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show(); }); mainWindow.on('close', (event) => { if (!app.isQuitting) { event.preventDefault(); mainWindow.hide(); } }); return tray; } module.exports = { createTray }; 

Tauri: нативне меню та трей

У Tauri v2 меню будується через tauri::menu::Menu, трей — через TrayIconBuilder. Rust-код компілюється в нативний бінарник, тому нативне меню працює без затримок. Tauri дає виграш у розмірі бінарника в 10 разів порівняно з Electron.

// src-tauri/src/lib.rs use tauri::menu::{Menu, MenuItem, Submenu, PredefinedMenuItem}; use tauri::tray::{TrayIconBuilder, TrayIconEvent, MouseButton}; use tauri::Manager; pub fn run() { tauri::Builder::default() .setup(|app| { let handle = app.handle(); // Меню let file_menu = Submenu::with_items(handle, "Файл", true, &[ &MenuItem::with_id(handle, "new", "Новий", true, Some("CmdOrCtrl+N"))?, &MenuItem::with_id(handle, "open", "Відкрити...", true, Some("CmdOrCtrl+O"))?, &PredefinedMenuItem::separator(handle)?, &PredefinedMenuItem::quit(handle, Some("Вийти"))?, ])?; let edit_menu = Submenu::with_items(handle, "Правка", true, &[ &PredefinedMenuItem::undo(handle, None)?, &PredefinedMenuItem::redo(handle, None)?, &PredefinedMenuItem::separator(handle)?, &PredefinedMenuItem::cut(handle, None)?, &PredefinedMenuItem::copy(handle, None)?, &PredefinedMenuItem::paste(handle, None)?, &PredefinedMenuItem::select_all(handle, None)?, ])?; let menu = Menu::with_items(handle, &[&file_menu, &edit_menu])?; app.set_menu(menu)?; // Трей let quit = MenuItem::with_id(handle, "quit", "Вийти", true, None::<&str>)?; let show = MenuItem::with_id(handle, "show", "Відкрити", true, None::<&str>)?; let tray_menu = Menu::with_items(handle, &[&show, &PredefinedMenuItem::separator(handle)?, &quit])?; TrayIconBuilder::with_id("main-tray") .tooltip("My Application") .icon(app.default_window_icon().unwrap().clone()) .menu(&tray_menu) .on_menu_event(|app, event| match event.id().as_ref() { "quit" => app.exit(0), "show" => { if let Some(window) = app.get_webview_window("main") { let _ = window.show(); let _ = window.set_focus(); } } _ => {} }) .on_tray_icon_event(|tray, event| { if let TrayIconEvent::Click { button: MouseButton::Left, .. } = event { let app = tray.app_handle(); if let Some(window) = app.get_webview_window("main") { if window.is_visible().unwrap_or(false) { let _ = window.hide(); } else { let _ = window.show(); let _ = window.set_focus(); } } } }) .build(app)?; Ok(()) }) .run(tauri::generate_context!()) .expect("error while running app"); } 

Порівняння Electron і Tauri для нативних елементів

Параметр Electron Tauri (v2)
Розмір бінарника 150 МБ+ (Chromium) 5–15 МБ (WebKit на macOS/Linux, Edge на Windows)
Продуктивність меню Нативне, але із затримкою IPC Нативне, прямий виклик Rust
Складність реалізації трею Середня (Node.js події) Середня (Rust-колбеки)
Крос-платформеність Відмінна (єдиний API) Добра (деякі нюанси на Linux)
Підтримка macOS Template icon Так, потрібен шаблон Так, аналогічно

Типові помилки при реалізації та їх вирішення

Часті проблеми з якими ми стикалися в проєктах
Помилка Вирішення
Іконка трею без Template на macOS Підготуйте PNG 16×16 з прозорістю; назвіть файл з префіксом Template (наприклад, iconTemplate.png).
Змішування IPC і UI потоків Завжди перевіряйте існування вікна перед викликом show(); використовуйте mainWindow.isDestroyed().
Відсутність обробника закриття вікна Встановіть прапорець app.isQuitting і обробляйте подію close з event.preventDefault().
Ігнорування ролей macOS Вставляйте app.name як перший пункт меню та використовуйте role для стандартних команд ОС.

Як нативне меню покращує користувацький досвід?

Нативне меню забезпечує звичні для користувача поєднання клавіш, підтримує системні сервіси (наприклад, macOS Services) та інтегрується з VoiceOver/Narrator для доступності. В одному проєкті для медичного застосунку ми реалізували меню з більш ніж 20 пунктами та трей зі сповіщеннями — це скоротило час виконання типових операцій на 30% порівняно з кастомним меню. В іншому кейсі міграція з Electron на Tauri зменшила розмір бінарника з 180 МБ до 12 МБ і прискорила запуск застосунку на 40%.

Що входить в роботу

  • Документація — опис структури меню, обробників подій, інструкція зі збірки іконок.
  • Доступи — репозиторій з кодом, CI/CD для збірки під усі платформи.
  • Навчання — роз'яснення коду, консультації щодо доробок.
  • Підтримка — гарантія 1 місяць на виправлення помилок, виявлених після здачі.
  • Аудит поточного стану — при міграції з Electron на Tauri або інший фреймворк.

Процес роботи над проєктом

  1. Аналітика — вивчаємо вимоги до меню та трею, список гарячих клавіш, поведінку на різних ОС.
  2. Проектування — готуємо структуру меню, макети іконок, обробники подій.
  3. Реалізація — пишемо код на JavaScript (Electron) або Rust (Tauri), інтегруємо з backend.
  4. Тестування — перевірка на macOS, Windows, Linux; юніт-тести для IPC і подій трею.
  5. Деплой — збірка під усі платформи, публікація в магазини (якщо потрібно).

Середній термін розробки нативного меню та трею — від 2 до 5 днів залежно від складності. Підсумкова вартість розраховується індивідуально після аналізу проєкту. Отримайте консультацію щодо вашого проєкту — допоможемо вибрати стек та підготуємо прототип за 1 день. Компанія на ринку більше 5 років, реалізовано понад 30 проєктів з нативними елементами для десктопу. Зв'яжіться з нами, щоб обговорити ваш проєкт.