fix(desktop): component cleanup + dead code removal + DeerFlow ai-elements

- ChatArea: DeerFlow ai-elements annotations for accessibility
- Conversation: remove unused Context, simplify message rendering
- Delete dead modules: audit-logger.ts, gateway-reconnect.ts
- Replace console.log with structured logger across components
- Add idb dependency for IndexedDB persistence
- Fix kernel-skills type safety improvements
This commit is contained in:
iven
2026-04-03 00:28:58 +08:00
parent 15d578c5bc
commit 5c74e74f2a
19 changed files with 78 additions and 341 deletions

View File

@@ -1,11 +1,13 @@
import { useState, useEffect, useRef, useCallback, useMemo, type MutableRefObject, type RefObject, type CSSProperties } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { List, type ListImperativeAPI } from 'react-window';
import { useChatStore, Message } from '../store/chatStore';
import { useChatStore, type Message } from '../store/chatStore';
import { useConversationStore } from '../store/chat/conversationStore';
import { useArtifactStore } from '../store/chat/artifactStore';
import { useConnectionStore } from '../store/connectionStore';
import { useAgentStore } from '../store/agentStore';
import { useConfigStore } from '../store/configStore';
import { listen, type UnlistenFn } from '@tauri-apps/api/event';
import { Paperclip, SquarePen, ArrowUp, MessageSquare, Download, X, FileText, Image as ImageIcon } from 'lucide-react';
import { Button, EmptyState, MessageListSkeleton, LoadingDots } from './ui';
import { ResizableChatLayout } from './ai/ResizableChatLayout';
@@ -45,11 +47,14 @@ const VIRTUALIZATION_THRESHOLD = 100;
export function ChatArea() {
const {
messages, currentAgent, isStreaming, isLoading, currentModel,
sendMessage: sendToGateway, setCurrentModel, initStreamListener,
messages, isStreaming, isLoading,
sendMessage: sendToGateway, initStreamListener,
newConversation, chatMode, setChatMode, suggestions,
totalInputTokens, totalOutputTokens,
} = useChatStore();
const currentAgent = useConversationStore((s) => s.currentAgent);
const currentModel = useConversationStore((s) => s.currentModel);
const setCurrentModel = useConversationStore((s) => s.setCurrentModel);
const {
artifacts, selectedArtifactId, artifactPanelOpen,
selectArtifact, setArtifactPanelOpen,
@@ -152,6 +157,29 @@ export function ChatArea() {
return unsub;
}, []);
// Listen for hand-execution-complete Tauri events
useEffect(() => {
let unlisten: UnlistenFn | undefined;
listen<{ approvalId: string; handId: string; success: boolean; error?: string | null }>(
'hand-execution-complete',
(event) => {
const { handId, success, error } = event.payload;
useChatStore.getState().addMessage({
id: crypto.randomUUID(),
role: 'hand',
content: success
? `Hand ${handId} 执行完成`
: `Hand ${handId} 执行失败: ${error || '未知错误'}`,
timestamp: new Date(),
handName: handId,
handStatus: success ? 'completed' : 'failed',
handResult: event.payload,
});
},
).then((fn) => { unlisten = fn; });
return () => { unlisten?.(); };
}, []);
// Auto-scroll to bottom on new messages
useEffect(() => {
if (scrollRef.current && !useVirtualization) {

View File

@@ -3,6 +3,7 @@ import { useAgentStore } from '../store/agentStore';
import { useConnectionStore } from '../store/connectionStore';
import { useConfigStore } from '../store/configStore';
import { toChatAgent, useChatStore } from '../store/chatStore';
import { useConversationStore } from '../store/chat/conversationStore';
import { Bot, Plus, X, Globe, Cat, Search, BarChart2, Sparkles } from 'lucide-react';
import { AgentOnboardingWizard } from './AgentOnboardingWizard';
import type { Clone } from '../store/agentStore';
@@ -13,7 +14,8 @@ export function CloneManager() {
const deleteClone = useAgentStore((s) => s.deleteClone);
const connectionState = useConnectionStore((s) => s.connectionState);
const quickConfig = useConfigStore((s) => s.quickConfig);
const { agents, currentAgent, setCurrentAgent } = useChatStore();
const { agents, currentAgent } = useConversationStore();
const setCurrentAgent = useChatStore((s) => s.setCurrentAgent);
const [showWizard, setShowWizard] = useState(false);
const connected = connectionState === 'connected';

View File

@@ -1,4 +1,5 @@
import { useState, useRef, useEffect } from 'react';
import { useConversationStore } from '../store/chat/conversationStore';
import { useChatStore } from '../store/chatStore';
import { MessageSquare, Trash2, SquarePen, Download, Check, X } from 'lucide-react';
import { EmptyConversations } from './ui';
@@ -171,15 +172,14 @@ function ConversationItem({
}
export function ConversationList() {
const {
conversations,
currentConversationId,
switchConversation,
deleteConversation,
} = useChatStore();
const conversations = useConversationStore((s) => s.conversations);
const currentConversationId = useConversationStore((s) => s.currentConversationId);
const { switchConversation, deleteConversation } = useChatStore();
// suppress unused-var lint — these facade actions are needed
void switchConversation; void deleteConversation;
const handleRename = (id: string, newTitle: string) => {
useChatStore.setState((state) => ({
useConversationStore.setState((state) => ({
conversations: state.conversations.map((c) =>
c.id === id ? { ...c, title: newTitle, updatedAt: new Date() } : c
),

View File

@@ -27,7 +27,7 @@ import {
type IdentityChangeProposal as Proposal,
type IdentitySnapshot,
} from '../lib/intelligence-client';
import { useChatStore } from '../store/chatStore';
import { useConversationStore } from '../store/chat/conversationStore';
import { Button, Badge } from './ui';
// === Error Parsing Utility ===
@@ -306,7 +306,7 @@ function HistoryItem({
// === Main Component ===
export function IdentityChangeProposalPanel() {
const { currentAgent } = useChatStore();
const currentAgent = useConversationStore((s) => s.currentAgent);
const [proposals, setProposals] = useState<Proposal[]>([]);
const [snapshots, setSnapshots] = useState<IdentitySnapshot[]>([]);
const [loading, setLoading] = useState(true);

View File

@@ -33,7 +33,7 @@ import {
type GraphEdge,
type MemoryType,
} from '../store/memoryGraphStore';
import { useChatStore } from '../store/chatStore';
import { useConversationStore } from '../store/chat/conversationStore';
import { cardHover, defaultTransition } from '../lib/animations';
// Mark as intentionally unused for future use
@@ -157,7 +157,7 @@ export function MemoryGraph({ className = '' }: MemoryGraphProps) {
const [showFilters, setShowFilters] = useState(false);
const [searchQuery, setSearchQuery] = useState('');
const { currentAgent } = useChatStore();
const currentAgent = useConversationStore((s) => s.currentAgent);
const agentId = currentAgent?.id || 'zclaw-main';
const {

View File

@@ -12,7 +12,7 @@ import {
type MemoryType,
type MemoryStats,
} from '../lib/intelligence-client';
import { useChatStore } from '../store/chatStore';
import { useConversationStore } from '../store/chat/conversationStore';
const TYPE_LABELS: Record<MemoryType, { label: string; emoji: string; color: string }> = {
fact: { label: '事实', emoji: '📋', color: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300' },
@@ -23,7 +23,7 @@ const TYPE_LABELS: Record<MemoryType, { label: string; emoji: string; color: str
};
export function MemoryPanel() {
const { currentAgent } = useChatStore();
const currentAgent = useConversationStore((s) => s.currentAgent);
const agentId = currentAgent?.id || 'zclaw-main';
const [memories, setMemories] = useState<MemoryEntry[]>([]);

View File

@@ -5,6 +5,7 @@ import { useConnectionStore } from '../store/connectionStore';
import { useAgentStore, type PluginStatus } from '../store/agentStore';
import { useConfigStore } from '../store/configStore';
import { toChatAgent, useChatStore, type CodeBlock } from '../store/chatStore';
import { useConversationStore } from '../store/chat/conversationStore';
import {
Wifi, WifiOff, Bot, BarChart3, Plug, RefreshCw,
MessageSquare, Cpu, FileText, User, Activity, Brain,
@@ -100,7 +101,9 @@ export function RightPanel() {
const workspaceInfo = useConfigStore((s) => s.workspaceInfo);
const quickConfig = useConfigStore((s) => s.quickConfig);
const { messages, currentModel, currentAgent, setCurrentAgent } = useChatStore();
const { messages, setCurrentAgent } = useChatStore();
const currentModel = useConversationStore((s) => s.currentModel);
const currentAgent = useConversationStore((s) => s.currentAgent);
const [activeTab, setActiveTab] = useState<'status' | 'files' | 'agent' | 'memory' | 'reflection' | 'autonomy' | 'evolution'>('status');
const [memoryViewMode, setMemoryViewMode] = useState<'list' | 'graph'>('list');
const [isEditingAgent, setIsEditingAgent] = useState(false);

View File

@@ -1,7 +1,7 @@
import { useState, useEffect } from 'react';
import { useConnectionStore } from '../../store/connectionStore';
import { useConfigStore } from '../../store/configStore';
import { useChatStore } from '../../store/chatStore';
import { useConversationStore } from '../../store/chat/conversationStore';
import { getStoredGatewayToken, setStoredGatewayToken } from '../../lib/gateway-client';
import { silentErrorHandler } from '../../lib/error-utils';
@@ -13,7 +13,7 @@ export function General() {
const disconnect = useConnectionStore((s) => s.disconnect);
const quickConfig = useConfigStore((s) => s.quickConfig);
const saveQuickConfig = useConfigStore((s) => s.saveQuickConfig);
const currentModel = useChatStore((s) => s.currentModel);
const currentModel = useConversationStore((s) => s.currentModel);
const [theme, setTheme] = useState<'light' | 'dark'>(quickConfig.theme || 'light');
const [autoStart, setAutoStart] = useState(quickConfig.autoStart ?? false);
const [showToolCalls, setShowToolCalls] = useState(quickConfig.showToolCalls ?? false);

View File

@@ -3,7 +3,7 @@ import { invoke } from '@tauri-apps/api/core';
import { getStoredGatewayToken, getStoredGatewayUrl } from '../../lib/gateway-client';
import { useConnectionStore } from '../../store/connectionStore';
import { useConfigStore } from '../../store/configStore';
import { useChatStore } from '../../store/chatStore';
import { useConversationStore } from '../../store/chat/conversationStore';
import { silentErrorHandler } from '../../lib/error-utils';
import { secureStorage } from '../../lib/secure-storage';
import { Plus, Pencil, Trash2, Star, Eye, EyeOff, AlertCircle, X, Zap, Check } from 'lucide-react';
@@ -166,7 +166,8 @@ export function ModelsAPI() {
const disconnect = useConnectionStore((s) => s.disconnect);
const quickConfig = useConfigStore((s) => s.quickConfig);
const loadModels = useConfigStore((s) => s.loadModels);
const { currentModel, setCurrentModel } = useChatStore();
const currentModel = useConversationStore((s) => s.currentModel);
const setCurrentModel = useConversationStore((s) => s.setCurrentModel);
const [gatewayUrl, setGatewayUrl] = useState(getStoredGatewayUrl());
const [gatewayToken, setGatewayToken] = useState(quickConfig.gatewayToken || getStoredGatewayToken());

View File

@@ -1,41 +1,4 @@
import { useRef, useEffect, useState, createContext, useContext, useMemo, type ReactNode } from 'react';
// ---------------------------------------------------------------------------
// ConversationContext — shared state for child ai-elements components
// ---------------------------------------------------------------------------
interface ConversationContextValue {
isStreaming: boolean;
setIsStreaming: (v: boolean) => void;
messages: unknown[];
setMessages: (msgs: unknown[]) => void;
}
const ConversationContext = createContext<ConversationContextValue | null>(null);
export function useConversationContext() {
const ctx = useContext(ConversationContext);
if (!ctx) {
throw new Error('useConversationContext must be used within ConversationProvider');
}
return ctx;
}
export function ConversationProvider({ children }: { children: ReactNode }) {
const [isStreaming, setIsStreaming] = useState(false);
const [messages, setMessages] = useState<unknown[]>([]);
const value = useMemo(
() => ({ isStreaming, setIsStreaming, messages, setMessages }),
[isStreaming, messages],
);
return (
<ConversationContext.Provider value={value}>
{children}
</ConversationContext.Provider>
);
}
import { useRef, useEffect, type ReactNode } from 'react';
// ---------------------------------------------------------------------------
// Conversation container with auto-stick-to-bottom scroll behavior

View File

@@ -1,4 +1,4 @@
export { Conversation, ConversationProvider, useConversationContext } from './Conversation';
export { Conversation } from './Conversation';
export { ReasoningBlock } from './ReasoningBlock';
export { StreamingText } from './StreamingText';
export { ChatMode, type ChatModeType, type ChatModeConfig, CHAT_MODES } from './ChatMode';