feat: implement launch at startup functionality in zn-ai
- Added a new setting for "launch at startup" in the GeneralSettingsPanel. - Integrated the setting with the existing settings store and IPC mechanisms. - Implemented platform-specific logic for enabling/disabling startup behavior in the main process. - Created a new service for managing launch at startup settings, including Linux desktop entry creation. - Added unit tests for the new functionality and ensured existing tests are updated accordingly. - Updated i18n messages for the new setting in English, Chinese, and Japanese.
This commit is contained in:
111
electron/service/launch-at-startup/index.ts
Normal file
111
electron/service/launch-at-startup/index.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { app } from 'electron';
|
||||
import { mkdir, rm, writeFile } from 'node:fs/promises';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { CONFIG_KEYS } from '@runtime/lib/constants';
|
||||
import configManager from '@electron/service/config-service';
|
||||
import { logManager } from '@electron/service/logger';
|
||||
|
||||
const LINUX_AUTOSTART_DIR = join('.config', 'autostart');
|
||||
|
||||
function getApplicationName(): string {
|
||||
const applicationName = typeof app.getName === 'function' ? app.getName() : '';
|
||||
return applicationName.trim() || 'zn-ai';
|
||||
}
|
||||
|
||||
function sanitizeDesktopFileName(name: string): string {
|
||||
const normalized = name
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '');
|
||||
|
||||
return normalized || 'zn-ai';
|
||||
}
|
||||
|
||||
function quoteDesktopArg(value: string): string {
|
||||
if (!value) return '""';
|
||||
|
||||
const escaped = value.replace(/(["\\`$])/g, '\\$1');
|
||||
if (/[\s"'\\`$]/.test(value)) {
|
||||
return `"${escaped}"`;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function getLinuxExecCommand(): string {
|
||||
if (app.isPackaged) {
|
||||
return quoteDesktopArg(process.execPath);
|
||||
}
|
||||
|
||||
const launchArgs = process.argv.slice(1).filter(Boolean);
|
||||
const commandParts = [process.execPath, ...launchArgs].map(quoteDesktopArg);
|
||||
return commandParts.join(' ');
|
||||
}
|
||||
|
||||
function getLinuxDesktopEntryPath(): string {
|
||||
const desktopFileName = `${sanitizeDesktopFileName(getApplicationName())}.desktop`;
|
||||
return join(app.getPath('home'), LINUX_AUTOSTART_DIR, desktopFileName);
|
||||
}
|
||||
|
||||
function getLinuxDesktopEntry(): string {
|
||||
const applicationName = getApplicationName();
|
||||
|
||||
return [
|
||||
'[Desktop Entry]',
|
||||
'Type=Application',
|
||||
'Version=1.0',
|
||||
`Name=${applicationName}`,
|
||||
`Comment=${applicationName} desktop application`,
|
||||
`Exec=${getLinuxExecCommand()}`,
|
||||
'Terminal=false',
|
||||
'Categories=Utility;',
|
||||
'X-GNOME-Autostart-enabled=true',
|
||||
'',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
async function applyLinuxLaunchAtStartup(enabled: boolean): Promise<void> {
|
||||
const targetPath = getLinuxDesktopEntryPath();
|
||||
|
||||
if (enabled) {
|
||||
await mkdir(dirname(targetPath), { recursive: true });
|
||||
await writeFile(targetPath, getLinuxDesktopEntry(), 'utf8');
|
||||
logManager.info(`Launch-at-startup enabled via desktop entry: ${targetPath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
await rm(targetPath, { force: true });
|
||||
logManager.info(`Launch-at-startup disabled and desktop entry removed: ${targetPath}`);
|
||||
}
|
||||
|
||||
function applyWindowsOrMacLaunchAtStartup(enabled: boolean): void {
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: enabled,
|
||||
openAsHidden: false,
|
||||
});
|
||||
logManager.info(`Launch-at-startup ${enabled ? 'enabled' : 'disabled'} via login items`);
|
||||
}
|
||||
|
||||
export async function applyLaunchAtStartupSetting(enabled: boolean): Promise<void> {
|
||||
try {
|
||||
if (process.platform === 'linux') {
|
||||
await applyLinuxLaunchAtStartup(enabled);
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.platform === 'win32' || process.platform === 'darwin') {
|
||||
applyWindowsOrMacLaunchAtStartup(enabled);
|
||||
return;
|
||||
}
|
||||
|
||||
logManager.warn(`Launch-at-startup unsupported on platform: ${process.platform}`);
|
||||
} catch (error) {
|
||||
logManager.error(`Failed to apply launch-at-startup=${enabled}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function syncLaunchAtStartupSettingFromConfig(): Promise<void> {
|
||||
const launchAtStartup = configManager.get<boolean>(CONFIG_KEYS.LAUNCH_AT_STARTUP);
|
||||
await applyLaunchAtStartupSetting(Boolean(launchAtStartup));
|
||||
}
|
||||
Reference in New Issue
Block a user