cherry-studio/src/main/services/TrayService.ts
fullex dc06c103e0
chore[lint]: add import type lint (#11091)
chore: add import type lint
2025-11-01 10:40:02 +08:00

136 lines
4.0 KiB
TypeScript

import { preferenceService } from '@data/PreferenceService'
import { isLinux, isMac, isWin } from '@main/constant'
import { getI18n } from '@main/utils/language'
import type { MenuItemConstructorOptions } from 'electron'
import { app, Menu, nativeImage, nativeTheme, Tray } from 'electron'
import icon from '../../../build/tray_icon.png?asset'
import iconDark from '../../../build/tray_icon_dark.png?asset'
import iconLight from '../../../build/tray_icon_light.png?asset'
import selectionService from './SelectionService'
import { windowService } from './WindowService'
export class TrayService {
private static instance: TrayService
private tray: Tray | null = null
private contextMenu: Menu | null = null
constructor() {
this.watchConfigChanges()
this.updateTray()
TrayService.instance = this
}
public static getInstance() {
return TrayService.instance
}
private createTray() {
this.destroyTray()
const iconPath = isMac ? (nativeTheme.shouldUseDarkColors ? iconLight : iconDark) : icon
const tray = new Tray(iconPath)
if (isWin) {
tray.setImage(iconPath)
} else if (isMac) {
const image = nativeImage.createFromPath(iconPath)
const resizedImage = image.resize({ width: 16, height: 16 })
resizedImage.setTemplateImage(true)
tray.setImage(resizedImage)
} else if (isLinux) {
const image = nativeImage.createFromPath(iconPath)
const resizedImage = image.resize({ width: 16, height: 16 })
tray.setImage(resizedImage)
}
this.tray = tray
this.updateContextMenu()
if (isLinux) {
this.tray.setContextMenu(this.contextMenu)
}
this.tray.setToolTip('Cherry Studio')
this.tray.on('right-click', () => {
if (this.contextMenu) {
this.tray?.popUpContextMenu(this.contextMenu)
}
})
this.tray.on('click', () => {
const quickAssistantEnabled = preferenceService.get('feature.quick_assistant.enabled')
const clickTrayToShowQuickAssistant = preferenceService.get('feature.quick_assistant.click_tray_to_show')
if (quickAssistantEnabled && clickTrayToShowQuickAssistant) {
windowService.showMiniWindow()
} else {
windowService.showMainWindow()
}
})
}
private updateContextMenu() {
const i18n = getI18n()
const { tray: trayLocale, selection: selectionLocale } = i18n.translation
const quickAssistantEnabled = preferenceService.get('feature.quick_assistant.enabled')
const selectionAssistantEnabled = preferenceService.get('feature.selection.enabled')
const template = [
{
label: trayLocale.show_window,
click: () => windowService.showMainWindow()
},
quickAssistantEnabled && {
label: trayLocale.show_mini_window,
click: () => windowService.showMiniWindow()
},
(isWin || isMac) && {
label: selectionLocale.name + (selectionAssistantEnabled ? ' - On' : ' - Off'),
click: () => {
if (selectionService) {
selectionService.toggleEnabled()
this.updateContextMenu()
}
}
},
{ type: 'separator' },
{
label: trayLocale.quit,
click: () => this.quit()
}
].filter(Boolean) as MenuItemConstructorOptions[]
this.contextMenu = Menu.buildFromTemplate(template)
}
private updateTray() {
const showTray = preferenceService.get('app.tray.enabled')
if (showTray) {
this.createTray()
} else {
this.destroyTray()
}
}
private destroyTray() {
if (this.tray) {
this.tray.destroy()
this.tray = null
}
}
private watchConfigChanges() {
preferenceService.subscribeChange('app.tray.enabled', () => this.updateTray())
preferenceService.subscribeChange('app.language', () => this.updateContextMenu())
preferenceService.subscribeChange('feature.quick_assistant.enabled', () => this.updateContextMenu())
preferenceService.subscribeChange('feature.selection.enabled', () => this.updateContextMenu())
}
private quit() {
app.quit()
}
}