feat(settings): add 'Apply to Claude Code extension' toggle
- Apply immediately on save (write or remove primaryApiKey) - Honor setting on provider switch (enabled: write for non-official, remove for official; disabled: no auto writes) - Remove per-provider Claude plugin buttons from ProviderList - Upsert primaryApiKey=any preserving other fields; respect override dir - Add zh/en i18n for the new setting
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Provider } from "../types";
|
||||
import { Play, Edit3, Trash2, CheckCircle2, Users, Check } from "lucide-react";
|
||||
@@ -58,55 +58,7 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const [claudeApplied, setClaudeApplied] = useState<boolean>(false);
|
||||
|
||||
// 检查 Claude 插件配置是否已应用
|
||||
useEffect(() => {
|
||||
const checkClaude = async () => {
|
||||
if (appType !== "claude" || !currentProviderId) {
|
||||
setClaudeApplied(false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const applied = await window.api.isClaudePluginApplied();
|
||||
setClaudeApplied(applied);
|
||||
} catch (error) {
|
||||
console.error(t("console.setupListenerFailed"), error);
|
||||
setClaudeApplied(false);
|
||||
}
|
||||
};
|
||||
checkClaude();
|
||||
}, [appType, currentProviderId, providers, t]);
|
||||
|
||||
const handleApplyToClaudePlugin = async () => {
|
||||
try {
|
||||
await window.api.applyClaudePluginConfig({ official: false });
|
||||
onNotify?.(t("notifications.appliedToClaudePlugin"), "success", 3000);
|
||||
setClaudeApplied(true);
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
const msg =
|
||||
error && error.message
|
||||
? error.message
|
||||
: t("notifications.syncClaudePluginFailed");
|
||||
onNotify?.(msg, "error", 5000);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveFromClaudePlugin = async () => {
|
||||
try {
|
||||
await window.api.applyClaudePluginConfig({ official: true });
|
||||
onNotify?.(t("notifications.removedFromClaudePlugin"), "success", 3000);
|
||||
setClaudeApplied(false);
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
const msg =
|
||||
error && error.message
|
||||
? error.message
|
||||
: t("notifications.syncClaudePluginFailed");
|
||||
onNotify?.(msg, "error", 5000);
|
||||
}
|
||||
};
|
||||
// 列表页不再提供 Claude 插件按钮,统一在“设置”中控制
|
||||
|
||||
// 对供应商列表进行排序
|
||||
const sortedProviders = Object.values(providers).sort((a, b) => {
|
||||
@@ -201,34 +153,6 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 ml-4">
|
||||
{appType === "claude" ? (
|
||||
<div className="flex-shrink-0">
|
||||
{provider.category !== "official" && isCurrent && (
|
||||
<button
|
||||
onClick={() =>
|
||||
claudeApplied
|
||||
? handleRemoveFromClaudePlugin()
|
||||
: handleApplyToClaudePlugin()
|
||||
}
|
||||
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",
|
||||
claudeApplied
|
||||
? "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-green-300 hover:text-green-600 hover:bg-green-50 dark:border-gray-600 dark:text-gray-300 dark:hover:border-green-700 dark:hover:text-green-400 dark:hover:bg-green-900/20",
|
||||
)}
|
||||
title={
|
||||
claudeApplied
|
||||
? t("provider.removeFromClaudePlugin")
|
||||
: t("provider.applyToClaudePlugin")
|
||||
}
|
||||
>
|
||||
{claudeApplied
|
||||
? t("provider.removeFromClaudePlugin")
|
||||
: t("provider.applyToClaudePlugin")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
<button
|
||||
onClick={() => onSwitch(provider.id)}
|
||||
disabled={isCurrent}
|
||||
|
||||
@@ -50,6 +50,7 @@ export default function SettingsModal({
|
||||
const [settings, setSettings] = useState<Settings>({
|
||||
showInTray: true,
|
||||
minimizeToTrayOnClose: true,
|
||||
enableClaudePluginIntegration: false,
|
||||
claudeConfigDir: undefined,
|
||||
codexConfigDir: undefined,
|
||||
language: persistedLanguage,
|
||||
@@ -116,6 +117,11 @@ export default function SettingsModal({
|
||||
setSettings({
|
||||
showInTray,
|
||||
minimizeToTrayOnClose,
|
||||
enableClaudePluginIntegration:
|
||||
typeof (loadedSettings as any)?.enableClaudePluginIntegration ===
|
||||
"boolean"
|
||||
? (loadedSettings as any).enableClaudePluginIntegration
|
||||
: false,
|
||||
claudeConfigDir:
|
||||
typeof (loadedSettings as any)?.claudeConfigDir === "string"
|
||||
? (loadedSettings as any).claudeConfigDir
|
||||
@@ -184,6 +190,16 @@ export default function SettingsModal({
|
||||
language: selectedLanguage,
|
||||
};
|
||||
await window.api.saveSettings(payload);
|
||||
// 立即生效:根据开关无条件写入/移除 ~/.claude/config.json
|
||||
try {
|
||||
if (payload.enableClaudePluginIntegration) {
|
||||
await window.api.applyClaudePluginConfig({ official: false });
|
||||
} else {
|
||||
await window.api.applyClaudePluginConfig({ official: true });
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("[Settings] Apply Claude plugin config on save failed", e);
|
||||
}
|
||||
setSettings(payload);
|
||||
try {
|
||||
window.localStorage.setItem("language", selectedLanguage);
|
||||
@@ -506,6 +522,28 @@ export default function SettingsModal({
|
||||
className="w-4 h-4 text-blue-500 rounded focus:ring-blue-500/20"
|
||||
/>
|
||||
</label>
|
||||
{/* Claude 插件联动开关 */}
|
||||
<label className="flex items-center justify-between">
|
||||
<div>
|
||||
<span className="text-sm text-gray-900 dark:text-gray-100">
|
||||
{t("settings.enableClaudePluginIntegration")}
|
||||
</span>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1 max-w-[34rem]">
|
||||
{t("settings.enableClaudePluginIntegrationDescription")}
|
||||
</p>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={!!settings.enableClaudePluginIntegration}
|
||||
onChange={(e) =>
|
||||
setSettings((prev) => ({
|
||||
...prev,
|
||||
enableClaudePluginIntegration: e.target.checked,
|
||||
}))
|
||||
}
|
||||
className="w-4 h-4 text-blue-500 rounded focus:ring-blue-500/20"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user