系统合并
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