Remove deprecated VS Code Codex integration
This commit is contained in:
@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### ✨ Features
|
### ✨ Features
|
||||||
- Enable internationalization via i18next with a Chinese default and English fallback, plus an in-app language switcher
|
- Enable internationalization via i18next with a Chinese default and English fallback, plus an in-app language switcher
|
||||||
- Add Claude plugin sync alongside the existing VS Code integration controls
|
- Add Claude plugin sync while retiring the legacy VS Code integration controls (Codex no longer requires settings.json edits)
|
||||||
- Extend provider presets with optional API key URLs and updated models, including DeepSeek-V3.1-Terminus and Qwen3-Max
|
- Extend provider presets with optional API key URLs and updated models, including DeepSeek-V3.1-Terminus and Qwen3-Max
|
||||||
- Support portable mode launches and enforce a single running instance to avoid conflicts
|
- Support portable mode launches and enforce a single running instance to avoid conflicts
|
||||||
|
|
||||||
@@ -22,13 +22,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### 🐛 Fixes
|
### 🐛 Fixes
|
||||||
- Remove the unnecessary OpenAI auth requirement from third-party provider configurations
|
- Remove the unnecessary OpenAI auth requirement from third-party provider configurations
|
||||||
- Fix layout shifts while switching app types with Claude plugin sync enabled
|
- Fix layout shifts while switching app types with Claude plugin sync enabled
|
||||||
- Align Enable/In Use button states to avoid visual jank across VS Code and Codex views
|
- Align Enable/In Use button states to avoid visual jank across app views
|
||||||
|
|
||||||
## [3.3.0] - 2025-09-22
|
## [3.3.0] - 2025-09-22
|
||||||
|
|
||||||
### ✨ Features
|
### ✨ Features
|
||||||
- Add “Apply to VS Code / Remove from VS Code” actions on provider cards, writing settings for Code/Insiders/VSCodium variants
|
- Add “Apply to VS Code / Remove from VS Code” actions on provider cards, writing settings for Code/Insiders/VSCodium variants *(Removed in 3.4.x)*
|
||||||
- Enable VS Code auto-sync by default with window broadcast and tray hooks so Codex switches sync silently
|
- Enable VS Code auto-sync by default with window broadcast and tray hooks so Codex switches sync silently *(Removed in 3.4.x)*
|
||||||
- Extend the Codex provider wizard with display name, dedicated API key URL, and clearer guidance
|
- Extend the Codex provider wizard with display name, dedicated API key URL, and clearer guidance
|
||||||
- Introduce shared common config snippets with JSON/TOML reuse, validation, and consistent error surfaces
|
- Introduce shared common config snippets with JSON/TOML reuse, validation, and consistent error surfaces
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
> v3.4.0 :新增 i18next 国际化(还有部分未完成)、对新模型(qwen-3-max, GLM-4.6, DeepSeek-V3.2-Exp)的支持、Claude 插件、单实例守护、托盘最小化及安装器优化等。
|
> v3.4.0 :新增 i18next 国际化(还有部分未完成)、对新模型(qwen-3-max, GLM-4.6, DeepSeek-V3.2-Exp)的支持、Claude 插件、单实例守护、托盘最小化及安装器优化等。
|
||||||
|
|
||||||
> v3.3.0 :VS Code Codex 插件一键配置/移除(默认自动同步)、Codex 通用配置片段与自定义向导增强、WSL 环境支持、跨平台托盘与 UI 优化。
|
> v3.3.0 :VS Code Codex 插件一键配置/移除(默认自动同步)、Codex 通用配置片段与自定义向导增强、WSL 环境支持、跨平台托盘与 UI 优化。(该 VS Code 写入功能已在 v3.4.x 停用)
|
||||||
|
|
||||||
> v3.2.0 :全新 UI、macOS系统托盘、内置更新器、原子写入与回滚、改进暗色样式、单一事实源(SSOT)与一次性迁移/归档。
|
> v3.2.0 :全新 UI、macOS系统托盘、内置更新器、原子写入与回滚、改进暗色样式、单一事实源(SSOT)与一次性迁移/归档。
|
||||||
|
|
||||||
@@ -19,7 +19,8 @@
|
|||||||
## 功能特性(v3.4.0)
|
## 功能特性(v3.4.0)
|
||||||
|
|
||||||
- **国际化与语言切换**:内置 i18next,默认显示中文,可在设置中快速切换到英文,界面文文案自动实时刷新。
|
- **国际化与语言切换**:内置 i18next,默认显示中文,可在设置中快速切换到英文,界面文文案自动实时刷新。
|
||||||
- **Claude 插件同步**:在 VS Code 同步按钮旁新增 Claude 插件同步选项,与 Codex 同步互不冲突,切换供应商后立即应用。
|
- **Claude 插件同步**:内置按钮可一键应用或恢复 Claude 插件配置,切换供应商后立即生效。
|
||||||
|
- **VS Code Codex 设置停用**:由于新版 Codex 插件无需修改 `settings.json`,应用不再写入 VS Code 设置,避免潜在冲突。
|
||||||
- **供应商预设扩展**:新增 DeepSeek--V3.2-Exp、Qwen3-Max、GLM-4.6 等最新模型。
|
- **供应商预设扩展**:新增 DeepSeek--V3.2-Exp、Qwen3-Max、GLM-4.6 等最新模型。
|
||||||
- **系统托盘与窗口行为**:窗口关闭可最小化到托盘,macOS 支持托盘模式下隐藏/显示 Dock,托盘切换时同步 Claude/Codex/插件状态。
|
- **系统托盘与窗口行为**:窗口关闭可最小化到托盘,macOS 支持托盘模式下隐藏/显示 Dock,托盘切换时同步 Claude/Codex/插件状态。
|
||||||
- **单实例**:保证同一时间仅运行一个实例,避免多开冲突。
|
- **单实例**:保证同一时间仅运行一个实例,避免多开冲突。
|
||||||
|
|||||||
@@ -5,4 +5,3 @@
|
|||||||
- i18n
|
- i18n
|
||||||
- gemini cli
|
- gemini cli
|
||||||
- homebrew 支持
|
- homebrew 支持
|
||||||
- 自定义 vscode 路径
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ use crate::codex_config;
|
|||||||
use crate::config::{self, get_claude_settings_path, ConfigStatus};
|
use crate::config::{self, get_claude_settings_path, ConfigStatus};
|
||||||
use crate::provider::Provider;
|
use crate::provider::Provider;
|
||||||
use crate::store::AppState;
|
use crate::store::AppState;
|
||||||
use crate::vscode;
|
|
||||||
|
|
||||||
fn validate_provider_settings(app_type: &AppType, provider: &Provider) -> Result<(), String> {
|
fn validate_provider_settings(app_type: &AppType, provider: &Provider) -> Result<(), String> {
|
||||||
match app_type {
|
match app_type {
|
||||||
@@ -693,45 +692,6 @@ pub async fn is_portable_mode() -> Result<bool, String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// VS Code: 获取用户 settings.json 状态
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_vscode_settings_status() -> Result<ConfigStatus, String> {
|
|
||||||
if let Some(p) = vscode::find_existing_settings() {
|
|
||||||
Ok(ConfigStatus {
|
|
||||||
exists: true,
|
|
||||||
path: p.to_string_lossy().to_string(),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// 默认返回 macOS 稳定版路径(或其他平台首选项的第一个候选),但标记不存在
|
|
||||||
let preferred = vscode::candidate_settings_paths().into_iter().next();
|
|
||||||
Ok(ConfigStatus {
|
|
||||||
exists: false,
|
|
||||||
path: preferred.unwrap_or_default().to_string_lossy().to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// VS Code: 读取 settings.json 文本(仅当文件存在)
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn read_vscode_settings() -> Result<String, String> {
|
|
||||||
if let Some(p) = vscode::find_existing_settings() {
|
|
||||||
std::fs::read_to_string(&p).map_err(|e| format!("读取 VS Code 设置失败: {}", e))
|
|
||||||
} else {
|
|
||||||
Err("未找到 VS Code 用户设置文件".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// VS Code: 写入 settings.json 文本(仅当文件存在;不自动创建)
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn write_vscode_settings(content: String) -> Result<bool, String> {
|
|
||||||
if let Some(p) = vscode::find_existing_settings() {
|
|
||||||
config::write_text_file(&p, &content)?;
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
Err("未找到 VS Code 用户设置文件".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Claude 插件:获取 ~/.claude/config.json 状态
|
/// Claude 插件:获取 ~/.claude/config.json 状态
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn get_claude_plugin_status() -> Result<ConfigStatus, String> {
|
pub async fn get_claude_plugin_status() -> Result<ConfigStatus, String> {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ mod migration;
|
|||||||
mod provider;
|
mod provider;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod store;
|
mod store;
|
||||||
mod vscode;
|
|
||||||
|
|
||||||
use store::AppState;
|
use store::AppState;
|
||||||
use tauri::{
|
use tauri::{
|
||||||
@@ -416,9 +415,6 @@ pub fn run() {
|
|||||||
commands::save_settings,
|
commands::save_settings,
|
||||||
commands::check_for_updates,
|
commands::check_for_updates,
|
||||||
commands::is_portable_mode,
|
commands::is_portable_mode,
|
||||||
commands::get_vscode_settings_status,
|
|
||||||
commands::read_vscode_settings,
|
|
||||||
commands::write_vscode_settings,
|
|
||||||
commands::get_claude_plugin_status,
|
commands::get_claude_plugin_status,
|
||||||
commands::read_claude_plugin_config,
|
commands::read_claude_plugin_config,
|
||||||
commands::apply_claude_plugin_config,
|
commands::apply_claude_plugin_config,
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
/// 枚举可能的 VS Code 发行版配置目录名称
|
|
||||||
fn vscode_product_dirs() -> Vec<&'static str> {
|
|
||||||
vec![
|
|
||||||
"Code", // VS Code Stable
|
|
||||||
"Code - Insiders", // VS Code Insiders
|
|
||||||
"VSCodium", // VSCodium
|
|
||||||
"Code - OSS", // OSS 发行版
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取 VS Code 用户 settings.json 的候选路径列表(按优先级排序)
|
|
||||||
pub fn candidate_settings_paths() -> Vec<PathBuf> {
|
|
||||||
let mut paths = Vec::new();
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
{
|
|
||||||
if let Some(home) = dirs::home_dir() {
|
|
||||||
for prod in vscode_product_dirs() {
|
|
||||||
paths.push(
|
|
||||||
home.join("Library")
|
|
||||||
.join("Application Support")
|
|
||||||
.join(prod)
|
|
||||||
.join("User")
|
|
||||||
.join("settings.json"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
{
|
|
||||||
// Windows: %APPDATA%\Code\User\settings.json
|
|
||||||
if let Some(roaming) = dirs::config_dir() {
|
|
||||||
for prod in vscode_product_dirs() {
|
|
||||||
paths.push(roaming.join(prod).join("User").join("settings.json"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(unix, not(target_os = "macos")))]
|
|
||||||
{
|
|
||||||
// Linux: ~/.config/Code/User/settings.json
|
|
||||||
if let Some(config) = dirs::config_dir() {
|
|
||||||
for prod in vscode_product_dirs() {
|
|
||||||
paths.push(config.join(prod).join("User").join("settings.json"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paths
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 返回第一个存在的 settings.json 路径
|
|
||||||
pub fn find_existing_settings() -> Option<PathBuf> {
|
|
||||||
for p in candidate_settings_paths() {
|
|
||||||
if let Ok(meta) = std::fs::metadata(&p) {
|
|
||||||
if meta.is_file() {
|
|
||||||
return Some(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
72
src/App.tsx
72
src/App.tsx
@@ -13,14 +13,10 @@ import { Plus, Settings, Moon, Sun } from "lucide-react";
|
|||||||
import { buttonStyles } from "./lib/styles";
|
import { buttonStyles } from "./lib/styles";
|
||||||
import { useDarkMode } from "./hooks/useDarkMode";
|
import { useDarkMode } from "./hooks/useDarkMode";
|
||||||
import { extractErrorMessage } from "./utils/errorUtils";
|
import { extractErrorMessage } from "./utils/errorUtils";
|
||||||
import { applyProviderToVSCode } from "./utils/vscodeSettings";
|
|
||||||
import { getCodexBaseUrl } from "./utils/providerConfigUtils";
|
|
||||||
import { useVSCodeAutoSync } from "./hooks/useVSCodeAutoSync";
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { isDarkMode, toggleDarkMode } = useDarkMode();
|
const { isDarkMode, toggleDarkMode } = useDarkMode();
|
||||||
const { isAutoSyncEnabled } = useVSCodeAutoSync();
|
|
||||||
const [activeApp, setActiveApp] = useState<AppType>("claude");
|
const [activeApp, setActiveApp] = useState<AppType>("claude");
|
||||||
const [providers, setProviders] = useState<Record<string, Provider>>({});
|
const [providers, setProviders] = useState<Record<string, Provider>>({});
|
||||||
const [currentProviderId, setCurrentProviderId] = useState<string>("");
|
const [currentProviderId, setCurrentProviderId] = useState<string>("");
|
||||||
@@ -98,11 +94,7 @@ function App() {
|
|||||||
await loadProviders();
|
await loadProviders();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 若为 Codex 且开启自动同步,则静默同步到 VS Code(覆盖)
|
// 若为 Claude,则同步插件配置
|
||||||
if (data.appType === "codex" && isAutoSyncEnabled) {
|
|
||||||
await syncCodexToVSCode(data.providerId, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.appType === "claude") {
|
if (data.appType === "claude") {
|
||||||
await syncClaudePlugin(data.providerId, true);
|
await syncClaudePlugin(data.providerId, true);
|
||||||
}
|
}
|
||||||
@@ -120,7 +112,7 @@ function App() {
|
|||||||
unlisten();
|
unlisten();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [activeApp, isAutoSyncEnabled]);
|
}, [activeApp]);
|
||||||
|
|
||||||
const loadProviders = async () => {
|
const loadProviders = async () => {
|
||||||
const loadedProviders = await window.api.getProviders(activeApp);
|
const loadedProviders = await window.api.getProviders(activeApp);
|
||||||
@@ -189,61 +181,6 @@ function App() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 同步Codex供应商到VS Code设置(静默覆盖)
|
|
||||||
const syncCodexToVSCode = async (providerId: string, silent = false) => {
|
|
||||||
try {
|
|
||||||
const status = await window.api.getVSCodeSettingsStatus();
|
|
||||||
if (!status.exists) {
|
|
||||||
if (!silent) {
|
|
||||||
showNotification(
|
|
||||||
t("notifications.vscodeSettingsNotFound"),
|
|
||||||
"error",
|
|
||||||
3000
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const raw = await window.api.readVSCodeSettings();
|
|
||||||
const provider = providers[providerId];
|
|
||||||
const isOfficial = provider?.category === "official";
|
|
||||||
|
|
||||||
// 非官方供应商需要解析 base_url(使用公共工具函数)
|
|
||||||
let baseUrl: string | undefined = undefined;
|
|
||||||
if (!isOfficial) {
|
|
||||||
const parsed = getCodexBaseUrl(provider);
|
|
||||||
if (!parsed) {
|
|
||||||
if (!silent) {
|
|
||||||
showNotification(t("notifications.missingBaseUrl"), "error", 4000);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
baseUrl = parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedSettings = applyProviderToVSCode(raw, {
|
|
||||||
baseUrl,
|
|
||||||
isOfficial,
|
|
||||||
});
|
|
||||||
if (updatedSettings !== raw) {
|
|
||||||
await window.api.writeVSCodeSettings(updatedSettings);
|
|
||||||
if (!silent) {
|
|
||||||
showNotification(t("notifications.syncedToVSCode"), "success", 1500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 触发providers重新加载,以更新VS Code按钮状态
|
|
||||||
await loadProviders();
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error(t("console.syncToVSCodeFailed"), error);
|
|
||||||
if (!silent) {
|
|
||||||
const errorMessage =
|
|
||||||
error?.message || t("notifications.syncVSCodeFailed");
|
|
||||||
showNotification(errorMessage, "error", 5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 同步 Claude 插件配置(写入/移除固定 JSON)
|
// 同步 Claude 插件配置(写入/移除固定 JSON)
|
||||||
const syncClaudePlugin = async (providerId: string, silent = false) => {
|
const syncClaudePlugin = async (providerId: string, silent = false) => {
|
||||||
try {
|
try {
|
||||||
@@ -284,11 +221,6 @@ function App() {
|
|||||||
// 更新托盘菜单
|
// 更新托盘菜单
|
||||||
await window.api.updateTrayMenu();
|
await window.api.updateTrayMenu();
|
||||||
|
|
||||||
// Codex: 切换供应商后,只在自动同步启用时同步到 VS Code
|
|
||||||
if (activeApp === "codex" && isAutoSyncEnabled) {
|
|
||||||
await syncCodexToVSCode(id, true); // silent模式,不显示通知
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeApp === "claude") {
|
if (activeApp === "claude") {
|
||||||
await syncClaudePlugin(id, true);
|
await syncClaudePlugin(id, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,6 @@ import { Provider } from "../types";
|
|||||||
import { Play, Edit3, Trash2, CheckCircle2, Users, Check } from "lucide-react";
|
import { Play, Edit3, Trash2, CheckCircle2, Users, Check } from "lucide-react";
|
||||||
import { buttonStyles, cardStyles, badgeStyles, cn } from "../lib/styles";
|
import { buttonStyles, cardStyles, badgeStyles, cn } from "../lib/styles";
|
||||||
import { AppType } from "../lib/tauri-api";
|
import { AppType } from "../lib/tauri-api";
|
||||||
import {
|
|
||||||
applyProviderToVSCode,
|
|
||||||
detectApplied,
|
|
||||||
normalizeBaseUrl,
|
|
||||||
} from "../utils/vscodeSettings";
|
|
||||||
import { getCodexBaseUrl } from "../utils/providerConfigUtils";
|
|
||||||
import { useVSCodeAutoSync } from "../hooks/useVSCodeAutoSync";
|
|
||||||
// 不再在列表中显示分类徽章,避免造成困惑
|
// 不再在列表中显示分类徽章,避免造成困惑
|
||||||
|
|
||||||
interface ProviderListProps {
|
interface ProviderListProps {
|
||||||
@@ -65,46 +58,8 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 解析 Codex 配置中的 base_url(已提取到公共工具)
|
|
||||||
|
|
||||||
// VS Code 按钮:仅在 Codex + 当前供应商显示;按钮文案根据是否"已应用"变化
|
|
||||||
const [vscodeAppliedFor, setVscodeAppliedFor] = useState<string | null>(null);
|
|
||||||
const { enableAutoSync, disableAutoSync } = useVSCodeAutoSync();
|
|
||||||
const [claudeApplied, setClaudeApplied] = useState<boolean>(false);
|
const [claudeApplied, setClaudeApplied] = useState<boolean>(false);
|
||||||
|
|
||||||
// 当当前供应商或 appType 变化时,尝试读取 VS Code settings 并检测状态
|
|
||||||
useEffect(() => {
|
|
||||||
const check = async () => {
|
|
||||||
if (appType !== "codex" || !currentProviderId) {
|
|
||||||
setVscodeAppliedFor(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const status = await window.api.getVSCodeSettingsStatus();
|
|
||||||
if (!status.exists) {
|
|
||||||
setVscodeAppliedFor(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const content = await window.api.readVSCodeSettings();
|
|
||||||
const detected = detectApplied(content);
|
|
||||||
// 认为“已应用”的条件(非官方供应商):VS Code 中的 apiBase 与当前供应商的 base_url 完全一致
|
|
||||||
const current = providers[currentProviderId];
|
|
||||||
let applied = false;
|
|
||||||
if (current && current.category !== "official") {
|
|
||||||
const base = getCodexBaseUrl(current);
|
|
||||||
if (detected.apiBase && base) {
|
|
||||||
applied =
|
|
||||||
normalizeBaseUrl(detected.apiBase) === normalizeBaseUrl(base);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setVscodeAppliedFor(applied ? currentProviderId : null);
|
|
||||||
} catch {
|
|
||||||
setVscodeAppliedFor(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
check();
|
|
||||||
}, [appType, currentProviderId, providers]);
|
|
||||||
|
|
||||||
// 检查 Claude 插件配置是否已应用
|
// 检查 Claude 插件配置是否已应用
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const checkClaude = async () => {
|
const checkClaude = async () => {
|
||||||
@@ -123,83 +78,6 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
|||||||
checkClaude();
|
checkClaude();
|
||||||
}, [appType, currentProviderId, providers]);
|
}, [appType, currentProviderId, providers]);
|
||||||
|
|
||||||
const handleApplyToVSCode = async (provider: Provider) => {
|
|
||||||
try {
|
|
||||||
const status = await window.api.getVSCodeSettingsStatus();
|
|
||||||
if (!status.exists) {
|
|
||||||
onNotify?.(t("notifications.vscodeSettingsNotFound"), "error", 3000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const raw = await window.api.readVSCodeSettings();
|
|
||||||
|
|
||||||
const isOfficial = provider.category === "official";
|
|
||||||
// 非官方且缺少 base_url 时直接报错并返回,避免“空写入”假成功
|
|
||||||
if (!isOfficial) {
|
|
||||||
const parsed = getCodexBaseUrl(provider);
|
|
||||||
if (!parsed) {
|
|
||||||
onNotify?.(t("notifications.missingBaseUrl"), "error", 4000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseUrl = isOfficial ? undefined : getCodexBaseUrl(provider);
|
|
||||||
const next = applyProviderToVSCode(raw, { baseUrl, isOfficial });
|
|
||||||
|
|
||||||
if (next === raw) {
|
|
||||||
// 幂等:没有变化也提示成功
|
|
||||||
onNotify?.(t("notifications.appliedToVSCode"), "success", 3000);
|
|
||||||
setVscodeAppliedFor(provider.id);
|
|
||||||
// 用户手动应用时,启用自动同步
|
|
||||||
enableAutoSync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await window.api.writeVSCodeSettings(next);
|
|
||||||
onNotify?.(t("notifications.appliedToVSCode"), "success", 3000);
|
|
||||||
setVscodeAppliedFor(provider.id);
|
|
||||||
// 用户手动应用时,启用自动同步
|
|
||||||
enableAutoSync();
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e);
|
|
||||||
const msg =
|
|
||||||
e && e.message ? e.message : t("notifications.syncVSCodeFailed");
|
|
||||||
onNotify?.(msg, "error", 5000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRemoveFromVSCode = async () => {
|
|
||||||
try {
|
|
||||||
const status = await window.api.getVSCodeSettingsStatus();
|
|
||||||
if (!status.exists) {
|
|
||||||
onNotify?.(t("notifications.vscodeSettingsNotFound"), "error", 3000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const raw = await window.api.readVSCodeSettings();
|
|
||||||
const next = applyProviderToVSCode(raw, {
|
|
||||||
baseUrl: undefined,
|
|
||||||
isOfficial: true,
|
|
||||||
});
|
|
||||||
if (next === raw) {
|
|
||||||
onNotify?.(t("notifications.removedFromVSCode"), "success", 3000);
|
|
||||||
setVscodeAppliedFor(null);
|
|
||||||
// 用户手动移除时,禁用自动同步
|
|
||||||
disableAutoSync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await window.api.writeVSCodeSettings(next);
|
|
||||||
onNotify?.(t("notifications.removedFromVSCode"), "success", 3000);
|
|
||||||
setVscodeAppliedFor(null);
|
|
||||||
// 用户手动移除时,禁用自动同步
|
|
||||||
disableAutoSync();
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e);
|
|
||||||
const msg =
|
|
||||||
e && e.message ? e.message : t("notifications.syncVSCodeFailed");
|
|
||||||
onNotify?.(msg, "error", 5000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleApplyToClaudePlugin = async () => {
|
const handleApplyToClaudePlugin = async () => {
|
||||||
try {
|
try {
|
||||||
await window.api.applyClaudePluginConfig({ official: false });
|
await window.api.applyClaudePluginConfig({ official: false });
|
||||||
@@ -320,36 +198,6 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 ml-4">
|
<div className="flex items-center gap-2 ml-4">
|
||||||
{/* 同步按钮占位容器 - 只在对应模式下渲染,避免布局跳动 */}
|
|
||||||
{appType === "codex" ? (
|
|
||||||
<div className="w-[130px]">
|
|
||||||
{provider.category !== "official" && isCurrent && (
|
|
||||||
<button
|
|
||||||
onClick={() =>
|
|
||||||
vscodeAppliedFor === provider.id
|
|
||||||
? handleRemoveFromVSCode()
|
|
||||||
: handleApplyToVSCode(provider)
|
|
||||||
}
|
|
||||||
className={cn(
|
|
||||||
"inline-flex items-center gap-1 px-3 py-1.5 text-sm font-medium rounded-md transition-colors w-full whitespace-nowrap justify-center",
|
|
||||||
vscodeAppliedFor === provider.id
|
|
||||||
? "border border-gray-300 text-gray-600 hover:border-red-300 hover:text-red-600 hover:bg-red-50 dark:border-gray-600 dark:text-gray-400 dark:hover:border-red-800 dark:hover:text-red-400 dark:hover:bg-red-900/20"
|
|
||||||
: "border border-gray-300 text-gray-700 hover:border-blue-300 hover:text-blue-600 hover:bg-blue-50 dark:border-gray-600 dark:text-gray-300 dark:hover:border-blue-700 dark:hover:text-blue-400 dark:hover:bg-blue-900/20"
|
|
||||||
)}
|
|
||||||
title={
|
|
||||||
vscodeAppliedFor === provider.id
|
|
||||||
? t("provider.removeFromVSCode")
|
|
||||||
: t("provider.applyToVSCode")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{vscodeAppliedFor === provider.id
|
|
||||||
? t("provider.removeFromVSCode")
|
|
||||||
: t("provider.applyToVSCode")}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{appType === "claude" ? (
|
{appType === "claude" ? (
|
||||||
<div className="w-[130px]">
|
<div className="w-[130px]">
|
||||||
{provider.category !== "official" && isCurrent && (
|
{provider.category !== "official" && isCurrent && (
|
||||||
|
|||||||
@@ -435,8 +435,6 @@ export default function SettingsModal({ onClose }: SettingsModalProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* VS Code 自动同步设置已移除 */}
|
|
||||||
|
|
||||||
{/* 配置文件位置 */}
|
{/* 配置文件位置 */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">
|
<h3 className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
import { useState, useEffect, useCallback } from "react";
|
|
||||||
|
|
||||||
const VSCODE_AUTO_SYNC_KEY = "vscode-auto-sync-enabled";
|
|
||||||
const VSCODE_AUTO_SYNC_EVENT = "vscode-auto-sync-changed";
|
|
||||||
|
|
||||||
export function useVSCodeAutoSync() {
|
|
||||||
// 默认开启自动同步;若本地存储存在记录,则以记录为准
|
|
||||||
const [isAutoSyncEnabled, setIsAutoSyncEnabled] = useState<boolean>(true);
|
|
||||||
|
|
||||||
// 从 localStorage 读取初始状态
|
|
||||||
useEffect(() => {
|
|
||||||
try {
|
|
||||||
const saved = localStorage.getItem(VSCODE_AUTO_SYNC_KEY);
|
|
||||||
if (saved !== null) {
|
|
||||||
setIsAutoSyncEnabled(saved === "true");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("读取自动同步状态失败:", error);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// 订阅同窗口的自定义事件,以及跨窗口的 storage 事件,实现全局同步
|
|
||||||
useEffect(() => {
|
|
||||||
const onCustom = (e: Event) => {
|
|
||||||
try {
|
|
||||||
const detail = (e as CustomEvent).detail as
|
|
||||||
| { enabled?: boolean }
|
|
||||||
| undefined;
|
|
||||||
if (detail && typeof detail.enabled === "boolean") {
|
|
||||||
setIsAutoSyncEnabled(detail.enabled);
|
|
||||||
} else {
|
|
||||||
// 兜底:从 localStorage 读取
|
|
||||||
const saved = localStorage.getItem(VSCODE_AUTO_SYNC_KEY);
|
|
||||||
if (saved !== null) setIsAutoSyncEnabled(saved === "true");
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// 忽略
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const onStorage = (e: StorageEvent) => {
|
|
||||||
if (e.key === VSCODE_AUTO_SYNC_KEY) {
|
|
||||||
setIsAutoSyncEnabled(e.newValue === "true");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
window.addEventListener(VSCODE_AUTO_SYNC_EVENT, onCustom as EventListener);
|
|
||||||
window.addEventListener("storage", onStorage);
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener(
|
|
||||||
VSCODE_AUTO_SYNC_EVENT,
|
|
||||||
onCustom as EventListener,
|
|
||||||
);
|
|
||||||
window.removeEventListener("storage", onStorage);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// 启用自动同步
|
|
||||||
const enableAutoSync = useCallback(() => {
|
|
||||||
try {
|
|
||||||
localStorage.setItem(VSCODE_AUTO_SYNC_KEY, "true");
|
|
||||||
setIsAutoSyncEnabled(true);
|
|
||||||
// 通知同窗口其他订阅者
|
|
||||||
window.dispatchEvent(
|
|
||||||
new CustomEvent(VSCODE_AUTO_SYNC_EVENT, { detail: { enabled: true } }),
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("保存自动同步状态失败:", error);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// 禁用自动同步
|
|
||||||
const disableAutoSync = useCallback(() => {
|
|
||||||
try {
|
|
||||||
localStorage.setItem(VSCODE_AUTO_SYNC_KEY, "false");
|
|
||||||
setIsAutoSyncEnabled(false);
|
|
||||||
// 通知同窗口其他订阅者
|
|
||||||
window.dispatchEvent(
|
|
||||||
new CustomEvent(VSCODE_AUTO_SYNC_EVENT, { detail: { enabled: false } }),
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("保存自动同步状态失败:", error);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// 切换自动同步状态
|
|
||||||
const toggleAutoSync = useCallback(() => {
|
|
||||||
if (isAutoSyncEnabled) {
|
|
||||||
disableAutoSync();
|
|
||||||
} else {
|
|
||||||
enableAutoSync();
|
|
||||||
}
|
|
||||||
}, [isAutoSyncEnabled, enableAutoSync, disableAutoSync]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
isAutoSyncEnabled,
|
|
||||||
enableAutoSync,
|
|
||||||
disableAutoSync,
|
|
||||||
toggleAutoSync,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -38,8 +38,6 @@
|
|||||||
"addNewProvider": "Add New Provider",
|
"addNewProvider": "Add New Provider",
|
||||||
"configError": "Configuration Error",
|
"configError": "Configuration Error",
|
||||||
"notConfigured": "Not configured for official website",
|
"notConfigured": "Not configured for official website",
|
||||||
"applyToVSCode": "Apply to VS Code",
|
|
||||||
"removeFromVSCode": "Remove from VS Code",
|
|
||||||
"applyToClaudePlugin": "Apply to Claude plugin",
|
"applyToClaudePlugin": "Apply to Claude plugin",
|
||||||
"removeFromClaudePlugin": "Remove from Claude plugin"
|
"removeFromClaudePlugin": "Remove from Claude plugin"
|
||||||
},
|
},
|
||||||
@@ -49,14 +47,8 @@
|
|||||||
"switchSuccess": "Switch successful! Please restart {{appName}} terminal to take effect",
|
"switchSuccess": "Switch successful! Please restart {{appName}} terminal to take effect",
|
||||||
"switchFailed": "Switch failed, please check configuration",
|
"switchFailed": "Switch failed, please check configuration",
|
||||||
"autoImported": "Default provider created from existing configuration",
|
"autoImported": "Default provider created from existing configuration",
|
||||||
"appliedToVSCode": "Applied to VS Code, restart Codex plugin to take effect",
|
|
||||||
"removedFromVSCode": "Removed from VS Code, restart Codex plugin to take effect",
|
|
||||||
"syncedToVSCode": "Synced to VS Code",
|
|
||||||
"vscodeSettingsNotFound": "VS Code user settings file (settings.json) not found",
|
|
||||||
"missingBaseUrl": "Current configuration missing base_url, cannot write to VS Code",
|
|
||||||
"saveFailed": "Save failed: {{error}}",
|
"saveFailed": "Save failed: {{error}}",
|
||||||
"saveFailedGeneric": "Save failed, please try again",
|
"saveFailedGeneric": "Save failed, please try again",
|
||||||
"syncVSCodeFailed": "Sync to VS Code failed",
|
|
||||||
"appliedToClaudePlugin": "Applied to Claude plugin",
|
"appliedToClaudePlugin": "Applied to Claude plugin",
|
||||||
"removedFromClaudePlugin": "Removed from Claude plugin",
|
"removedFromClaudePlugin": "Removed from Claude plugin",
|
||||||
"syncClaudePluginFailed": "Sync Claude plugin failed"
|
"syncClaudePluginFailed": "Sync Claude plugin failed"
|
||||||
@@ -101,7 +93,6 @@
|
|||||||
"providerSwitchReceived": "Received provider switch event:",
|
"providerSwitchReceived": "Received provider switch event:",
|
||||||
"setupListenerFailed": "Failed to setup provider switch listener:",
|
"setupListenerFailed": "Failed to setup provider switch listener:",
|
||||||
"updateProviderFailed": "Update provider failed:",
|
"updateProviderFailed": "Update provider failed:",
|
||||||
"syncToVSCodeFailed": "Sync to VS Code failed:",
|
|
||||||
"autoImportFailed": "Auto import default configuration failed:",
|
"autoImportFailed": "Auto import default configuration failed:",
|
||||||
"openLinkFailed": "Failed to open link:",
|
"openLinkFailed": "Failed to open link:",
|
||||||
"getVersionFailed": "Failed to get version info:",
|
"getVersionFailed": "Failed to get version info:",
|
||||||
|
|||||||
@@ -38,8 +38,6 @@
|
|||||||
"addNewProvider": "添加新供应商",
|
"addNewProvider": "添加新供应商",
|
||||||
"configError": "配置错误",
|
"configError": "配置错误",
|
||||||
"notConfigured": "未配置官网地址",
|
"notConfigured": "未配置官网地址",
|
||||||
"applyToVSCode": "应用到 VS Code",
|
|
||||||
"removeFromVSCode": "从 VS Code 移除",
|
|
||||||
"applyToClaudePlugin": "应用到 Claude 插件",
|
"applyToClaudePlugin": "应用到 Claude 插件",
|
||||||
"removeFromClaudePlugin": "从 Claude 插件移除"
|
"removeFromClaudePlugin": "从 Claude 插件移除"
|
||||||
},
|
},
|
||||||
@@ -49,14 +47,8 @@
|
|||||||
"switchSuccess": "切换成功!请重启 {{appName}} 终端以生效",
|
"switchSuccess": "切换成功!请重启 {{appName}} 终端以生效",
|
||||||
"switchFailed": "切换失败,请检查配置",
|
"switchFailed": "切换失败,请检查配置",
|
||||||
"autoImported": "已从现有配置创建默认供应商",
|
"autoImported": "已从现有配置创建默认供应商",
|
||||||
"appliedToVSCode": "已应用到 VS Code,重启 Codex 插件以生效",
|
|
||||||
"removedFromVSCode": "已从 VS Code 移除,重启 Codex 插件以生效",
|
|
||||||
"syncedToVSCode": "已同步到 VS Code",
|
|
||||||
"vscodeSettingsNotFound": "未找到 VS Code 用户设置文件 (settings.json)",
|
|
||||||
"missingBaseUrl": "当前配置缺少 base_url,无法写入 VS Code",
|
|
||||||
"saveFailed": "保存失败:{{error}}",
|
"saveFailed": "保存失败:{{error}}",
|
||||||
"saveFailedGeneric": "保存失败,请重试",
|
"saveFailedGeneric": "保存失败,请重试",
|
||||||
"syncVSCodeFailed": "同步 VS Code 失败",
|
|
||||||
"appliedToClaudePlugin": "已应用到 Claude 插件",
|
"appliedToClaudePlugin": "已应用到 Claude 插件",
|
||||||
"removedFromClaudePlugin": "已从 Claude 插件移除",
|
"removedFromClaudePlugin": "已从 Claude 插件移除",
|
||||||
"syncClaudePluginFailed": "同步 Claude 插件失败"
|
"syncClaudePluginFailed": "同步 Claude 插件失败"
|
||||||
@@ -101,7 +93,6 @@
|
|||||||
"providerSwitchReceived": "收到供应商切换事件:",
|
"providerSwitchReceived": "收到供应商切换事件:",
|
||||||
"setupListenerFailed": "设置供应商切换监听器失败:",
|
"setupListenerFailed": "设置供应商切换监听器失败:",
|
||||||
"updateProviderFailed": "更新供应商失败:",
|
"updateProviderFailed": "更新供应商失败:",
|
||||||
"syncToVSCodeFailed": "同步到VS Code失败:",
|
|
||||||
"autoImportFailed": "自动导入默认配置失败:",
|
"autoImportFailed": "自动导入默认配置失败:",
|
||||||
"openLinkFailed": "打开链接失败:",
|
"openLinkFailed": "打开链接失败:",
|
||||||
"getVersionFailed": "获取版本信息失败:",
|
"getVersionFailed": "获取版本信息失败:",
|
||||||
|
|||||||
@@ -197,8 +197,6 @@ export const tauriAPI = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// (保留空位,取消迁移提示)
|
|
||||||
|
|
||||||
// 选择配置目录
|
// 选择配置目录
|
||||||
selectConfigDirectory: async (
|
selectConfigDirectory: async (
|
||||||
defaultPath?: string,
|
defaultPath?: string,
|
||||||
@@ -275,38 +273,6 @@ export const tauriAPI = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// VS Code: 获取 settings.json 状态
|
|
||||||
getVSCodeSettingsStatus: async (): Promise<{
|
|
||||||
exists: boolean;
|
|
||||||
path: string;
|
|
||||||
error?: string;
|
|
||||||
}> => {
|
|
||||||
try {
|
|
||||||
return await invoke("get_vscode_settings_status");
|
|
||||||
} catch (error) {
|
|
||||||
console.error("获取 VS Code 设置状态失败:", error);
|
|
||||||
return { exists: false, path: "", error: String(error) };
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// VS Code: 读取 settings.json 文本
|
|
||||||
readVSCodeSettings: async (): Promise<string> => {
|
|
||||||
try {
|
|
||||||
return await invoke("read_vscode_settings");
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error(`读取 VS Code 设置失败: ${String(error)}`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// VS Code: 写回 settings.json 文本(不自动创建)
|
|
||||||
writeVSCodeSettings: async (content: string): Promise<boolean> => {
|
|
||||||
try {
|
|
||||||
return await invoke("write_vscode_settings", { content });
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error(`写入 VS Code 设置失败: ${String(error)}`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Claude 插件:获取 ~/.claude/config.json 状态
|
// Claude 插件:获取 ~/.claude/config.json 状态
|
||||||
getClaudePluginStatus: async (): Promise<ConfigStatus> => {
|
getClaudePluginStatus: async (): Promise<ConfigStatus> => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,134 +0,0 @@
|
|||||||
import { applyEdits, modify, parse } from "jsonc-parser";
|
|
||||||
|
|
||||||
const fmt = { insertSpaces: true, tabSize: 2, eol: "\n" } as const;
|
|
||||||
|
|
||||||
export interface AppliedCheck {
|
|
||||||
hasApiBase: boolean;
|
|
||||||
apiBase?: string;
|
|
||||||
hasPreferredAuthMethod: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeBaseUrl(url: string): string {
|
|
||||||
return url.replace(/\/+$/, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
const isDocEmpty = (s: string) => s.trim().length === 0;
|
|
||||||
|
|
||||||
// 检查 settings.json(JSONC 文本)中是否已经应用了我们的键
|
|
||||||
export function detectApplied(content: string): AppliedCheck {
|
|
||||||
try {
|
|
||||||
// 允许 JSONC 的宽松解析:jsonc-parser 的 parse 可以直接处理注释
|
|
||||||
const data = parse(content) as any;
|
|
||||||
const apiBase = data?.["chatgpt.apiBase"];
|
|
||||||
const method = data?.["chatgpt.config"]?.preferred_auth_method;
|
|
||||||
return {
|
|
||||||
hasApiBase: typeof apiBase === "string",
|
|
||||||
apiBase,
|
|
||||||
hasPreferredAuthMethod: typeof method === "string",
|
|
||||||
};
|
|
||||||
} catch {
|
|
||||||
return { hasApiBase: false, hasPreferredAuthMethod: false };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成“清理我们管理的键”后的文本(仅删除我们写入的两个键)
|
|
||||||
export function removeManagedKeys(content: string): string {
|
|
||||||
if (isDocEmpty(content)) return content; // 空文档无需删除
|
|
||||||
let out = content;
|
|
||||||
// 删除 chatgpt.apiBase
|
|
||||||
try {
|
|
||||||
out = applyEdits(
|
|
||||||
out,
|
|
||||||
modify(out, ["chatgpt.apiBase"], undefined, { formattingOptions: fmt }),
|
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
// 忽略删除失败
|
|
||||||
}
|
|
||||||
// 删除 chatgpt.config.preferred_auth_method(注意 chatgpt.config 是顶层带点的键)
|
|
||||||
try {
|
|
||||||
out = applyEdits(
|
|
||||||
out,
|
|
||||||
modify(out, ["chatgpt.config", "preferred_auth_method"], undefined, {
|
|
||||||
formattingOptions: fmt,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
// 忽略删除失败
|
|
||||||
}
|
|
||||||
|
|
||||||
// 兼容早期错误写入:若曾写成嵌套 chatgpt.config.preferred_auth_method,也一并清理
|
|
||||||
try {
|
|
||||||
out = applyEdits(
|
|
||||||
out,
|
|
||||||
modify(out, ["chatgpt", "config", "preferred_auth_method"], undefined, {
|
|
||||||
formattingOptions: fmt,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
// 忽略删除失败
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清理 chatgpt.config 的异常情况:
|
|
||||||
// 1. 早期遗留的标量值(字符串/数字/null等)
|
|
||||||
// 2. 空对象
|
|
||||||
// 3. 数组类型
|
|
||||||
try {
|
|
||||||
const data = parse(out) as any;
|
|
||||||
const cfg = data?.["chatgpt.config"];
|
|
||||||
|
|
||||||
// 需要清理的情况:
|
|
||||||
// - 标量值(null、字符串、数字等)
|
|
||||||
// - 数组
|
|
||||||
// - 空对象
|
|
||||||
const shouldRemove = cfg !== undefined && (
|
|
||||||
cfg === null ||
|
|
||||||
typeof cfg !== "object" ||
|
|
||||||
Array.isArray(cfg) ||
|
|
||||||
(typeof cfg === "object" && Object.keys(cfg).length === 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (shouldRemove) {
|
|
||||||
out = applyEdits(
|
|
||||||
out,
|
|
||||||
modify(out, ["chatgpt.config"], undefined, { formattingOptions: fmt }),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// 忽略解析失败,保持已删除的键
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成“应用供应商到 VS Code”后的文本:
|
|
||||||
// - 先清理我们管理的键
|
|
||||||
// - 再根据是否官方决定写入(官方:不写入;非官方:写入两个键)
|
|
||||||
export function applyProviderToVSCode(
|
|
||||||
content: string,
|
|
||||||
opts: { baseUrl?: string | null; isOfficial?: boolean },
|
|
||||||
): string {
|
|
||||||
let out = removeManagedKeys(content);
|
|
||||||
if (!opts.isOfficial && opts.baseUrl) {
|
|
||||||
const apiBase = normalizeBaseUrl(opts.baseUrl);
|
|
||||||
if (isDocEmpty(out)) {
|
|
||||||
// 简化:空文档直接写入新对象
|
|
||||||
const obj: any = {
|
|
||||||
"chatgpt.apiBase": apiBase,
|
|
||||||
"chatgpt.config": { preferred_auth_method: "apikey" },
|
|
||||||
};
|
|
||||||
out = JSON.stringify(obj, null, 2) + "\n";
|
|
||||||
} else {
|
|
||||||
out = applyEdits(
|
|
||||||
out,
|
|
||||||
modify(out, ["chatgpt.apiBase"], apiBase, { formattingOptions: fmt }),
|
|
||||||
);
|
|
||||||
out = applyEdits(
|
|
||||||
out,
|
|
||||||
modify(out, ["chatgpt.config", "preferred_auth_method"], "apikey", {
|
|
||||||
formattingOptions: fmt,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
4
src/vite-env.d.ts
vendored
4
src/vite-env.d.ts
vendored
@@ -42,10 +42,6 @@ declare global {
|
|||||||
isPortable: () => Promise<boolean>;
|
isPortable: () => Promise<boolean>;
|
||||||
getAppConfigPath: () => Promise<string>;
|
getAppConfigPath: () => Promise<string>;
|
||||||
openAppConfigFolder: () => Promise<void>;
|
openAppConfigFolder: () => Promise<void>;
|
||||||
// VS Code settings.json 能力
|
|
||||||
getVSCodeSettingsStatus: () => Promise<ConfigStatus>;
|
|
||||||
readVSCodeSettings: () => Promise<string>;
|
|
||||||
writeVSCodeSettings: (content: string) => Promise<boolean>;
|
|
||||||
// Claude 插件配置能力
|
// Claude 插件配置能力
|
||||||
getClaudePluginStatus: () => Promise<ConfigStatus>;
|
getClaudePluginStatus: () => Promise<ConfigStatus>;
|
||||||
readClaudePluginConfig: () => Promise<string | null>;
|
readClaudePluginConfig: () => Promise<string | null>;
|
||||||
|
|||||||
Reference in New Issue
Block a user