diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 8c6810bcdc..87aad8d936 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -2,7 +2,7 @@ import fs from 'node:fs' import { arch } from 'node:os' import path from 'node:path' -import { isMac, isWin } from '@main/constant' +import { isLinux, isMac, isWin } from '@main/constant' import { getBinaryPath, isBinaryExists, runInstallScript } from '@main/utils/process' import { handleZoomFactor } from '@main/utils/zoom' import { UpgradeChannel } from '@shared/config/constant' @@ -306,6 +306,17 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { // Relaunch app ipcMain.handle(IpcChannel.App_RelaunchApp, (_, options?: Electron.RelaunchOptions) => { + // Fix for .AppImage + if (isLinux && process.env.APPIMAGE) { + log.info('Relaunching app with options:', process.env.APPIMAGE, options) + // On Linux, we need to use the APPIMAGE environment variable to relaunch + // https://github.com/electron-userland/electron-builder/issues/1727#issuecomment-769896927 + options = options || {} + options.execPath = process.env.APPIMAGE + options.args = options.args || [] + options.args.unshift('--appimage-extract-and-run') + } + app.relaunch(options) app.exit(0) }) diff --git a/src/main/utils/file.ts b/src/main/utils/file.ts index 177a28a90f..a85c5cf8ed 100644 --- a/src/main/utils/file.ts +++ b/src/main/utils/file.ts @@ -2,7 +2,7 @@ import * as fs from 'node:fs' import os from 'node:os' import path from 'node:path' -import { isPortable } from '@main/constant' +import { isLinux, isPortable } from '@main/constant' import { audioExts, documentExts, imageExts, textExts, videoExts } from '@shared/config/constant' import { FileType, FileTypes } from '@types' import { app } from 'electron' @@ -59,6 +59,13 @@ function getAppDataPathFromConfig() { return null } + let executablePath = app.getPath('exe') + if (isLinux && process.env.APPIMAGE) { + // 如果是 AppImage 打包的应用,直接使用 APPIMAGE 环境变量 + // 这样可以确保获取到正确的可执行文件路径 + executablePath = path.join(path.dirname(process.env.APPIMAGE), 'cherry-studio.appimage') + } + let appDataPath = null // 兼容旧版本 if (config.appDataPath && typeof config.appDataPath === 'string') { @@ -67,7 +74,7 @@ function getAppDataPathFromConfig() { appDataPath && updateAppDataConfig(appDataPath) } else { appDataPath = config.appDataPath.find( - (item: { executablePath: string }) => item.executablePath === app.getPath('exe') + (item: { executablePath: string }) => item.executablePath === executablePath )?.dataPath } @@ -90,11 +97,13 @@ export function updateAppDataConfig(appDataPath: string) { // config.json // appDataPath: [{ executablePath: string, dataPath: string }] const configPath = path.join(getConfigDir(), 'config.json') + let executablePath = app.getPath('exe') + if (isLinux && process.env.APPIMAGE) { + executablePath = path.join(path.dirname(process.env.APPIMAGE), 'cherry-studio.appimage') + } + if (!fs.existsSync(configPath)) { - fs.writeFileSync( - configPath, - JSON.stringify({ appDataPath: [{ executablePath: app.getPath('exe'), dataPath: appDataPath }] }, null, 2) - ) + fs.writeFileSync(configPath, JSON.stringify({ appDataPath: [{ executablePath, dataPath: appDataPath }] }, null, 2)) return } @@ -104,13 +113,13 @@ export function updateAppDataConfig(appDataPath: string) { } const existingPath = config.appDataPath.find( - (item: { executablePath: string }) => item.executablePath === app.getPath('exe') + (item: { executablePath: string }) => item.executablePath === executablePath ) if (existingPath) { existingPath.dataPath = appDataPath } else { - config.appDataPath.push({ executablePath: app.getPath('exe'), dataPath: appDataPath }) + config.appDataPath.push({ executablePath, dataPath: appDataPath }) } fs.writeFileSync(configPath, JSON.stringify(config, null, 2))