系统合并
This commit is contained in:
1 parent
4cc8b3198b
commit
fea3cc741f
2 files changed
+226
No files matched your search
@@ -0,0 +1,81 @@
|
||||
import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react';
|
||||
|
||||
export interface ChatMessage {
|
||||
id: string;
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
timestamp: number;
|
||||
type?: 'general' | 'agent' | 'compare';
|
||||
agentName?: string;
|
||||
}
|
||||
|
||||
interface ChatContextType {
|
||||
messages: ChatMessage[];
|
||||
addMessage: (role: 'user' | 'assistant', content: string, type?: 'general' | 'agent' | 'compare', agentName?: string) => void;
|
||||
clearMessages: () => void;
|
||||
deleteMessage: (id: string) => void;
|
||||
}
|
||||
|
||||
const ChatContext = createContext<ChatContextType | undefined>(undefined);
|
||||
|
||||
export function ChatProvider({ children }: { children: ReactNode }) {
|
||||
const [messages, setMessages] = useState<ChatMessage[]>(() => {
|
||||
try {
|
||||
const stored = localStorage.getItem('qa-chat-history');
|
||||
return stored ? JSON.parse(stored) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
const saveToStorage = useCallback((newMessages: ChatMessage[]) => {
|
||||
try {
|
||||
localStorage.setItem('qa-chat-history', JSON.stringify(newMessages));
|
||||
} catch (error) {
|
||||
console.error('Failed to save chat history:', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const addMessage = useCallback((role: 'user' | 'assistant', content: string, type?: 'general' | 'agent' | 'compare', agentName?: string) => {
|
||||
const newMessage: ChatMessage = {
|
||||
id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
||||
role,
|
||||
content,
|
||||
timestamp: Date.now(),
|
||||
type,
|
||||
agentName,
|
||||
};
|
||||
setMessages((prev) => {
|
||||
const updated = [...prev, newMessage];
|
||||
saveToStorage(updated);
|
||||
return updated;
|
||||
});
|
||||
}, [saveToStorage]);
|
||||
|
||||
const clearMessages = useCallback(() => {
|
||||
setMessages([]);
|
||||
localStorage.removeItem('qa-chat-history');
|
||||
}, []);
|
||||
|
||||
const deleteMessage = useCallback((id: string) => {
|
||||
setMessages((prev) => {
|
||||
const updated = prev.filter((msg) => msg.id !== id);
|
||||
saveToStorage(updated);
|
||||
return updated;
|
||||
});
|
||||
}, [saveToStorage]);
|
||||
|
||||
return (
|
||||
<ChatContext.Provider value={{ messages, addMessage, clearMessages, deleteMessage }}>
|
||||
{children}
|
||||
</ChatContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useChatContext() {
|
||||
const context = useContext(ChatContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useChatContext must be used within a ChatProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
||||
|
||||
// 主题类型
|
||||
export type ThemeType = 'dark' | 'light';
|
||||
|
||||
// 主题配置
|
||||
export interface ThemeConfig {
|
||||
type: ThemeType;
|
||||
name: string;
|
||||
colors: {
|
||||
background: string;
|
||||
backgroundSecondary: string;
|
||||
text: string;
|
||||
textSecondary: string;
|
||||
primary: string;
|
||||
primaryHover: string;
|
||||
border: string;
|
||||
accent: string;
|
||||
success: string;
|
||||
warning: string;
|
||||
error: string;
|
||||
card: string;
|
||||
cardHover: string;
|
||||
input: string;
|
||||
inputBorder: string;
|
||||
overlay: string;
|
||||
};
|
||||
}
|
||||
|
||||
// 主题配置
|
||||
const themes: Record<ThemeType, ThemeConfig> = {
|
||||
dark: {
|
||||
type: 'dark',
|
||||
name: '深色主题',
|
||||
colors: {
|
||||
background: 'slate-900',
|
||||
backgroundSecondary: 'slate-800',
|
||||
text: 'white',
|
||||
textSecondary: 'slate-400',
|
||||
primary: 'blue-500',
|
||||
primaryHover: 'blue-400',
|
||||
border: 'blue-800',
|
||||
accent: 'cyan-400',
|
||||
success: 'emerald-500',
|
||||
warning: 'amber-500',
|
||||
error: 'rose-500',
|
||||
card: 'slate-800',
|
||||
cardHover: 'slate-700',
|
||||
input: 'slate-800',
|
||||
inputBorder: 'blue-800',
|
||||
overlay: 'slate-900/50',
|
||||
},
|
||||
},
|
||||
light: {
|
||||
type: 'light',
|
||||
name: '红白主题',
|
||||
colors: {
|
||||
background: 'white',
|
||||
backgroundSecondary: 'red-50',
|
||||
text: 'gray-900',
|
||||
textSecondary: 'gray-600',
|
||||
primary: 'red-600',
|
||||
primaryHover: 'red-500',
|
||||
border: 'red-200',
|
||||
accent: 'red-700',
|
||||
success: 'green-600',
|
||||
warning: 'amber-500',
|
||||
error: 'red-600',
|
||||
card: 'white',
|
||||
cardHover: 'red-50',
|
||||
input: 'white',
|
||||
inputBorder: 'red-200',
|
||||
overlay: 'black/30',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// 主题上下文
|
||||
interface ThemeContextType {
|
||||
theme: ThemeType;
|
||||
themeConfig: ThemeConfig;
|
||||
toggleTheme: () => void;
|
||||
setTheme: (theme: ThemeType) => void;
|
||||
}
|
||||
|
||||
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
||||
|
||||
// 主题提供者
|
||||
interface ThemeProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
|
||||
const [theme, setThemeState] = useState<ThemeType>(() => {
|
||||
// 从 localStorage 读取保存的主题
|
||||
const saved = localStorage.getItem('strategy-theme');
|
||||
return (saved as ThemeType) || 'dark';
|
||||
});
|
||||
|
||||
// 切换主题
|
||||
const toggleTheme = () => {
|
||||
setThemeState((prev) => (prev === 'dark' ? 'light' : 'dark'));
|
||||
};
|
||||
|
||||
// 设置特定主题
|
||||
const setTheme = (newTheme: ThemeType) => {
|
||||
setThemeState(newTheme);
|
||||
};
|
||||
|
||||
// 保存到 localStorage
|
||||
useEffect(() => {
|
||||
localStorage.setItem('strategy-theme', theme);
|
||||
// 更新文档类名
|
||||
document.documentElement.classList.remove('dark', 'light');
|
||||
document.documentElement.classList.add(theme);
|
||||
}, [theme]);
|
||||
|
||||
const themeConfig = themes[theme];
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={{ theme, themeConfig, toggleTheme, setTheme }}>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// 使用主题的 Hook
|
||||
export const useTheme = (): ThemeContextType => {
|
||||
const context = useContext(ThemeContext);
|
||||
if (!context) {
|
||||
throw new Error('useTheme must be used within a ThemeProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
// 主题工具函数 - 生成动态类名
|
||||
export const getThemeClasses = (colorKey: keyof ThemeConfig['colors']): string => {
|
||||
const { themeConfig } = useTheme();
|
||||
return themeConfig.colors[colorKey];
|
||||
};
|
||||
|
||||
// 组合类名工具函数
|
||||
export const cn = (...classes: (string | undefined | null | false)[]): string => {
|
||||
return classes.filter(Boolean).join(' ');
|
||||
};
|
||||
Reference in new issue
Block a user