fix(desktop): 修复 10 个 agent 对话测试发现的缺陷
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

- BUG-001+002 P0: 模型选择器合并 SaaS 可用模型列表
- BUG-003 P1: 修复 relay 错误消息重复显示
- BUG-005 P1: 设置页面显示实际连接模式和地址
- BUG-006 P2: 统一 UI 语言为中文
- BUG-009 P3: 错误时隐藏建议按钮
- BUG-010 P3: 密码切换按钮添加 aria-label

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-04-04 01:31:28 +08:00
parent 912f117ea3
commit 769bfdf5d6
5 changed files with 30 additions and 10 deletions

View File

@@ -7,6 +7,7 @@ import { useArtifactStore } from '../store/chat/artifactStore';
import { useConnectionStore } from '../store/connectionStore';
import { useAgentStore } from '../store/agentStore';
import { useConfigStore } from '../store/configStore';
import { useSaaSStore } from '../store/saasStore';
import { type UnlistenFn } from '@tauri-apps/api/event';
import { safeListenEvent } from '../lib/safe-tauri';
import { Paperclip, SquarePen, ArrowUp, MessageSquare, Download, X, FileText, Image as ImageIcon, Search } from 'lucide-react';
@@ -63,7 +64,21 @@ export function ChatArea() {
const connectionState = useConnectionStore((s) => s.connectionState);
const { activeClassroom, classroomOpen, closeClassroom, generating, progressPercent, progressActivity, error: classroomError, clearError: clearClassroomError } = useClassroomStore();
const clones = useAgentStore((s) => s.clones);
const models = useConfigStore((s) => s.models);
const configModels = useConfigStore((s) => s.models);
const saasModels = useSaaSStore((s) => s.availableModels);
const isLoggedIn = useSaaSStore((s) => s.isLoggedIn);
// Merge models: SaaS available models take priority when logged in
const models = useMemo(() => {
if (isLoggedIn && saasModels.length > 0) {
return saasModels.map(m => ({
id: m.alias || m.id,
name: m.alias || m.id,
provider: m.provider_id,
}));
}
return configModels;
}, [isLoggedIn, saasModels, configModels]);
const [input, setInput] = useState('');
const [pendingFiles, setPendingFiles] = useState<File[]>([]);
@@ -418,8 +433,8 @@ export function ChatArea() {
) : (
<EmptyState
icon={<MessageSquare className="w-8 h-8" />}
title="Welcome to ZCLAW"
description={connected ? 'Send a message to start the conversation.' : 'Please connect to Gateway first in Settings.'}
title="欢迎使用 ZCLAW"
description={connected ? '发送消息开始对话。' : '请先在设置中连接 Gateway。'}
/>
)}
</motion.div>
@@ -457,7 +472,7 @@ export function ChatArea() {
<div className="p-4 bg-white dark:bg-gray-900">
<div className="max-w-4xl mx-auto">
{/* Suggestion chips */}
{!isStreaming && suggestions.length > 0 && (
{!isStreaming && suggestions.length > 0 && !messages.some(m => m.error) && (
<SuggestionChips
suggestions={suggestions}
onSelect={(text) => { setInput(text); textareaRef.current?.focus(); }}

View File

@@ -440,6 +440,7 @@ export function LoginPage() {
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-white/30 hover:text-white/60 cursor-pointer"
tabIndex={-1}
aria-label={showPassword ? '隐藏密码' : '显示密码'}
>
{showPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</button>

View File

@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
import { useConnectionStore } from '../../store/connectionStore';
import { useConfigStore } from '../../store/configStore';
import { useConversationStore } from '../../store/chat/conversationStore';
import { useSaaSStore } from '../../store/saasStore';
import { getStoredGatewayToken, setStoredGatewayToken } from '../../lib/gateway-client';
import { silentErrorHandler } from '../../lib/error-utils';
@@ -22,6 +23,7 @@ export function General() {
const connected = connectionState === 'connected';
const connecting = connectionState === 'connecting' || connectionState === 'reconnecting';
const saasUrl = useSaaSStore((s) => s.saasUrl);
// 同步主题设置
useEffect(() => {
@@ -97,7 +99,9 @@ export function General() {
</div>
<div className="flex justify-between items-center">
<span className="text-sm text-gray-700"></span>
<span className="text-sm text-gray-500 font-mono">ws://127.0.0.1:50051</span>
<span className="text-sm text-gray-500 font-mono">
{gatewayVersion === 'saas-relay' && saasUrl ? saasUrl : 'ws://127.0.0.1:50051'}
</span>
</div>
<div className="flex justify-between items-center">
<span className="text-sm text-gray-700">Token</span>

View File

@@ -100,8 +100,8 @@ export function EmptyConversations({ action, className, size }: PrebuiltEmptySta
return (
<EmptyState
icon={<Inbox className="w-8 h-8" />}
title="No conversations"
description="Your conversation history will appear here."
title="暂无对话"
description="你的对话历史将显示在这里。"
action={action}
className={className}
size={size}
@@ -177,8 +177,8 @@ export function EmptyAgents({ action, className, size }: PrebuiltEmptyStateProps
* Empty state for welcome screen.
*/
export function WelcomeEmptyState({
title = "Welcome to ZCLAW",
description = "Send a message to start the conversation.",
title = "欢迎使用 ZCLAW",
description = "发送消息开始对话。",
connected = true,
action,
className,

View File

@@ -410,7 +410,7 @@ export const useStreamStore = create<StreamState>()(
_chat?.updateMessages(msgs =>
msgs.map(m =>
m.id === assistantId
? { ...m, content: `⚠️ ${error}`, streaming: false, error }
? { ...m, content: '', streaming: false, error }
: m.role === 'user' && m.optimistic && m.timestamp.getTime() >= streamStartTime
? { ...m, optimistic: false }
: m