Various functional updates and improvements across provider dialogs, MCP panel, skills page, and internationalization. Provider Dialogs: - AddProviderDialog * Simplified form state management * Improved preset selection workflow * Better validation error messages * Enhanced template variable handling - EditProviderDialog * Streamlined edit flow with better state synchronization * Improved handling of live config backfilling * Better error recovery for failed updates * Enhanced integration with parent components MCP & Skills: - UnifiedMcpPanel * Reduced complexity from 140+ to ~95 lines * Improved multi-app server management * Better server type detection (stdio/http) * Enhanced server status indicators * Cleaner integration with MCP form modal - SkillsPage * Simplified navigation and state management * Better integration with RepoManagerPanel * Improved error handling for repository operations * Enhanced loading states - SkillCard * Minor layout adjustments * Better action button placement Environment & Configuration: - EnvWarningBanner * Improved conflict detection messages * Better visual hierarchy for warnings * Enhanced dismissal behavior - tauri.conf.json * Updated build configuration * Added new window management options Internationalization: - en.json & zh.json * Added 17 new translation keys for new features * Updated existing keys for better clarity * Added translations for new settings page * Improved consistency across UI text Code Cleanup: - mutations.ts * Removed 14 lines of unused mutation definitions * Cleaned up deprecated query invalidation logic * Better type safety for mutation parameters Overall Impact: - Reduced total lines by 51 (-10% in affected files) - Improved component integration and data flow - Better error handling and user feedback - Enhanced i18n coverage for new features These changes improve the overall polish and integration of various components while removing technical debt and unused code.
143 lines
4.0 KiB
TypeScript
143 lines
4.0 KiB
TypeScript
import { useCallback, useEffect, useMemo, useState } from "react";
|
||
import { useTranslation } from "react-i18next";
|
||
import { Save } from "lucide-react";
|
||
import { Button } from "@/components/ui/button";
|
||
import { FullScreenPanel } from "@/components/common/FullScreenPanel";
|
||
import type { Provider } from "@/types";
|
||
import {
|
||
ProviderForm,
|
||
type ProviderFormValues,
|
||
} from "@/components/providers/forms/ProviderForm";
|
||
import { providersApi, vscodeApi, type AppId } from "@/lib/api";
|
||
|
||
interface EditProviderDialogProps {
|
||
open: boolean;
|
||
provider: Provider | null;
|
||
onOpenChange: (open: boolean) => void;
|
||
onSubmit: (provider: Provider) => Promise<void> | void;
|
||
appId: AppId;
|
||
}
|
||
|
||
export function EditProviderDialog({
|
||
open,
|
||
provider,
|
||
onOpenChange,
|
||
onSubmit,
|
||
appId,
|
||
}: EditProviderDialogProps) {
|
||
const { t } = useTranslation();
|
||
|
||
// 默认使用传入的 provider.settingsConfig,若当前编辑对象是"当前生效供应商",则尝试读取实时配置替换初始值
|
||
const [liveSettings, setLiveSettings] = useState<Record<
|
||
string,
|
||
unknown
|
||
> | null>(null);
|
||
|
||
useEffect(() => {
|
||
let cancelled = false;
|
||
const load = async () => {
|
||
if (!open || !provider) {
|
||
setLiveSettings(null);
|
||
return;
|
||
}
|
||
try {
|
||
const currentId = await providersApi.getCurrent(appId);
|
||
if (currentId && provider.id === currentId) {
|
||
try {
|
||
const live = (await vscodeApi.getLiveProviderSettings(
|
||
appId,
|
||
)) as Record<string, unknown>;
|
||
if (!cancelled && live && typeof live === "object") {
|
||
setLiveSettings(live);
|
||
}
|
||
} catch {
|
||
// 读取实时配置失败则回退到 SSOT(不打断编辑流程)
|
||
if (!cancelled) setLiveSettings(null);
|
||
}
|
||
} else {
|
||
if (!cancelled) setLiveSettings(null);
|
||
}
|
||
} finally {
|
||
// no-op
|
||
}
|
||
};
|
||
void load();
|
||
return () => {
|
||
cancelled = true;
|
||
};
|
||
}, [open, provider, appId]);
|
||
|
||
const initialSettingsConfig = useMemo(() => {
|
||
return (liveSettings ?? provider?.settingsConfig ?? {}) as Record<
|
||
string,
|
||
unknown
|
||
>;
|
||
}, [liveSettings, provider]);
|
||
|
||
const handleSubmit = useCallback(
|
||
async (values: ProviderFormValues) => {
|
||
if (!provider) return;
|
||
|
||
const parsedConfig = JSON.parse(values.settingsConfig) as Record<
|
||
string,
|
||
unknown
|
||
>;
|
||
|
||
const updatedProvider: Provider = {
|
||
...provider,
|
||
name: values.name.trim(),
|
||
notes: values.notes?.trim() || undefined,
|
||
websiteUrl: values.websiteUrl?.trim() || undefined,
|
||
settingsConfig: parsedConfig,
|
||
...(values.presetCategory ? { category: values.presetCategory } : {}),
|
||
// 保留或更新 meta 字段
|
||
...(values.meta ? { meta: values.meta } : {}),
|
||
};
|
||
|
||
await onSubmit(updatedProvider);
|
||
onOpenChange(false);
|
||
},
|
||
[onSubmit, onOpenChange, provider],
|
||
);
|
||
|
||
if (!provider) {
|
||
return null;
|
||
}
|
||
|
||
return (
|
||
<FullScreenPanel
|
||
isOpen={open}
|
||
title={t("provider.editProvider")}
|
||
onClose={() => onOpenChange(false)}
|
||
>
|
||
<ProviderForm
|
||
appId={appId}
|
||
providerId={provider.id}
|
||
submitLabel={t("common.save")}
|
||
onSubmit={handleSubmit}
|
||
onCancel={() => onOpenChange(false)}
|
||
initialData={{
|
||
name: provider.name,
|
||
notes: provider.notes,
|
||
websiteUrl: provider.websiteUrl,
|
||
// 若读取到实时配置则优先使用
|
||
settingsConfig: initialSettingsConfig,
|
||
category: provider.category,
|
||
meta: provider.meta,
|
||
}}
|
||
showButtons={false}
|
||
/>
|
||
<div className="flex justify-end pt-6">
|
||
<Button
|
||
type="submit"
|
||
form="provider-form"
|
||
className="bg-primary text-primary-foreground hover:bg-primary/90"
|
||
>
|
||
<Save className="h-4 w-4 mr-2" />
|
||
{t("common.save")}
|
||
</Button>
|
||
</div>
|
||
</FullScreenPanel>
|
||
);
|
||
}
|