feat(runtime-notice): 添加 Linux 非 AppImage 运行时警告提示

- 新增 onRuntimeNotice IPC 接口与预加载桥接代码
- 在主进程添加 Linux 环境检查,未使用 AppImage 运行的打包版会提示更新组件可能受影响
- 新建 RuntimeNotices 组件处理并展示去重的持久化警告弹窗
- 将该组件注册到根布局中自动挂载
- 添加项目的 Git 提交信息规范文件
This commit is contained in:
2026-09-05 21:14:43 +08:00
parent aef49b173f
commit 74512bf935
7 changed files with 52 additions and 1 deletions
+6
View File
@@ -0,0 +1,6 @@
---
alwaysApply: true
scene: git_message
---
在此处编写规则,自定义 AI 生成提交信息的风格。
File diff suppressed because one or more lines are too long
+7
View File
@@ -190,6 +190,13 @@ function createMainWindow(): electron.BrowserWindow {
main.webContents.on('did-finish-load', () => { main.webContents.on('did-finish-load', () => {
if (main.isDestroyed()) return; if (main.isDestroyed()) return;
main.webContents.send('config:preload', { config: getConfig(), isFirstLaunch: isFirstLaunchFlag }); main.webContents.send('config:preload', { config: getConfig(), isFirstLaunch: isFirstLaunchFlag });
// Linux 打包但并非以 AppImage 方式运行(无 APPIMAGE 环境变量)→ 提示影响更新组件
if (app.isPackaged && process.platform === 'linux' && !process.env.APPIMAGE) {
main.webContents.send('runtime:notice', {
kind: 'linux-appimage-unpacked',
message: '您并未解包安装,这可能会影响更新组件的运行',
});
}
}); });
main.on('maximize', () => { main.on('maximize', () => {
+7
View File
@@ -53,6 +53,13 @@ contextBridge.exposeInMainWorld('electronAPI', {
return () => ipcRenderer.removeListener('config:changed', handler); return () => ipcRenderer.removeListener('config:changed', handler);
}, },
// 主进程运行时提示(如 Linux 未以 AppImage 方式运行,影响更新组件)
onRuntimeNotice: (callback: (notice: { kind: string; message: string }) => void) => {
const handler = (_event: Electron.IpcRendererEvent, notice: { kind: string; message: string }) => callback(notice);
ipcRenderer.on('runtime:notice', handler);
return () => ipcRenderer.removeListener('runtime:notice', handler);
},
// 背景图 — 选择本地图片:主进程复制到 userData、按窗口尺寸优化并落盘, // 背景图 — 选择本地图片:主进程复制到 userData、按窗口尺寸优化并落盘,
// 返回【文件路径】(配置/Store 以路径保存,不使用 BASE64)。 // 返回【文件路径】(配置/Store 以路径保存,不使用 BASE64)。
pickBackgroundImage: async (): Promise<string | null> => { pickBackgroundImage: async (): Promise<string | null> => {
+25
View File
@@ -0,0 +1,25 @@
import { useEffect } from "react";
import { toast } from "sonner";
/**
* 主进程运行时提示(一次性,kind 去重):
* 目前用于 Linux 未以 AppImage 方式运行时提示更新组件受影响。
*/
export function RuntimeNotices() {
useEffect(() => {
const unsub = window.electronAPI?.onRuntimeNotice?.((notice) => {
if (!notice?.message) return;
// id 固定 → 同一提示只出现一次(页面刷新也不会重复弹)
toast.warning(notice.message, {
id: `runtime-notice:${notice.kind ?? "generic"}`,
duration: Infinity,
closeButton: true,
});
});
return () => {
unsub?.();
};
}, []);
return null;
}
+4
View File
@@ -3,6 +3,7 @@ import { BackgroundLayer } from "@/components/background/BackgroundLayer";
import { SystemLayer } from "@/components/system/SystemLayer"; import { SystemLayer } from "@/components/system/SystemLayer";
import { ConfirmDialog } from "@/components/ui/confirm-dialog"; import { ConfirmDialog } from "@/components/ui/confirm-dialog";
import { UpdateAvailableDialog } from "@/components/UpdateAvailableDialog"; import { UpdateAvailableDialog } from "@/components/UpdateAvailableDialog";
import { RuntimeNotices } from "@/components/RuntimeNotices";
import { Toaster } from "sonner"; import { Toaster } from "sonner";
import { useA11yStore } from "@/stores/a11yStore"; import { useA11yStore } from "@/stores/a11yStore";
import clsx from "clsx"; import clsx from "clsx";
@@ -59,6 +60,9 @@ export function RootLayout({
{/* 全局:发现新版本弹窗(检查到新版本时自动弹出) */} {/* 全局:发现新版本弹窗(检查到新版本时自动弹出) */}
<UpdateAvailableDialog /> <UpdateAvailableDialog />
{/* 运行时提示(Linux AppImage 未解包安装等) */}
<RuntimeNotices />
{/* Sonner toaster */} {/* Sonner toaster */}
<Toaster <Toaster
position="bottom-right" position="bottom-right"
+2
View File
@@ -16,6 +16,8 @@ interface ElectronAPI {
onConfigChanged: (callback: (config: unknown) => void) => () => void; onConfigChanged: (callback: (config: unknown) => void) => () => void;
onRuntimeNotice: (callback: (notice: { kind: string; message: string }) => void) => () => void;
openExternal: (url: string) => Promise<void>; openExternal: (url: string) => Promise<void>;
// Crash monitoring // Crash monitoring