chore: format code and fix bundle identifier for v3.0.0 release
- Format all TypeScript/React code with Prettier - Format all Rust code with cargo fmt - Fix bundle identifier from .app to .desktop to avoid macOS conflicts - Prepare codebase for v3.0.0 Tauri release
This commit is contained in:
@@ -26,7 +26,8 @@
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.refresh-btn, .add-btn {
|
||||
.refresh-btn,
|
||||
.add-btn {
|
||||
padding: 0.5rem 1rem;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
@@ -123,12 +124,12 @@
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 100;
|
||||
|
||||
|
||||
padding: 0.75rem 1.25rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
|
||||
|
||||
width: fit-content;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
34
src/App.tsx
34
src/App.tsx
@@ -10,9 +10,12 @@ function App() {
|
||||
const [providers, setProviders] = useState<Record<string, Provider>>({});
|
||||
const [currentProviderId, setCurrentProviderId] = useState<string>("");
|
||||
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
|
||||
const [configStatus, setConfigStatus] = useState<{ exists: boolean; path: string } | null>(null);
|
||||
const [configStatus, setConfigStatus] = useState<{
|
||||
exists: boolean;
|
||||
path: string;
|
||||
} | null>(null);
|
||||
const [editingProviderId, setEditingProviderId] = useState<string | null>(
|
||||
null
|
||||
null,
|
||||
);
|
||||
const [notification, setNotification] = useState<{
|
||||
message: string;
|
||||
@@ -31,7 +34,7 @@ function App() {
|
||||
const showNotification = (
|
||||
message: string,
|
||||
type: "success" | "error",
|
||||
duration = 3000
|
||||
duration = 3000,
|
||||
) => {
|
||||
// 清除之前的定时器
|
||||
if (timeoutRef.current) {
|
||||
@@ -73,7 +76,7 @@ function App() {
|
||||
const currentId = await window.api.getCurrentProvider();
|
||||
setProviders(loadedProviders);
|
||||
setCurrentProviderId(currentId);
|
||||
|
||||
|
||||
// 如果供应商列表为空,尝试自动导入现有配置为"default"供应商
|
||||
if (Object.keys(loadedProviders).length === 0) {
|
||||
await handleAutoImportDefault();
|
||||
@@ -82,7 +85,10 @@ function App() {
|
||||
|
||||
const loadConfigStatus = async () => {
|
||||
const status = await window.api.getClaudeConfigStatus();
|
||||
setConfigStatus({ exists: Boolean(status?.exists), path: String(status?.path || "") });
|
||||
setConfigStatus({
|
||||
exists: Boolean(status?.exists),
|
||||
path: String(status?.path || ""),
|
||||
});
|
||||
};
|
||||
|
||||
// 生成唯一ID
|
||||
@@ -137,7 +143,7 @@ function App() {
|
||||
showNotification(
|
||||
"切换成功!请重启 Claude Code 终端以生效",
|
||||
"success",
|
||||
2000
|
||||
2000,
|
||||
);
|
||||
} else {
|
||||
showNotification("切换失败,请检查配置", "error");
|
||||
@@ -147,18 +153,22 @@ function App() {
|
||||
// 自动导入现有配置为"default"供应商
|
||||
const handleAutoImportDefault = async () => {
|
||||
try {
|
||||
const result = await window.api.importCurrentConfigAsDefault()
|
||||
|
||||
const result = await window.api.importCurrentConfigAsDefault();
|
||||
|
||||
if (result.success) {
|
||||
await loadProviders()
|
||||
showNotification("已自动导入现有配置为 default 供应商", "success", 3000)
|
||||
await loadProviders();
|
||||
showNotification(
|
||||
"已自动导入现有配置为 default 供应商",
|
||||
"success",
|
||||
3000,
|
||||
);
|
||||
}
|
||||
// 如果导入失败(比如没有现有配置),静默处理,不显示错误
|
||||
} catch (error) {
|
||||
console.error('自动导入默认配置失败:', error)
|
||||
console.error("自动导入默认配置失败:", error);
|
||||
// 静默处理,不影响用户体验
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleOpenConfigFolder = async () => {
|
||||
await window.api.openConfigFolder();
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.2);
|
||||
position: relative;
|
||||
z-index: 1001;
|
||||
display: flex; /* 纵向布局,便于底栏固定 */
|
||||
display: flex; /* 纵向布局,便于底栏固定 */
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,10 @@
|
||||
}
|
||||
|
||||
/* 左侧占位以保证标题居中(与右侧关闭按钮宽度相当) */
|
||||
.modal-spacer { width: 32px; flex: 0 0 32px; }
|
||||
.modal-spacer {
|
||||
width: 32px;
|
||||
flex: 0 0 32px;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
flex: 1;
|
||||
@@ -69,16 +72,17 @@
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.modal-form { /* 表单外层包裹 body + footer */
|
||||
.modal-form {
|
||||
/* 表单外层包裹 body + footer */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0; /* 允许子元素正确计算高度 */
|
||||
min-height: 0; /* 允许子元素正确计算高度 */
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 1.25rem 1.5rem 1.5rem;
|
||||
overflow: auto; /* 仅内容区滚动 */
|
||||
overflow: auto; /* 仅内容区滚动 */
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
}
|
||||
@@ -175,7 +179,8 @@
|
||||
border-color: #3498db;
|
||||
}
|
||||
|
||||
.modal-footer { /* 固定在弹窗底部(非滚动区) */
|
||||
.modal-footer {
|
||||
/* 固定在弹窗底部(非滚动区) */
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: flex-end;
|
||||
|
||||
@@ -68,7 +68,9 @@
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
transition: background-color 0.2s, transform 0.1s;
|
||||
transition:
|
||||
background-color 0.2s,
|
||||
transform 0.1s;
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
@@ -102,4 +104,4 @@
|
||||
.confirm-btn:focus {
|
||||
outline: 2px solid #007bff;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import './ConfirmDialog.css';
|
||||
import React from "react";
|
||||
import "./ConfirmDialog.css";
|
||||
|
||||
interface ConfirmDialogProps {
|
||||
isOpen: boolean;
|
||||
@@ -15,10 +15,10 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
|
||||
isOpen,
|
||||
title,
|
||||
message,
|
||||
confirmText = '确定',
|
||||
cancelText = '取消',
|
||||
confirmText = "确定",
|
||||
cancelText = "取消",
|
||||
onConfirm,
|
||||
onCancel
|
||||
onCancel,
|
||||
}) => {
|
||||
if (!isOpen) return null;
|
||||
|
||||
@@ -32,15 +32,15 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
|
||||
<p>{message}</p>
|
||||
</div>
|
||||
<div className="confirm-actions">
|
||||
<button
|
||||
className="confirm-btn cancel-btn"
|
||||
<button
|
||||
className="confirm-btn cancel-btn"
|
||||
onClick={onCancel}
|
||||
autoFocus
|
||||
>
|
||||
{cancelText}
|
||||
</button>
|
||||
<button
|
||||
className="confirm-btn confirm-btn-primary"
|
||||
<button
|
||||
className="confirm-btn confirm-btn-primary"
|
||||
onClick={onConfirm}
|
||||
>
|
||||
{confirmText}
|
||||
@@ -49,4 +49,4 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
import React from 'react'
|
||||
import { Provider } from '../types'
|
||||
import ProviderForm from './ProviderForm'
|
||||
import React from "react";
|
||||
import { Provider } from "../types";
|
||||
import ProviderForm from "./ProviderForm";
|
||||
|
||||
interface EditProviderModalProps {
|
||||
provider: Provider
|
||||
onSave: (provider: Provider) => void
|
||||
onClose: () => void
|
||||
provider: Provider;
|
||||
onSave: (provider: Provider) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const EditProviderModal: React.FC<EditProviderModalProps> = ({ provider, onSave, onClose }) => {
|
||||
const handleSubmit = (data: Omit<Provider, 'id'>) => {
|
||||
const EditProviderModal: React.FC<EditProviderModalProps> = ({
|
||||
provider,
|
||||
onSave,
|
||||
onClose,
|
||||
}) => {
|
||||
const handleSubmit = (data: Omit<Provider, "id">) => {
|
||||
onSave({
|
||||
...provider,
|
||||
...data
|
||||
})
|
||||
}
|
||||
...data,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<ProviderForm
|
||||
@@ -25,7 +29,7 @@ const EditProviderModal: React.FC<EditProviderModalProps> = ({ provider, onSave,
|
||||
onSubmit={handleSubmit}
|
||||
onClose={onClose}
|
||||
/>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default EditProviderModal
|
||||
export default EditProviderModal;
|
||||
|
||||
@@ -80,7 +80,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
};
|
||||
|
||||
const handleChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
const { name, value } = e.target;
|
||||
|
||||
@@ -117,7 +117,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
// 更新JSON配置
|
||||
const updatedConfig = updateCoAuthoredSetting(
|
||||
formData.settingsConfig,
|
||||
checked
|
||||
checked,
|
||||
);
|
||||
setFormData({
|
||||
...formData,
|
||||
@@ -152,7 +152,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
const configString = setApiKeyInConfig(
|
||||
formData.settingsConfig,
|
||||
key.trim(),
|
||||
{ createIfMissing: selectedPreset !== null }
|
||||
{ createIfMissing: selectedPreset !== null },
|
||||
);
|
||||
|
||||
// 更新表单配置
|
||||
@@ -174,7 +174,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
useEffect(() => {
|
||||
if (initialData) {
|
||||
const parsedKey = getApiKeyFromConfig(
|
||||
JSON.stringify(initialData.settingsConfig)
|
||||
JSON.stringify(initialData.settingsConfig),
|
||||
);
|
||||
if (parsedKey) setApiKey(parsedKey);
|
||||
}
|
||||
@@ -255,7 +255,9 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={`form-group api-key-group ${!showApiKey ? 'hidden' : ''}`}>
|
||||
<div
|
||||
className={`form-group api-key-group ${!showApiKey ? "hidden" : ""}`}
|
||||
>
|
||||
<label htmlFor="apiKey">API Key *</label>
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -203,4 +203,4 @@
|
||||
.delete-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React from 'react'
|
||||
import { Provider } from '../types'
|
||||
import './ProviderList.css'
|
||||
import React from "react";
|
||||
import { Provider } from "../types";
|
||||
import "./ProviderList.css";
|
||||
|
||||
interface ProviderListProps {
|
||||
providers: Record<string, Provider>
|
||||
currentProviderId: string
|
||||
onSwitch: (id: string) => void
|
||||
onDelete: (id: string) => void
|
||||
onEdit: (id: string) => void
|
||||
providers: Record<string, Provider>;
|
||||
currentProviderId: string;
|
||||
onSwitch: (id: string) => void;
|
||||
onDelete: (id: string) => void;
|
||||
onEdit: (id: string) => void;
|
||||
}
|
||||
|
||||
const ProviderList: React.FC<ProviderListProps> = ({
|
||||
@@ -15,28 +15,28 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
||||
currentProviderId,
|
||||
onSwitch,
|
||||
onDelete,
|
||||
onEdit
|
||||
onEdit,
|
||||
}) => {
|
||||
// 提取API地址
|
||||
const getApiUrl = (provider: Provider): string => {
|
||||
try {
|
||||
const config = provider.settingsConfig
|
||||
const config = provider.settingsConfig;
|
||||
if (config?.env?.ANTHROPIC_BASE_URL) {
|
||||
return config.env.ANTHROPIC_BASE_URL
|
||||
return config.env.ANTHROPIC_BASE_URL;
|
||||
}
|
||||
return '未设置'
|
||||
return "未设置";
|
||||
} catch {
|
||||
return '配置错误'
|
||||
return "配置错误";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleUrlClick = async (url: string) => {
|
||||
try {
|
||||
await window.api.openExternal(url)
|
||||
await window.api.openExternal(url);
|
||||
} catch (error) {
|
||||
console.error('打开链接失败:', error)
|
||||
console.error("打开链接失败:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="provider-list">
|
||||
@@ -48,25 +48,27 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
||||
) : (
|
||||
<div className="provider-items">
|
||||
{Object.values(providers).map((provider) => {
|
||||
const isCurrent = provider.id === currentProviderId
|
||||
|
||||
const isCurrent = provider.id === currentProviderId;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={provider.id}
|
||||
className={`provider-item ${isCurrent ? 'current' : ''}`}
|
||||
<div
|
||||
key={provider.id}
|
||||
className={`provider-item ${isCurrent ? "current" : ""}`}
|
||||
>
|
||||
<div className="provider-info">
|
||||
<div className="provider-name">
|
||||
<span>{provider.name}</span>
|
||||
{isCurrent && <span className="current-badge">当前使用</span>}
|
||||
{isCurrent && (
|
||||
<span className="current-badge">当前使用</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="provider-url">
|
||||
{provider.websiteUrl ? (
|
||||
<a
|
||||
href="#"
|
||||
<a
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
handleUrlClick(provider.websiteUrl!)
|
||||
e.preventDefault();
|
||||
handleUrlClick(provider.websiteUrl!);
|
||||
}}
|
||||
className="url-link"
|
||||
title={`访问 ${provider.websiteUrl}`}
|
||||
@@ -80,23 +82,23 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="provider-actions">
|
||||
<button
|
||||
<button
|
||||
className="enable-btn"
|
||||
onClick={() => onSwitch(provider.id)}
|
||||
disabled={isCurrent}
|
||||
>
|
||||
启用
|
||||
</button>
|
||||
<button
|
||||
<button
|
||||
className="edit-btn"
|
||||
onClick={() => onEdit(provider.id)}
|
||||
disabled={isCurrent}
|
||||
>
|
||||
编辑
|
||||
</button>
|
||||
<button
|
||||
<button
|
||||
className="delete-btn"
|
||||
onClick={() => onDelete(provider.id)}
|
||||
disabled={isCurrent}
|
||||
@@ -105,12 +107,12 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default ProviderList
|
||||
export default ProviderList;
|
||||
|
||||
@@ -63,5 +63,4 @@ export const providerPresets: ProviderPreset[] = [
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu",
|
||||
"Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { Provider } from '../types';
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { Provider } from "../types";
|
||||
|
||||
// 定义配置状态类型
|
||||
interface ConfigStatus {
|
||||
@@ -19,9 +19,9 @@ export const tauriAPI = {
|
||||
// 获取所有供应商
|
||||
getProviders: async (): Promise<Record<string, Provider>> => {
|
||||
try {
|
||||
return await invoke('get_providers');
|
||||
return await invoke("get_providers");
|
||||
} catch (error) {
|
||||
console.error('获取供应商列表失败:', error);
|
||||
console.error("获取供应商列表失败:", error);
|
||||
return {};
|
||||
}
|
||||
},
|
||||
@@ -29,19 +29,19 @@ export const tauriAPI = {
|
||||
// 获取当前供应商ID
|
||||
getCurrentProvider: async (): Promise<string> => {
|
||||
try {
|
||||
return await invoke('get_current_provider');
|
||||
return await invoke("get_current_provider");
|
||||
} catch (error) {
|
||||
console.error('获取当前供应商失败:', error);
|
||||
return '';
|
||||
console.error("获取当前供应商失败:", error);
|
||||
return "";
|
||||
}
|
||||
},
|
||||
|
||||
// 添加供应商
|
||||
addProvider: async (provider: Provider): Promise<boolean> => {
|
||||
try {
|
||||
return await invoke('add_provider', { provider });
|
||||
return await invoke("add_provider", { provider });
|
||||
} catch (error) {
|
||||
console.error('添加供应商失败:', error);
|
||||
console.error("添加供应商失败:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@@ -49,9 +49,9 @@ export const tauriAPI = {
|
||||
// 更新供应商
|
||||
updateProvider: async (provider: Provider): Promise<boolean> => {
|
||||
try {
|
||||
return await invoke('update_provider', { provider });
|
||||
return await invoke("update_provider", { provider });
|
||||
} catch (error) {
|
||||
console.error('更新供应商失败:', error);
|
||||
console.error("更新供应商失败:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@@ -59,9 +59,9 @@ export const tauriAPI = {
|
||||
// 删除供应商
|
||||
deleteProvider: async (id: string): Promise<boolean> => {
|
||||
try {
|
||||
return await invoke('delete_provider', { id });
|
||||
return await invoke("delete_provider", { id });
|
||||
} catch (error) {
|
||||
console.error('删除供应商失败:', error);
|
||||
console.error("删除供应商失败:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@@ -69,9 +69,9 @@ export const tauriAPI = {
|
||||
// 切换供应商
|
||||
switchProvider: async (providerId: string): Promise<boolean> => {
|
||||
try {
|
||||
return await invoke('switch_provider', { id: providerId });
|
||||
return await invoke("switch_provider", { id: providerId });
|
||||
} catch (error) {
|
||||
console.error('切换供应商失败:', error);
|
||||
console.error("切换供应商失败:", error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
@@ -79,16 +79,16 @@ export const tauriAPI = {
|
||||
// 导入当前配置为默认供应商
|
||||
importCurrentConfigAsDefault: async (): Promise<ImportResult> => {
|
||||
try {
|
||||
const success = await invoke<boolean>('import_default_config');
|
||||
const success = await invoke<boolean>("import_default_config");
|
||||
return {
|
||||
success,
|
||||
message: success ? '成功导入默认配置' : '导入失败'
|
||||
message: success ? "成功导入默认配置" : "导入失败",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('导入默认配置失败:', error);
|
||||
console.error("导入默认配置失败:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: String(error)
|
||||
message: String(error),
|
||||
};
|
||||
}
|
||||
},
|
||||
@@ -96,23 +96,23 @@ export const tauriAPI = {
|
||||
// 获取 Claude Code 配置文件路径
|
||||
getClaudeCodeConfigPath: async (): Promise<string> => {
|
||||
try {
|
||||
return await invoke('get_claude_code_config_path');
|
||||
return await invoke("get_claude_code_config_path");
|
||||
} catch (error) {
|
||||
console.error('获取配置路径失败:', error);
|
||||
return '';
|
||||
console.error("获取配置路径失败:", error);
|
||||
return "";
|
||||
}
|
||||
},
|
||||
|
||||
// 获取 Claude Code 配置状态
|
||||
getClaudeConfigStatus: async (): Promise<ConfigStatus> => {
|
||||
try {
|
||||
return await invoke('get_claude_config_status');
|
||||
return await invoke("get_claude_config_status");
|
||||
} catch (error) {
|
||||
console.error('获取配置状态失败:', error);
|
||||
console.error("获取配置状态失败:", error);
|
||||
return {
|
||||
exists: false,
|
||||
path: '',
|
||||
error: String(error)
|
||||
path: "",
|
||||
error: String(error),
|
||||
};
|
||||
}
|
||||
},
|
||||
@@ -120,34 +120,33 @@ export const tauriAPI = {
|
||||
// 打开配置文件夹
|
||||
openConfigFolder: async (): Promise<void> => {
|
||||
try {
|
||||
await invoke('open_config_folder');
|
||||
await invoke("open_config_folder");
|
||||
} catch (error) {
|
||||
console.error('打开配置文件夹失败:', error);
|
||||
console.error("打开配置文件夹失败:", error);
|
||||
}
|
||||
},
|
||||
|
||||
// 打开外部链接
|
||||
openExternal: async (url: string): Promise<void> => {
|
||||
try {
|
||||
await invoke('open_external', { url });
|
||||
await invoke("open_external", { url });
|
||||
} catch (error) {
|
||||
console.error('打开外部链接失败:', error);
|
||||
console.error("打开外部链接失败:", error);
|
||||
}
|
||||
},
|
||||
|
||||
// 选择配置文件(Tauri 暂不实现,保留接口兼容性)
|
||||
selectConfigFile: async (): Promise<string | null> => {
|
||||
console.warn('selectConfigFile 在 Tauri 版本中暂不支持');
|
||||
console.warn("selectConfigFile 在 Tauri 版本中暂不支持");
|
||||
return null;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// 创建全局 API 对象,兼容现有代码
|
||||
if (typeof window !== 'undefined') {
|
||||
if (typeof window !== "undefined") {
|
||||
// 绑定到 window.api,避免 Electron 命名造成误解
|
||||
// API 内部已做 try/catch,非 Tauri 环境下也会安全返回默认值
|
||||
(window as any).api = tauriAPI;
|
||||
|
||||
}
|
||||
|
||||
export default tauriAPI;
|
||||
|
||||
24
src/main.tsx
24
src/main.tsx
@@ -1,24 +1,24 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
import './index.css'
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App";
|
||||
import "./index.css";
|
||||
// 导入 Tauri API(自动绑定到 window.api)
|
||||
import './lib/tauri-api'
|
||||
import "./lib/tauri-api";
|
||||
|
||||
// 根据平台添加 body class,便于平台特定样式
|
||||
try {
|
||||
const ua = navigator.userAgent || ''
|
||||
const plat = (navigator.platform || '').toLowerCase()
|
||||
const isMac = /mac/i.test(ua) || plat.includes('mac')
|
||||
const ua = navigator.userAgent || "";
|
||||
const plat = (navigator.platform || "").toLowerCase();
|
||||
const isMac = /mac/i.test(ua) || plat.includes("mac");
|
||||
if (isMac) {
|
||||
document.body.classList.add('is-mac')
|
||||
document.body.classList.add("is-mac");
|
||||
}
|
||||
} catch {
|
||||
// 忽略平台检测失败
|
||||
}
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
)
|
||||
</React.StrictMode>,
|
||||
);
|
||||
|
||||
@@ -1,91 +1,97 @@
|
||||
// 供应商配置处理工具函数
|
||||
|
||||
// 处理includeCoAuthoredBy字段的添加/删除
|
||||
export const updateCoAuthoredSetting = (jsonString: string, disable: boolean): string => {
|
||||
export const updateCoAuthoredSetting = (
|
||||
jsonString: string,
|
||||
disable: boolean,
|
||||
): string => {
|
||||
try {
|
||||
const config = JSON.parse(jsonString)
|
||||
|
||||
const config = JSON.parse(jsonString);
|
||||
|
||||
if (disable) {
|
||||
// 添加或更新includeCoAuthoredBy字段
|
||||
config.includeCoAuthoredBy = false
|
||||
config.includeCoAuthoredBy = false;
|
||||
} else {
|
||||
// 删除includeCoAuthoredBy字段
|
||||
delete config.includeCoAuthoredBy
|
||||
delete config.includeCoAuthoredBy;
|
||||
}
|
||||
|
||||
return JSON.stringify(config, null, 2)
|
||||
|
||||
return JSON.stringify(config, null, 2);
|
||||
} catch (err) {
|
||||
// 如果JSON解析失败,返回原始字符串
|
||||
return jsonString
|
||||
return jsonString;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 从JSON配置中检查是否包含includeCoAuthoredBy设置
|
||||
export const checkCoAuthoredSetting = (jsonString: string): boolean => {
|
||||
try {
|
||||
const config = JSON.parse(jsonString)
|
||||
return config.includeCoAuthoredBy === false
|
||||
const config = JSON.parse(jsonString);
|
||||
return config.includeCoAuthoredBy === false;
|
||||
} catch (err) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 从JSON配置中提取并处理官网地址
|
||||
export const extractWebsiteUrl = (jsonString: string): string => {
|
||||
try {
|
||||
const config = JSON.parse(jsonString)
|
||||
const baseUrl = config?.env?.ANTHROPIC_BASE_URL
|
||||
|
||||
if (baseUrl && typeof baseUrl === 'string') {
|
||||
const config = JSON.parse(jsonString);
|
||||
const baseUrl = config?.env?.ANTHROPIC_BASE_URL;
|
||||
|
||||
if (baseUrl && typeof baseUrl === "string") {
|
||||
// 去掉 "api." 前缀
|
||||
return baseUrl.replace(/^https?:\/\/api\./, 'https://')
|
||||
return baseUrl.replace(/^https?:\/\/api\./, "https://");
|
||||
}
|
||||
} catch (err) {
|
||||
// 忽略JSON解析错误
|
||||
}
|
||||
return ''
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
// 读取配置中的 API Key(env.ANTHROPIC_AUTH_TOKEN)
|
||||
export const getApiKeyFromConfig = (jsonString: string): string => {
|
||||
try {
|
||||
const config = JSON.parse(jsonString)
|
||||
const key = config?.env?.ANTHROPIC_AUTH_TOKEN
|
||||
return typeof key === 'string' ? key : ''
|
||||
const config = JSON.parse(jsonString);
|
||||
const key = config?.env?.ANTHROPIC_AUTH_TOKEN;
|
||||
return typeof key === "string" ? key : "";
|
||||
} catch (err) {
|
||||
return ''
|
||||
return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 判断配置中是否存在 API Key 字段
|
||||
export const hasApiKeyField = (jsonString: string): boolean => {
|
||||
try {
|
||||
const config = JSON.parse(jsonString)
|
||||
return Object.prototype.hasOwnProperty.call(config?.env ?? {}, 'ANTHROPIC_AUTH_TOKEN')
|
||||
const config = JSON.parse(jsonString);
|
||||
return Object.prototype.hasOwnProperty.call(
|
||||
config?.env ?? {},
|
||||
"ANTHROPIC_AUTH_TOKEN",
|
||||
);
|
||||
} catch (err) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 写入/更新配置中的 API Key,默认不新增缺失字段
|
||||
export const setApiKeyInConfig = (
|
||||
jsonString: string,
|
||||
apiKey: string,
|
||||
options: { createIfMissing?: boolean } = {}
|
||||
options: { createIfMissing?: boolean } = {},
|
||||
): string => {
|
||||
const { createIfMissing = false } = options
|
||||
const { createIfMissing = false } = options;
|
||||
try {
|
||||
const config = JSON.parse(jsonString)
|
||||
const config = JSON.parse(jsonString);
|
||||
if (!config.env) {
|
||||
if (!createIfMissing) return jsonString
|
||||
config.env = {}
|
||||
if (!createIfMissing) return jsonString;
|
||||
config.env = {};
|
||||
}
|
||||
if (!('ANTHROPIC_AUTH_TOKEN' in config.env) && !createIfMissing) {
|
||||
return jsonString
|
||||
if (!("ANTHROPIC_AUTH_TOKEN" in config.env) && !createIfMissing) {
|
||||
return jsonString;
|
||||
}
|
||||
config.env.ANTHROPIC_AUTH_TOKEN = apiKey
|
||||
return JSON.stringify(config, null, 2)
|
||||
config.env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
||||
return JSON.stringify(config, null, 2);
|
||||
} catch (err) {
|
||||
return jsonString
|
||||
return jsonString;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
2
src/vite-env.d.ts
vendored
2
src/vite-env.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
import { Provider } from './types';
|
||||
import { Provider } from "./types";
|
||||
|
||||
interface ImportResult {
|
||||
success: boolean;
|
||||
|
||||
Reference in New Issue
Block a user