372 lines
10 KiB
TypeScript
Vendored
372 lines
10 KiB
TypeScript
Vendored
import path from 'path';
|
|
import fsExtra from 'fs-extra';
|
|
|
|
import combineFiles from '@/utils/combine';
|
|
import logger from '@/options/logger';
|
|
import { PakeAppOptions, PlatformMap } from '@/types';
|
|
import {
|
|
tauriConfigDirectory,
|
|
npmDirectory,
|
|
getUserHomeDir,
|
|
} from '@/utils/dir';
|
|
|
|
export async function mergeConfig(
|
|
url: string,
|
|
options: PakeAppOptions,
|
|
tauriConf: any,
|
|
) {
|
|
// Ensure .pake directory exists and copy source templates if needed
|
|
const srcTauriDir = path.join(npmDirectory, 'src-tauri');
|
|
await fsExtra.ensureDir(tauriConfigDirectory);
|
|
|
|
// Copy source config files to .pake directory (as templates)
|
|
const sourceFiles = [
|
|
'tauri.conf.json',
|
|
'tauri.macos.conf.json',
|
|
'tauri.windows.conf.json',
|
|
'tauri.linux.conf.json',
|
|
'pake.json',
|
|
];
|
|
|
|
await Promise.all(
|
|
sourceFiles.map(async (file) => {
|
|
const sourcePath = path.join(srcTauriDir, file);
|
|
const destPath = path.join(tauriConfigDirectory, file);
|
|
|
|
if (
|
|
(await fsExtra.pathExists(sourcePath)) &&
|
|
!(await fsExtra.pathExists(destPath))
|
|
) {
|
|
await fsExtra.copy(sourcePath, destPath);
|
|
}
|
|
}),
|
|
);
|
|
const {
|
|
width,
|
|
height,
|
|
fullscreen,
|
|
hideTitleBar,
|
|
alwaysOnTop,
|
|
appVersion,
|
|
darkMode,
|
|
disabledWebShortcuts,
|
|
activationShortcut,
|
|
userAgent,
|
|
showSystemTray,
|
|
systemTrayIcon,
|
|
useLocalFile,
|
|
identifier,
|
|
name,
|
|
resizable = true,
|
|
inject,
|
|
proxyUrl,
|
|
installerLanguage,
|
|
hideOnClose,
|
|
incognito,
|
|
title,
|
|
} = options;
|
|
|
|
const { platform } = process;
|
|
|
|
// Set Windows parameters.
|
|
const tauriConfWindowOptions = {
|
|
width,
|
|
height,
|
|
fullscreen,
|
|
resizable,
|
|
hide_title_bar: hideTitleBar,
|
|
activation_shortcut: activationShortcut,
|
|
always_on_top: alwaysOnTop,
|
|
dark_mode: darkMode,
|
|
disabled_web_shortcuts: disabledWebShortcuts,
|
|
hide_on_close: hideOnClose,
|
|
incognito: incognito,
|
|
title: title || null,
|
|
};
|
|
Object.assign(tauriConf.pake.windows[0], { url, ...tauriConfWindowOptions });
|
|
|
|
tauriConf.productName = name;
|
|
tauriConf.identifier = identifier;
|
|
tauriConf.version = appVersion;
|
|
|
|
if (platform == 'win32') {
|
|
tauriConf.bundle.windows.wix.language[0] = installerLanguage;
|
|
}
|
|
|
|
//Judge the type of URL, whether it is a file or a website.
|
|
const pathExists = await fsExtra.pathExists(url);
|
|
if (pathExists) {
|
|
logger.warn('✼ Your input might be a local file.');
|
|
tauriConf.pake.windows[0].url_type = 'local';
|
|
|
|
const fileName = path.basename(url);
|
|
const dirName = path.dirname(url);
|
|
|
|
const distDir = path.join(npmDirectory, 'dist');
|
|
const distBakDir = path.join(npmDirectory, 'dist_bak');
|
|
|
|
if (!useLocalFile) {
|
|
const urlPath = path.join(distDir, fileName);
|
|
await fsExtra.copy(url, urlPath);
|
|
} else {
|
|
fsExtra.moveSync(distDir, distBakDir, { overwrite: true });
|
|
fsExtra.copySync(dirName, distDir, { overwrite: true });
|
|
|
|
// ignore it, because about_pake.html have be erased.
|
|
// const filesToCopyBack = ['cli.js', 'about_pake.html'];
|
|
const filesToCopyBack = ['cli.js'];
|
|
await Promise.all(
|
|
filesToCopyBack.map((file) =>
|
|
fsExtra.copy(path.join(distBakDir, file), path.join(distDir, file)),
|
|
),
|
|
);
|
|
}
|
|
|
|
tauriConf.pake.windows[0].url = fileName;
|
|
tauriConf.pake.windows[0].url_type = 'local';
|
|
} else {
|
|
tauriConf.pake.windows[0].url_type = 'web';
|
|
}
|
|
|
|
const platformMap: PlatformMap = {
|
|
win32: 'windows',
|
|
linux: 'linux',
|
|
darwin: 'macos',
|
|
};
|
|
const currentPlatform = platformMap[platform];
|
|
|
|
if (userAgent.length > 0) {
|
|
tauriConf.pake.user_agent[currentPlatform] = userAgent;
|
|
}
|
|
|
|
tauriConf.pake.system_tray[currentPlatform] = showSystemTray;
|
|
|
|
// Processing targets are currently only open to Linux.
|
|
if (platform === 'linux') {
|
|
delete tauriConf.bundle.linux.deb.files;
|
|
const validTargets = ['deb', 'appimage', 'rpm'];
|
|
if (validTargets.includes(options.targets)) {
|
|
tauriConf.bundle.targets = [options.targets];
|
|
} else {
|
|
logger.warn(
|
|
`✼ The target must be one of ${validTargets.join(', ')}, the default 'deb' will be used.`,
|
|
);
|
|
}
|
|
}
|
|
|
|
// Set icon.
|
|
const platformIconMap: PlatformMap = {
|
|
win32: {
|
|
fileExt: '.ico',
|
|
path: `png/${name.toLowerCase()}_256.ico`,
|
|
defaultIcon: 'png/icon_256.ico',
|
|
message: 'Windows icon must be .ico and 256x256px.',
|
|
},
|
|
linux: {
|
|
fileExt: '.png',
|
|
path: `png/${name.toLowerCase()}_512.png`,
|
|
defaultIcon: 'png/icon_512.png',
|
|
message: 'Linux icon must be .png and 512x512px.',
|
|
},
|
|
darwin: {
|
|
fileExt: '.icns',
|
|
path: `icons/${name.toLowerCase()}.icns`,
|
|
defaultIcon: 'icons/icon.icns',
|
|
message: 'macOS icon must be .icns type.',
|
|
},
|
|
};
|
|
const iconInfo = platformIconMap[platform];
|
|
const exists = await fsExtra.pathExists(options.icon);
|
|
if (exists) {
|
|
let updateIconPath = true;
|
|
let customIconExt = path.extname(options.icon).toLowerCase();
|
|
|
|
if (customIconExt !== iconInfo.fileExt) {
|
|
updateIconPath = false;
|
|
logger.warn(`✼ ${iconInfo.message}, but you give ${customIconExt}`);
|
|
tauriConf.bundle.icon = [iconInfo.defaultIcon];
|
|
} else {
|
|
// Save icon to .pake directory instead of src-tauri
|
|
const iconPath = path.join(tauriConfigDirectory, iconInfo.path);
|
|
await fsExtra.ensureDir(path.dirname(iconPath));
|
|
tauriConf.bundle.resources = [`.pake/${iconInfo.path}`];
|
|
await fsExtra.copy(options.icon, iconPath);
|
|
}
|
|
|
|
if (updateIconPath) {
|
|
tauriConf.bundle.icon = [options.icon];
|
|
} else {
|
|
logger.warn(`✼ Icon will remain as default.`);
|
|
}
|
|
} else {
|
|
logger.warn(
|
|
'✼ Custom icon path may be invalid, default icon will be used instead.',
|
|
);
|
|
tauriConf.bundle.icon = [iconInfo.defaultIcon];
|
|
}
|
|
|
|
// Set system tray icon path
|
|
let trayIconPath = 'icons/icon.png'; // default fallback
|
|
|
|
if (showSystemTray) {
|
|
if (systemTrayIcon.length > 0) {
|
|
// User provided custom system tray icon
|
|
trayIconPath = await handleCustomTrayIcon(
|
|
systemTrayIcon,
|
|
name,
|
|
tauriConfigDirectory,
|
|
);
|
|
} else {
|
|
// Use original downloaded PNG icon for system tray
|
|
trayIconPath = await handleDownloadedTrayIcon(name, tauriConfigDirectory);
|
|
}
|
|
}
|
|
|
|
tauriConf.app.trayIcon.iconPath = trayIconPath;
|
|
tauriConf.pake.system_tray_path = trayIconPath;
|
|
|
|
delete tauriConf.app.trayIcon;
|
|
|
|
const injectFilePath = path.join(
|
|
npmDirectory,
|
|
`src-tauri/src/inject/custom.js`,
|
|
);
|
|
|
|
// inject js or css files
|
|
if (inject?.length > 0) {
|
|
if (
|
|
!inject.every((item) => item.endsWith('.css') || item.endsWith('.js'))
|
|
) {
|
|
logger.error('The injected file must be in either CSS or JS format.');
|
|
return;
|
|
}
|
|
const files = inject.map((filepath) =>
|
|
path.isAbsolute(filepath) ? filepath : path.join(process.cwd(), filepath),
|
|
);
|
|
tauriConf.pake.inject = files;
|
|
await combineFiles(files, injectFilePath);
|
|
} else {
|
|
tauriConf.pake.inject = [];
|
|
await fsExtra.writeFile(injectFilePath, '');
|
|
}
|
|
tauriConf.pake.proxy_url = proxyUrl || '';
|
|
|
|
// Save config file.
|
|
const platformConfigPaths: PlatformMap = {
|
|
win32: 'tauri.windows.conf.json',
|
|
darwin: 'tauri.macos.conf.json',
|
|
linux: 'tauri.linux.conf.json',
|
|
};
|
|
|
|
const configPath = path.join(
|
|
tauriConfigDirectory,
|
|
platformConfigPaths[platform],
|
|
);
|
|
|
|
const bundleConf = { bundle: tauriConf.bundle };
|
|
await fsExtra.outputJSON(configPath, bundleConf, { spaces: 4 });
|
|
const pakeConfigPath = path.join(tauriConfigDirectory, 'pake.json');
|
|
await fsExtra.outputJSON(pakeConfigPath, tauriConf.pake, { spaces: 4 });
|
|
|
|
let tauriConf2 = JSON.parse(JSON.stringify(tauriConf));
|
|
delete tauriConf2.pake;
|
|
|
|
// delete tauriConf2.bundle;
|
|
if (process.env.NODE_ENV === 'development') {
|
|
tauriConf2.bundle = bundleConf.bundle;
|
|
}
|
|
const configJsonPath = path.join(tauriConfigDirectory, 'tauri.conf.json');
|
|
await fsExtra.outputJSON(configJsonPath, tauriConf2, { spaces: 4 });
|
|
}
|
|
|
|
/**
|
|
* Handle custom system tray icon provided by user
|
|
*/
|
|
async function handleCustomTrayIcon(
|
|
systemTrayIcon: string,
|
|
appName: string,
|
|
configDir: string,
|
|
): Promise<string> {
|
|
const defaultPath = 'icons/icon.png';
|
|
|
|
if (!(await fsExtra.pathExists(systemTrayIcon))) {
|
|
logger.warn(`✼ Custom tray icon ${systemTrayIcon} not found!`);
|
|
logger.warn(`✼ Using default icon for system tray.`);
|
|
return defaultPath;
|
|
}
|
|
|
|
const iconExt = path.extname(systemTrayIcon).toLowerCase();
|
|
if (iconExt !== '.png' && iconExt !== '.ico') {
|
|
logger.warn(
|
|
`✼ System tray icon must be .png or .ico, but you provided ${iconExt}.`,
|
|
);
|
|
logger.warn(`✼ Using default icon for system tray.`);
|
|
return defaultPath;
|
|
}
|
|
|
|
try {
|
|
const trayIconPath = path.join(
|
|
configDir,
|
|
`png/${appName.toLowerCase()}${iconExt}`,
|
|
);
|
|
await fsExtra.ensureDir(path.dirname(trayIconPath));
|
|
await fsExtra.copy(systemTrayIcon, trayIconPath);
|
|
|
|
const relativePath = `.pake/png/${appName.toLowerCase()}${iconExt}`;
|
|
logger.info(`✓ Using custom system tray icon: ${systemTrayIcon}`);
|
|
return relativePath;
|
|
} catch (error) {
|
|
logger.warn(`✼ Failed to copy custom tray icon: ${error}`);
|
|
logger.warn(`✼ Using default icon for system tray.`);
|
|
return defaultPath;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle system tray icon from downloaded app icon
|
|
*/
|
|
async function handleDownloadedTrayIcon(
|
|
appName: string,
|
|
configDir: string,
|
|
): Promise<string> {
|
|
const defaultPath = 'icons/icon.png';
|
|
const homeDir = getUserHomeDir();
|
|
const downloadedIconPath = path.join(
|
|
homeDir,
|
|
'.pake',
|
|
'icons',
|
|
'downloaded-icon.png',
|
|
);
|
|
|
|
if (!(await fsExtra.pathExists(downloadedIconPath))) {
|
|
logger.warn(
|
|
`✼ No downloaded icon found, using default icon for system tray.`,
|
|
);
|
|
return defaultPath;
|
|
}
|
|
|
|
try {
|
|
const trayPngPath = path.join(
|
|
configDir,
|
|
`png/${appName.toLowerCase()}_tray.png`,
|
|
);
|
|
await fsExtra.ensureDir(path.dirname(trayPngPath));
|
|
|
|
// Resize the original PNG to appropriate tray size (32x32 for optimal display)
|
|
const sharp = await import('sharp');
|
|
await sharp
|
|
.default(downloadedIconPath)
|
|
.resize(32, 32)
|
|
.png()
|
|
.toFile(trayPngPath);
|
|
|
|
const relativePath = `.pake/png/${appName.toLowerCase()}_tray.png`;
|
|
logger.info(`✓ Using downloaded app icon for system tray: ${relativePath}`);
|
|
return relativePath;
|
|
} catch (error) {
|
|
logger.warn(`✼ Failed to process downloaded icon for tray: ${error}`);
|
|
logger.warn(`✼ Using default icon for system tray.`);
|
|
return defaultPath;
|
|
}
|
|
}
|