refactor: 迁移至 electron-vite 官方推荐目录结构

- electron/* -> src/main/* (主进程)
- electron/preload.ts -> src/preload/index.ts (预加载)
- src/electron-api.d.ts -> src/preload/index.d.ts (类型声明)
- src/* -> src/renderer/src/* (渲染进程)
- src/index.html -> src/renderer/index.html
- electron.vite.config.ts 简化为自动入口检测 (零配置)
- tsconfig.app.json -> tsconfig.web.json, 更新 includes/paths
- 构建产物 dist-electron/ -> out/ (修复 main/preload 输出覆盖 bug)
- 更新 package.json main 字段 + electron-builder files
- 更新 .gitignore + eslint ignores
- 更新 AGENTS.md 架构文档
This commit is contained in:
2026-07-10 23:46:34 +08:00
parent 157af4399f
commit eda30250c6
71 changed files with 112 additions and 127 deletions
+144
View File
@@ -0,0 +1,144 @@
/// <reference types="vite/client" />
/**
* Electron preload bridge type declarations.
*
* Mirrors the API exposed via contextBridge in `electron/preload.ts`.
* Keeps the renderer fully typed at the IPC boundary without any Node access.
*/
export interface ElectronAPI {
window: {
minimize: () => void
maximize: () => void
close: () => void
}
theme: {
get: () => Promise<boolean>
set: (theme: 'system' | 'dark' | 'light') => void
onOsUpdated: (callback: (isDark: boolean) => void) => void
}
dialog: {
openFile: (options: {
title?: string
filters?: { name: string; extensions: string[] }[]
}) => Promise<any>
}
credential: {
encrypt: (text: string) => Promise<string>
decrypt: (encoded: string) => Promise<string>
}
storage: {
getConnections: () => Promise<any[]>
saveConnection: (conn: any) => Promise<any[]>
deleteConnection: (id: string) => Promise<any[]>
reorderConnections: (ids: string[]) => Promise<any[]>
getSettings: () => Promise<any>
saveSettings: (settings: any) => Promise<void>
}
redis: {
// Connection
connect: (id: string, opts: any) => Promise<any>
disconnect: (id: string) => Promise<void>
execute: (id: string, command: string, args: string[]) => Promise<any>
getInfo: (id: string) => Promise<string>
ping: (id: string) => Promise<string>
isConnected: (id: string) => Promise<boolean>
selectDb: (id: string, db: number) => Promise<void>
dbSize: (id: string) => Promise<number>
// Key operations
scanKeys: (id: string, cursor: string, pattern: string, count: number) => Promise<any>
getKeyType: (id: string, key: string) => Promise<string>
getKeyTTL: (id: string, key: string) => Promise<number>
deleteKey: (id: string, key: string) => Promise<void>
renameKey: (id: string, oldKey: string, newKey: string) => Promise<void>
existsKey: (id: string, key: string) => Promise<boolean>
setTTL: (id: string, key: string, ttl: number) => Promise<void>
batchDelete: (id: string, keys: string[]) => Promise<number>
memoryUsage: (id: string, key: string) => Promise<number | null>
// String
getString: (id: string, key: string) => Promise<string | null>
setString: (id: string, key: string, value: string) => Promise<void>
// Hash
hashScan: (id: string, key: string, cursor: string, count: number) => Promise<any>
hashSet: (id: string, key: string, field: string, value: string) => Promise<void>
hashDel: (id: string, key: string, field: string) => Promise<void>
// List
listRange: (id: string, key: string, start: number, stop: number) => Promise<string[]>
listLength: (id: string, key: string) => Promise<number>
listPush: (id: string, key: string, values: string[]) => Promise<number>
listSet: (id: string, key: string, index: number, value: string) => Promise<void>
listRemove: (id: string, key: string, count: number, value: string) => Promise<number>
// Set
setMembers: (id: string, key: string) => Promise<string[]>
setSize: (id: string, key: string) => Promise<number>
setAdd: (id: string, key: string, members: string[]) => Promise<number>
setRemove: (id: string, key: string, members: string[]) => Promise<number>
// Sorted set
zsetRange: (
id: string,
key: string,
start: number,
stop: number,
withScores: boolean,
) => Promise<string[]>
zsetSize: (id: string, key: string) => Promise<number>
zsetAdd: (id: string, key: string, score: number, member: string) => Promise<number>
zsetRemove: (id: string, key: string, members: string[]) => Promise<number>
// Stream
streamInfo: (id: string, key: string) => Promise<any>
streamRange: (
id: string,
key: string,
start: string,
end: string,
count: number,
) => Promise<any[]>
streamAdd: (id: string, key: string, streamId: string, fieldValues: string[]) => Promise<string>
streamTrim: (id: string, key: string, maxLen: number) => Promise<number>
streamDel: (id: string, key: string, ids: string[]) => Promise<number>
streamGroups: (id: string, key: string) => Promise<any[]>
streamConsumers: (id: string, key: string, group: string) => Promise<any[]>
streamGroupCreate: (
id: string,
key: string,
group: string,
startId: string,
mkstream: boolean,
) => Promise<any>
streamGroupDestroy: (id: string, key: string, group: string) => Promise<number>
streamAck: (id: string, key: string, group: string, ids: string[]) => Promise<number>
// ReJson
rejsonGet: (id: string, key: string, path: string) => Promise<string>
rejsonSet: (id: string, key: string, path: string, value: string) => Promise<void>
// Slow log
slowLogGet: (id: string, count: number) => Promise<any[]>
slowLogLen: (id: string) => Promise<number>
// Command log
getCommandLog: () => Promise<any[]>
clearCommandLog: () => Promise<void>
// Format
decodeValue: (value: string, format: string) => Promise<string>
detectFormat: (value: string) => Promise<string>
customFormat: (value: string, command: string, key: string) => Promise<string>
// Pub/Sub
subscribe: (id: string, channels: string[], isPattern: boolean) => Promise<any>
unsubscribe: (id: string) => Promise<void>
onSubscribeMessage: (callback: (msg: any) => void) => void
// Monitor
monitor: (id: string) => Promise<any>
monitorStop: (id: string) => Promise<void>
onMonitorMessage: (callback: (msg: any) => void) => void
}
updater: {
check: () => Promise<any>
download: () => Promise<any>
install: () => Promise<any>
onStatus: (callback: (status: any) => void) => void
}
}
declare global {
interface Window {
electronAPI: ElectronAPI
}
}
+171
View File
@@ -0,0 +1,171 @@
import { contextBridge, ipcRenderer } from 'electron'
const electronAPI = {
window: {
minimize: () => ipcRenderer.send('window:minimize'),
maximize: () => ipcRenderer.send('window:maximize'),
close: () => ipcRenderer.send('window:close'),
},
theme: {
get: (): Promise<boolean> => ipcRenderer.invoke('theme:get'),
set: (theme: 'system' | 'dark' | 'light') => ipcRenderer.send('theme:set', theme),
onOsUpdated: (callback: (isDark: boolean) => void) => {
ipcRenderer.on('theme:os-updated', (_e, isDark: boolean) => callback(isDark))
},
},
dialog: {
openFile: (options: { title?: string; filters?: { name: string; extensions: string[] }[] }) =>
ipcRenderer.invoke('dialog:openFile', options),
},
credential: {
encrypt: (text: string): Promise<string> => ipcRenderer.invoke('credential:encrypt', text),
decrypt: (encoded: string): Promise<string> => ipcRenderer.invoke('credential:decrypt', encoded),
},
storage: {
getConnections: (): Promise<any[]> => ipcRenderer.invoke('storage:getConnections'),
saveConnection: (conn: any): Promise<any[]> => ipcRenderer.invoke('storage:saveConnection', conn),
deleteConnection: (id: string): Promise<any[]> => ipcRenderer.invoke('storage:deleteConnection', id),
reorderConnections: (ids: string[]): Promise<any[]> => ipcRenderer.invoke('storage:reorderConnections', ids),
getSettings: (): Promise<any> => ipcRenderer.invoke('storage:getSettings'),
saveSettings: (settings: any): Promise<void> => ipcRenderer.invoke('storage:saveSettings', settings),
},
redis: {
connect: (id: string, opts: any): Promise<any> => ipcRenderer.invoke('redis:connect', id, opts),
disconnect: (id: string): Promise<void> => ipcRenderer.invoke('redis:disconnect', id),
execute: (id: string, command: string, args: string[]): Promise<any> =>
ipcRenderer.invoke('redis:execute', id, command, args),
getInfo: (id: string): Promise<string> => ipcRenderer.invoke('redis:getInfo', id),
scanKeys: (id: string, cursor: string, pattern: string, count: number): Promise<any> =>
ipcRenderer.invoke('redis:scanKeys', id, cursor, pattern, count),
getKeyType: (id: string, key: string): Promise<string> => ipcRenderer.invoke('redis:getKeyType', id, key),
getKeyTTL: (id: string, key: string): Promise<number> => ipcRenderer.invoke('redis:getKeyTTL', id, key),
getString: (id: string, key: string): Promise<string | null> => ipcRenderer.invoke('redis:getString', id, key),
setString: (id: string, key: string, value: string): Promise<void> =>
ipcRenderer.invoke('redis:setString', id, key, value),
// ReJson
rejsonGet: (id: string, key: string, path: string): Promise<string> =>
ipcRenderer.invoke('redis:rejsonGet', id, key, path),
rejsonSet: (id: string, key: string, path: string, value: string): Promise<void> =>
ipcRenderer.invoke('redis:rejsonSet', id, key, path, value),
deleteKey: (id: string, key: string): Promise<void> => ipcRenderer.invoke('redis:deleteKey', id, key),
hashScan: (id: string, key: string, cursor: string, count: number): Promise<any> =>
ipcRenderer.invoke('redis:hashScan', id, key, cursor, count),
hashSet: (id: string, key: string, field: string, value: string): Promise<void> =>
ipcRenderer.invoke('redis:hashSet', id, key, field, value),
hashDel: (id: string, key: string, field: string): Promise<void> =>
ipcRenderer.invoke('redis:hashDel', id, key, field),
setTTL: (id: string, key: string, ttl: number): Promise<void> =>
ipcRenderer.invoke('redis:setTTL', id, key, ttl),
selectDb: (id: string, db: number): Promise<void> =>
ipcRenderer.invoke('redis:selectDb', id, db),
dbSize: (id: string): Promise<number> => ipcRenderer.invoke('redis:dbSize', id),
// List
listRange: (id: string, key: string, start: number, stop: number): Promise<string[]> =>
ipcRenderer.invoke('redis:listRange', id, key, start, stop),
listLength: (id: string, key: string): Promise<number> =>
ipcRenderer.invoke('redis:listLength', id, key),
listPush: (id: string, key: string, values: string[]): Promise<number> =>
ipcRenderer.invoke('redis:listPush', id, key, values),
listSet: (id: string, key: string, index: number, value: string): Promise<void> =>
ipcRenderer.invoke('redis:listSet', id, key, index, value),
listRemove: (id: string, key: string, count: number, value: string): Promise<number> =>
ipcRenderer.invoke('redis:listRemove', id, key, count, value),
// Set
setMembers: (id: string, key: string): Promise<string[]> =>
ipcRenderer.invoke('redis:setMembers', id, key),
setSize: (id: string, key: string): Promise<number> =>
ipcRenderer.invoke('redis:setSize', id, key),
setAdd: (id: string, key: string, members: string[]): Promise<number> =>
ipcRenderer.invoke('redis:setAdd', id, key, members),
setRemove: (id: string, key: string, members: string[]): Promise<number> =>
ipcRenderer.invoke('redis:setRemove', id, key, members),
// Sorted set
zsetRange: (id: string, key: string, start: number, stop: number, withScores: boolean): Promise<string[]> =>
ipcRenderer.invoke('redis:zsetRange', id, key, start, stop, withScores),
zsetSize: (id: string, key: string): Promise<number> =>
ipcRenderer.invoke('redis:zsetSize', id, key),
zsetAdd: (id: string, key: string, score: number, member: string): Promise<number> =>
ipcRenderer.invoke('redis:zsetAdd', id, key, score, member),
zsetRemove: (id: string, key: string, members: string[]): Promise<number> =>
ipcRenderer.invoke('redis:zsetRemove', id, key, members),
// Slow log
slowLogGet: (id: string, count: number): Promise<any[]> =>
ipcRenderer.invoke('redis:slowLogGet', id, count),
slowLogLen: (id: string): Promise<number> =>
ipcRenderer.invoke('redis:slowLogLen', id),
// Memory
memoryUsage: (id: string, key: string): Promise<number | null> =>
ipcRenderer.invoke('redis:memoryUsage', id, key),
batchDelete: (id: string, keys: string[]): Promise<number> =>
ipcRenderer.invoke('redis:batchDelete', id, keys),
// Stream
streamInfo: (id: string, key: string): Promise<any> =>
ipcRenderer.invoke('redis:streamInfo', id, key),
streamRange: (id: string, key: string, start: string, end: string, count: number): Promise<any[]> =>
ipcRenderer.invoke('redis:streamRange', id, key, start, end, count),
streamAdd: (id: string, key: string, streamId: string, fieldValues: string[]): Promise<string> =>
ipcRenderer.invoke('redis:streamAdd', id, key, streamId, fieldValues),
streamTrim: (id: string, key: string, maxLen: number): Promise<number> =>
ipcRenderer.invoke('redis:streamTrim', id, key, maxLen),
streamDel: (id: string, key: string, ids: string[]): Promise<number> =>
ipcRenderer.invoke('redis:streamDel', id, key, ids),
// Stream consumer groups
streamGroups: (id: string, key: string): Promise<any[]> =>
ipcRenderer.invoke('redis:streamGroups', id, key),
streamConsumers: (id: string, key: string, group: string): Promise<any[]> =>
ipcRenderer.invoke('redis:streamConsumers', id, key, group),
streamGroupCreate: (id: string, key: string, group: string, startId: string, mkstream: boolean): Promise<any> =>
ipcRenderer.invoke('redis:streamGroupCreate', id, key, group, startId, mkstream),
streamGroupDestroy: (id: string, key: string, group: string): Promise<number> =>
ipcRenderer.invoke('redis:streamGroupDestroy', id, key, group),
streamAck: (id: string, key: string, group: string, ids: string[]): Promise<number> =>
ipcRenderer.invoke('redis:streamAck', id, key, group, ids),
// Key ops
renameKey: (id: string, oldKey: string, newKey: string): Promise<void> =>
ipcRenderer.invoke('redis:renameKey', id, oldKey, newKey),
existsKey: (id: string, key: string): Promise<boolean> =>
ipcRenderer.invoke('redis:existsKey', id, key),
ping: (id: string): Promise<string> =>
ipcRenderer.invoke('redis:ping', id),
isConnected: (id: string): Promise<boolean> =>
ipcRenderer.invoke('redis:isConnected', id),
// Command log
getCommandLog: (): Promise<any[]> =>
ipcRenderer.invoke('redis:getCommandLog'),
clearCommandLog: (): Promise<void> =>
ipcRenderer.invoke('redis:clearCommandLog'),
// Format
decodeValue: (value: string, format: string): Promise<string> =>
ipcRenderer.invoke('redis:decodeValue', value, format),
detectFormat: (value: string): Promise<string> =>
ipcRenderer.invoke('redis:detectFormat', value),
customFormat: (value: string, command: string, key: string): Promise<string> =>
ipcRenderer.invoke('redis:customFormat', value, command, key),
// Pub/Sub
subscribe: (id: string, channels: string[], isPattern: boolean): Promise<any> =>
ipcRenderer.invoke('redis:subscribe', id, channels, isPattern),
unsubscribe: (id: string): Promise<void> =>
ipcRenderer.invoke('redis:unsubscribe', id),
onSubscribeMessage: (callback: (msg: any) => void) => {
ipcRenderer.on('redis:subscribeMessage', (_e, msg) => callback(msg))
},
// Monitor
monitor: (id: string): Promise<any> =>
ipcRenderer.invoke('redis:monitor', id),
monitorStop: (id: string): Promise<void> =>
ipcRenderer.invoke('redis:monitorStop', id),
onMonitorMessage: (callback: (msg: any) => void) => {
ipcRenderer.on('redis:monitorMessage', (_e, msg) => callback(msg))
},
},
updater: {
check: (): Promise<any> => ipcRenderer.invoke('updater:check'),
download: (): Promise<any> => ipcRenderer.invoke('updater:download'),
install: () => ipcRenderer.invoke('updater:install'),
onStatus: (callback: (status: any) => void) => {
ipcRenderer.on('updater:status', (_e, status) => callback(status))
},
},
}
contextBridge.exposeInMainWorld('electronAPI', electronAPI)