fix(linux): fix icon display and deb installation issues (#12561)

- Fix Wayland window icon not showing in alt-tab and titlebar
- Fix fcitx5 input method cannot switch after sending message
- Fix deb installation failure due to spaces in executable name
- Add pacman build target for Arch Linux
- Rename Linux executable from "Cherry Studio" to "CherryStudio"
- Use nativeImage for proper icon loading on Linux

Fixes #12295, Fixes #12494

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
SuYao 2026-01-23 14:18:56 +08:00 committed by GitHub
parent 56bfa95d08
commit 0d3f0f20fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 44 additions and 9 deletions

View File

@ -116,14 +116,17 @@ dmg:
writeUpdateInfo: false
linux:
artifactName: ${productName}-${version}-${arch}.${ext}
executableName: CherryStudio
target:
- target: AppImage
- target: deb
- target: rpm
- target: pacman
maintainer: electronjs.org
category: Utility
desktop:
entry:
Name: Cherry Studio
StartupWMClass: CherryStudio
mimeTypes:
- x-scheme-handler/cherrystudio

View File

@ -3,6 +3,7 @@
"version": "1.7.13",
"private": true,
"description": "A powerful AI assistant for producer.",
"desktopName": "CherryStudio.desktop",
"main": "./out/main/index.js",
"author": "support@cherry-ai.com",
"homepage": "https://github.com/CherryHQ/cherry-studio",

View File

@ -76,12 +76,12 @@ if (isLinux && process.env.XDG_SESSION_TYPE === 'wayland') {
}
/**
* Set window class and name for X11
* This ensures the system tray and window manager identify the app correctly
* Set window class and name for Linux
* This ensures the window manager identifies the app correctly on both X11 and Wayland
*/
if (isLinux) {
app.commandLine.appendSwitch('class', 'cherry-studio')
app.commandLine.appendSwitch('name', 'cherry-studio')
app.commandLine.appendSwitch('class', 'CherryStudio')
app.commandLine.appendSwitch('name', 'CherryStudio')
}
// DocumentPolicyIncludeJSCallStacksInCrashReports: Enable features for unresponsive renderer js call stacks

View File

@ -9,11 +9,11 @@ import { getFilesDir } from '@main/utils/file'
import { generateUserAgent } from '@main/utils/systemInfo'
import { MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH } from '@shared/config/constant'
import { IpcChannel } from '@shared/IpcChannel'
import { app, BrowserWindow, nativeTheme, screen, shell } from 'electron'
import { app, BrowserWindow, nativeImage, nativeTheme, screen, shell } from 'electron'
import windowStateKeeper from 'electron-window-state'
import { join } from 'path'
import icon from '../../../build/icon.png?asset'
import iconPath from '../../../build/icon.png?asset'
import { titleBarOverlayDark, titleBarOverlayLight } from '../config'
import { configManager } from './ConfigManager'
import { contextMenu } from './ContextMenu'
@ -25,6 +25,9 @@ const DEFAULT_MINIWINDOW_HEIGHT = 400
// const logger = loggerService.withContext('WindowService')
const logger = loggerService.withContext('WindowService')
// Create nativeImage for Linux window icon (required for Wayland)
const linuxIcon = isLinux ? nativeImage.createFromPath(iconPath) : undefined
export class WindowService {
private static instance: WindowService | null = null
private mainWindow: BrowserWindow | null = null
@ -82,7 +85,7 @@ export class WindowService {
}),
backgroundColor: isMac ? undefined : nativeTheme.shouldUseDarkColors ? '#181818' : '#FFFFFF',
darkTheme: nativeTheme.shouldUseDarkColors,
...(isLinux ? { icon } : {}),
...(isLinux ? { icon: linuxIcon } : {}),
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,

View File

@ -427,10 +427,24 @@ const AgentSessionInputbarInner: FC<InnerProps> = ({ assistant, agentId, session
// Clear text after successful send (draft is cleared automatically via onChange)
setText('')
setTimeoutTimer('agentSession_sendMessage', () => setText(''), 500)
// Restore focus to textarea after sending to maintain IME state (fcitx5 issue)
focusTextarea()
} catch (error) {
logger.warn('Failed to send message:', error as Error)
}
}, [sendDisabled, agentId, dispatch, assistant, sessionId, sessionTopicId, setText, setTimeoutTimer, text, files])
}, [
sendDisabled,
agentId,
dispatch,
assistant,
sessionId,
sessionTopicId,
setText,
setTimeoutTimer,
text,
files,
focusTextarea
])
useEffect(() => {
if (!document.querySelector('.topview-fullscreen-container')) {

View File

@ -260,11 +260,25 @@ const InputbarInner: FC<InputbarInnerProps> = ({ assistant: initialAssistant, se
setFiles([])
setTimeoutTimer('sendMessage_1', () => setText(''), 500)
setTimeoutTimer('sendMessage_2', () => resizeTextArea(), 0)
// Restore focus to textarea after sending to maintain IME state (fcitx5 issue)
focusTextarea()
} catch (error) {
logger.warn('Failed to send message:', error as Error)
parent?.recordException(error as Error)
}
}, [assistant, topic, text, mentionedModels, files, dispatch, setText, setFiles, setTimeoutTimer, resizeTextArea])
}, [
assistant,
topic,
text,
mentionedModels,
files,
dispatch,
setText,
setFiles,
setTimeoutTimer,
resizeTextArea,
focusTextarea
])
const tokenCountProps = useMemo(() => {
if (!config.showTokenCount || estimateTokenCount === undefined || !showInputEstimatedTokens) {