AI代码生成工具、个人中心
This commit is contained in:
121
apps/desktop/src/preload/index.ts
Normal file
121
apps/desktop/src/preload/index.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* preload:通过 contextBridge 向渲染层暴露 window.desktopAPI
|
||||
*
|
||||
* 运行在 sandbox 沙箱中(只能使用 electron 受限模块 + DOM),
|
||||
* 所有能力都走 ipcRenderer.invoke 白名单通道,渲染层拿不到任何 Node 能力。
|
||||
* web 侧的类型声明见 apps/web-antd/src/types/desktop.d.ts(两边字段需保持同步)。
|
||||
*/
|
||||
import type { IpcRendererEvent } from 'electron';
|
||||
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
|
||||
/**
|
||||
* 用 canvas 绘制 Windows 任务栏角标覆盖图(红底白字数字),返回 dataURL
|
||||
* 为什么在 preload 画:主进程没有 DOM canvas,nativeImage 也不能绘制文字;
|
||||
* preload 跑在渲染进程里有完整 DOM 能力,画完把 dataURL 交给主进程 setOverlayIcon
|
||||
*/
|
||||
function drawBadgeDataURL(count: number): string {
|
||||
const size = 32;
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return '';
|
||||
const text = count > 99 ? '99+' : String(count);
|
||||
ctx.beginPath();
|
||||
ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2);
|
||||
ctx.fillStyle = '#f5222d';
|
||||
ctx.fill();
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.font = `bold ${text.length > 2 ? 13 : 18}px sans-serif`;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(text, size / 2, size / 2 + 1);
|
||||
return canvas.toDataURL('image/png');
|
||||
}
|
||||
|
||||
/** 订阅主进程推送事件的通用封装,返回取消订阅函数(组件卸载时记得调用防止泄漏) */
|
||||
function subscribe<T>(
|
||||
channel: string,
|
||||
callback: (payload: T) => void,
|
||||
): () => void {
|
||||
const listener = (_event: IpcRendererEvent, payload: T) => callback(payload);
|
||||
ipcRenderer.on(channel, listener);
|
||||
return () => ipcRenderer.removeListener(channel, listener);
|
||||
}
|
||||
|
||||
const desktopAPI = {
|
||||
/** 恒为 true,web 侧用它判断是否运行在 Electron 壳内 */
|
||||
isElectron: true as const,
|
||||
/** 当前平台:win32 / darwin / linux(渲染层做平台差异 UI 时用) */
|
||||
platform: process.platform,
|
||||
/** 应用版本号(package.json version,用于「关于」展示) */
|
||||
getVersion: (): Promise<string> => ipcRenderer.invoke('desktop:get-version'),
|
||||
/**
|
||||
* 发系统通知;route 传入站内路由(如 /notice/list)时点击通知会唤起窗口并跳转
|
||||
* flash 默认 true:窗口未聚焦时同时闪烁任务栏(mac 表现为 Dock 弹跳)
|
||||
*/
|
||||
notify: (options: {
|
||||
title: string;
|
||||
body?: string;
|
||||
route?: string;
|
||||
silent?: boolean;
|
||||
flash?: boolean;
|
||||
}): Promise<void> => ipcRenderer.invoke('desktop:notify', options),
|
||||
/** 设置未读角标数,0 清除;mac 用 Dock 原生数字,Windows 用 canvas 画的覆盖图 */
|
||||
setBadge: (count: number): Promise<void> => {
|
||||
const dataUrl =
|
||||
count > 0 && process.platform === 'win32' ? drawBadgeDataURL(count) : '';
|
||||
return ipcRenderer.invoke('desktop:set-badge', count, dataUrl);
|
||||
},
|
||||
/** 手动控制任务栏闪烁(一般用不到,notify 已内置) */
|
||||
flashFrame: (flag = true): Promise<void> =>
|
||||
ipcRenderer.invoke('desktop:flash-frame', flag),
|
||||
/** 读取开机自启状态 */
|
||||
getAutoLaunch: (): Promise<boolean> =>
|
||||
ipcRenderer.invoke('desktop:get-auto-launch'),
|
||||
/** 设置开机自启(--hidden 静默启动进托盘) */
|
||||
setAutoLaunch: (enabled: boolean): Promise<boolean> =>
|
||||
ipcRenderer.invoke('desktop:set-auto-launch', enabled),
|
||||
/** 枚举系统打印机列表 */
|
||||
getPrinters: (): Promise<unknown[]> =>
|
||||
ipcRenderer.invoke('desktop:get-printers'),
|
||||
/**
|
||||
* 打印一段完整 HTML:不传 deviceName 时读持久化的默认打印机;
|
||||
* silent=true 静默打印,false 弹系统打印对话框
|
||||
*/
|
||||
printHTML: (options: {
|
||||
html: string;
|
||||
deviceName?: string;
|
||||
silent?: boolean;
|
||||
landscape?: boolean;
|
||||
}): Promise<{ message?: string; success: boolean }> =>
|
||||
ipcRenderer.invoke('desktop:print-html', options),
|
||||
/** 读桌面端本地设置(如默认打印机) */
|
||||
getSetting: (key: string): Promise<unknown> =>
|
||||
ipcRenderer.invoke('desktop:get-setting', key),
|
||||
/** 写桌面端本地设置 */
|
||||
setSetting: (key: string, value: unknown): Promise<void> =>
|
||||
ipcRenderer.invoke('desktop:set-setting', key, value),
|
||||
/** 手动检查更新(托盘菜单也有同款入口) */
|
||||
checkUpdate: (): Promise<void> => ipcRenderer.invoke('desktop:check-update'),
|
||||
/** 更新下载完成后立即重启安装 */
|
||||
quitAndInstall: (): Promise<void> =>
|
||||
ipcRenderer.invoke('desktop:quit-and-install'),
|
||||
/** 订阅「点击通知要求跳转路由」事件,web 侧收到后自行 router.push */
|
||||
onNavigate: (callback: (route: string) => void): (() => void) =>
|
||||
subscribe<string>('desktop:navigate', callback),
|
||||
/** 订阅更新流程事件(checking / available / progress / downloaded 等),用于展示进度 */
|
||||
onUpdateEvent: (
|
||||
callback: (event: { payload?: unknown; type: string }) => void,
|
||||
): (() => void) =>
|
||||
subscribe<{ payload?: unknown; type: string }>(
|
||||
'desktop:update-event',
|
||||
callback,
|
||||
),
|
||||
};
|
||||
|
||||
contextBridge.exposeInMainWorld('desktopAPI', desktopAPI);
|
||||
|
||||
/** 导出类型供主进程侧引用(仅类型层面,运行时无依赖) */
|
||||
export type DesktopAPI = typeof desktopAPI;
|
||||
Reference in New Issue
Block a user