feat: 增加对非数组值的处理,优化数组、文件和标签渲染器

This commit is contained in:
catlog22
2026-02-07 22:26:11 +08:00
parent ece02ab32a
commit dc9a1a1efb
4 changed files with 42 additions and 19 deletions

View File

@@ -58,6 +58,10 @@ function StringRenderer({ value, className }: { value: string; className?: strin
} }
function ArrayRenderer({ value, className }: { value: unknown[]; className?: string }) { function ArrayRenderer({ value, className }: { value: unknown[]; className?: string }) {
if (!Array.isArray(value)) {
return <FieldRenderer value={value} type="auto" className={className} />;
}
if (value.length === 0) { if (value.length === 0) {
return <span className="text-muted-foreground italic">Empty</span>; return <span className="text-muted-foreground italic">Empty</span>;
} }
@@ -98,6 +102,10 @@ function ObjectRenderer({ value, className }: { value: Record<string, unknown>;
} }
function FilesRenderer({ value, className }: { value: Array<{ path: string }>; className?: string }) { function FilesRenderer({ value, className }: { value: Array<{ path: string }>; className?: string }) {
if (!Array.isArray(value)) {
return <FieldRenderer value={value} type="auto" className={className} />;
}
if (value.length === 0) { if (value.length === 0) {
return <span className="text-muted-foreground italic">No files</span>; return <span className="text-muted-foreground italic">No files</span>;
} }
@@ -118,6 +126,10 @@ function FilesRenderer({ value, className }: { value: Array<{ path: string }>; c
} }
function TagsRenderer({ value, className }: { value: string[]; className?: string }) { function TagsRenderer({ value, className }: { value: string[]; className?: string }) {
if (!Array.isArray(value)) {
return <FieldRenderer value={value} type="auto" className={className} />;
}
if (value.length === 0) { if (value.length === 0) {
return <span className="text-muted-foreground italic">No tags</span>; return <span className="text-muted-foreground italic">No tags</span>;
} }

View File

@@ -10,7 +10,7 @@ import {
RefreshCw, RefreshCw,
} from 'lucide-react'; } from 'lucide-react';
import { Button } from '@/components/ui/Button'; import { Button } from '@/components/ui/Button';
import { TabsNavigation, type TabItem } from '@/components/ui/TabsNavigation'; import { TabsNavigation } from '@/components/ui/TabsNavigation';
import { import {
ProviderList, ProviderList,
ProviderModal, ProviderModal,
@@ -25,7 +25,7 @@ import {
ManageModelsModal, ManageModelsModal,
} from '@/components/api-settings'; } from '@/components/api-settings';
import { ConfigSync } from '@/components/shared'; import { ConfigSync } from '@/components/shared';
import { useProviders, useEndpoints, useModelPools, useCliSettings } from '@/hooks/useApiSettings'; import { useProviders, useEndpoints, useModelPools, useCliSettings, useSyncApiConfig } from '@/hooks/useApiSettings';
import { useNotifications } from '@/hooks/useNotifications'; import { useNotifications } from '@/hooks/useNotifications';
// Tab type definitions // Tab type definitions
@@ -35,6 +35,7 @@ export function ApiSettingsPage() {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const { success, error } = useNotifications(); const { success, error } = useNotifications();
const [activeTab, setActiveTab] = useState<TabType>('providers'); const [activeTab, setActiveTab] = useState<TabType>('providers');
const syncMutation = useSyncApiConfig();
// Get providers, endpoints, model pools, and CLI settings data // Get providers, endpoints, model pools, and CLI settings data
const { providers } = useProviders(); const { providers } = useProviders();
@@ -170,10 +171,19 @@ export function ApiSettingsPage() {
// Sync to CodexLens handler // Sync to CodexLens handler
const handleSyncToCodexLens = async (providerId: string) => { const handleSyncToCodexLens = async (providerId: string) => {
const providerName = providers.find((p) => p.id === providerId)?.name ?? providerId;
try { try {
// TODO: Implement actual sync API call const result = await syncMutation.mutateAsync();
// For now, just show a success message const messageParts = [
success(formatMessage({ id: 'apiSettings.messages.configSynced' })); providerName,
result.yamlPath ?? result.message,
].filter(Boolean);
success(
formatMessage({ id: 'apiSettings.messages.configSynced' }),
messageParts.length > 0 ? messageParts.join('\n') : undefined
);
} catch (err) { } catch (err) {
error(formatMessage({ id: 'apiSettings.providers.saveError' })); error(formatMessage({ id: 'apiSettings.providers.saveError' }));
} }

View File

@@ -25,6 +25,7 @@ import {
Package, Package,
Home, Home,
Folder, Folder,
FolderOpen,
Calendar, Calendar,
File, File,
ArrowUpCircle, ArrowUpCircle,
@@ -36,6 +37,7 @@ import { Input } from '@/components/ui/Input';
import { Badge } from '@/components/ui/Badge'; import { Badge } from '@/components/ui/Badge';
import { ThemeSelector } from '@/components/shared/ThemeSelector'; import { ThemeSelector } from '@/components/shared/ThemeSelector';
import { useTheme } from '@/hooks'; import { useTheme } from '@/hooks';
import { toast } from 'sonner';
import { useConfigStore, selectCliTools, selectDefaultCliTool, selectUserPreferences } from '@/stores/configStore'; import { useConfigStore, selectCliTools, selectDefaultCliTool, selectUserPreferences } from '@/stores/configStore';
import type { CliToolConfig, UserPreferences } from '@/types/store'; import type { CliToolConfig, UserPreferences } from '@/types/store';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
@@ -60,9 +62,6 @@ import {
const ENV_FILE_TOOLS = new Set(['gemini', 'qwen', 'opencode']); const ENV_FILE_TOOLS = new Set(['gemini', 'qwen', 'opencode']);
/** Tools that use --settings for Claude CLI settings file */ /** Tools that use --settings for Claude CLI settings file */
const SETTINGS_FILE_TOOLS = new Set(['claude']); const SETTINGS_FILE_TOOLS = new Set(['claude']);
/** Tools that don't need any config file */
const NO_CONFIG_FILE_TOOLS = new Set(['codex']);
function getConfigFileType(toolId: string): 'envFile' | 'settingsFile' | 'none' { function getConfigFileType(toolId: string): 'envFile' | 'settingsFile' | 'none' {
if (ENV_FILE_TOOLS.has(toolId)) return 'envFile'; if (ENV_FILE_TOOLS.has(toolId)) return 'envFile';
if (SETTINGS_FILE_TOOLS.has(toolId)) return 'settingsFile'; if (SETTINGS_FILE_TOOLS.has(toolId)) return 'settingsFile';
@@ -874,18 +873,13 @@ export function SettingsPage() {
throw new Error(`HTTP ${res.status}`); throw new Error(`HTTP ${res.status}`);
} }
// Show success notification via a brief visual indicator toast.success(formatMessage({ id: 'settings.cliTools.configSaved' }), {
const toast = document.createElement('div'); description: toolId,
toast.className = 'fixed bottom-4 right-4 z-50 bg-green-600 text-white px-4 py-2 rounded-lg shadow-lg text-sm animate-in fade-in slide-in-from-bottom-2'; });
toast.textContent = formatMessage({ id: 'settings.cliTools.configSaved' });
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 3000);
} catch { } catch {
const toast = document.createElement('div'); toast.error(formatMessage({ id: 'settings.cliTools.configSaveError' }), {
toast.className = 'fixed bottom-4 right-4 z-50 bg-red-600 text-white px-4 py-2 rounded-lg shadow-lg text-sm animate-in fade-in slide-in-from-bottom-2'; description: toolId,
toast.textContent = formatMessage({ id: 'settings.cliTools.configSaveError' }); });
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 4000);
} finally { } finally {
setSavingTools((prev) => { setSavingTools((prev) => {
const next = new Set(prev); const next = new Set(prev);

View File

@@ -43,6 +43,13 @@ const defaultCliTools: Record<string, CliToolConfig> = {
tags: [], tags: [],
type: 'builtin', type: 'builtin',
}, },
opencode: {
enabled: true,
primaryModel: 'opencode/glm-4.7-free',
secondaryModel: 'opencode/glm-4.7-free',
tags: [],
type: 'builtin',
},
}; };
// Default API endpoints // Default API endpoints