refactor(types): introduce Settings and apply in API
- style(prettier): format src files - style(rustfmt): format Rust sources - refactor(tauri-api): type-safe getSettings/saveSettings - refactor(d.ts): declare window.api with Settings [skip ci]
This commit is contained in:
@@ -4,9 +4,9 @@ use std::path::PathBuf;
|
||||
use crate::config::{
|
||||
atomic_write, delete_file, sanitize_provider_name, write_json_file, write_text_file,
|
||||
};
|
||||
use serde_json::Value;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use serde_json::Value;
|
||||
|
||||
/// 获取 Codex 配置目录路径
|
||||
pub fn get_codex_config_dir() -> PathBuf {
|
||||
@@ -77,7 +77,8 @@ pub fn write_codex_live_atomic(auth: &Value, config_text_opt: Option<&str>) -> R
|
||||
None => String::new(),
|
||||
};
|
||||
if !cfg_text.trim().is_empty() {
|
||||
toml::from_str::<toml::Table>(&cfg_text).map_err(|e| format!("config.toml 格式错误: {}", e))?;
|
||||
toml::from_str::<toml::Table>(&cfg_text)
|
||||
.map_err(|e| format!("config.toml 格式错误: {}", e))?;
|
||||
}
|
||||
|
||||
// 第一步:写 auth.json
|
||||
@@ -121,7 +122,9 @@ pub fn validate_config_toml(text: &str) -> Result<(), String> {
|
||||
if text.trim().is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
toml::from_str::<toml::Table>(text).map(|_| ()).map_err(|e| format!("config.toml 语法错误: {}", e))
|
||||
toml::from_str::<toml::Table>(text)
|
||||
.map(|_| ())
|
||||
.map_err(|e| format!("config.toml 语法错误: {}", e))
|
||||
}
|
||||
|
||||
/// 读取并校验 `~/.codex/config.toml`,返回文本(可能为空)
|
||||
|
||||
@@ -6,7 +6,7 @@ use tauri_plugin_opener::OpenerExt;
|
||||
|
||||
use crate::app_config::AppType;
|
||||
use crate::codex_config;
|
||||
use crate::config::{ConfigStatus, get_claude_settings_path};
|
||||
use crate::config::{get_claude_settings_path, ConfigStatus};
|
||||
use crate::provider::Provider;
|
||||
use crate::store::AppState;
|
||||
|
||||
@@ -116,7 +116,9 @@ pub async fn add_provider(
|
||||
let manager = config
|
||||
.get_manager_mut(&app_type)
|
||||
.ok_or_else(|| format!("应用类型不存在: {:?}", app_type))?;
|
||||
manager.providers.insert(provider.id.clone(), provider.clone());
|
||||
manager
|
||||
.providers
|
||||
.insert(provider.id.clone(), provider.clone());
|
||||
}
|
||||
state.save()?;
|
||||
|
||||
@@ -146,7 +148,10 @@ pub async fn update_provider(
|
||||
let manager = config
|
||||
.get_manager(&app_type)
|
||||
.ok_or_else(|| format!("应用类型不存在: {:?}", app_type))?;
|
||||
(manager.providers.contains_key(&provider.id), manager.current == provider.id)
|
||||
(
|
||||
manager.providers.contains_key(&provider.id),
|
||||
manager.current == provider.id,
|
||||
)
|
||||
};
|
||||
if !exists {
|
||||
return Err(format!("供应商不存在: {}", provider.id));
|
||||
@@ -182,7 +187,9 @@ pub async fn update_provider(
|
||||
let manager = config
|
||||
.get_manager_mut(&app_type)
|
||||
.ok_or_else(|| format!("应用类型不存在: {:?}", app_type))?;
|
||||
manager.providers.insert(provider.id.clone(), provider.clone());
|
||||
manager
|
||||
.providers
|
||||
.insert(provider.id.clone(), provider.clone());
|
||||
}
|
||||
state.save()?;
|
||||
|
||||
@@ -390,7 +397,8 @@ pub async fn import_default_config(
|
||||
if !auth_path.exists() {
|
||||
return Err("Codex 配置文件不存在".to_string());
|
||||
}
|
||||
let auth: serde_json::Value = crate::config::read_json_file::<serde_json::Value>(&auth_path)?;
|
||||
let auth: serde_json::Value =
|
||||
crate::config::read_json_file::<serde_json::Value>(&auth_path)?;
|
||||
let config_str = match crate::codex_config::read_and_validate_codex_config_text() {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Err(e),
|
||||
@@ -488,7 +496,7 @@ pub async fn open_config_folder(
|
||||
.or_else(|| app.as_deref().map(|s| s.into()))
|
||||
.or_else(|| appType.as_deref().map(|s| s.into()))
|
||||
.unwrap_or(AppType::Claude);
|
||||
|
||||
|
||||
let config_dir = match app_type {
|
||||
AppType::Claude => crate::config::get_claude_config_dir(),
|
||||
AppType::Codex => crate::codex_config::get_codex_config_dir(),
|
||||
@@ -500,7 +508,8 @@ pub async fn open_config_folder(
|
||||
}
|
||||
|
||||
// 使用 opener 插件打开文件夹
|
||||
handle.opener()
|
||||
handle
|
||||
.opener()
|
||||
.open_path(config_dir.to_string_lossy().to_string(), None::<String>)
|
||||
.map_err(|e| format!("打开文件夹失败: {}", e))?;
|
||||
|
||||
@@ -524,3 +533,68 @@ pub async fn open_external(app: tauri::AppHandle, url: String) -> Result<bool, S
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// 获取应用配置文件路径
|
||||
#[tauri::command]
|
||||
pub async fn get_app_config_path() -> Result<String, String> {
|
||||
use crate::config::get_app_config_path;
|
||||
|
||||
let config_path = get_app_config_path();
|
||||
Ok(config_path.to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
/// 打开应用配置文件夹
|
||||
#[tauri::command]
|
||||
pub async fn open_app_config_folder(handle: tauri::AppHandle) -> Result<bool, String> {
|
||||
use crate::config::get_app_config_dir;
|
||||
|
||||
let config_dir = get_app_config_dir();
|
||||
|
||||
// 确保目录存在
|
||||
if !config_dir.exists() {
|
||||
std::fs::create_dir_all(&config_dir).map_err(|e| format!("创建目录失败: {}", e))?;
|
||||
}
|
||||
|
||||
// 使用 opener 插件打开文件夹
|
||||
handle
|
||||
.opener()
|
||||
.open_path(config_dir.to_string_lossy().to_string(), None::<String>)
|
||||
.map_err(|e| format!("打开文件夹失败: {}", e))?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// 获取设置
|
||||
#[tauri::command]
|
||||
pub async fn get_settings(_state: State<'_, AppState>) -> Result<serde_json::Value, String> {
|
||||
// 暂时返回默认设置
|
||||
Ok(serde_json::json!({
|
||||
"showInDock": true
|
||||
}))
|
||||
}
|
||||
|
||||
/// 保存设置
|
||||
#[tauri::command]
|
||||
pub async fn save_settings(
|
||||
_state: State<'_, AppState>,
|
||||
settings: serde_json::Value,
|
||||
) -> Result<bool, String> {
|
||||
// TODO: 实现设置保存逻辑
|
||||
log::info!("保存设置: {:?}", settings);
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// 检查更新
|
||||
#[tauri::command]
|
||||
pub async fn check_for_updates(handle: tauri::AppHandle) -> Result<bool, String> {
|
||||
// 打开 GitHub releases 页面
|
||||
handle
|
||||
.opener()
|
||||
.open_url(
|
||||
"https://github.com/yungookim/cc-switch/releases",
|
||||
None::<String>,
|
||||
)
|
||||
.map_err(|e| format!("打开更新页面失败: {}", e))?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
@@ -88,7 +88,6 @@ pub fn archive_file(ts: u64, category: &str, src: &Path) -> Result<Option<PathBu
|
||||
Ok(Some(dest))
|
||||
}
|
||||
|
||||
|
||||
/// 清理供应商名称,确保文件名安全
|
||||
pub fn sanitize_provider_name(name: &str) -> String {
|
||||
name.chars()
|
||||
|
||||
@@ -326,6 +326,11 @@ pub fn run() {
|
||||
commands::get_claude_code_config_path,
|
||||
commands::open_config_folder,
|
||||
commands::open_external,
|
||||
commands::get_app_config_path,
|
||||
commands::open_app_config_folder,
|
||||
commands::get_settings,
|
||||
commands::save_settings,
|
||||
commands::check_for_updates,
|
||||
update_tray_menu,
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
|
||||
@@ -47,7 +47,10 @@ fn extract_claude_api_key(value: &Value) -> Option<String> {
|
||||
fn extract_codex_api_key(value: &Value) -> Option<String> {
|
||||
value
|
||||
.get("auth")
|
||||
.and_then(|auth| auth.get("OPENAI_API_KEY").or_else(|| auth.get("openai_api_key")))
|
||||
.and_then(|auth| {
|
||||
auth.get("OPENAI_API_KEY")
|
||||
.or_else(|| auth.get("openai_api_key"))
|
||||
})
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string())
|
||||
}
|
||||
@@ -77,7 +80,9 @@ fn scan_claude_copies() -> Vec<(String, PathBuf, Value)> {
|
||||
if !fname.starts_with("settings-") || !fname.ends_with(".json") {
|
||||
continue;
|
||||
}
|
||||
let name = fname.trim_start_matches("settings-").trim_end_matches(".json");
|
||||
let name = fname
|
||||
.trim_start_matches("settings-")
|
||||
.trim_end_matches(".json");
|
||||
if let Ok(val) = crate::config::read_json_file::<Value>(&p) {
|
||||
items.push((name.to_string(), p, val));
|
||||
}
|
||||
@@ -104,7 +109,9 @@ fn scan_codex_copies() -> Vec<(String, Option<PathBuf>, Option<PathBuf>, Value)>
|
||||
let entry = by_name.entry(name.to_string()).or_default();
|
||||
entry.0 = Some(p);
|
||||
} else if fname.starts_with("config-") && fname.ends_with(".toml") {
|
||||
let name = fname.trim_start_matches("config-").trim_end_matches(".toml");
|
||||
let name = fname
|
||||
.trim_start_matches("config-")
|
||||
.trim_end_matches(".toml");
|
||||
let entry = by_name.entry(name.to_string()).or_default();
|
||||
entry.1 = Some(p);
|
||||
}
|
||||
@@ -183,17 +190,14 @@ pub fn migrate_copies_into_config(config: &mut MultiAppConfig) -> Result<bool, S
|
||||
|
||||
if let Some((name, value)) = &live_claude {
|
||||
let cand_key = extract_claude_api_key(value);
|
||||
let exist_id = manager
|
||||
.providers
|
||||
.iter()
|
||||
.find_map(|(id, p)| {
|
||||
let pk = extract_claude_api_key(&p.settings_config);
|
||||
if norm_name(&p.name) == norm_name(name) && pk == cand_key {
|
||||
Some(id.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let exist_id = manager.providers.iter().find_map(|(id, p)| {
|
||||
let pk = extract_claude_api_key(&p.settings_config);
|
||||
if norm_name(&p.name) == norm_name(name) && pk == cand_key {
|
||||
Some(id.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if let Some(exist_id) = exist_id {
|
||||
if let Some(prov) = manager.providers.get_mut(&exist_id) {
|
||||
log::info!("合并到已存在 Claude 供应商 '{}' (by name+key)", name);
|
||||
@@ -203,43 +207,36 @@ pub fn migrate_copies_into_config(config: &mut MultiAppConfig) -> Result<bool, S
|
||||
} else {
|
||||
let id = next_unique_id(&ids, name);
|
||||
ids.insert(id.clone());
|
||||
let provider = crate::provider::Provider::with_id(
|
||||
id.clone(),
|
||||
name.clone(),
|
||||
value.clone(),
|
||||
None,
|
||||
);
|
||||
let provider =
|
||||
crate::provider::Provider::with_id(id.clone(), name.clone(), value.clone(), None);
|
||||
manager.providers.insert(provider.id.clone(), provider);
|
||||
live_claude_id = Some(id);
|
||||
}
|
||||
}
|
||||
for (name, path, value) in claude_items.iter() {
|
||||
let cand_key = extract_claude_api_key(value);
|
||||
let exist_id = manager
|
||||
.providers
|
||||
.iter()
|
||||
.find_map(|(id, p)| {
|
||||
let pk = extract_claude_api_key(&p.settings_config);
|
||||
if norm_name(&p.name) == norm_name(name) && pk == cand_key {
|
||||
Some(id.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let exist_id = manager.providers.iter().find_map(|(id, p)| {
|
||||
let pk = extract_claude_api_key(&p.settings_config);
|
||||
if norm_name(&p.name) == norm_name(name) && pk == cand_key {
|
||||
Some(id.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if let Some(exist_id) = exist_id {
|
||||
if let Some(prov) = manager.providers.get_mut(&exist_id) {
|
||||
log::info!("覆盖 Claude 供应商 '{}' 来自 {} (by name+key)", name, path.display());
|
||||
log::info!(
|
||||
"覆盖 Claude 供应商 '{}' 来自 {} (by name+key)",
|
||||
name,
|
||||
path.display()
|
||||
);
|
||||
prov.settings_config = value.clone();
|
||||
}
|
||||
} else {
|
||||
let id = next_unique_id(&ids, name);
|
||||
ids.insert(id.clone());
|
||||
let provider = crate::provider::Provider::with_id(
|
||||
id.clone(),
|
||||
name.clone(),
|
||||
value.clone(),
|
||||
None,
|
||||
);
|
||||
let provider =
|
||||
crate::provider::Provider::with_id(id.clone(), name.clone(), value.clone(), None);
|
||||
manager.providers.insert(provider.id.clone(), provider);
|
||||
}
|
||||
}
|
||||
@@ -257,7 +254,10 @@ pub fn migrate_copies_into_config(config: &mut MultiAppConfig) -> Result<bool, S
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
Some(("default".to_string(), serde_json::json!({"auth": auth, "config": cfg})))
|
||||
Some((
|
||||
"default".to_string(),
|
||||
serde_json::json!({"auth": auth, "config": cfg}),
|
||||
))
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("读取 Codex live auth.json 失败: {}", e);
|
||||
@@ -277,17 +277,14 @@ pub fn migrate_copies_into_config(config: &mut MultiAppConfig) -> Result<bool, S
|
||||
|
||||
if let Some((name, value)) = &live_codex {
|
||||
let cand_key = extract_codex_api_key(value);
|
||||
let exist_id = manager
|
||||
.providers
|
||||
.iter()
|
||||
.find_map(|(id, p)| {
|
||||
let pk = extract_codex_api_key(&p.settings_config);
|
||||
if norm_name(&p.name) == norm_name(name) && pk == cand_key {
|
||||
Some(id.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let exist_id = manager.providers.iter().find_map(|(id, p)| {
|
||||
let pk = extract_codex_api_key(&p.settings_config);
|
||||
if norm_name(&p.name) == norm_name(name) && pk == cand_key {
|
||||
Some(id.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if let Some(exist_id) = exist_id {
|
||||
if let Some(prov) = manager.providers.get_mut(&exist_id) {
|
||||
log::info!("合并到已存在 Codex 供应商 '{}' (by name+key)", name);
|
||||
@@ -297,43 +294,37 @@ pub fn migrate_copies_into_config(config: &mut MultiAppConfig) -> Result<bool, S
|
||||
} else {
|
||||
let id = next_unique_id(&ids, name);
|
||||
ids.insert(id.clone());
|
||||
let provider = crate::provider::Provider::with_id(
|
||||
id.clone(),
|
||||
name.clone(),
|
||||
value.clone(),
|
||||
None,
|
||||
);
|
||||
let provider =
|
||||
crate::provider::Provider::with_id(id.clone(), name.clone(), value.clone(), None);
|
||||
manager.providers.insert(provider.id.clone(), provider);
|
||||
live_codex_id = Some(id);
|
||||
}
|
||||
}
|
||||
for (name, authp, cfgp, value) in codex_items.iter() {
|
||||
let cand_key = extract_codex_api_key(value);
|
||||
let exist_id = manager
|
||||
.providers
|
||||
.iter()
|
||||
.find_map(|(id, p)| {
|
||||
let pk = extract_codex_api_key(&p.settings_config);
|
||||
if norm_name(&p.name) == norm_name(name) && pk == cand_key {
|
||||
Some(id.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let exist_id = manager.providers.iter().find_map(|(id, p)| {
|
||||
let pk = extract_codex_api_key(&p.settings_config);
|
||||
if norm_name(&p.name) == norm_name(name) && pk == cand_key {
|
||||
Some(id.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if let Some(exist_id) = exist_id {
|
||||
if let Some(prov) = manager.providers.get_mut(&exist_id) {
|
||||
log::info!("覆盖 Codex 供应商 '{}' 来自 {:?}/{:?} (by name+key)", name, authp, cfgp);
|
||||
log::info!(
|
||||
"覆盖 Codex 供应商 '{}' 来自 {:?}/{:?} (by name+key)",
|
||||
name,
|
||||
authp,
|
||||
cfgp
|
||||
);
|
||||
prov.settings_config = value.clone();
|
||||
}
|
||||
} else {
|
||||
let id = next_unique_id(&ids, name);
|
||||
ids.insert(id.clone());
|
||||
let provider = crate::provider::Provider::with_id(
|
||||
id.clone(),
|
||||
name.clone(),
|
||||
value.clone(),
|
||||
None,
|
||||
);
|
||||
let provider =
|
||||
crate::provider::Provider::with_id(id.clone(), name.clone(), value.clone(), None);
|
||||
manager.providers.insert(provider.id.clone(), provider);
|
||||
}
|
||||
}
|
||||
@@ -370,13 +361,17 @@ pub fn migrate_copies_into_config(config: &mut MultiAppConfig) -> Result<bool, S
|
||||
for (_, ap, cp, _) in codex_items.into_iter() {
|
||||
if let Some(ap) = ap {
|
||||
match archive_file(ts, "codex", &ap) {
|
||||
Ok(Some(_)) => { let _ = delete_file(&ap); }
|
||||
Ok(Some(_)) => {
|
||||
let _ = delete_file(&ap);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if let Some(cp) = cp {
|
||||
match archive_file(ts, "codex", &cp) {
|
||||
Ok(Some(_)) => { let _ = delete_file(&cp); }
|
||||
Ok(Some(_)) => {
|
||||
let _ = delete_file(&cp);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -404,7 +399,11 @@ pub fn dedupe_config(config: &mut MultiAppConfig) -> usize {
|
||||
let mut keep: Map<String, String> = Map::new(); // key -> id 保留
|
||||
let mut remove: Vec<String> = Vec::new();
|
||||
for (id, p) in mgr.providers.iter() {
|
||||
let k = format!("{}|{}", norm_name(&p.name), extract_key(&p.settings_config).unwrap_or_default());
|
||||
let k = format!(
|
||||
"{}|{}",
|
||||
norm_name(&p.name),
|
||||
extract_key(&p.settings_config).unwrap_or_default()
|
||||
);
|
||||
if let Some(exist_id) = keep.get(&k) {
|
||||
// 若当前是正在使用的,则用当前替换之前的,反之丢弃当前
|
||||
if *id == mgr.current {
|
||||
|
||||
@@ -330,9 +330,7 @@ function App() {
|
||||
)}
|
||||
|
||||
{isSettingsOpen && (
|
||||
<SettingsModal
|
||||
onClose={() => setIsSettingsOpen(false)}
|
||||
/>
|
||||
<SettingsModal onClose={() => setIsSettingsOpen(false)} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -100,7 +100,10 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
||||
{provider.websiteUrl}
|
||||
</button>
|
||||
) : (
|
||||
<span className="text-[var(--color-text-secondary)]" title={apiUrl}>
|
||||
<span
|
||||
className="text-[var(--color-text-secondary)]"
|
||||
title={apiUrl}
|
||||
>
|
||||
{apiUrl}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { X, Info, RefreshCw, FolderOpen } from "lucide-react";
|
||||
import "../lib/tauri-api";
|
||||
|
||||
interface Settings {
|
||||
showInDock: boolean;
|
||||
}
|
||||
import type { Settings } from "../types";
|
||||
|
||||
interface SettingsModalProps {
|
||||
onClose: () => void;
|
||||
@@ -36,9 +33,9 @@ export default function SettingsModal({ onClose }: SettingsModalProps) {
|
||||
|
||||
const loadConfigPath = async () => {
|
||||
try {
|
||||
const status = await window.api.getConfigStatus("claude");
|
||||
if (status?.path) {
|
||||
setConfigPath(status.path.replace("/claude_code_config.json", ""));
|
||||
const path = await window.api.getAppConfigPath();
|
||||
if (path) {
|
||||
setConfigPath(path);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取配置路径失败:", error);
|
||||
@@ -67,7 +64,7 @@ export default function SettingsModal({ onClose }: SettingsModalProps) {
|
||||
|
||||
const handleOpenConfigFolder = async () => {
|
||||
try {
|
||||
await window.api.openConfigFolder("claude");
|
||||
await window.api.openAppConfigFolder();
|
||||
} catch (error) {
|
||||
console.error("打开配置文件夹失败:", error);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { listen, UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { Provider } from "../types";
|
||||
import { Provider, Settings } from "../types";
|
||||
|
||||
// 应用类型
|
||||
export type AppType = "claude" | "codex";
|
||||
@@ -196,7 +196,7 @@ export const tauriAPI = {
|
||||
},
|
||||
|
||||
// 获取设置
|
||||
getSettings: async (): Promise<any> => {
|
||||
getSettings: async (): Promise<Settings> => {
|
||||
try {
|
||||
return await invoke("get_settings");
|
||||
} catch (error) {
|
||||
@@ -206,7 +206,7 @@ export const tauriAPI = {
|
||||
},
|
||||
|
||||
// 保存设置
|
||||
saveSettings: async (settings: any): Promise<boolean> => {
|
||||
saveSettings: async (settings: Settings): Promise<boolean> => {
|
||||
try {
|
||||
return await invoke("save_settings", { settings });
|
||||
} catch (error) {
|
||||
@@ -223,6 +223,25 @@ export const tauriAPI = {
|
||||
console.error("检查更新失败:", error);
|
||||
}
|
||||
},
|
||||
|
||||
// 获取应用配置文件路径
|
||||
getAppConfigPath: async (): Promise<string> => {
|
||||
try {
|
||||
return await invoke("get_app_config_path");
|
||||
} catch (error) {
|
||||
console.error("获取应用配置路径失败:", error);
|
||||
return "";
|
||||
}
|
||||
},
|
||||
|
||||
// 打开应用配置文件夹
|
||||
openAppConfigFolder: async (): Promise<void> => {
|
||||
try {
|
||||
await invoke("open_app_config_folder");
|
||||
} catch (error) {
|
||||
console.error("打开应用配置文件夹失败:", error);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// 创建全局 API 对象,兼容现有代码
|
||||
|
||||
@@ -9,3 +9,8 @@ export interface AppConfig {
|
||||
providers: Record<string, Provider>;
|
||||
current: string;
|
||||
}
|
||||
|
||||
// 应用设置类型(用于 SettingsModal 与 Tauri API)
|
||||
export interface Settings {
|
||||
showInDock: boolean;
|
||||
}
|
||||
|
||||
8
src/vite-env.d.ts
vendored
8
src/vite-env.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
import { Provider } from "./types";
|
||||
import { Provider, Settings } from "./types";
|
||||
import { AppType } from "./lib/tauri-api";
|
||||
import type { UnlistenFn } from "@tauri-apps/api/event";
|
||||
|
||||
@@ -35,9 +35,11 @@ declare global {
|
||||
onProviderSwitched: (
|
||||
callback: (data: { appType: string; providerId: string }) => void,
|
||||
) => Promise<UnlistenFn>;
|
||||
getSettings: () => Promise<any>;
|
||||
saveSettings: (settings: any) => Promise<boolean>;
|
||||
getSettings: () => Promise<Settings>;
|
||||
saveSettings: (settings: Settings) => Promise<boolean>;
|
||||
checkForUpdates: () => Promise<void>;
|
||||
getAppConfigPath: () => Promise<string>;
|
||||
openAppConfigFolder: () => Promise<void>;
|
||||
};
|
||||
platform: {
|
||||
isMac: boolean;
|
||||
|
||||
Reference in New Issue
Block a user