2025-08-06 09:56:27 +08:00
|
|
|
import { app, BrowserWindow, ipcMain, dialog, shell } from 'electron'
|
2025-08-04 22:16:26 +08:00
|
|
|
import path from 'path'
|
2025-08-07 10:09:17 +08:00
|
|
|
import { Provider } from '../shared/types'
|
2025-08-07 15:48:30 +08:00
|
|
|
import {
|
|
|
|
|
switchProvider,
|
|
|
|
|
getClaudeCodeConfig,
|
|
|
|
|
saveProviderConfig,
|
|
|
|
|
deleteProviderConfig,
|
|
|
|
|
sanitizeProviderName,
|
|
|
|
|
importCurrentConfig
|
|
|
|
|
} from './services'
|
2025-08-07 10:09:17 +08:00
|
|
|
import { store } from './store'
|
2025-08-04 22:16:26 +08:00
|
|
|
|
|
|
|
|
let mainWindow: BrowserWindow | null = null
|
|
|
|
|
|
|
|
|
|
function createWindow() {
|
|
|
|
|
mainWindow = new BrowserWindow({
|
|
|
|
|
width: 800,
|
|
|
|
|
height: 600,
|
|
|
|
|
webPreferences: {
|
2025-08-05 09:51:21 +08:00
|
|
|
preload: path.join(__dirname, '../main/preload.js'),
|
2025-08-04 22:16:26 +08:00
|
|
|
contextIsolation: true,
|
|
|
|
|
nodeIntegration: false
|
|
|
|
|
},
|
|
|
|
|
titleBarStyle: 'hiddenInset',
|
|
|
|
|
autoHideMenuBar: true
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (app.isPackaged) {
|
|
|
|
|
mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'))
|
|
|
|
|
} else {
|
|
|
|
|
mainWindow.loadURL('http://localhost:3000')
|
|
|
|
|
mainWindow.webContents.openDevTools()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mainWindow.on('closed', () => {
|
|
|
|
|
mainWindow = null
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
app.whenReady().then(() => {
|
|
|
|
|
createWindow()
|
|
|
|
|
|
|
|
|
|
app.on('activate', () => {
|
|
|
|
|
if (BrowserWindow.getAllWindows().length === 0) {
|
|
|
|
|
createWindow()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
app.on('window-all-closed', () => {
|
|
|
|
|
if (process.platform !== 'darwin') {
|
|
|
|
|
app.quit()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// IPC handlers
|
|
|
|
|
ipcMain.handle('getProviders', () => {
|
2025-08-07 10:09:17 +08:00
|
|
|
return store.get('providers', {} as Record<string, Provider>)
|
2025-08-04 22:16:26 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ipcMain.handle('getCurrentProvider', () => {
|
|
|
|
|
return store.get('current', '')
|
|
|
|
|
})
|
|
|
|
|
|
2025-08-07 10:09:17 +08:00
|
|
|
ipcMain.handle('addProvider', async (_, provider: Provider) => {
|
2025-08-07 15:48:30 +08:00
|
|
|
try {
|
|
|
|
|
// 1. 保存供应商配置到独立文件
|
|
|
|
|
const saveSuccess = await saveProviderConfig(provider)
|
|
|
|
|
if (!saveSuccess) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 更新应用配置
|
|
|
|
|
const providers = store.get('providers', {} as Record<string, Provider>)
|
|
|
|
|
providers[provider.id] = {
|
|
|
|
|
...provider,
|
|
|
|
|
createdAt: Date.now(),
|
|
|
|
|
updatedAt: Date.now()
|
|
|
|
|
}
|
|
|
|
|
await store.set('providers', providers)
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('添加供应商失败:', error)
|
|
|
|
|
return false
|
|
|
|
|
}
|
2025-08-04 22:16:26 +08:00
|
|
|
})
|
|
|
|
|
|
2025-08-07 10:09:17 +08:00
|
|
|
ipcMain.handle('deleteProvider', async (_, id: string) => {
|
2025-08-07 15:48:30 +08:00
|
|
|
try {
|
|
|
|
|
// 1. 删除供应商配置文件
|
|
|
|
|
const deleteSuccess = await deleteProviderConfig(id)
|
|
|
|
|
if (!deleteSuccess) {
|
|
|
|
|
console.error('删除供应商配置文件失败')
|
|
|
|
|
// 仍然继续删除应用配置,避免配置不同步
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 更新应用配置
|
|
|
|
|
const providers = store.get('providers', {} as Record<string, Provider>)
|
|
|
|
|
delete providers[id]
|
|
|
|
|
await store.set('providers', providers)
|
|
|
|
|
|
|
|
|
|
// 3. 如果删除的是当前供应商,清空当前选择
|
|
|
|
|
const currentProviderId = store.get('current', '')
|
|
|
|
|
if (currentProviderId === id) {
|
|
|
|
|
await store.set('current', '')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('删除供应商失败:', error)
|
|
|
|
|
return false
|
|
|
|
|
}
|
2025-08-04 22:16:26 +08:00
|
|
|
})
|
|
|
|
|
|
2025-08-06 11:50:13 +08:00
|
|
|
ipcMain.handle('updateProvider', async (_, provider: Provider) => {
|
2025-08-07 15:48:30 +08:00
|
|
|
try {
|
|
|
|
|
const providers = store.get('providers', {} as Record<string, Provider>)
|
|
|
|
|
const currentProviderId = store.get('current', '')
|
|
|
|
|
|
|
|
|
|
// 1. 保存更新后的配置到文件
|
|
|
|
|
const saveSuccess = await saveProviderConfig({
|
|
|
|
|
...provider,
|
|
|
|
|
updatedAt: Date.now()
|
|
|
|
|
})
|
|
|
|
|
if (!saveSuccess) {
|
2025-08-06 11:50:13 +08:00
|
|
|
return false
|
|
|
|
|
}
|
2025-08-07 15:48:30 +08:00
|
|
|
|
|
|
|
|
// 2. 更新应用配置
|
|
|
|
|
providers[provider.id] = {
|
|
|
|
|
...provider,
|
|
|
|
|
updatedAt: Date.now()
|
|
|
|
|
}
|
|
|
|
|
await store.set('providers', providers)
|
|
|
|
|
|
|
|
|
|
// 3. 如果编辑的是当前激活的供应商,需要重新切换以应用更改
|
|
|
|
|
if (provider.id === currentProviderId) {
|
|
|
|
|
const switchSuccess = await switchProvider(provider, currentProviderId)
|
|
|
|
|
if (!switchSuccess) {
|
|
|
|
|
console.error('更新当前供应商的Claude Code配置失败')
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('更新供应商失败:', error)
|
|
|
|
|
return false
|
2025-08-06 11:50:13 +08:00
|
|
|
}
|
2025-08-05 09:51:21 +08:00
|
|
|
})
|
|
|
|
|
|
2025-08-04 22:16:26 +08:00
|
|
|
ipcMain.handle('switchProvider', async (_, providerId: string) => {
|
2025-08-07 15:48:30 +08:00
|
|
|
try {
|
|
|
|
|
const providers = store.get('providers', {} as Record<string, Provider>)
|
|
|
|
|
const provider = providers[providerId]
|
|
|
|
|
const currentProviderId = store.get('current', '')
|
|
|
|
|
|
|
|
|
|
if (!provider) {
|
|
|
|
|
console.error(`供应商不存在: ${providerId}`)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 执行切换
|
|
|
|
|
const success = await switchProvider(provider, currentProviderId)
|
2025-08-04 22:16:26 +08:00
|
|
|
if (success) {
|
2025-08-07 10:09:17 +08:00
|
|
|
await store.set('current', providerId)
|
2025-08-07 15:48:30 +08:00
|
|
|
console.log(`成功切换到供应商: ${provider.name}`)
|
2025-08-04 22:16:26 +08:00
|
|
|
}
|
2025-08-07 15:48:30 +08:00
|
|
|
|
2025-08-04 22:16:26 +08:00
|
|
|
return success
|
2025-08-07 15:48:30 +08:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error('切换供应商失败:', error)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ipcMain.handle('importCurrentConfig', async (_, name: string) => {
|
|
|
|
|
try {
|
|
|
|
|
const result = await importCurrentConfig(name)
|
|
|
|
|
|
|
|
|
|
if (result.success && result.provider) {
|
|
|
|
|
// 将导入的供应商添加到store中
|
|
|
|
|
const providers = store.get('providers', {} as Record<string, Provider>)
|
|
|
|
|
providers[result.provider.id] = result.provider
|
|
|
|
|
await store.set('providers', providers)
|
|
|
|
|
|
|
|
|
|
return { success: true, providerId: result.provider.id }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
console.error('导入配置失败:', error)
|
|
|
|
|
return { success: false }
|
2025-08-04 22:16:26 +08:00
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ipcMain.handle('getClaudeCodeConfigPath', () => {
|
|
|
|
|
return getClaudeCodeConfig().path
|
2025-08-05 20:30:18 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ipcMain.handle('selectConfigFile', async () => {
|
|
|
|
|
if (!mainWindow) return null
|
|
|
|
|
|
|
|
|
|
const result = await dialog.showOpenDialog(mainWindow, {
|
|
|
|
|
properties: ['openFile'],
|
|
|
|
|
title: '选择 Claude Code 配置文件',
|
|
|
|
|
filters: [
|
|
|
|
|
{ name: 'JSON 文件', extensions: ['json'] },
|
|
|
|
|
{ name: '所有文件', extensions: ['*'] }
|
|
|
|
|
],
|
|
|
|
|
defaultPath: 'settings.json'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (result.canceled || result.filePaths.length === 0) {
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result.filePaths[0]
|
2025-08-06 07:41:43 +08:00
|
|
|
})
|
|
|
|
|
|
2025-08-06 09:56:27 +08:00
|
|
|
ipcMain.handle('openExternal', async (_, url: string) => {
|
|
|
|
|
try {
|
|
|
|
|
await shell.openExternal(url)
|
|
|
|
|
return true
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('打开外部链接失败:', error)
|
|
|
|
|
return false
|
|
|
|
|
}
|
2025-08-04 22:16:26 +08:00
|
|
|
})
|