import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron' import { electronAPI } from '@electron-toolkit/preload' import { IPC } from '@shared/ipc/channels' /** 暴露给渲染进程的类型安全 API */ const api = { store: { get: (key: string): Promise => ipcRenderer.invoke(IPC.STORE_GET, key), set: (key: string, value: unknown): Promise => ipcRenderer.invoke(IPC.STORE_SET, key, value), delete: (key: string): Promise => ipcRenderer.invoke(IPC.STORE_DELETE, key) }, db: { query: (sql: string, params: unknown[] = []): Promise => ipcRenderer.invoke(IPC.DB_QUERY, sql, params), exec: ( sql: string, params: unknown[] = [] ): Promise<{ changes: number; lastInsertRowid: number }> => ipcRenderer.invoke(IPC.DB_EXEC, sql, params) }, http: { start: (root: string, port: number) => ipcRenderer.invoke(IPC.HTTP_START, root, port), stop: () => ipcRenderer.invoke(IPC.HTTP_STOP), status: () => ipcRenderer.invoke(IPC.HTTP_STATUS) }, net: { lanIps: () => ipcRenderer.invoke(IPC.NET_LAN_IPS), fetchJson: (url: string): Promise => ipcRenderer.invoke(IPC.NET_FETCH_JSON, url), fetchText: (url: string): Promise => ipcRenderer.invoke(IPC.NET_FETCH_TEXT, url) }, library: { sync: (): Promise<{ version: number; albums: number; musics: number }> => ipcRenderer.invoke(IPC.LIBRARY_SYNC), dataVersion: (): Promise => ipcRenderer.invoke(IPC.LIBRARY_DATA_VERSION) }, convert: { start: (input: string, output: string) => ipcRenderer.invoke(IPC.CONVERT_START, input, output), stop: () => ipcRenderer.invoke(IPC.CONVERT_STOP), onProgress: (cb: (data: { input: string; progress: number }) => void) => { const listener = (_: IpcRendererEvent, data: { input: string; progress: number }): void => cb(data) ipcRenderer.on(IPC.CONVERT_PROGRESS, listener) return () => ipcRenderer.removeListener(IPC.CONVERT_PROGRESS, listener) }, onDone: (cb: (data: { input: string; output: string }) => void) => { const listener = (_: IpcRendererEvent, data: { input: string; output: string }): void => cb(data) ipcRenderer.on(IPC.CONVERT_DONE, listener) return () => ipcRenderer.removeListener(IPC.CONVERT_DONE, listener) } }, wifi: { ensureIosWav: (items: { baseUrl: string; musicPath: string }[]) => ipcRenderer.invoke(IPC.WIFI_ENSURE_IOS_WAV, items), cleanIosWav: (item: { baseUrl: string; musicPath: string }) => ipcRenderer.invoke(IPC.WIFI_CLEAN_IOS_WAV, item) }, musicServer: { start: () => ipcRenderer.invoke(IPC.MUSIC_SERVER_START), stop: () => ipcRenderer.invoke(IPC.MUSIC_SERVER_STOP), send: (cmd: string, body: string) => ipcRenderer.invoke(IPC.MUSIC_SERVER_SEND, cmd, body), onEvent: (cb: (e: unknown) => void) => { const listener = (_: IpcRendererEvent, e: unknown): void => cb(e) ipcRenderer.on(IPC.MUSIC_SERVER_EVENT, listener) return () => ipcRenderer.removeListener(IPC.MUSIC_SERVER_EVENT, listener) } }, dataServer: { start: () => ipcRenderer.invoke(IPC.DATA_SERVER_START), stop: () => ipcRenderer.invoke(IPC.DATA_SERVER_STOP), send: (cmd: string, body: string) => ipcRenderer.invoke(IPC.DATA_SERVER_SEND, cmd, body), onEvent: (cb: (e: unknown) => void) => { const listener = (_: IpcRendererEvent, e: unknown): void => cb(e) ipcRenderer.on(IPC.DATA_SERVER_EVENT, listener) return () => ipcRenderer.removeListener(IPC.DATA_SERVER_EVENT, listener) } }, dialog: { openDir: (): Promise => ipcRenderer.invoke(IPC.DIALOG_OPEN_DIR), openFile: (): Promise => ipcRenderer.invoke(IPC.DIALOG_OPEN_FILE) }, lyric: { toggle: (show: boolean) => ipcRenderer.invoke(IPC.LYRIC_TOGGLE, show), update: (payload: unknown) => ipcRenderer.send(IPC.LYRIC_UPDATE, payload), setIgnoreMouse: (ignore: boolean) => ipcRenderer.send(IPC.LYRIC_SET_IGNORE_MOUSE, ignore), onUpdate: (cb: (payload: unknown) => void) => { const listener = (_: IpcRendererEvent, p: unknown): void => cb(p) ipcRenderer.on(IPC.LYRIC_UPDATE, listener) return () => ipcRenderer.removeListener(IPC.LYRIC_UPDATE, listener) }, onState: (cb: (show: boolean) => void) => { const listener = (_: IpcRendererEvent, show: boolean): void => cb(show) ipcRenderer.on(IPC.LYRIC_STATE, listener) return () => ipcRenderer.removeListener(IPC.LYRIC_STATE, listener) } }, win: { minimize: () => ipcRenderer.send(IPC.WIN_MIN), maximize: () => ipcRenderer.send(IPC.WIN_MAX), close: () => ipcRenderer.send(IPC.WIN_CLOSE) }, player: { onControl: (cb: (action: 'playpause' | 'next' | 'prev') => void) => { const listener = (_: IpcRendererEvent, action: 'playpause' | 'next' | 'prev'): void => cb(action) ipcRenderer.on(IPC.PLAYER_CONTROL, listener) return () => ipcRenderer.removeListener(IPC.PLAYER_CONTROL, listener) } }, exporter: { run: ( dest: string, platform: 'android' | 'ios', list: unknown[] ): Promise<{ done: number; total: number; fail: number }> => ipcRenderer.invoke(IPC.EXPORT_SONGS, dest, platform, list), onProgress: ( cb: (p: { musicUId: string status: 'converting' | 'copying' | 'done' | 'fail' done: number total: number }) => void ) => { const listener = ( _: IpcRendererEvent, p: { musicUId: string status: 'converting' | 'copying' | 'done' | 'fail' done: number total: number } ): void => cb(p) ipcRenderer.on(IPC.EXPORT_PROGRESS, listener) return () => ipcRenderer.removeListener(IPC.EXPORT_PROGRESS, listener) } }, update: { check: (): Promise<{ version: string latest: string hasUpdate: boolean message: string url: string error?: boolean }> => ipcRenderer.invoke(IPC.UPDATE_CHECK), openDownload: (url: string): Promise => ipcRenderer.invoke(IPC.UPDATE_OPEN_DOWNLOAD, url) } } export type ApiType = typeof api if (process.contextIsolated) { try { contextBridge.exposeInMainWorld('electron', electronAPI) contextBridge.exposeInMainWorld('api', api) } catch (error) { console.error(error) } } else { // @ts-ignore (define in dts) window.electron = electronAPI // @ts-ignore window.api = api }