feat: 重构背景图处理体系,优化用户UX体验

- 重构背景图全链路处理:改用文件路径存储而非base64,新增`koring-res://`自定义协议流式加载本地壁纸,大幅降低内存占用
- 调整配置文件存储路径至userData目录,解决NSIS重装/升级时配置被清空的问题
- 新增路由调试页面,支持快速跳转所有已注册的应用页面
- 优化OOBE引导和更新完成页的「前往首页」按钮,添加4秒延迟渐入动画
- 自动迁移旧版配置:将安装目录的配置文件迁移至userData,同时将base64格式的背景配置自动转换为文件路径存储
- 重构预加载脚本、主进程IPC处理器与前端页面的背景图相关逻辑
- 更新AGENTS.md开发文档,修正路径与逻辑说明
- 更新应用背景图资源文件
This commit is contained in:
2026-09-04 19:34:58 +08:00
parent cd4578de5a
commit fad8f612bf
22 changed files with 757 additions and 196 deletions
+26 -51
View File
@@ -1,30 +1,8 @@
import electron from 'electron';
import path from 'path';
import fs from 'fs';
const { contextBridge, ipcRenderer } = electron;
const MIME_MAP: Record<string, string> = {
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.webp': 'image/webp',
'.gif': 'image/gif',
'.bmp': 'image/bmp',
};
function getFileAsDataUrl(filePath: string): string | null {
try {
const buffer = fs.readFileSync(filePath);
const ext = path.extname(filePath).toLowerCase();
const mime = MIME_MAP[ext] || 'image/png';
return `data:${mime};base64,${buffer.toString('base64')}`;
} catch {
return null;
}
}
// 按当前窗口实际像素需求计算背景图长边上限(含高分屏余量),
// 避免把数 MB~数十 MB 原图原样塞进渲染进程。
// 按当前窗口实际像素需求计算壁纸长边上限(含高分屏余量),
// 主进程据此把大图压到「屏幕可见」尺寸后落盘(配置只存文件路径,不存 BASE64)。
function computeMaxEdge(): number {
const dpr = window.devicePixelRatio || 1;
const css = Math.max(window.innerWidth || 1280, window.innerHeight || 800);
@@ -32,22 +10,6 @@ function computeMaxEdge(): number {
return Math.min(4096, Math.max(1920, target));
}
// 经主进程降采样/重编码后返回 data URL;主进程无法处理时回退到原始文件(行为不变)。
async function prepareBackgroundDataUrl(filePath: string): Promise<string | null> {
try {
const result = (await ipcRenderer.invoke('background:prepare', {
srcPath: filePath,
maxEdge: computeMaxEdge(),
})) as { success?: boolean; data?: { dataUrl?: string | null } | null };
if (result?.success && typeof result.data?.dataUrl === 'string' && result.data.dataUrl.length > 0) {
return result.data.dataUrl;
}
} catch {
// fallthrough to raw
}
return getFileAsDataUrl(filePath);
}
contextBridge.exposeInMainWorld('electronAPI', {
// Generic IPC
invoke: (channel: string, ...args: unknown[]) =>
@@ -91,24 +53,37 @@ contextBridge.exposeInMainWorld('electronAPI', {
return () => ipcRenderer.removeListener('config:changed', handler);
},
// Background image — pick file, copy to userData, return base64 data URL
// 背景图 — 选择本地图片:主进程复制到 userData、按窗口尺寸优化并落盘,
// 返回【文件路径】(配置/Store 以路径保存,不使用 BASE64)。
pickBackgroundImage: async (): Promise<string | null> => {
const result = await ipcRenderer.invoke('dialog:openFile', {
filters: [{ name: '图片', extensions: ['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp'] }],
});
if (!result) return null;
const { srcPath, ext } = result as { srcPath: string; ext: string };
// Copy to userData via main process
const destPath = await ipcRenderer.invoke('background:copyToUserData', srcPath, ext);
if (!destPath) return null;
return prepareBackgroundDataUrl(destPath);
const { srcPath } = result as { srcPath: string };
const imported = await ipcRenderer.invoke('background:import', {
srcPath,
maxEdge: computeMaxEdge(),
});
if (imported && imported.success && typeof imported.data?.filePath === 'string' && imported.data.filePath) {
return imported.data.filePath;
}
return null;
},
// Get cached background as base64 data URL(自动降采样到屏幕所需尺寸)
getBackgroundDataUrl: async (): Promise<string | null> => {
const filePath = await ipcRenderer.invoke('background:getCachedPath');
if (!filePath) return null;
return prepareBackgroundDataUrl(filePath);
// 把配置文件/Store 中的壁纸值解析为可显示的资源引用:
// data:/http(s):/file: 及 /、./ 相对 URL → 原样返回;
// userData 内受管壁纸文件路径 → koring-res:// 协议 URL(流式读取,无 base64 副本)。
resolveBackgroundResource: async (value: string): Promise<{ url: string | null; bytes: number }> => {
try {
const result = await ipcRenderer.invoke('background:resolve', { value });
if (result && result.success && typeof result.data?.url === 'string' && result.data.url) {
return { url: result.data.url, bytes: typeof result.data.bytes === 'number' ? result.data.bytes : 0 };
}
} catch {
// fallthrough
}
return { url: null, bytes: 0 };
},
// Open external URL in system browser