mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-02 18:41:35 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ac4a64087 | |||
| 6f555cb274 | |||
| 7d495aa772 | |||
| db8180aa31 | |||
| 1586451862 |
@@ -146,7 +146,9 @@ impl ConfigService {
|
|||||||
let cfg_text = settings.get("config").and_then(Value::as_str);
|
let cfg_text = settings.get("config").and_then(Value::as_str);
|
||||||
|
|
||||||
crate::codex_config::write_codex_live_atomic(auth, cfg_text)?;
|
crate::codex_config::write_codex_live_atomic(auth, cfg_text)?;
|
||||||
crate::mcp::sync_enabled_to_codex(config)?;
|
// 注意:MCP 同步在 v3.7.0 中已通过 McpService 进行,不再在此调用
|
||||||
|
// sync_enabled_to_codex 使用旧的 config.mcp.codex 结构,在新架构中为空
|
||||||
|
// MCP 的启用/禁用应通过 McpService::toggle_app 进行
|
||||||
|
|
||||||
let cfg_text_after = crate::codex_config::read_and_validate_codex_config_text()?;
|
let cfg_text_after = crate::codex_config::read_and_validate_codex_config_text()?;
|
||||||
if let Some(manager) = config.get_manager_mut(&AppType::Codex) {
|
if let Some(manager) = config.get_manager_mut(&AppType::Codex) {
|
||||||
|
|||||||
@@ -76,19 +76,8 @@ fn sync_codex_provider_writes_auth_and_config() {
|
|||||||
|
|
||||||
let mut config = MultiAppConfig::default();
|
let mut config = MultiAppConfig::default();
|
||||||
|
|
||||||
// 添加入测 MCP 启用项,确保 sync_enabled_to_codex 会写入 TOML
|
// 注意:v3.7.0 后 MCP 同步由 McpService 独立处理,不再通过 provider 切换触发
|
||||||
config.mcp.codex.servers.insert(
|
// 此测试仅验证 auth.json 和 config.toml 基础配置的写入
|
||||||
"echo-server".into(),
|
|
||||||
json!({
|
|
||||||
"id": "echo-server",
|
|
||||||
"enabled": true,
|
|
||||||
"server": {
|
|
||||||
"type": "stdio",
|
|
||||||
"command": "echo",
|
|
||||||
"args": ["hello"]
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let provider_config = json!({
|
let provider_config = json!({
|
||||||
"auth": {
|
"auth": {
|
||||||
@@ -133,9 +122,10 @@ fn sync_codex_provider_writes_auth_and_config() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let toml_text = fs::read_to_string(&config_path).expect("read config.toml");
|
let toml_text = fs::read_to_string(&config_path).expect("read config.toml");
|
||||||
|
// 验证基础配置正确写入
|
||||||
assert!(
|
assert!(
|
||||||
toml_text.contains("command = \"echo\""),
|
toml_text.contains("base_url"),
|
||||||
"config.toml should contain serialized enabled MCP server"
|
"config.toml should contain base_url from provider config"
|
||||||
);
|
);
|
||||||
|
|
||||||
// 当前供应商应同步最新 config 文本
|
// 当前供应商应同步最新 config 文本
|
||||||
|
|||||||
+24
-8
@@ -65,6 +65,22 @@ function App() {
|
|||||||
const [envConflicts, setEnvConflicts] = useState<EnvConflict[]>([]);
|
const [envConflicts, setEnvConflicts] = useState<EnvConflict[]>([]);
|
||||||
const [showEnvBanner, setShowEnvBanner] = useState(false);
|
const [showEnvBanner, setShowEnvBanner] = useState(false);
|
||||||
|
|
||||||
|
// 保存最后一个有效的 provider,用于动画退出期间显示内容
|
||||||
|
const lastUsageProviderRef = useRef<Provider | null>(null);
|
||||||
|
const lastEditingProviderRef = useRef<Provider | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (usageProvider) {
|
||||||
|
lastUsageProviderRef.current = usageProvider;
|
||||||
|
}
|
||||||
|
}, [usageProvider]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (editingProvider) {
|
||||||
|
lastEditingProviderRef.current = editingProvider;
|
||||||
|
}
|
||||||
|
}, [editingProvider]);
|
||||||
|
|
||||||
const promptPanelRef = useRef<any>(null);
|
const promptPanelRef = useRef<any>(null);
|
||||||
const mcpPanelRef = useRef<any>(null);
|
const mcpPanelRef = useRef<any>(null);
|
||||||
const skillsPageRef = useRef<any>(null);
|
const skillsPageRef = useRef<any>(null);
|
||||||
@@ -512,7 +528,7 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="flex items-center gap-2 min-h-[40px]"
|
className="flex items-center gap-2 h-[32px]"
|
||||||
style={{ WebkitAppRegion: "no-drag" } as any}
|
style={{ WebkitAppRegion: "no-drag" } as any}
|
||||||
>
|
>
|
||||||
{currentView === "prompts" && (
|
{currentView === "prompts" && (
|
||||||
@@ -625,9 +641,7 @@ function App() {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main className="flex-1 pb-12 animate-fade-in ">
|
<main className="flex-1 pb-12 animate-fade-in ">
|
||||||
<div
|
<div className="pb-12">
|
||||||
className={cn("pb-12", currentView === "providers" ? "pt-6" : "pt-4")}
|
|
||||||
>
|
|
||||||
{renderContent()}
|
{renderContent()}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
@@ -641,7 +655,7 @@ function App() {
|
|||||||
|
|
||||||
<EditProviderDialog
|
<EditProviderDialog
|
||||||
open={Boolean(editingProvider)}
|
open={Boolean(editingProvider)}
|
||||||
provider={editingProvider}
|
provider={lastEditingProviderRef.current}
|
||||||
onOpenChange={(open) => {
|
onOpenChange={(open) => {
|
||||||
if (!open) {
|
if (!open) {
|
||||||
setEditingProvider(null);
|
setEditingProvider(null);
|
||||||
@@ -652,14 +666,16 @@ function App() {
|
|||||||
isProxyTakeover={isProxyRunning && isCurrentAppTakeoverActive}
|
isProxyTakeover={isProxyRunning && isCurrentAppTakeoverActive}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{usageProvider && (
|
{lastUsageProviderRef.current && (
|
||||||
<UsageScriptModal
|
<UsageScriptModal
|
||||||
provider={usageProvider}
|
provider={lastUsageProviderRef.current}
|
||||||
appId={activeApp}
|
appId={activeApp}
|
||||||
isOpen={Boolean(usageProvider)}
|
isOpen={Boolean(usageProvider)}
|
||||||
onClose={() => setUsageProvider(null)}
|
onClose={() => setUsageProvider(null)}
|
||||||
onSave={(script) => {
|
onSave={(script) => {
|
||||||
void saveUsageScript(usageProvider, script);
|
if (usageProvider) {
|
||||||
|
void saveUsageScript(usageProvider, script);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
import { motion } from "framer-motion";
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
import { ArrowLeft } from "lucide-react";
|
import { ArrowLeft } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
@@ -33,49 +33,52 @@ export const FullScreenPanel: React.FC<FullScreenPanelProps> = ({
|
|||||||
};
|
};
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
if (!isOpen) return null;
|
|
||||||
|
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<motion.div
|
<AnimatePresence>
|
||||||
initial={{ opacity: 0 }}
|
{isOpen && (
|
||||||
animate={{ opacity: 1 }}
|
<motion.div
|
||||||
transition={{ duration: 0.2 }}
|
initial={{ opacity: 0 }}
|
||||||
className="fixed inset-0 z-[60] flex flex-col"
|
animate={{ opacity: 1 }}
|
||||||
style={{ backgroundColor: "hsl(var(--background))" }}
|
exit={{ opacity: 0 }}
|
||||||
>
|
transition={{ duration: 0.2 }}
|
||||||
{/* Header */}
|
className="fixed inset-0 z-[60] flex flex-col"
|
||||||
<div
|
|
||||||
className="flex-shrink-0 py-3 border-b border-border-default"
|
|
||||||
style={{ backgroundColor: "hsl(var(--background))" }}
|
|
||||||
>
|
|
||||||
<div className="h-4 w-full" data-tauri-drag-region />
|
|
||||||
<div className="mx-auto max-w-[56rem] px-6 flex items-center gap-4">
|
|
||||||
<Button type="button" variant="outline" size="icon" onClick={onClose}>
|
|
||||||
<ArrowLeft className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
<h2 className="text-lg font-semibold text-foreground">{title}</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Content */}
|
|
||||||
<div className="flex-1 overflow-y-auto scroll-overlay">
|
|
||||||
<div className="mx-auto max-w-[56rem] px-6 py-6 space-y-6 w-full">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Footer */}
|
|
||||||
{footer && (
|
|
||||||
<div
|
|
||||||
className="flex-shrink-0 py-4 border-t border-border-default"
|
|
||||||
style={{ backgroundColor: "hsl(var(--background))" }}
|
style={{ backgroundColor: "hsl(var(--background))" }}
|
||||||
>
|
>
|
||||||
<div className="mx-auto max-w-[56rem] px-6 flex items-center justify-end gap-3">
|
{/* Header */}
|
||||||
{footer}
|
<div
|
||||||
|
className="flex-shrink-0 py-3 border-b border-border-default"
|
||||||
|
style={{ backgroundColor: "hsl(var(--background))" }}
|
||||||
|
>
|
||||||
|
<div className="h-4 w-full" data-tauri-drag-region />
|
||||||
|
<div className="mx-auto max-w-[56rem] px-6 flex items-center gap-4">
|
||||||
|
<Button type="button" variant="outline" size="icon" onClick={onClose}>
|
||||||
|
<ArrowLeft className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<h2 className="text-lg font-semibold text-foreground">{title}</h2>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className="flex-1 overflow-y-auto scroll-overlay">
|
||||||
|
<div className="mx-auto max-w-[56rem] px-6 py-6 space-y-6 w-full">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
{footer && (
|
||||||
|
<div
|
||||||
|
className="flex-shrink-0 py-4 border-t border-border-default"
|
||||||
|
style={{ backgroundColor: "hsl(var(--background))" }}
|
||||||
|
>
|
||||||
|
<div className="mx-auto max-w-[56rem] px-6 flex items-center justify-end gap-3">
|
||||||
|
{footer}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</motion.div>,
|
</AnimatePresence>,
|
||||||
document.body,
|
document.body,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user