🐛 Support Chinese naming and fix the use of local icons

This commit is contained in:
Tw93
2025-09-16 10:20:50 +08:00
parent dd11c6dd7a
commit 6aaa9a4517
4 changed files with 59 additions and 23 deletions

31
bin/helpers/merge.ts vendored
View File

@@ -3,6 +3,7 @@ import fsExtra from 'fs-extra';
import combineFiles from '@/utils/combine'; import combineFiles from '@/utils/combine';
import logger from '@/options/logger'; import logger from '@/options/logger';
import { generateSafeFilename, generateIdentifierSafeName } from '@/utils/name';
import { PakeAppOptions, PlatformMap } from '@/types'; import { PakeAppOptions, PlatformMap } from '@/types';
import { tauriConfigDirectory, npmDirectory } from '@/utils/dir'; import { tauriConfigDirectory, npmDirectory } from '@/utils/dir';
@@ -91,7 +92,7 @@ export async function mergeConfig(
tauriConf.version = appVersion; tauriConf.version = appVersion;
if (platform === 'linux') { if (platform === 'linux') {
tauriConf.mainBinaryName = `pake-${name.toLowerCase()}`; tauriConf.mainBinaryName = `pake-${generateIdentifierSafeName(name)}`;
} }
if (platform == 'win32') { if (platform == 'win32') {
@@ -151,8 +152,8 @@ export async function mergeConfig(
delete tauriConf.bundle.linux.deb.files; delete tauriConf.bundle.linux.deb.files;
// Generate correct desktop file configuration // Generate correct desktop file configuration
const appNameLower = name.toLowerCase(); const appNameSafe = generateSafeFilename(name).toLowerCase();
const identifier = `com.pake.${appNameLower}`; const identifier = `com.pake.${appNameSafe}`;
const desktopFileName = `${identifier}.desktop`; const desktopFileName = `${identifier}.desktop`;
// Create desktop file content // Create desktop file content
@@ -161,8 +162,8 @@ Version=1.0
Type=Application Type=Application
Name=${name} Name=${name}
Comment=${name} Comment=${name}
Exec=pake-${appNameLower} Exec=pake-${appNameSafe}
Icon=${appNameLower}_512 Icon=${appNameSafe}_512
Categories=Network;WebBrowser; Categories=Network;WebBrowser;
MimeType=text/html;text/xml;application/xhtml_xml; MimeType=text/html;text/xml;application/xhtml_xml;
StartupNotify=true StartupNotify=true
@@ -213,28 +214,29 @@ StartupNotify=true
const platformIconMap: PlatformMap = { const platformIconMap: PlatformMap = {
win32: { win32: {
fileExt: '.ico', fileExt: '.ico',
path: `png/${name.toLowerCase()}_256.ico`, path: `png/${generateSafeFilename(name).toLowerCase()}_256.ico`,
defaultIcon: 'png/icon_256.ico', defaultIcon: 'png/icon_256.ico',
message: 'Windows icon must be .ico and 256x256px.', message: 'Windows icon must be .ico and 256x256px.',
}, },
linux: { linux: {
fileExt: '.png', fileExt: '.png',
path: `png/${name.toLowerCase()}_512.png`, path: `png/${generateSafeFilename(name).toLowerCase()}_512.png`,
defaultIcon: 'png/icon_512.png', defaultIcon: 'png/icon_512.png',
message: 'Linux icon must be .png and 512x512px.', message: 'Linux icon must be .png and 512x512px.',
}, },
darwin: { darwin: {
fileExt: '.icns', fileExt: '.icns',
path: `icons/${name.toLowerCase()}.icns`, path: `icons/${generateSafeFilename(name).toLowerCase()}.icns`,
defaultIcon: 'icons/icon.icns', defaultIcon: 'icons/icon.icns',
message: 'macOS icon must be .icns type.', message: 'macOS icon must be .icns type.',
}, },
}; };
const iconInfo = platformIconMap[platform]; const iconInfo = platformIconMap[platform];
const exists = options.icon && (await fsExtra.pathExists(options.icon)); const resolvedIconPath = options.icon ? path.resolve(options.icon) : null;
const exists = resolvedIconPath && (await fsExtra.pathExists(resolvedIconPath));
if (exists) { if (exists) {
let updateIconPath = true; let updateIconPath = true;
let customIconExt = path.extname(options.icon).toLowerCase(); let customIconExt = path.extname(resolvedIconPath).toLowerCase();
if (customIconExt !== iconInfo.fileExt) { if (customIconExt !== iconInfo.fileExt) {
updateIconPath = false; updateIconPath = false;
@@ -245,10 +247,9 @@ StartupNotify=true
tauriConf.bundle.resources = [iconInfo.path]; tauriConf.bundle.resources = [iconInfo.path];
// Avoid copying if source and destination are the same // Avoid copying if source and destination are the same
const absoluteIconPath = path.resolve(options.icon);
const absoluteDestPath = path.resolve(iconPath); const absoluteDestPath = path.resolve(iconPath);
if (absoluteIconPath !== absoluteDestPath) { if (resolvedIconPath !== absoluteDestPath) {
await fsExtra.copy(options.icon, iconPath); await fsExtra.copy(resolvedIconPath, iconPath);
} }
} }
@@ -275,9 +276,9 @@ StartupNotify=true
if (iconExt == '.png' || iconExt == '.ico') { if (iconExt == '.png' || iconExt == '.ico') {
const trayIcoPath = path.join( const trayIcoPath = path.join(
npmDirectory, npmDirectory,
`src-tauri/png/${name.toLowerCase()}${iconExt}`, `src-tauri/png/${generateSafeFilename(name).toLowerCase()}${iconExt}`,
); );
trayIconPath = `png/${name.toLowerCase()}${iconExt}`; trayIconPath = `png/${generateSafeFilename(name).toLowerCase()}${iconExt}`;
await fsExtra.copy(systemTrayIcon, trayIcoPath); await fsExtra.copy(systemTrayIcon, trayIcoPath);
} else { } else {
logger.warn( logger.warn(

8
bin/options/icon.ts vendored
View File

@@ -43,9 +43,11 @@ const API_KEYS = {
/** /**
* Generates platform-specific icon paths and handles copying for Windows * Generates platform-specific icon paths and handles copying for Windows
*/ */
import { generateSafeFilename } from '@/utils/name';
function generateIconPath(appName: string, isDefault = false): string { function generateIconPath(appName: string, isDefault = false): string {
const safeName = appName.toLowerCase().replace(/[^a-z0-9-_]/g, '_'); const safeName = isDefault ? 'icon' : generateSafeFilename(appName).toLowerCase();
const baseName = isDefault ? 'icon' : safeName; const baseName = safeName;
if (IS_WIN) { if (IS_WIN) {
return path.join(npmDirectory, 'src-tauri', 'png', `${baseName}_256.ico`); return path.join(npmDirectory, 'src-tauri', 'png', `${baseName}_256.ico`);
@@ -122,7 +124,7 @@ async function convertIconFormat(
await fsExtra.ensureDir(platformOutputDir); await fsExtra.ensureDir(platformOutputDir);
const processedInputPath = await preprocessIcon(inputPath); const processedInputPath = await preprocessIcon(inputPath);
const iconName = appName.toLowerCase(); const iconName = generateSafeFilename(appName).toLowerCase();
// Generate platform-specific format // Generate platform-specific format
if (IS_WIN) { if (IS_WIN) {

View File

@@ -4,6 +4,7 @@ import logger from '@/options/logger';
import { handleIcon } from './icon'; import { handleIcon } from './icon';
import { getDomain } from '@/utils/url'; import { getDomain } from '@/utils/url';
import { getIdentifier, promptText, capitalizeFirstLetter } from '@/utils/info'; import { getIdentifier, promptText, capitalizeFirstLetter } from '@/utils/info';
import { generateLinuxPackageName } from '@/utils/name';
import { PakeAppOptions, PakeCliOptions, PlatformMap } from '@/types'; import { PakeAppOptions, PakeCliOptions, PlatformMap } from '@/types';
function resolveAppName(name: string, platform: NodeJS.Platform): string { function resolveAppName(name: string, platform: NodeJS.Platform): string {
@@ -13,8 +14,8 @@ function resolveAppName(name: string, platform: NodeJS.Platform): string {
function isValidName(name: string, platform: NodeJS.Platform): boolean { function isValidName(name: string, platform: NodeJS.Platform): boolean {
const platformRegexMapping: PlatformMap = { const platformRegexMapping: PlatformMap = {
linux: /^[a-z0-9][a-z0-9-]*$/, linux: /^[a-z0-9\u4e00-\u9fff][a-z0-9\u4e00-\u9fff-]*$/,
default: /^[a-zA-Z0-9][a-zA-Z0-9- ]*$/, default: /^[a-zA-Z0-9\u4e00-\u9fff][a-zA-Z0-9\u4e00-\u9fff- ]*$/,
}; };
const reg = platformRegexMapping[platform] || platformRegexMapping.default; const reg = platformRegexMapping[platform] || platformRegexMapping.default;
return !!name && reg.test(name); return !!name && reg.test(name);
@@ -36,10 +37,8 @@ export default async function handleOptions(
name = namePrompt || defaultName; name = namePrompt || defaultName;
} }
// Handle platform-specific name formatting
if (name && platform === 'linux') { if (name && platform === 'linux') {
// Convert to lowercase and replace spaces with dashes for Linux name = generateLinuxPackageName(name);
name = name.toLowerCase().replace(/\s+/g, '-');
} }
if (!isValidName(name, platform)) { if (!isValidName(name, platform)) {

34
bin/utils/name.ts vendored Normal file
View File

@@ -0,0 +1,34 @@
export function generateSafeFilename(name: string): string {
return name
.replace(/[<>:"/\\|?*]/g, '_')
.replace(/\s+/g, '_')
.replace(/\.+$/g, '')
.slice(0, 255);
}
export function generateLinuxPackageName(name: string): string {
return name
.toLowerCase()
.replace(/[^a-z0-9\u4e00-\u9fff]+/g, '-')
.replace(/^-+|-+$/g, '')
.replace(/-+/g, '-');
}
export function generateIdentifierSafeName(name: string): string {
return name
.replace(/[^a-zA-Z0-9]/g, '')
.toLowerCase();
}
export function generateWindowsFilename(name: string): string {
return name
.replace(/[<>:"/\\|?*]/g, '_')
.replace(/^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/i, '${name}_')
.slice(0, 255);
}
export function generateMacOSFilename(name: string): string {
return name
.replace(/[:]/g, '_')
.slice(0, 255);
}