refactor: 清理 Electron 遗留代码并优化项目结构

- 删除 Electron 主进程代码 (src/main/)
- 删除构建产物文件夹 (build/, dist/, release/)
- 清理 package.json 中的 Electron 依赖和脚本
- 删除 TypeScript 配置中的 Electron 相关文件
- 优化前端代码结构至 Tauri 标准结构 (src/renderer → src/)
- 删除移动端图标和不必要文件
- 更新文档说明技术栈变更为 Tauri
This commit is contained in:
farion1231
2025-08-23 21:13:25 +08:00
parent 29581b85d9
commit 12fa80e002
63 changed files with 16 additions and 922 deletions

View File

@@ -1,277 +0,0 @@
import { app, BrowserWindow, ipcMain, dialog, shell } from "electron";
import path from "path";
import fs from "fs/promises";
import { Provider } from "../shared/types";
import {
switchProvider,
getClaudeCodeConfig,
saveProviderConfig,
deleteProviderConfig,
sanitizeProviderName,
importCurrentConfigAsDefault,
getProviderConfigPath,
fileExists,
} from "./services";
import { store } from "./store";
let mainWindow: BrowserWindow | null = null;
const isMac = process.platform === "darwin";
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
minWidth: 600,
minHeight: 400,
webPreferences: {
preload: path.join(__dirname, "../main/preload.js"),
contextIsolation: true,
nodeIntegration: false,
},
// 使用 macOS 隐藏式标题栏,仅在 macOS 启用
...(isMac ? { titleBarStyle: "hiddenInset" as const } : {}),
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", async () => {
return await store.get("providers", {} as Record<string, Provider>);
});
ipcMain.handle("getCurrentProvider", async () => {
return await store.get("current", "");
});
ipcMain.handle("addProvider", async (_, provider: Provider) => {
try {
// 1. 保存供应商配置到独立文件
const saveSuccess = await saveProviderConfig(provider);
if (!saveSuccess) {
return false;
}
// 2. 更新应用配置
const providers = await store.get("providers", {} as Record<string, Provider>);
providers[provider.id] = provider;
await store.set("providers", providers);
return true;
} catch (error) {
console.error("添加供应商失败:", error);
return false;
}
});
ipcMain.handle("deleteProvider", async (_, id: string) => {
try {
const providers = await store.get("providers", {} as Record<string, Provider>);
const provider = providers[id];
// 1. 删除供应商配置文件
const deleteSuccess = await deleteProviderConfig(id, provider?.name);
if (!deleteSuccess) {
console.error("删除供应商配置文件失败");
// 仍然继续删除应用配置,避免配置不同步
}
// 2. 更新应用配置
delete providers[id];
await store.set("providers", providers);
// 3. 如果删除的是当前供应商,清空当前选择
const currentProviderId = await store.get("current", "");
if (currentProviderId === id) {
await store.set("current", "");
}
return true;
} catch (error) {
console.error("删除供应商失败:", error);
return false;
}
});
ipcMain.handle("updateProvider", async (_, provider: Provider) => {
try {
const providers = await store.get("providers", {} as Record<string, Provider>);
const currentProviderId = await store.get("current", "");
const oldProvider = providers[provider.id];
// 1. 如果名字发生变化,需要重命名配置文件
if (oldProvider && oldProvider.name !== provider.name) {
const oldConfigPath = getProviderConfigPath(
provider.id,
oldProvider.name
);
const newConfigPath = getProviderConfigPath(provider.id, provider.name);
// 如果旧配置文件存在且路径不同,需要重命名
if (
(await fileExists(oldConfigPath)) &&
oldConfigPath !== newConfigPath
) {
// 如果新路径已存在文件,先删除避免冲突
if (await fileExists(newConfigPath)) {
await fs.unlink(newConfigPath);
}
await fs.rename(oldConfigPath, newConfigPath);
console.log(
`已重命名配置文件: ${oldProvider.name} -> ${provider.name}`
);
}
}
// 2. 保存更新后的配置到文件
const saveSuccess = await saveProviderConfig(provider);
if (!saveSuccess) {
return false;
}
// 3. 更新应用配置
providers[provider.id] = provider;
await store.set("providers", providers);
// 4. 如果编辑的是当前激活的供应商,需要重新切换以应用更改
if (provider.id === currentProviderId) {
const switchSuccess = await switchProvider(
provider,
currentProviderId,
providers
);
if (!switchSuccess) {
console.error("更新当前供应商的Claude Code配置失败");
return false;
}
}
return true;
} catch (error) {
console.error("更新供应商失败:", error);
return false;
}
});
ipcMain.handle("switchProvider", async (_, providerId: string) => {
try {
const providers = await store.get("providers", {} as Record<string, Provider>);
const provider = providers[providerId];
const currentProviderId = await store.get("current", "");
if (!provider) {
console.error(`供应商不存在: ${providerId}`);
return false;
}
// 执行切换
const success = await switchProvider(
provider,
currentProviderId,
providers
);
if (success) {
await store.set("current", providerId);
console.log(`成功切换到供应商: ${provider.name}`);
}
return success;
} catch (error) {
console.error("切换供应商失败:", error);
return false;
}
});
ipcMain.handle("importCurrentConfigAsDefault", async () => {
try {
const result = await importCurrentConfigAsDefault();
if (result.success && result.provider) {
// 将默认供应商添加到store中
const providers = await store.get("providers", {} as Record<string, Provider>);
providers[result.provider.id] = result.provider;
await store.set("providers", providers);
// 设置为当前选中的供应商
await store.set("current", result.provider.id);
return { success: true, providerId: result.provider.id };
}
return result;
} catch (error) {
console.error("导入默认配置失败:", error);
return { success: false };
}
});
ipcMain.handle("getClaudeCodeConfigPath", () => {
return getClaudeCodeConfig().path;
});
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];
});
ipcMain.handle("openConfigFolder", async () => {
try {
const { dir } = getClaudeCodeConfig();
await shell.openPath(dir);
return true;
} catch (error) {
console.error("打开配置文件夹失败:", error);
return false;
}
});
ipcMain.handle("openExternal", async (_, url: string) => {
try {
await shell.openExternal(url);
return true;
} catch (error) {
console.error("打开外部链接失败:", error);
return false;
}
});

View File

@@ -1,21 +0,0 @@
import { contextBridge, ipcRenderer } from 'electron'
import { Provider } from '../shared/types'
contextBridge.exposeInMainWorld('electronAPI', {
getProviders: () => ipcRenderer.invoke('getProviders'),
getCurrentProvider: () => ipcRenderer.invoke('getCurrentProvider'),
addProvider: (provider: Provider) => ipcRenderer.invoke('addProvider', provider),
deleteProvider: (id: string) => ipcRenderer.invoke('deleteProvider', id),
updateProvider: (provider: Provider) => ipcRenderer.invoke('updateProvider', provider),
switchProvider: (providerId: string) => ipcRenderer.invoke('switchProvider', providerId),
importCurrentConfigAsDefault: () => ipcRenderer.invoke('importCurrentConfigAsDefault'),
getClaudeCodeConfigPath: () => ipcRenderer.invoke('getClaudeCodeConfigPath'),
selectConfigFile: () => ipcRenderer.invoke('selectConfigFile'),
openConfigFolder: () => ipcRenderer.invoke('openConfigFolder'),
openExternal: (url: string) => ipcRenderer.invoke('openExternal', url)
})
// 暴露平台信息给渲染进程,用于平台特定样式控制
contextBridge.exposeInMainWorld('platform', {
isMac: process.platform === 'darwin'
})

View File

@@ -1,181 +0,0 @@
import fs from "fs/promises";
import path from "path";
import os from "os";
import { Provider } from "../shared/types";
/**
* 清理供应商名称,确保文件名安全
*/
export function sanitizeProviderName(name: string): string {
return name.replace(/[<>:"/\\|?*]/g, "-").toLowerCase();
}
export function getClaudeCodeConfig() {
// Claude Code 配置文件路径
const configDir = path.join(os.homedir(), ".claude");
const configPath = path.join(configDir, "settings.json");
return { path: configPath, dir: configDir };
}
/**
* 获取供应商配置文件路径(基于供应商名称)
*/
export function getProviderConfigPath(
providerId: string,
providerName?: string
): string {
const { dir } = getClaudeCodeConfig();
// 如果提供了名称使用名称否则使用ID向后兼容
const baseName = providerName
? sanitizeProviderName(providerName)
: sanitizeProviderName(providerId);
return path.join(dir, `settings-${baseName}.json`);
}
/**
* 保存供应商配置到独立文件
*/
export async function saveProviderConfig(provider: Provider): Promise<boolean> {
try {
const { dir } = getClaudeCodeConfig();
const providerConfigPath = getProviderConfigPath(
provider.id,
provider.name
);
// 确保目录存在
await fs.mkdir(dir, { recursive: true });
// 保存配置到供应商专用文件
await fs.writeFile(
providerConfigPath,
JSON.stringify(provider.settingsConfig, null, 2),
"utf-8"
);
return true;
} catch (error) {
console.error("保存供应商配置失败:", error);
return false;
}
}
/**
* 检查文件是否存在
*/
export async function fileExists(filePath: string): Promise<boolean> {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
/**
* 切换供应商配置(基于文件复制)
*/
export async function switchProvider(
provider: Provider,
currentProviderId?: string,
providers?: Record<string, Provider>
): Promise<boolean> {
try {
const { path: settingsPath, dir: configDir } = getClaudeCodeConfig();
// 确保目录存在
await fs.mkdir(configDir, { recursive: true });
const newSettingsPath = getProviderConfigPath(provider.id, provider.name);
// 检查目标配置文件是否存在
if (!(await fileExists(newSettingsPath))) {
console.error(`供应商配置文件不存在: ${newSettingsPath}`);
return false;
}
// 1. 如果当前存在settings.json先备份到当前供应商的配置文件
if (await fileExists(settingsPath)) {
if (currentProviderId && providers && providers[currentProviderId]) {
const currentProvider = providers[currentProviderId];
const currentProviderPath = getProviderConfigPath(
currentProviderId,
currentProvider.name
);
// 使用复制而不是重命名,避免删除原文件
await fs.copyFile(settingsPath, currentProviderPath);
console.log(`已备份当前供应商配置: ${currentProvider.name}`);
}
}
// 2. 复制新配置到settings.json保留原文件
await fs.copyFile(newSettingsPath, settingsPath);
console.log(`成功切换到供应商: ${provider.name}`);
return true;
} catch (error) {
console.error("切换供应商失败:", error);
return false;
}
}
/**
* 导入当前配置为默认供应商
*/
export async function importCurrentConfigAsDefault(): Promise<{ success: boolean; provider?: Provider }> {
try {
const { path: settingsPath } = getClaudeCodeConfig();
// 检查当前配置是否存在
if (!(await fileExists(settingsPath))) {
return { success: false };
}
// 读取当前配置
const configContent = await fs.readFile(settingsPath, "utf-8");
const settingsConfig = JSON.parse(configContent);
// 创建默认供应商对象
const provider: Provider = {
id: "default",
name: "default",
settingsConfig: settingsConfig,
};
// 保存默认供应商的配置到独立文件
const saveSuccess = await saveProviderConfig(provider);
if (!saveSuccess) {
return { success: false };
}
console.log(`已导入当前配置为默认供应商配置文件settings-default.json`);
return { success: true, provider };
} catch (error) {
console.error("导入默认配置失败:", error);
return { success: false };
}
}
/**
* 删除供应商配置文件
*/
export async function deleteProviderConfig(
providerId: string,
providerName?: string
): Promise<boolean> {
try {
const providerConfigPath = getProviderConfigPath(providerId, providerName);
if (await fileExists(providerConfigPath)) {
await fs.unlink(providerConfigPath);
console.log(`已删除供应商配置文件: ${providerConfigPath}`);
}
return true;
} catch (error) {
console.error("删除供应商配置失败:", error);
return false;
}
}

View File

@@ -1,60 +0,0 @@
import fs from 'fs/promises'
import path from 'path'
import os from 'os'
import { AppConfig } from '../shared/types'
export class SimpleStore {
private configPath: string
private configDir: string
private data: AppConfig = { providers: {}, current: '' }
private initPromise: Promise<void>
constructor() {
this.configDir = path.join(os.homedir(), '.cc-switch')
this.configPath = path.join(this.configDir, 'config.json')
// 立即开始加载,但不阻塞构造函数
this.initPromise = this.loadData()
}
private async loadData(): Promise<void> {
try {
const content = await fs.readFile(this.configPath, 'utf-8')
this.data = JSON.parse(content)
} catch (error) {
// 文件不存在或格式错误,使用默认数据
this.data = { providers: {}, current: '' }
await this.saveData()
}
}
private async saveData(): Promise<void> {
try {
// 确保目录存在
await fs.mkdir(this.configDir, { recursive: true })
// 写入配置文件
await fs.writeFile(this.configPath, JSON.stringify(this.data, null, 2), 'utf-8')
} catch (error) {
console.error('保存配置失败:', error)
}
}
async get<T>(key: keyof AppConfig, defaultValue?: T): Promise<T> {
await this.initPromise // 等待初始化完成
const value = this.data[key] as T
return value !== undefined ? value : (defaultValue as T)
}
async set<K extends keyof AppConfig>(key: K, value: AppConfig[K]): Promise<void> {
await this.initPromise // 等待初始化完成
this.data[key] = value
await this.saveData()
}
// 获取配置文件路径,用于调试
getConfigPath(): string {
return this.configPath
}
}
// 创建单例
export const store = new SimpleStore()