update pay function

This commit is contained in:
2025-11-22 08:57:37 +08:00
parent 990ca3663e
commit e778742590
210 changed files with 8236 additions and 5345 deletions

View File

@@ -1,144 +0,0 @@
// src/components/Navbars/components/SecondaryNav/config.js
// 二级导航配置数据
/**
* 二级导航配置结构
* - key: 匹配的路径前缀
* - title: 导航组标题
* - items: 导航项列表
* - path: 路径
* - label: 显示文本
* - badges: 徽章列表 (可选)
* - external: 是否外部链接 (可选)
*/
export const secondaryNavConfig = {
'/community': {
title: '高频跟踪',
items: [
{
path: '/community',
label: '事件中心',
badges: [
{ text: 'HOT', colorScheme: 'green' },
{ text: 'NEW', colorScheme: 'red' }
]
},
{
path: '/concepts',
label: '概念中心',
badges: [{ text: 'NEW', colorScheme: 'red' }]
},
{
path: '/data-browser',
label: '数据浏览器',
badges: [{ text: 'NEW', colorScheme: 'red' }]
}
]
},
'/concepts': {
title: '高频跟踪',
items: [
{
path: '/community',
label: '事件中心',
badges: [
{ text: 'HOT', colorScheme: 'green' },
{ text: 'NEW', colorScheme: 'red' }
]
},
{
path: '/concepts',
label: '概念中心',
badges: [{ text: 'NEW', colorScheme: 'red' }]
},
{
path: '/data-browser',
label: '数据浏览器',
badges: [{ text: 'NEW', colorScheme: 'red' }]
}
]
},
'/data-browser': {
title: '高频跟踪',
items: [
{
path: '/community',
label: '事件中心',
badges: [
{ text: 'HOT', colorScheme: 'green' },
{ text: 'NEW', colorScheme: 'red' }
]
},
{
path: '/concepts',
label: '概念中心',
badges: [{ text: 'NEW', colorScheme: 'red' }]
},
{
path: '/data-browser',
label: '数据浏览器',
badges: [{ text: 'NEW', colorScheme: 'red' }]
}
]
},
'/limit-analyse': {
title: '行情复盘',
items: [
{
path: '/limit-analyse',
label: '涨停分析',
badges: [{ text: 'FREE', colorScheme: 'blue' }]
},
{
path: '/stocks',
label: '个股中心',
badges: [{ text: 'HOT', colorScheme: 'green' }]
},
{
path: '/trading-simulation',
label: '模拟盘',
badges: [{ text: 'NEW', colorScheme: 'red' }]
}
]
},
'/stocks': {
title: '行情复盘',
items: [
{
path: '/limit-analyse',
label: '涨停分析',
badges: [{ text: 'FREE', colorScheme: 'blue' }]
},
{
path: '/stocks',
label: '个股中心',
badges: [{ text: 'HOT', colorScheme: 'green' }]
},
{
path: '/trading-simulation',
label: '模拟盘',
badges: [{ text: 'NEW', colorScheme: 'red' }]
}
]
},
'/trading-simulation': {
title: '行情复盘',
items: [
{
path: '/limit-analyse',
label: '涨停分析',
badges: [{ text: 'FREE', colorScheme: 'blue' }]
},
{
path: '/stocks',
label: '个股中心',
badges: [{ text: 'HOT', colorScheme: 'green' }]
},
{
path: '/trading-simulation',
label: '模拟盘',
badges: [{ text: 'NEW', colorScheme: 'red' }]
}
]
}
};

View File

@@ -1,138 +0,0 @@
// src/components/Navbars/components/SecondaryNav/index.js
// 二级导航栏组件 - 显示当前一级菜单下的所有二级菜单项
import React, { memo } from 'react';
import {
Box,
Container,
HStack,
Text,
Button,
Flex,
Badge,
useColorModeValue
} from '@chakra-ui/react';
import { useNavigate, useLocation } from 'react-router-dom';
import { useNavigationEvents } from '../../../../hooks/useNavigationEvents';
import { secondaryNavConfig } from './config';
/**
* 二级导航栏组件
* 根据当前路径显示对应的二级菜单项
*
* @param {Object} props
* @param {boolean} props.showCompletenessAlert - 是否显示完整性提醒(影响 sticky top 位置)
*/
const SecondaryNav = memo(({ showCompletenessAlert }) => {
const navigate = useNavigate();
const location = useLocation();
// 颜色模式
const navbarBg = useColorModeValue('gray.50', 'gray.700');
const itemHoverBg = useColorModeValue('white', 'gray.600');
const borderColorValue = useColorModeValue('gray.200', 'gray.600');
// 导航埋点
const navEvents = useNavigationEvents({ component: 'secondary_nav' });
// 找到当前路径对应的二级导航配置
const currentConfig = Object.keys(secondaryNavConfig).find(key =>
location.pathname.includes(key)
);
// 如果没有匹配的二级导航,不显示
if (!currentConfig) return null;
const config = secondaryNavConfig[currentConfig];
return (
<Box
bg={navbarBg}
borderBottom="1px"
borderColor={borderColorValue}
py={2}
position="sticky"
top={showCompletenessAlert ? "120px" : "60px"}
zIndex={100}
>
<Container maxW="container.xl" px={4}>
<HStack spacing={1}>
{/* 显示一级菜单标题 */}
<Text fontSize="sm" color="gray.500" mr={2}>
{config.title}:
</Text>
{/* 二级菜单项 */}
{config.items.map((item, index) => {
const isActive = location.pathname.includes(item.path);
return item.external ? (
<Button
key={index}
as="a"
href={item.path}
size="sm"
variant="ghost"
bg="transparent"
color="inherit"
fontWeight="normal"
_hover={{ bg: itemHoverBg }}
borderRadius="md"
px={3}
>
<Flex align="center" gap={2}>
<Text>{item.label}</Text>
{item.badges && item.badges.length > 0 && (
<HStack spacing={1}>
{item.badges.map((badge, bIndex) => (
<Badge key={bIndex} size="xs" colorScheme={badge.colorScheme}>
{badge.text}
</Badge>
))}
</HStack>
)}
</Flex>
</Button>
) : (
<Button
key={index}
onClick={() => {
// 追踪侧边栏菜单点击
navEvents.trackSidebarMenuClicked(item.label, item.path, 2, false);
navigate(item.path);
}}
size="sm"
variant="ghost"
bg={isActive ? 'blue.50' : 'transparent'}
color={isActive ? 'blue.600' : 'inherit'}
fontWeight={isActive ? 'bold' : 'normal'}
borderBottom={isActive ? '2px solid' : 'none'}
borderColor="blue.600"
borderRadius={isActive ? '0' : 'md'}
_hover={{ bg: isActive ? 'blue.100' : itemHoverBg }}
px={3}
>
<Flex align="center" gap={2}>
<Text>{item.label}</Text>
{item.badges && item.badges.length > 0 && (
<HStack spacing={1}>
{item.badges.map((badge, bIndex) => (
<Badge key={bIndex} size="xs" colorScheme={badge.colorScheme}>
{badge.text}
</Badge>
))}
</HStack>
)}
</Flex>
</Button>
);
})}
</HStack>
</Container>
</Box>
);
});
SecondaryNav.displayName = 'SecondaryNav';
export default SecondaryNav;

21
src/views/AgentChat/.gitignore vendored Normal file
View File

@@ -0,0 +1,21 @@
# Next.js 应用忽略文件
neuratalk/node_modules/
neuratalk/.next/
neuratalk/.env.local
neuratalk/.env.production
neuratalk/out/
neuratalk/build/
# 日志文件
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# IDE
.idea
.vscode
# OS
.DS_Store
Thumbs.db

File diff suppressed because it is too large Load Diff

View File

@@ -1,53 +0,0 @@
// src/views/AgentChat/index.js
// Agent聊天页面
import React from 'react';
import {
Box,
Container,
Heading,
Text,
VStack,
useColorModeValue,
} from '@chakra-ui/react';
import { ChatInterfaceV2 } from '../../components/ChatBot';
/**
* Agent聊天页面
* 提供基于MCP的AI助手对话功能
*/
const AgentChat = () => {
const bgColor = useColorModeValue('gray.50', 'gray.900');
const cardBg = useColorModeValue('white', 'gray.800');
return (
<Box minH="calc(100vh - 200px)" bg={bgColor} py={8}>
<Container maxW="container.xl" h="100%">
<VStack spacing={6} align="stretch" h="100%">
{/* 页面标题 */}
<Box>
<Heading size="lg" mb={2}>AI投资助手</Heading>
<Text color="gray.600" fontSize="sm">
基于MCP协议的智能投资顾问支持股票查询新闻搜索概念分析等多种功能
</Text>
</Box>
{/* 聊天界面 */}
<Box
flex="1"
bg={cardBg}
borderRadius="xl"
boxShadow="xl"
overflow="hidden"
h="calc(100vh - 300px)"
minH="600px"
>
<ChatInterfaceV2 />
</Box>
</VStack>
</Container>
</Box>
);
};
export default AgentChat;

View File

@@ -1,857 +0,0 @@
// src/views/AgentChat/index_v3.js
// Agent聊天页面 V3 - 带左侧会话列表和用户信息集成
import React, { useState, useEffect, useRef } from 'react';
import {
Box,
Flex,
VStack,
HStack,
Text,
Input,
IconButton,
Button,
Avatar,
Heading,
Divider,
Spinner,
Badge,
useColorModeValue,
useToast,
Progress,
Fade,
Collapse,
useDisclosure,
InputGroup,
InputLeftElement,
Menu,
MenuButton,
MenuList,
MenuItem,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalBody,
ModalCloseButton,
Tooltip,
} from '@chakra-ui/react';
import {
FiSend,
FiSearch,
FiPlus,
FiMessageSquare,
FiTrash2,
FiMoreVertical,
FiRefreshCw,
FiDownload,
FiCpu,
FiUser,
FiZap,
FiClock,
} from 'react-icons/fi';
import { useAuth } from '@contexts/AuthContext';
import { PlanCard } from '@components/ChatBot/PlanCard';
import { StepResultCard } from '@components/ChatBot/StepResultCard';
import { logger } from '@utils/logger';
import axios from 'axios';
/**
* Agent消息类型
*/
const MessageTypes = {
USER: 'user',
AGENT_THINKING: 'agent_thinking',
AGENT_PLAN: 'agent_plan',
AGENT_EXECUTING: 'agent_executing',
AGENT_RESPONSE: 'agent_response',
ERROR: 'error',
};
/**
* Agent聊天页面 V3
*/
const AgentChatV3 = () => {
const { user } = useAuth(); // 获取当前用户信息
const toast = useToast();
// 会话相关状态
const [sessions, setSessions] = useState([]);
const [currentSessionId, setCurrentSessionId] = useState(null);
const [isLoadingSessions, setIsLoadingSessions] = useState(true);
// 消息相关状态
const [messages, setMessages] = useState([]);
const [inputValue, setInputValue] = useState('');
const [isProcessing, setIsProcessing] = useState(false);
const [currentProgress, setCurrentProgress] = useState(0);
// UI 状态
const [searchQuery, setSearchQuery] = useState('');
const { isOpen: isSidebarOpen, onToggle: toggleSidebar } = useDisclosure({ defaultIsOpen: true });
// Refs
const messagesEndRef = useRef(null);
const inputRef = useRef(null);
// 颜色主题
const bgColor = useColorModeValue('gray.50', 'gray.900');
const sidebarBg = useColorModeValue('white', 'gray.800');
const chatBg = useColorModeValue('white', 'gray.800');
const inputBg = useColorModeValue('white', 'gray.700');
const borderColor = useColorModeValue('gray.200', 'gray.600');
const hoverBg = useColorModeValue('gray.100', 'gray.700');
const activeBg = useColorModeValue('blue.50', 'blue.900');
const userBubbleBg = useColorModeValue('blue.500', 'blue.600');
const agentBubbleBg = useColorModeValue('white', 'gray.700');
// ==================== 会话管理函数 ====================
// 加载会话列表
const loadSessions = async () => {
if (!user?.id) return;
setIsLoadingSessions(true);
try {
const response = await axios.get('/mcp/agent/sessions', {
params: { user_id: user.id, limit: 50 },
});
if (response.data.success) {
setSessions(response.data.data);
logger.info('会话列表加载成功', response.data.data);
}
} catch (error) {
logger.error('加载会话列表失败', error);
toast({
title: '加载失败',
description: '无法加载会话列表',
status: 'error',
duration: 3000,
});
} finally {
setIsLoadingSessions(false);
}
};
// 加载会话历史
const loadSessionHistory = async (sessionId) => {
if (!sessionId) return;
try {
const response = await axios.get(`/mcp/agent/history/${sessionId}`, {
params: { limit: 100 },
});
if (response.data.success) {
const history = response.data.data;
// 将历史记录转换为消息格式
const formattedMessages = history.map((msg, idx) => ({
id: `${sessionId}-${idx}`,
type: msg.message_type === 'user' ? MessageTypes.USER : MessageTypes.AGENT_RESPONSE,
content: msg.message,
plan: msg.plan ? JSON.parse(msg.plan) : null,
stepResults: msg.steps ? JSON.parse(msg.steps) : null,
timestamp: msg.timestamp,
}));
setMessages(formattedMessages);
logger.info('会话历史加载成功', formattedMessages);
}
} catch (error) {
logger.error('加载会话历史失败', error);
toast({
title: '加载失败',
description: '无法加载会话历史',
status: 'error',
duration: 3000,
});
}
};
// 创建新会话
const createNewSession = () => {
setCurrentSessionId(null);
setMessages([
{
id: Date.now(),
type: MessageTypes.AGENT_RESPONSE,
content: `你好${user?.nickname || ''}我是价小前北京价值前沿科技公司的AI投研助手。\n\n我会通过多步骤分析来帮助你深入了解金融市场。\n\n你可以问我:\n• 全面分析某只股票\n• 某个行业的投资机会\n• 今日市场热点\n• 某个概念板块的表现`,
timestamp: new Date().toISOString(),
},
]);
};
// 切换会话
const switchSession = (sessionId) => {
setCurrentSessionId(sessionId);
loadSessionHistory(sessionId);
};
// 删除会话需要后端API支持
const deleteSession = async (sessionId) => {
// TODO: 实现删除会话的后端API
toast({
title: '删除会话',
description: '此功能尚未实现',
status: 'info',
duration: 2000,
});
};
// ==================== 消息处理函数 ====================
// 自动滚动到底部
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(() => {
scrollToBottom();
}, [messages]);
// 添加消息
const addMessage = (message) => {
setMessages((prev) => [...prev, { ...message, id: Date.now() }]);
};
// 发送消息
const handleSendMessage = async () => {
if (!inputValue.trim() || isProcessing) return;
// 权限检查
if (user?.id !== 'max') {
toast({
title: '权限不足',
description: '「价小前投研」功能目前仅对特定用户开放。如需使用,请联系管理员。',
status: 'warning',
duration: 5000,
isClosable: true,
});
return;
}
const userMessage = {
type: MessageTypes.USER,
content: inputValue,
timestamp: new Date().toISOString(),
};
addMessage(userMessage);
const userInput = inputValue;
setInputValue('');
setIsProcessing(true);
setCurrentProgress(0);
let currentPlan = null;
let stepResults = [];
try {
// 1. 显示思考状态
addMessage({
type: MessageTypes.AGENT_THINKING,
content: '正在分析你的问题...',
timestamp: new Date().toISOString(),
});
setCurrentProgress(10);
// 2. 调用后端API非流式
const response = await axios.post('/mcp/agent/chat', {
message: userInput,
conversation_history: messages
.filter((m) => m.type === MessageTypes.USER || m.type === MessageTypes.AGENT_RESPONSE)
.map((m) => ({
isUser: m.type === MessageTypes.USER,
content: m.content,
})),
user_id: user?.id || 'anonymous',
user_nickname: user?.nickname || '匿名用户',
user_avatar: user?.avatar || '',
session_id: currentSessionId,
});
// 移除思考消息
setMessages((prev) => prev.filter((m) => m.type !== MessageTypes.AGENT_THINKING));
if (response.data.success) {
const data = response.data;
// 更新 session_id如果是新会话
if (data.session_id && !currentSessionId) {
setCurrentSessionId(data.session_id);
}
// 显示执行计划
if (data.plan) {
currentPlan = data.plan;
addMessage({
type: MessageTypes.AGENT_PLAN,
content: '已制定执行计划',
plan: data.plan,
timestamp: new Date().toISOString(),
});
setCurrentProgress(30);
}
// 显示执行步骤
if (data.steps && data.steps.length > 0) {
stepResults = data.steps;
addMessage({
type: MessageTypes.AGENT_EXECUTING,
content: '正在执行步骤...',
plan: currentPlan,
stepResults: stepResults,
timestamp: new Date().toISOString(),
});
setCurrentProgress(70);
}
// 移除执行中消息
setMessages((prev) => prev.filter((m) => m.type !== MessageTypes.AGENT_EXECUTING));
// 显示最终结果
addMessage({
type: MessageTypes.AGENT_RESPONSE,
content: data.final_answer || data.message || '处理完成',
plan: currentPlan,
stepResults: stepResults,
metadata: data.metadata,
timestamp: new Date().toISOString(),
});
setCurrentProgress(100);
// 重新加载会话列表
loadSessions();
}
} catch (error) {
logger.error('Agent chat error', error);
// 移除思考/执行中消息
setMessages((prev) =>
prev.filter(
(m) => m.type !== MessageTypes.AGENT_THINKING && m.type !== MessageTypes.AGENT_EXECUTING
)
);
const errorMessage = error.response?.data?.error || error.message || '处理失败';
addMessage({
type: MessageTypes.ERROR,
content: `处理失败:${errorMessage}`,
timestamp: new Date().toISOString(),
});
toast({
title: '处理失败',
description: errorMessage,
status: 'error',
duration: 5000,
isClosable: true,
});
} finally {
setIsProcessing(false);
setCurrentProgress(0);
inputRef.current?.focus();
}
};
// 处理键盘事件
const handleKeyPress = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSendMessage();
}
};
// 清空对话
const handleClearChat = () => {
createNewSession();
};
// 导出对话
const handleExportChat = () => {
const chatText = messages
.filter((m) => m.type === MessageTypes.USER || m.type === MessageTypes.AGENT_RESPONSE)
.map((msg) => `[${msg.type === MessageTypes.USER ? '用户' : '价小前'}] ${msg.content}`)
.join('\n\n');
const blob = new Blob([chatText], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `chat_${new Date().toISOString().slice(0, 10)}.txt`;
a.click();
URL.revokeObjectURL(url);
};
// ==================== 初始化 ====================
useEffect(() => {
if (user) {
loadSessions();
createNewSession();
}
}, [user]);
// ==================== 渲染 ====================
// 快捷问题
const quickQuestions = [
'全面分析贵州茅台这只股票',
'今日涨停股票有哪些亮点',
'新能源概念板块的投资机会',
'半导体行业最新动态',
];
// 筛选会话
const filteredSessions = sessions.filter(
(session) =>
!searchQuery ||
session.last_message?.toLowerCase().includes(searchQuery.toLowerCase())
);
return (
<Flex h="calc(100vh - 80px)" bg={bgColor}>
{/* 左侧会话列表 */}
<Collapse in={isSidebarOpen} animateOpacity>
<Box
w="300px"
bg={sidebarBg}
borderRight="1px"
borderColor={borderColor}
h="100%"
display="flex"
flexDirection="column"
>
{/* 侧边栏头部 */}
<Box p={4} borderBottom="1px" borderColor={borderColor}>
<Button
leftIcon={<FiPlus />}
colorScheme="blue"
w="100%"
onClick={createNewSession}
size="sm"
>
新建对话
</Button>
{/* 搜索框 */}
<InputGroup mt={3} size="sm">
<InputLeftElement pointerEvents="none">
<FiSearch color="gray.300" />
</InputLeftElement>
<Input
placeholder="搜索对话..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</InputGroup>
</Box>
{/* 会话列表 */}
<VStack
flex="1"
overflowY="auto"
spacing={0}
align="stretch"
css={{
'&::-webkit-scrollbar': { width: '6px' },
'&::-webkit-scrollbar-thumb': {
background: '#CBD5E0',
borderRadius: '3px',
},
}}
>
{isLoadingSessions ? (
<Flex justify="center" align="center" h="200px">
<Spinner />
</Flex>
) : filteredSessions.length === 0 ? (
<Flex justify="center" align="center" h="200px" direction="column">
<FiMessageSquare size={32} color="gray" />
<Text mt={2} fontSize="sm" color="gray.500">
{searchQuery ? '没有找到匹配的对话' : '暂无对话记录'}
</Text>
</Flex>
) : (
filteredSessions.map((session) => (
<Box
key={session.session_id}
p={3}
cursor="pointer"
bg={currentSessionId === session.session_id ? activeBg : 'transparent'}
_hover={{ bg: hoverBg }}
borderBottom="1px"
borderColor={borderColor}
onClick={() => switchSession(session.session_id)}
>
<Flex justify="space-between" align="start">
<VStack align="start" spacing={1} flex="1">
<Text fontSize="sm" fontWeight="medium" noOfLines={2}>
{session.last_message || '新对话'}
</Text>
<HStack spacing={2} fontSize="xs" color="gray.500">
<FiClock size={12} />
<Text>
{new Date(session.last_timestamp).toLocaleDateString('zh-CN', {
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
})}
</Text>
<Badge colorScheme="blue" fontSize="xx-small">
{session.message_count}
</Badge>
</HStack>
</VStack>
<Menu>
<MenuButton
as={IconButton}
icon={<FiMoreVertical />}
size="xs"
variant="ghost"
onClick={(e) => e.stopPropagation()}
/>
<MenuList>
<MenuItem
icon={<FiTrash2 />}
color="red.500"
onClick={(e) => {
e.stopPropagation();
deleteSession(session.session_id);
}}
>
删除对话
</MenuItem>
</MenuList>
</Menu>
</Flex>
</Box>
))
)}
</VStack>
{/* 用户信息 */}
<Box p={4} borderTop="1px" borderColor={borderColor}>
<HStack spacing={3}>
<Avatar size="sm" name={user?.nickname} src={user?.avatar} />
<VStack align="start" spacing={0} flex="1">
<Text fontSize="sm" fontWeight="medium">
{user?.nickname || '未登录'}
</Text>
<Text fontSize="xs" color="gray.500">
{user?.id || 'anonymous'}
</Text>
</VStack>
</HStack>
</Box>
</Box>
</Collapse>
{/* 主聊天区域 */}
<Flex flex="1" direction="column" h="100%">
{/* 聊天头部 */}
<Box bg={chatBg} borderBottom="1px" borderColor={borderColor} px={6} py={4}>
<HStack justify="space-between">
<HStack spacing={4}>
<IconButton
icon={<FiMessageSquare />}
size="sm"
variant="ghost"
aria-label="切换侧边栏"
onClick={toggleSidebar}
/>
<Avatar size="md" bg="blue.500" icon={<FiCpu fontSize="1.5rem" />} />
<VStack align="start" spacing={0}>
<Heading size="md">价小前投研</Heading>
<HStack>
<Badge colorScheme="green" fontSize="xs">
<HStack spacing={1}>
<FiZap size={10} />
<span>智能分析</span>
</HStack>
</Badge>
<Text fontSize="xs" color="gray.500">
多步骤深度研究
</Text>
</HStack>
</VStack>
</HStack>
<HStack>
<IconButton
icon={<FiRefreshCw />}
size="sm"
variant="ghost"
aria-label="清空对话"
onClick={handleClearChat}
/>
<IconButton
icon={<FiDownload />}
size="sm"
variant="ghost"
aria-label="导出对话"
onClick={handleExportChat}
/>
</HStack>
</HStack>
{/* 进度条 */}
{isProcessing && (
<Progress
value={currentProgress}
size="xs"
colorScheme="blue"
mt={3}
borderRadius="full"
isAnimated
/>
)}
</Box>
{/* 消息列表 */}
<Box
flex="1"
overflowY="auto"
px={6}
py={4}
css={{
'&::-webkit-scrollbar': { width: '8px' },
'&::-webkit-scrollbar-thumb': {
background: '#CBD5E0',
borderRadius: '4px',
},
}}
>
<VStack spacing={4} align="stretch">
{messages.map((message) => (
<Fade in key={message.id}>
<MessageRenderer message={message} userAvatar={user?.avatar} />
</Fade>
))}
<div ref={messagesEndRef} />
</VStack>
</Box>
{/* 快捷问题 */}
{messages.length <= 2 && !isProcessing && (
<Box px={6} py={3} bg={chatBg} borderTop="1px" borderColor={borderColor}>
<Text fontSize="xs" color="gray.500" mb={2}>
💡 试试这些问题
</Text>
<Flex wrap="wrap" gap={2}>
{quickQuestions.map((question, idx) => (
<Button
key={idx}
size="sm"
variant="outline"
colorScheme="blue"
fontSize="xs"
onClick={() => {
setInputValue(question);
inputRef.current?.focus();
}}
>
{question}
</Button>
))}
</Flex>
</Box>
)}
{/* 输入框 */}
<Box px={6} py={4} bg={chatBg} borderTop="1px" borderColor={borderColor}>
<Flex>
<Input
ref={inputRef}
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyPress={handleKeyPress}
placeholder="输入你的问题,我会进行深度分析..."
bg={inputBg}
border="1px"
borderColor={borderColor}
_focus={{ borderColor: 'blue.500', boxShadow: '0 0 0 1px #3182CE' }}
mr={2}
disabled={isProcessing}
size="lg"
/>
<IconButton
icon={isProcessing ? <Spinner size="sm" /> : <FiSend />}
colorScheme="blue"
aria-label="发送"
onClick={handleSendMessage}
isLoading={isProcessing}
disabled={!inputValue.trim() || isProcessing}
size="lg"
/>
</Flex>
</Box>
</Flex>
</Flex>
);
};
/**
* 消息渲染器
*/
const MessageRenderer = ({ message, userAvatar }) => {
const userBubbleBg = useColorModeValue('blue.500', 'blue.600');
const agentBubbleBg = useColorModeValue('white', 'gray.700');
const borderColor = useColorModeValue('gray.200', 'gray.600');
switch (message.type) {
case MessageTypes.USER:
return (
<Flex justify="flex-end">
<HStack align="flex-start" maxW="75%">
<Box
bg={userBubbleBg}
color="white"
px={4}
py={3}
borderRadius="lg"
boxShadow="md"
>
<Text fontSize="sm" whiteSpace="pre-wrap">
{message.content}
</Text>
</Box>
<Avatar size="sm" src={userAvatar} icon={<FiUser fontSize="1rem" />} />
</HStack>
</Flex>
);
case MessageTypes.AGENT_THINKING:
return (
<Flex justify="flex-start">
<HStack align="flex-start" maxW="75%">
<Avatar size="sm" bg="purple.500" icon={<FiCpu fontSize="1rem" />} />
<Box
bg={agentBubbleBg}
px={4}
py={3}
borderRadius="lg"
borderWidth="1px"
borderColor={borderColor}
boxShadow="sm"
>
<HStack>
<Spinner size="sm" color="purple.500" />
<Text fontSize="sm" color="purple.600">
{message.content}
</Text>
</HStack>
</Box>
</HStack>
</Flex>
);
case MessageTypes.AGENT_PLAN:
return (
<Flex justify="flex-start">
<HStack align="flex-start" maxW="85%">
<Avatar size="sm" bg="blue.500" icon={<FiCpu fontSize="1rem" />} />
<VStack align="stretch" flex="1">
<PlanCard plan={message.plan} stepResults={[]} />
</VStack>
</HStack>
</Flex>
);
case MessageTypes.AGENT_EXECUTING:
return (
<Flex justify="flex-start">
<HStack align="flex-start" maxW="85%">
<Avatar size="sm" bg="orange.500" icon={<FiCpu fontSize="1rem" />} />
<VStack align="stretch" flex="1" spacing={3}>
<PlanCard plan={message.plan} stepResults={message.stepResults} />
{message.stepResults?.map((result, idx) => (
<StepResultCard key={idx} stepResult={result} />
))}
</VStack>
</HStack>
</Flex>
);
case MessageTypes.AGENT_RESPONSE:
return (
<Flex justify="flex-start">
<HStack align="flex-start" maxW="85%">
<Avatar size="sm" bg="green.500" icon={<FiCpu fontSize="1rem" />} />
<VStack align="stretch" flex="1" spacing={3}>
{/* 最终总结 */}
<Box
bg={agentBubbleBg}
px={4}
py={3}
borderRadius="lg"
borderWidth="1px"
borderColor={borderColor}
boxShadow="md"
>
<Text fontSize="sm" whiteSpace="pre-wrap">
{message.content}
</Text>
{/* 元数据 */}
{message.metadata && (
<HStack mt={3} spacing={4} fontSize="xs" color="gray.500">
<Text>总步骤: {message.metadata.total_steps}</Text>
<Text> {message.metadata.successful_steps}</Text>
{message.metadata.failed_steps > 0 && (
<Text> {message.metadata.failed_steps}</Text>
)}
<Text>耗时: {message.metadata.total_execution_time?.toFixed(1)}s</Text>
</HStack>
)}
</Box>
{/* 执行详情(可选) */}
{message.plan && message.stepResults && message.stepResults.length > 0 && (
<VStack align="stretch" spacing={2}>
<Divider />
<Text fontSize="xs" fontWeight="bold" color="gray.500">
📊 执行详情点击展开查看
</Text>
{message.stepResults.map((result, idx) => (
<StepResultCard key={idx} stepResult={result} />
))}
</VStack>
)}
</VStack>
</HStack>
</Flex>
);
case MessageTypes.ERROR:
return (
<Flex justify="flex-start">
<HStack align="flex-start" maxW="75%">
<Avatar size="sm" bg="red.500" icon={<FiCpu fontSize="1rem" />} />
<Box
bg="red.50"
color="red.700"
px={4}
py={3}
borderRadius="lg"
borderWidth="1px"
borderColor="red.200"
>
<Text fontSize="sm">{message.content}</Text>
</Box>
</HStack>
</Flex>
);
default:
return null;
}
};
export default AgentChatV3;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

View File

@@ -0,0 +1,5 @@
import CheckEmailPage from "@/templates/Auth/CheckEmailPage";
export default function Page() {
return <CheckEmailPage />;
}

View File

@@ -0,0 +1,5 @@
import EnterCodePage from "@/templates/Auth/EnterCodePage";
export default function Page() {
return <EnterCodePage />;
}

View File

@@ -0,0 +1,5 @@
import ForgotPasswordPage from "@/templates/Auth/ForgotPasswordPage";
export default function Page() {
return <ForgotPasswordPage />;
}

View File

@@ -0,0 +1,5 @@
import ResetPasswordPage from "@/templates/Auth/ResetPasswordPage";
export default function Page() {
return <ResetPasswordPage />;
}

View File

@@ -0,0 +1,5 @@
import SignInPage from "@/templates/Auth/SignInPage";
export default function Page() {
return <SignInPage />;
}

View File

@@ -0,0 +1,5 @@
import SignUpPage from "@/templates/Auth/SignUpPage";
export default function Page() {
return <SignUpPage />;
}

View File

@@ -0,0 +1,5 @@
import DataAnalyticsPage from "@/templates/DataAnalyticsPage";
export default function Page() {
return <DataAnalyticsPage />;
}

View File

@@ -0,0 +1,5 @@
import DocumentGenerationPage from "@/templates/DocumentGenerationPage";
export default function Page() {
return <DocumentGenerationPage />;
}

View File

@@ -0,0 +1,5 @@
import DocumentsPage from "@/templates/DocumentsPage";
export default function Page() {
return <DocumentsPage />;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,5 @@
import GenerateCodePage from "@/templates/GenerateCodePage";
export default function Page() {
return <GenerateCodePage />;
}

View File

@@ -0,0 +1,411 @@
@import "tailwindcss";
@plugin "tailwind-scrollbar";
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
:root {
--static-white: #ffffff;
--static-black: #0e121b;
--white-0: #ffffff;
--strong-950: #0e121b;
--bg-weak-50: #f9fafc;
--bg-surface-800: #222530;
--bg-soft-200: #e1e4ea;
--bg-sub-300: #cacfd8;
--text-sub-600: #525866;
--text-disabled-300: #cacfd8;
--text-soft-400: #99a0ae;
--faded-light: #e1e4ea;
--faded-base: #717784;
--faded-dark: #222530;
--faded-lighter: #f2f5f8;
--error-base: #fb3748;
--error-light: #ffc0c5;
--error-dark: #681219;
--error-lighter: #ffebec;
--warning-dark: #682f12;
--warning-base: #ff8447;
--warning-light: #ffd5c0;
--warning-lighter: #fff1eb;
--information-dark: #122368;
--information-base: #335cff;
--information-light: #c0d5ff;
--information-lighter: #ebf1ff;
--success-dark: #0b4627;
--success-base: #1fc16b;
--success-light: #c2f5da;
--success-lighter: #e0faec;
--away-dark: #624c18;
--away-base: #f6b51e;
--away-light: #ffecc0;
--away-lighter: #fffaeb;
--feature-dark: #351a75;
--feature-base: #7d52f4;
--feature-light: #cac0ff;
--feature-lighter: #efebff;
--verified-dark: #124b68;
--verified-base: #47c2ff;
--verified-light: #c0eaff;
--verified-lighter: #ebf8ff;
--highlighted-dark: #68123d;
--highlighted-base: #fb4ba3;
--highlighted-light: #ffc0df;
--highlighted-lighter: #ffebf4;
--stable-dark: #0b463e;
--stable-base: #22d3bb;
--stable-light: #c2f5ee;
--stable-lighter: #e4fbf8;
--stroke-soft-200: #e1e4ea;
--stroke-strong-950: #0e121b;
--stroke-white-0: #ffffff;
--stroke-sub-300: #cacfd8;
--icon-sub-600: #525866;
--icon-strong-950: #0e121b;
--icon-soft-400: #99a0ae;
--icon-disabled-300: #cacfd8;
--icon-white-0: #ffffff;
--social-apple: #000000;
--social-twitter: #010101;
--social-github: #24292f;
--social-notion: #1e2226;
--social-tidal: #000000;
--social-amazon: #353e47;
--social-zendesk: #16140d;
--illustration-strong-400: #99a0ae;
--illustration-sub-300: #cacfd8;
--illustration-soft-200: #e1e4ea;
--illustration-weak-100: #f2f5f8;
--illustration-white-0: #ffffff;
--overlay: rgba(2, 13, 23, 0.24);
--blue-50: #ebf1ff;
--blue-100: #d5e2ff;
--blue-200: #c0d5ff;
--blue-300: #97baff;
--blue-400: #6895ff;
--blue-500: #335cff;
--blue-600: #3559e9;
--blue-700: #2547d0;
--blue-800: #1f3bad;
--blue-900: #182f8b;
--blue-950: #122368;
--green-50: #e0faec;
--green-100: #d0fbe9;
--green-200: #c2f5da;
--green-300: #84ebb4;
--green-400: #3ee089;
--green-500: #1fc16b;
--green-600: #1daf61;
--green-700: #178c4e;
--green-800: #1a7544;
--green-900: #16643b;
--green-950: #0b4627;
}
[data-theme="dark"] {
--white-0: #0e121b;
--strong-950: #ffffff;
--bg-weak-50: #181b25;
--bg-surface-800: #e1e4ea;
--bg-soft-200: #2b303b;
--bg-sub-300: #525866;
--text-sub-600: #99a0ae;
--text-disabled-300: #525866;
--text-soft-400: #717784;
--faded-light: rgba(153, 160, 174, 0.24);
--faded-base: #717784;
--faded-dark: #cacfd8;
--faded-lighter: rgba(153, 160, 174, 0.16);
--error-base: #e93544;
--error-light: rgba(251, 55, 72, 0.24);
--error-dark: #ff6875;
--error-lighter: rgba(251, 55, 72, 0.16);
--warning-dark: #ff9a68;
--warning-base: #e97135;
--warning-light: rgba(255, 145, 71, 0.24);
--warning-lighter: rgba(255, 145, 71, 0.16);
--information-dark: #6895ff;
--information-base: #335cff;
--information-light: rgba(71, 108, 255, 0.24);
--information-lighter: rgba(71, 108, 255, 0.16);
--success-dark: #3ee089;
--success-base: #1daf61;
--success-light: rgba(31, 193, 107, 0.16);
--success-lighter: rgba(31, 193, 107, 0.1);
--away-dark: #ffd268;
--away-base: #e6a819;
--away-light: rgba(251, 198, 75, 0.24);
--away-lighter: rgba(251, 198, 75, 0.16);
--feature-dark: #8c71f6;
--feature-base: #7d52f4;
--feature-light: rgba(120, 77, 239, 0.24);
--feature-lighter: rgba(120, 77, 239, 0.16);
--verified-dark: #68cdff;
--verified-base: #35ade9;
--verified-light: rgba(71, 194, 255, 0.24);
--verified-lighter: rgba(71, 194, 255, 0.16);
--highlighted-dark: #ff68b3;
--highlighted-base: #e9358f;
--highlighted-light: rgba(251, 75, 163, 0.24);
--highlighted-lighter: rgba(251, 75, 163, 0.16);
--stable-dark: #3fdec9;
--stable-base: #1daf9c;
--stable-light: rgba(34, 211, 187, 0.24);
--stable-lighter: rgba(34, 211, 187, 0.16);
--stroke-soft-200: #2b303b;
--stroke-strong-950: #ffffff;
--stroke-white-0: #0e121b;
--stroke-sub-300: #525866;
--icon-sub-600: #99a0ae;
--icon-strong-950: #ffffff;
--icon-soft-400: #717784;
--icon-disabled-300: #525866;
--icon-white-0: #0e121b;
--social-apple: #ffffff;
--social-twitter: #ffffff;
--social-github: #ffffff;
--social-notion: #ffffff;
--social-tidal: #ffffff;
--social-amazon: #ffffff;
--social-zendesk: #ffffff;
--illustration-strong-400: #525866;
--illustration-sub-300: #2b303b;
--illustration-soft-200: #222530;
--illustration-weak-100: #181b25;
--illustration-white-0: #0e121b;
--overlay: rgba(82, 88, 102, 0.32);
}
@theme {
--breakpoint-*: initial;
--breakpoint-sm: 480px;
--breakpoint-md: 767px;
--breakpoint-lg: 1023px;
--breakpoint-xl: 1259px;
--breakpoint-2xl: 1419px;
--breakpoint-3xl: 1719px;
--breakpoint-4xl: 1899px;
--color-static-white: var(--static-white);
--color-static-black: var(--static-black);
--color-white-0: var(--white-0);
--color-strong-950: var(--strong-950);
--color-weak-50: var(--bg-weak-50);
--color-surface-800: var(--bg-surface-800);
--color-soft-200: var(--bg-soft-200);
--color-sub-300: var(--bg-sub-300);
--color-sub-600: var(--text-sub-600);
--color-disabled-300: var(--text-disabled-300);
--color-soft-400: var(--text-soft-400);
--color-faded-light: var(--faded-light);
--color-faded-base: var(--faded-base);
--color-faded-dark: var(--faded-dark);
--color-faded-lighter: var(--faded-lighter);
--color-error-base: var(--error-base);
--color-error-light: var(--error-light);
--color-error-dark: var(--error-dark);
--color-error-lighter: var(--error-lighter);
--color-warning-dark: var(--warning-dark);
--color-warning-base: var(--warning-base);
--color-warning-light: var(--warning-light);
--color-warning-lighter: var(--warning-lighter);
--color-information-dark: var(--information-dark);
--color-information-base: var(--information-base);
--color-information-light: var(--information-light);
--color-information-lighter: var(--information-lighter);
--color-success-dark: var(--success-dark);
--color-success-base: var(--success-base);
--color-success-light: var(--success-light);
--color-success-lighter: var(--success-lighter);
--color-away-dark: var(--away-dark);
--color-away-base: var(--away-base);
--color-away-light: var(--away-light);
--color-away-lighter: var(--away-lighter);
--color-feature-dark: var(--feature-dark);
--color-feature-base: var(--feature-base);
--color-feature-light: var(--feature-light);
--color-feature-lighter: var(--feature-lighter);
--color-verified-dark: var(--verified-dark);
--color-verified-base: var(--verified-base);
--color-verified-light: var(--verified-light);
--color-verified-lighter: var(--verified-lighter);
--color-highlighted-dark: var(--highlighted-dark);
--color-highlighted-base: var(--highlighted-base);
--color-highlighted-light: var(--highlighted-light);
--color-highlighted-lighter: var(--highlighted-lighter);
--color-stable-dark: var(--stable-dark);
--color-stable-base: var(--stable-base);
--color-stable-light: var(--stable-light);
--color-stable-lighter: var(--stable-lighter);
--color-stroke-soft-200: var(--stroke-soft-200);
--color-stroke-strong-950: var(--stroke-strong-950);
--color-stroke-white-0: var(--stroke-white-0);
--color-stroke-sub-300: var(--stroke-sub-300);
--color-icon-sub-600: var(--icon-sub-600);
--color-icon-strong-950: var(--icon-strong-950);
--color-icon-soft-400: var(--icon-soft-400);
--color-icon-disabled-300: var(--icon-disabled-300);
--color-icon-white-0: var(--icon-white-0);
--color-social-apple: var(--social-apple);
--color-social-twitter: var(--social-twitter);
--color-social-github: var(--social-github);
--color-social-notion: var(--social-notion);
--color-social-tidal: var(--social-tidal);
--color-social-amazon: var(--social-amazon);
--color-social-zendesk: var(--social-zendesk);
--color-illustration-strong-400: var(--illustration-strong-400);
--color-illustration-sub-300: var(--illustration-sub-300);
--color-illustration-soft-200: var(--illustration-soft-200);
--color-illustration-weak-100: var(--illustration-weak-100);
--color-illustration-white-0: var(--illustration-white-0);
--color-overlay: var(--overlay);
--color-blue-50: var(--blue-50);
--color-blue-100: var(--blue-100);
--color-blue-200: var(--blue-200);
--color-blue-300: var(--blue-300);
--color-blue-400: var(--blue-400);
--color-blue-500: var(--blue-500);
--color-blue-600: var(--blue-600);
--color-blue-700: var(--blue-700);
--color-blue-800: var(--blue-800);
--color-blue-900: var(--blue-900);
--color-blue-950: var(--blue-950);
--color-green-50: var(--green-50);
--color-green-100: var(--green-100);
--color-green-200: var(--green-200);
--color-green-300: var(--green-300);
--color-green-400: var(--green-400);
--color-green-500: var(--green-500);
--color-green-600: var(--green-600);
--color-green-700: var(--green-700);
--color-green-800: var(--green-800);
--color-green-900: var(--green-900);
--color-green-950: var(--green-950);
--default-transition-duration: 0.2s;
--font-satoshi: var(--font-satoshi);
--font-inter: var(--font-inter);
--font-inter-display: var(--font-inter-display);
--text-0: 0;
--text-h1: 4rem;
--text-h1--line-height: 1.09;
--text-h1--letter-spacing: -0.01em;
--text-h1--font-weight: 700;
--text-h3: 2.5rem;
--text-h3--line-height: 1.2;
--text-h3--letter-spacing: -0.01em;
--text-h3--font-weight: 500;
--text-h4: 2.25rem;
--text-h4--line-height: 1.5;
--text-h4--letter-spacing: -0.005em;
--text-h4--font-weight: 500;
--text-h5: 1.75rem;
--text-h5--line-height: 1.5;
--text-h5--letter-spacing: 0;
--text-h5--font-weight: 700;
--text-h6: 1.25rem;
--text-h6--line-height: 1.5;
--text-h6--letter-spacing: 0;
--text-h6--font-weight: 500;
--text-label-xl: 1.5rem;
--text-label-xl--line-height: 1.33;
--text-label-xl--letter-spacing: -0.015em;
--text-label-xl--font-weight: 500;
--text-label-lg: 1.125rem;
--text-label-lg--line-height: 1.5;
--text-label-lg--letter-spacing: -0.015em;
--text-label-lg--font-weight: 500;
--text-label-md: 1rem;
--text-label-md--line-height: 1.5;
--text-label-md--letter-spacing: -0.011em;
--text-label-md--font-weight: 500;
--text-label-sm: 0.875rem;
--text-label-sm--line-height: 1.4;
--text-label-sm--letter-spacing: -0.006em;
--text-label-sm--font-weight: 500;
--text-label-xs: 0.75rem;
--text-label-xs--line-height: 1.33;
--text-label-xs--letter-spacing: 0;
--text-label-xs--font-weight: 500;
--text-p-xl: 1.25rem;
--text-p-xl--line-height: 1.5;
--text-p-xl--letter-spacing: -0.015em;
--text-p-xl--font-weight: 400;
--text-p-lg: 1.125rem;
--text-p-lg--line-height: 1.33;
--text-p-lg--letter-spacing: -0.015em;
--text-p-lg--font-weight: 400;
--text-p-md: 1rem;
--text-p-md--line-height: 1.5;
--text-p-md--letter-spacing: -0.011em;
--text-p-md--font-weight: 300;
--text-p-sm: 0.875rem;
--text-p-sm--line-height: 1.5;
--text-p-sm--letter-spacing: -0.006em;
--text-p-sm--font-weight: 400;
--text-p-xs: 0.75rem;
--text-p-xs--line-height: 1.33;
--text-p-xs--letter-spacing: 0;
--text-p-xs--font-weight: 400;
--text-sub-md: 1rem;
--text-sub-md--line-height: 1.5;
--text-sub-md--letter-spacing: 0;
--text-sub-md--font-weight: 700;
--text-sub-sm: 0.875rem;
--text-sub-sm--line-height: 1.4;
--text-sub-sm--letter-spacing: 0.06em;
--text-sub-sm--font-weight: 500;
--text-sub-xs: 0.75rem;
--text-sub-xs--line-height: 1.33;
--text-sub-xs--letter-spacing: 0.04em;
--text-sub-xs--font-weight: 500;
--text-sub-2xs: 0.6875rem;
--text-sub-2xs--line-height: 1.1;
--text-sub-2xs--letter-spacing: 0.02em;
--text-sub-2xs--font-weight: 500;
}
@layer base {
button {
@apply cursor-pointer;
}
}
@layer components {
.text-h3 {
@apply font-inter-display;
}
.text-label-xl,
.text-p-lg,
.text-sub-sm,
.text-sub-xs,
.text-sub-2xs {
@apply font-inter;
}
.chat-wrapper {
@apply flex flex-col h-[calc(100svh-8.25rem)] bg-white-0 rounded-3xl shadow-[0_0_1.25rem_0_rgba(0,0,0,0.03)] max-2xl:h-[calc(100svh-7.125rem)] max-lg:h-[calc(100svh-6.125rem)] max-md:h-[calc(100svh-5rem)] max-md:rounded-xl;
}
.content h3 {
@apply mb-5 text-h3 max-2xl:text-[2rem] max-md:text-[1.6rem];
}
.content p {
@apply text-label-md not-last:mb-4 max-md:text-p-sm max-md:not-last:mb-2;
}
.recharts-surface {
@apply outline-none;
}
.recharts-sector {
@apply stroke-transparent outline-none;
}
}
/* SVG Progress Animation */
@keyframes progress {
from {
stroke-dashoffset: 100.48;
}
to {
stroke-dashoffset: 0;
}
}
.animate-progress {
animation: progress linear forwards;
}

View File

@@ -0,0 +1,5 @@
import HistoryPage from "@/templates/HistoryPage";
export default function Page() {
return <HistoryPage />;
}

View File

@@ -0,0 +1,5 @@
import ImageGenerationPage from "@/templates/ImageGenerationPage";
export default function Page() {
return <ImageGenerationPage />;
}

View File

@@ -0,0 +1,158 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import localFont from "next/font/local";
import Providers from "./providers";
import "./globals.css";
const inter = Inter({
variable: "--font-inter",
subsets: ["latin"],
});
const satoshi = localFont({
src: [
{
path: "../public/fonts/Satoshi-Light.woff2",
weight: "300",
},
{
path: "../public/fonts/Satoshi-Regular.woff2",
weight: "400",
},
{
path: "../public/fonts/Satoshi-Medium.woff2",
weight: "500",
},
{
path: "../public/fonts/Satoshi-Bold.woff2",
weight: "700",
},
],
variable: "--font-satoshi",
});
const interDisplay = localFont({
src: [
{
path: "../public/fonts/InterDisplay-Medium.woff2",
weight: "500",
},
],
variable: "--font-inter-display",
});
export const metadata: Metadata = {
title: "NeuraTalk",
description: "NeuraTalk: Coded AI Chat Companion",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html
className="text-[calc(0.7rem+0.35vw)] max-[2300px]:text-[calc(0.7rem+0.32vw)] max-[2150px]:text-[calc(0.7rem+0.28vw)] max-4xl:text-[1rem]"
lang="en"
suppressHydrationWarning
>
<head>
{/* Description no longer than 155 characters */}
<meta
name="description"
content="NeuraTalk: Coded AI Chat Companion"
/>
{/* NeuraTalk: Coded AI Chat Companion */}
<meta
name="product-name"
content="NeuraTalk: Coded AI Chat Companion"
/>
{/* Twitter Card data */}
<meta name="twitter:card" content="summary" />
<meta name="twitter:site" content="@ui8" />
<meta
name="twitter:title"
content="NeuraTalk: Coded AI Chat Companion"
/>
<meta
name="twitter:description"
content="A powerful coded AI chat experience"
/>
<meta name="twitter:creator" content="@ui8" />
{/* Twitter Summary card images must be at least 120x120px */}
<meta
name="twitter:image"
content="https://neuratalk-tau.vercel.app/twitter-card.png"
/>
{/* Open Graph data for Facebook */}
<meta
property="og:title"
content="NeuraTalk: Coded AI Chat Companion"
/>
<meta property="og:type" content="Article" />
<meta
property="og:url"
content="https://ui8.net/odyssey-agency-d2aaee/products/neuratalk-your-smart-ai-chat-companion-coded"
/>
<meta
property="og:image"
content="https://neuratalk-tau.vercel.app/fb-og-image.png"
/>
<meta
property="og:description"
content="A powerful coded AI chat experience"
/>
<meta
property="og:site_name"
content="NeuraTalk: Coded AI Chat Companion"
/>
<meta property="fb:admins" content="132951670226590" />
{/* Open Graph data for LinkedIn */}
<meta
property="og:title"
content="NeuraTalk: Coded AI Chat Companion"
/>
<meta
property="og:url"
content="https://ui8.net/odyssey-agency-d2aaee/products/neuratalk-your-smart-ai-chat-companion-coded"
/>
<meta
property="og:image"
content="https://neuratalk-tau.vercel.app/linkedin-og-image.png"
/>
<meta
property="og:description"
content="A powerful coded AI chat experience"
/>
{/* Open Graph data for Pinterest */}
<meta
property="og:title"
content="NeuraTalk: Coded AI Chat Companion"
/>
<meta
property="og:url"
content="https://ui8.net/slabdsgn/products/elitefinancial---fintech-html--react--tailwind"
/>
<meta
property="og:image"
content="https://neuratalk-tau.vercel.app/pinterest-og-image.png"
/>
<meta
property="og:description"
content="A powerful coded AI chat experience"
/>
</head>
<body
className={`${satoshi.variable} ${inter.variable} ${interDisplay.variable} bg-weak-50 font-satoshi text-p-sm text-strong-950 antialiased`}
>
<Providers>{children}</Providers>
</body>
</html>
);
}

View File

@@ -0,0 +1,15 @@
// 可以选择使用原始主页或直接进入聊天
// import HomePage from "@/templates/HomePage";
import MCPChat from "@/components/Chat/MCPChat";
export default function Page() {
// 直接显示聊天界面
return (
<main className="h-screen">
<MCPChat />
</main>
);
// 或者使用原始主页
// return <HomePage />;
}

View File

@@ -0,0 +1,13 @@
"use client";
import { ThemeProvider } from "next-themes";
const Providers = ({ children }: { children: React.ReactNode }) => {
return (
<ThemeProvider defaultTheme="light" disableTransitionOnChange>
{children}
</ThemeProvider>
);
};
export default Providers;

View File

@@ -0,0 +1,5 @@
import ResearchPage from "@/templates/ResearchPage";
export default function Page() {
return <ResearchPage />;
}

View File

@@ -0,0 +1,5 @@
import TemplatesPage from "@/templates/TemplatesPage";
export default function Page() {
return <TemplatesPage />;
}

View File

@@ -0,0 +1,5 @@
import WriteCopyPage from "@/templates/WriteCopyPage";
export default function Page() {
return <WriteCopyPage />;
}

View File

@@ -0,0 +1,47 @@
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
import Icon from "@/components/Icon";
type Props = {
className?: string;
classNameButton?: string;
items: {
name: string;
onClick: () => void;
}[];
};
const Actions = ({ className, classNameButton, items }: Props) => {
return (
<Menu className={`${className || ""}`} as="div">
<MenuButton
className={`group flex rounded-sm outline-0 transition-colors data-[open]:bg-weak-50 ${
classNameButton || ""
}`}
>
<Icon
className="fill-icon-sub-600 transition-colors group-hover:fill-strong-950 group-[[data-open]]:fill-strong-950"
name="dots"
/>
</MenuButton>
<MenuItems
className="[--anchor-gap:0.5rem] [--anchor-offset:0.5rem] z-20 flex flex-col w-40 p-1 border border-stroke-soft-200 rounded-xl outline-0 bg-white-0 shadow-xl transition duration-200 ease-out origin-top data-closed:scale-95 data-closed:opacity-0"
anchor="bottom end"
transition
modal={false}
>
{items.map((item, index) => (
<MenuItem
className="group flex items-center gap-3 h-8 px-3 rounded-lg text-label-sm text-soft-600 transition-colors hover:bg-weak-50 hover:text-strong-950"
key={index}
onClick={() => item.onClick()}
as="button"
>
{item.name}
</MenuItem>
))}
</MenuItems>
</Menu>
);
};
export default Actions;

View File

@@ -0,0 +1,25 @@
import Image from "@/components/Image";
type Props = {
children: React.ReactNode;
};
const Answer = ({ children }: Props) => (
<div className="flex">
<div className="shrink-0">
<Image
className="size-9 opacity-100 rounded-full object-cover"
src="/images/avatar-chat.svg"
width={36}
height={36}
alt="Avatar"
/>
</div>
<div className="w-[calc(100%-2.25rem)] pl-3">
<div className="mb-1 text-label-sm">Odyssey AI</div>
{children}
</div>
</div>
);
export default Answer;

View File

@@ -0,0 +1,70 @@
import { useState } from "react";
import Chat from "@/components/Chat";
import Question from "@/components/Question";
import Answer from "@/components/Answer";
import CodeEditor from "@/components/CodeEditor";
const ApiIntegrator = () => {
const [code, setCode] = useState(`{
"location": "Paris",
"temperature": 18,
"condition": "Partly Cloudy"
}`);
return (
<Chat>
<Question>
<div className="mb-1">
Use the API to get the current weather in Paris.
</div>
<a
className="text-label-sm text-[#7D52F4] transition-colors hover:text-[#8c71f6]"
href="https://api.weatherly.ai/v1/current"
target="_blank"
rel="noopener noreferrer"
>
https://api.weatherly.ai/v1/current
</a>
</Question>
<Answer>
<div className="flex flex-col gap-1">
<div className="">
<p>
Absolutely! Here&apos;s the result for the API
setup, for the current weather in paris.
</p>
<br />
<p className="text-label-md">🔧 API Example</p>
<ul className="list-disc list-inside">
<li>API Name: Weatherly API</li>
<li>
Endpoint: https://api.weatherly.ai/v1/current
</li>
<li>Method: GET</li>
<li>
Query Params:
<ul className="ml-5 list-disc list-inside">
<li>city=Paris</li>
<li>unit=celsius</li>
</ul>
</li>
<li>Response Example:</li>
</ul>
</div>
<CodeEditor
title="Json"
language="json"
initialCode={code}
onCodeChange={setCode}
/>
<div className="">
Its currently 18°C and partly cloudy in Paris. A lovely
day to be outside!
</div>
</div>
</Answer>
</Chat>
);
};
export default ApiIntegrator;

View File

@@ -0,0 +1,53 @@
import Icon from "@/components/Icon";
import Button from "@/components/Button";
type Props = {
content: React.ReactNode;
onEdit: () => void;
};
const Article = ({ content, onEdit }: Props) => (
<div className="bg-white-0 border border-stroke-soft-200 rounded-xl p-4 pb-0 overflow-hidden max-md:p-0 ">
<div className="flex items-center gap-2 mb-5 max-md:mb-0 max-md:p-3">
<div className="flex items-center gap-2 mr-auto text-[1rem]">
<Icon className="fill-strong-950" name="document-check" />
Document
</div>
<Button
className="max-md:shrink-0 max-md:w-10 max-md:h-10 max-md:p-0 max-md:gap-0 max-md:text-0"
isStroke
>
<svg
className="size-5 fill-strong-950"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M10.838 11.042c0-.46-.373-.833-.833-.833s-.833.373-.833.833v3.75h-.495c-.146 0-.326 0-.473.018H8.2c-.105.013-.585.073-.814.544s.022.889.077.979l.002.003c.077.128.189.27.281.387l.02.025.881 1.043c.158.163.339.33.532.463.171.118.463.287.822.287s.651-.169.822-.287a3.67 3.67 0 0 0 .532-.463c.317-.326.635-.729.881-1.043l.02-.025.281-.387.002-.003c.055-.09.306-.507.077-.979s-.709-.531-.814-.544h-.003c-.147-.018-.327-.018-.473-.018h-.485v-3.75z"
/>
<path
opacity=".4"
d="M1.041 10.417c0-2.201 1.484-4.056 3.507-4.617.163-.045.245-.068.292-.116s.068-.13.109-.293a5.21 5.21 0 0 1 10.227.693c.023.204.034.307.086.366s.154.084.356.133c1.917.465 3.34 2.192 3.34 4.252 0 2.416-1.959 4.375-4.375 4.375h-.05c-.183 0-.274 0-.337-.042s-.106-.144-.191-.348c-.105-.251-.262-.486-.469-.687-.276-.268-.608-.445-.957-.53-.259-.063-.388-.095-.442-.163s-.053-.178-.053-.397v-2c0-1.151-.933-2.083-2.083-2.083s-2.083.933-2.083 2.083v2c0 .219 0 .329-.053.397s-.183.1-.442.163c-.349.085-.681.262-.957.53-.208.203-.384.438-.502.691-.097.208-.146.312-.214.352s-.153.032-.324.018c-2.455-.207-4.383-2.266-4.383-4.774z"
/>
</svg>
<span>Donwload Article</span>
</Button>
<Button
className="max-md:shrink-0 max-md:w-10 max-md:h-10 max-md:p-0 max-md:gap-0 max-md:text-0"
icon="edit"
isBlack
onClick={onEdit}
>
Edit Article
</Button>
</div>
<div className="content max-h-96 bg-white-0 px-18 py-16 rounded-xl shadow-[0_4px_3.4rem_0_rgba(14,18,27,0.10)] overflow-y-auto scrollbar-none max-2xl:p-12 max-xl:p-8 max-md:px-3 max-md:py-4 dark:shadow-[0_0.25rem_3.4rem_0_rgba(255,255,255,0.10)]">
{content}
</div>
</div>
);
export default Article;

View File

@@ -0,0 +1,18 @@
export const content = [
{
id: 0,
title: "The STAUD A Creative Powerhouse",
description:
"Key features: Smooth animations, seamless navigation, and intuitive user flow.",
link: "http://webflow.com/theSTAUD.com",
image: "/images/browser-pic-1.jpg",
},
{
id: 1,
title: "New Horizon Connecting you to faith",
description:
"Key features: Smooth animations, seamless navigation, and intuitive user flow.",
link: "http://webflow.com/newHorizon.com",
image: "/images/browser-pic-2.jpg",
},
];

View File

@@ -0,0 +1,71 @@
import Chat from "@/components/Chat";
import Question from "@/components/Question";
import Answer from "@/components/Answer";
import Image from "@/components/Image";
import Icon from "@/components/Icon";
import Actions from "@/components/Actions";
import { content } from "./content";
const actions = [
{ name: "Edit", onClick: () => {} },
{ name: "Delete", onClick: () => {} },
{ name: "Copy", onClick: () => {} },
];
const Browser = () => {
return (
<Chat>
<Question>
what is the most beatiful website made using{" "}
<span className="font-medium">webflow</span>
</Question>
<Answer>
<div className="mb-6 max-md:mb-4">
If you&apos;re searching for the most beautiful websites
built with Webflow, there are several standout examples that
showcase the platform&apos;s capabilities in design,
interactivity, and storytelling. Here are some notable ones:
</div>
<div className="flex flex-col gap-6 max-md:gap-4">
{content.map((item, index) => (
<div key={item.id}>
<div className="text-label-sm">
🌟 {index + 1}. {item.title}
</div>
<div>{item.description}</div>
<a
className="text-label-sm text-blue-500 transition-colors hover:text-blue-400"
href={item.link}
target="_blank"
rel="noopener noreferrer"
>
{item.link}
</a>
<div className="relative max-w-92 mt-1">
<Image
className="w-full rounded-lg"
src={item.image}
alt={item.title}
width={366}
height={216}
/>
<div className="absolute top-2 right-3.5 flex items-center gap-3 h-9 px-2.5 bg-white-0 border border-stroke-soft-200 rounded-xl max-md:right-2">
<button className="group text-0">
<Icon
className="!size-4 fill-icon-sub-600 transition-colors group-hover:fill-strong-950"
name="share-1"
/>
</button>
<Actions items={actions} />
</div>
</div>
</div>
))}
</div>
</Answer>
</Chat>
);
};
export default Browser;

View File

@@ -0,0 +1,88 @@
import React from "react";
import Link, { LinkProps } from "next/link";
import Icon from "@/components/Icon";
type CommonProps = {
className?: string;
icon?: string;
children?: React.ReactNode;
isBlack?: boolean;
isWhite?: boolean;
isRed?: boolean;
isBlue?: boolean;
isStroke?: boolean;
isCircle?: boolean;
};
type ButtonAsButton = {
as?: "button";
} & React.ButtonHTMLAttributes<HTMLButtonElement>;
type ButtonAsAnchor = {
as: "a";
} & React.AnchorHTMLAttributes<HTMLAnchorElement>;
type ButtonAsLink = {
as: "link";
} & LinkProps;
type ButtonProps = CommonProps &
(ButtonAsButton | ButtonAsAnchor | ButtonAsLink);
const Button: React.FC<ButtonProps> = ({
className,
icon,
children,
isBlack,
isWhite,
isRed,
isBlue,
isStroke,
isCircle,
as = "button",
...props
}) => {
const isLink = as === "link";
const Component: React.ElementType = isLink ? Link : as;
return (
<Component
className={`inline-flex items-center justify-center gap-2 h-11 px-4 border rounded-full text-label-sm transition-all cursor-pointer disabled:pointer-events-none ${
isBlack
? "bg-strong-950 border-transparent text-white-0 fill-white-0 hover:bg-strong-950/90"
: ""
} ${
isWhite
? "bg-white-0 border-transparent text-strong-950 fill-strong-950 hover:bg-soft-200"
: ""
} ${
isStroke
? "border-soft-200 bg-white-0 text-sub-600 fill-sub-600 hover:border-stroke-sub-300 hover:text-strong-950 hover:fill-strong-950"
: ""
} ${
isRed
? "bg-[#FB3748] border-transparent text-static-white fill-static-white hover:bg-[#FB3748]/90"
: ""
} ${
isBlue
? "bg-gradient-to-r from-blue-500 to-blue-300 border-stroke-soft-200 text-static-white fill-static-white hover:opacity-90"
: ""
} ${
isCircle ? "!gap-0 !w-11 !px-0 max-md:!w-10 max-md:!h-10" : ""
} ${className || ""}`}
{...(isLink ? (props as LinkProps) : props)}
>
{icon && (
<Icon
className={`fill-inherit ${
isCircle ? "!size-6 max-md:!size-5" : ""
}`}
name={icon}
/>
)}
{children}
</Component>
);
};
export default Button;

View File

@@ -0,0 +1,42 @@
import Chat from "@/components/Chat";
import Question from "@/components/Question";
import Answer from "@/components/Answer";
const Calculator = () => {
return (
<Chat>
<Question>
solve this problem for me:
<br />
Find the derivative of f(x) = x³ + 2x² - 7
</Question>
<Answer>
<div className="mb-1">Sure here is the response:</div>
<div className="">
<p className="text-label-sm"> Step-by-step solution:</p>
<br />
<p>Well differentiate f(x) term by term:</p>
<ol className="list-decimal list-inside">
<li>
Derivative of x³ is: <br />
&nbsp;&nbsp;&nbsp;&nbsp; 3x²
</li>
<li>
Derivative of 2x² is: <br />
&nbsp;&nbsp;&nbsp;&nbsp; 2 × 2x = 4x
</li>
<li>
Derivative of constant -7 is: <br />
&nbsp;&nbsp;&nbsp;&nbsp; 0
</li>
</ol>
<br />
<p className="text-label-sm">🎯 Final Answer:</p>
<p>f&apos;(x) = 3x² + 4x</p>
</div>
</Answer>
</Chat>
);
};
export default Calculator;

View File

@@ -0,0 +1,30 @@
export const data = [
{
name: "Oct 16",
tp: 32456,
},
{
name: "Oct 17",
tp: 52456,
},
{
name: "Oct 18",
tp: 21456,
},
{
name: "Oct 19",
tp: 27456,
},
{
name: "Oct 20",
tp: 52456,
},
{
name: "Oct 21",
tp: 22456,
},
{
name: "Oct 22",
tp: 32456,
},
];

View File

@@ -0,0 +1,162 @@
import { useState } from "react";
import {
ResponsiveContainer,
AreaChart,
Area,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
} from "recharts";
import millify from "millify";
import Select from "@/components/Select";
import Icon from "@/components/Icon";
import { data } from "./data";
const durationOptions = [
{ id: 1, name: "Last 7 days" },
{ id: 2, name: "Last 14 days" },
{ id: 3, name: "Last 21 days" },
];
const Chart = ({}) => {
const [duration, setDuration] = useState(durationOptions[0]);
const formatterYAxis = (value: number) => {
if (value === 0) {
return "$0";
}
return `$${millify(value, {
lowercase: false,
})}`;
};
const CustomTooltip = ({ payload }: { payload: { value: number }[] }) => {
if (payload && payload.length) {
return (
<div className="p-1.5 bg-strong-950 rounded-md text-label-xs text-white-0">
Bitcoin : ${payload[0].value}
</div>
);
}
return null;
};
return (
<div className="">
<div className="border border-soft-200 px-4.5 py-3 rounded-lg max-md:px-3">
<div className="flex items-center mb-2">
<div className="mr-auto text-h6">Bitcoin</div>
<div className="flex">
<Select
className=""
classButton="rounded-r-none max-md:rounded-lg"
value={duration}
onChange={setDuration}
options={durationOptions}
/>
<div className="flex items-center gap-2 pl-2 pr-4 border-l-0 rounded-r-xl border border-stroke-soft-200 text-label-sm text-sub-600 max-md:hidden">
<Icon className="fill-sub-600" name="calendar" />
Oct 16 - Oct 22 2025
</div>
</div>
</div>
<div className="flex items-center gap-2 mb-4">
<div className="text-h5 max-md:text-[1.5rem]">$94,112</div>
<div className="flex items-center text-label-xs text-[#009934]">
<Icon
className="-rotate-90 !size-4 fill-[#009934]"
name="arrow"
/>
12%
</div>
<div className="text-label-sm text-soft-400 max-md:hidden">
vs last Week
</div>
</div>
<div className="h-44 -ml-3">
<ResponsiveContainer width="100%" height="100%">
<AreaChart
width={730}
height={250}
data={data}
margin={{ top: 10, right: 5, left: 0, bottom: 0 }}
>
<defs>
<linearGradient
id="color"
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop
offset="0%"
stopColor="#335CFF"
stopOpacity={0.12}
/>
<stop
offset="95%"
stopColor="#335CFF"
stopOpacity={0}
/>
</linearGradient>
</defs>
<CartesianGrid
vertical={false}
strokeDasharray="5 2"
stroke="var(--stroke-soft-200)"
strokeOpacity={0.5}
/>
<XAxis
dataKey="name"
axisLine={false}
tickLine={false}
tick={{
fontSize: "0.875rem",
fill: "var(--text-soft-400)",
}}
height={32}
dy={8}
/>
<YAxis
tickFormatter={formatterYAxis}
axisLine={false}
tickLine={false}
tick={{
fontSize: "0.875rem",
fill: "var(--text-soft-400)",
}}
/>
<Tooltip
content={<CustomTooltip payload={[]} />}
cursor={{
stroke: "var(--strong-950)",
strokeDasharray: "5 5",
}}
/>
<Area
type="linear"
dataKey="tp"
stroke="#335CFF"
strokeWidth={2}
fillOpacity={1}
fill="url(#color)"
dot={false}
activeDot={{
r: 5,
fill: "var(--strong-950)",
stroke: "var(--white-0)",
strokeWidth: 2,
}}
/>
</AreaChart>
</ResponsiveContainer>
</div>
</div>
</div>
);
};
export default Chart;

View File

@@ -0,0 +1,62 @@
import { useState } from "react";
import Icon from "@/components/Icon";
import ModalShare from "@/components/ModalShare";
import Actions from "@/components/Actions";
const actions = [
{ name: "New chat", onClick: () => {} },
{ name: "Move to folder", onClick: () => {} },
{ name: "Clear chat", onClick: () => {} },
{ name: "Delete chat", onClick: () => {} },
];
type ButtonProps = {
icon: string;
onClick: () => void;
};
const Button = ({ icon, onClick }: ButtonProps) => (
<button className="group text-0" onClick={onClick}>
<Icon
className="fill-strong-950 transition-colors group-hover:fill-blue-500"
name={icon}
/>
</button>
);
type Props = {
title?: React.ReactNode;
};
const Head = ({ title }: Props) => {
const [visible, setVisible] = useState(false);
return (
<>
<div className="flex items-center shrink-0 h-13 px-3 border-b border-stroke-soft-200">
{title ? (
title
) : (
<div className="flex items-center gap-2 mr-auto">
<Icon className="fill-strong-950" name="chat" />
<div className="text-label-sm">Ask your AI</div>
<div className="px-3 py-0.5 bg-strong-950 rounded-md text-label-xs text-white-0">
Beta
</div>
</div>
)}
<div className="flex items-center gap-2 ml-auto">
<Button icon="share" onClick={() => setVisible(true)} />
<Button icon="link-1" onClick={() => {}} />
<Actions
classNameButton="[&_svg]:fill-strong-950"
items={actions}
/>
</div>
</div>
<ModalShare open={visible} onClose={() => setVisible(false)} />
</>
);
};
export default Head;

View File

@@ -0,0 +1,288 @@
// components/Chat/MCPChat.tsx - 集成 MCP 的聊天组件
'use client';
import React, { useState, useRef, useEffect } from 'react';
import { mcpService } from '../../services/mcp-real';
import { useAuth } from '../../hooks/useAuth';
import styles from './MCPChat.module.css';
interface Message {
id: string;
role: 'user' | 'assistant' | 'system';
content: string;
timestamp: Date;
isStreaming?: boolean;
}
export default function MCPChat() {
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [streamingMessage, setStreamingMessage] = useState('');
const [tools, setTools] = useState<any[]>([]);
const messagesEndRef = useRef<HTMLDivElement>(null);
const { user, isAuthenticated, canAccessChat, loading: authLoading } = useAuth();
// 加载可用工具
useEffect(() => {
if (isAuthenticated && canAccessChat) {
loadTools();
}
}, [isAuthenticated, canAccessChat]);
// 自动滚动到底部
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages, streamingMessage]);
const loadTools = async () => {
try {
const availableTools = await mcpService.getTools();
setTools(availableTools);
console.log('Available tools:', availableTools);
} catch (error) {
console.error('Failed to load tools:', error);
}
};
const handleSend = async () => {
if (!input.trim() || isLoading) return;
const userMessage: Message = {
id: Date.now().toString(),
role: 'user',
content: input,
timestamp: new Date(),
};
setMessages(prev => [...prev, userMessage]);
setInput('');
setIsLoading(true);
setStreamingMessage('');
try {
// 创建助手消息占位符
const assistantMessage: Message = {
id: (Date.now() + 1).toString(),
role: 'assistant',
content: '',
timestamp: new Date(),
isStreaming: true,
};
setMessages(prev => [...prev, assistantMessage]);
// 流式接收回复
let fullContent = '';
for await (const chunk of mcpService.streamMessage(input)) {
fullContent += chunk;
setStreamingMessage(fullContent);
}
// 更新最终消息
setMessages(prev =>
prev.map(msg =>
msg.id === assistantMessage.id
? { ...msg, content: fullContent, isStreaming: false }
: msg
)
);
setStreamingMessage('');
} catch (error) {
console.error('Send message error:', error);
setMessages(prev => [
...prev,
{
id: Date.now().toString(),
role: 'system',
content: '抱歉,发送消息时出错了。请稍后重试。',
timestamp: new Date(),
},
]);
} finally {
setIsLoading(false);
}
};
const handleToolCall = async (toolName: string) => {
// 示例:调用工具
try {
setIsLoading(true);
const result = await mcpService.callTool(toolName, {});
console.log('Tool result:', result);
// 显示工具结果
setMessages(prev => [
...prev,
{
id: Date.now().toString(),
role: 'system',
content: `工具 ${toolName} 执行结果:${JSON.stringify(result.result, null, 2)}`,
timestamp: new Date(),
},
]);
} catch (error) {
console.error('Tool call error:', error);
} finally {
setIsLoading(false);
}
};
// 权限检查
if (authLoading) {
return (
<div className="flex items-center justify-center h-screen">
<div className="text-lg">...</div>
</div>
);
}
if (!isAuthenticated) {
return (
<div className="flex flex-col items-center justify-center h-screen">
<h2 className="text-2xl mb-4"></h2>
<a
href={`${process.env.NEXT_PUBLIC_MAIN_APP_URL}/auth/sign-in?redirect=/chat`}
className="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700"
>
</a>
</div>
);
}
if (!canAccessChat) {
return (
<div className="flex flex-col items-center justify-center h-screen">
<h2 className="text-2xl mb-4">使 AI </h2>
<p className="text-gray-600 mb-6"></p>
<a
href={`${process.env.NEXT_PUBLIC_MAIN_APP_URL}/subscription`}
className="bg-green-600 text-white px-6 py-2 rounded hover:bg-green-700"
>
</a>
</div>
);
}
return (
<div className="flex h-screen bg-gray-50 dark:bg-gray-900">
{/* 侧边栏 - 工具列表 */}
<div className="w-64 bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 p-4">
<h3 className="font-semibold mb-4"></h3>
<div className="space-y-2">
{tools.map(tool => (
<button
key={tool.name}
onClick={() => handleToolCall(tool.name)}
disabled={isLoading}
className="w-full text-left p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-50"
>
<div className="font-medium">{tool.name}</div>
<div className="text-sm text-gray-600 dark:text-gray-400">
{tool.description}
</div>
</button>
))}
</div>
{/* 用户信息 */}
<div className="mt-auto pt-4 border-t border-gray-200 dark:border-gray-700">
<div className="flex items-center space-x-2">
<div className="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center text-white">
{user?.username?.[0]?.toUpperCase()}
</div>
<div>
<div className="font-medium">{user?.username}</div>
<div className="text-xs text-gray-500">{user?.subscription_tier}</div>
</div>
</div>
</div>
</div>
{/* 主聊天区域 */}
<div className="flex-1 flex flex-col">
{/* 消息列表 */}
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.map(msg => (
<div
key={msg.id}
className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-2xl px-4 py-2 rounded-lg ${
msg.role === 'user'
? 'bg-blue-600 text-white'
: msg.role === 'system'
? 'bg-yellow-100 text-yellow-800'
: 'bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
}`}
>
{msg.isStreaming ? (
<div>
{streamingMessage || (
<span className="inline-block animate-pulse">...</span>
)}
</div>
) : (
<div className="whitespace-pre-wrap">{msg.content}</div>
)}
<div className="text-xs mt-1 opacity-70">
{msg.timestamp.toLocaleTimeString()}
</div>
</div>
</div>
))}
<div ref={messagesEndRef} />
</div>
{/* 输入区域 */}
<div className="border-t border-gray-200 dark:border-gray-700 p-4">
<div className="flex space-x-2">
<input
type="text"
value={input}
onChange={e => setInput(e.target.value)}
onKeyPress={e => e.key === 'Enter' && handleSend()}
disabled={isLoading}
placeholder="输入消息..."
className="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white"
/>
<button
onClick={handleSend}
disabled={!input.trim() || isLoading}
className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isLoading ? '发送中...' : '发送'}
</button>
</div>
{/* 快捷操作 */}
<div className="flex space-x-2 mt-2">
<button
onClick={() => setInput('/help')}
className="text-sm text-blue-600 hover:underline"
>
</button>
<button
onClick={() => setInput('/tools')}
className="text-sm text-blue-600 hover:underline"
>
</button>
<button
onClick={() => mcpService.clearHistory()}
className="text-sm text-red-600 hover:underline"
>
</button>
</div>
</div>
</div>
</div>
);
}
// CSS 模块文件需要单独创建

View File

@@ -0,0 +1,26 @@
import PanelMessage from "@/components/PanelMessage";
import Head from "./Head";
type Props = {
titleHead?: React.ReactNode;
hidePanelMessage?: boolean;
children: React.ReactNode;
};
const Chat = ({ titleHead, hidePanelMessage, children }: Props) => {
return (
<div className="chat-wrapper">
<Head title={titleHead} />
<div
className={`flex flex-col gap-4.5 grow p-7.5 overflow-auto scrollbar-none max-md:gap-3 max-md:p-4 max-md:pb-8 ${
hidePanelMessage ? "" : "-mb-3 pb-10"
}`}
>
{children}
</div>
{!hidePanelMessage && <PanelMessage />}
</div>
);
};
export default Chat;

View File

@@ -0,0 +1,107 @@
import { useState } from "react";
import { useTheme } from "next-themes";
import Editor from "@monaco-editor/react";
import Icon from "@/components/Icon";
import Button from "@/components/Button";
interface CodeEditorProps {
title?: string;
language?: string;
initialCode?: string;
onCodeChange?: (code: string) => void;
onGenerate?: () => void;
isGenerating?: boolean;
}
const CodeEditor = ({
title = "Code Editor",
language = "python",
initialCode = "",
onCodeChange,
onGenerate,
isGenerating = false,
}: CodeEditorProps) => {
const [code, setCode] = useState(initialCode);
const { theme } = useTheme();
const handleEditorChange = (value: string | undefined) => {
const newCode = value || "";
setCode(newCode);
onCodeChange?.(newCode);
};
const handleCopyCode = async () => {
try {
await navigator.clipboard.writeText(code);
console.log("Code copied to clipboard!");
} catch (err) {
console.error("Failed to copy code: ", err);
}
};
return (
<div className="bg-white-0 rounded-xl border border-stroke-soft-200 overflow-hidden">
<div className="flex items-center gap-2 p-4 font-[1rem] leading-[1.25rem]">
<Icon className="fill-strong-950" name="ai-programming" />
{title}
<Button
className="!h-9 ml-auto !px-3 rounded-lg text-sub-600 max-md:hidden"
isStroke
onClick={handleCopyCode}
>
Copy Code
<svg
className="size-3.25 fill-icon-soft-400"
xmlns="http://www.w3.org/2000/svg"
width="13"
height="13"
viewBox="0 0 13 13"
>
<path
opacity=".4"
d="M9.108 4.32l1.94.068c.523.07.964.221 1.314.571s.501.791.571 1.314C13 6.776 13 7.417 13 8.212h0v.645h0l-.068 1.94c-.07.523-.221.964-.571 1.314s-.791.501-1.314.571c-.504.068-1.145.068-1.94.068h0-.645 0c-.795 0-1.436 0-1.94-.068-.523-.07-.964-.221-1.314-.571s-.5-.791-.571-1.314c-.068-.504-.068-1.145-.068-1.94h0v-.645h0l.068-1.94c.07-.523.221-.964.571-1.314s.791-.5 1.314-.571c.504-.068 1.145-.068 1.94-.068h0 .645 0z"
/>
<path d="M3.698 8.161l.076-2.005c.083-.62.279-1.275.818-1.814s1.195-.735 1.814-.818c.562-.076 1.253-.076 2.005-.076h1.3c.172 0 .258 0 .31-.056s.046-.14.033-.307l-.042-.413c-.075-.558-.226-1.021-.546-1.412a2.76 2.76 0 0 0-.383-.383C8.671.539 8.178.39 7.578.319 6.995.25 6.257.25 5.325.25h-.057c-.932 0-1.67 0-2.253.069-.599.071-1.093.22-1.505.558a2.76 2.76 0 0 0-.383.383c-.338.412-.487.906-.558 1.505C.5 3.349.5 4.087.5 5.018v.057l.069 2.253c.071.599.22 1.093.558 1.505a2.76 2.76 0 0 0 .383.383c.391.321.854.471 1.412.546l.413.042c.168.012.251.019.307-.033s.056-.138.056-.31v-1.3z" />
</svg>
</Button>
</div>
<div className="h-64">
<Editor
height="100%"
language={language}
value={code}
onChange={handleEditorChange}
theme={theme === "light" ? "vs-light" : "vs-dark"}
options={{
minimap: { enabled: false },
scrollBeyondLastLine: false,
fontSize: 14,
lineNumbers: "on",
roundedSelection: false,
scrollbar: {
vertical: "auto",
horizontal: "auto",
},
automaticLayout: true,
padding: { top: 4, bottom: 0 },
fontFamily:
"'Monaco', 'Menlo', 'Ubuntu Mono', monospace",
}}
/>
</div>
{onGenerate && (
<div className="py-4 text-center">
<Button
isStroke
onClick={onGenerate}
disabled={isGenerating}
>
{isGenerating ? "Generating Code..." : "Generate Code"}
</Button>
</div>
)}
</div>
);
};
export default CodeEditor;

View File

@@ -0,0 +1,149 @@
import Icon from "@/components/Icon";
import Button from "@/components/Button";
import { useState, useRef, useEffect } from "react";
type Props = {
content: React.ReactNode;
onBack: () => void;
};
const EditorArticle = ({ content, onBack }: Props) => {
const [isTextSelected, setIsTextSelected] = useState(false);
const [selectionPosition, setSelectionPosition] = useState({
top: 0,
left: 0,
});
const contentRef = useRef<HTMLDivElement>(null);
const handleTextSelection = () => {
const selection = window.getSelection();
if (selection && selection.toString().trim().length > 0) {
const range = selection.getRangeAt(0);
const rect = range.getBoundingClientRect();
const contentRect = contentRef.current?.getBoundingClientRect();
if (contentRect) {
setSelectionPosition({
top: rect.top - contentRect.top - 50,
left: rect.left - contentRect.left + rect.width / 2 - 100,
});
setIsTextSelected(true);
}
} else {
setIsTextSelected(false);
}
};
const handleFormatText = (format: string) => {
const selection = window.getSelection();
if (selection && selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
const selectedText = selection.toString();
if (selectedText) {
let formattedText = selectedText;
switch (format) {
case "bold":
formattedText = `<strong>${selectedText}</strong>`;
break;
case "italic":
formattedText = `<em>${selectedText}</em>`;
break;
case "underline":
formattedText = `<u>${selectedText}</u>`;
break;
case "strikethrough":
formattedText = `<s>${selectedText}</s>`;
break;
}
range.deleteContents();
const tempDiv = document.createElement("div");
tempDiv.innerHTML = formattedText;
const fragment = document.createDocumentFragment();
while (tempDiv.firstChild) {
fragment.appendChild(tempDiv.firstChild);
}
range.insertNode(fragment);
selection.removeAllRanges();
setIsTextSelected(false);
}
}
};
useEffect(() => {
document.addEventListener("selectionchange", handleTextSelection);
return () => {
document.removeEventListener(
"selectionchange",
handleTextSelection
);
};
}, []);
return (
<div className="chat-wrapper p-5 max-md:p-0">
<div className="flex justify-between items-center mb-5 max-md:mb-0 max-md:p-3">
<button
className="group flex items-center gap-2 text-label-sm max-md:text-0"
onClick={onBack}
>
<div className="flex justify-center items-center size-9 bg-weak-50 rounded-lg transition-colors group-hover:bg-soft-200">
<Icon
className="rotate-90 fill-strong-950"
name="chevron"
/>
</div>
Non-Disclosure Agreement Document
</button>
<Button className="!h-8" isBlack onClick={onBack}>
Save Document
</Button>
</div>
<div
ref={contentRef}
className="relative content grow bg-white-0 px-18 py-16 rounded-xl shadow-[0_4px_3.4rem_0_rgba(14,18,27,0.10)] overflow-y-auto scrollbar-none max-2xl:p-12 max-xl:p-8 max-md:px-3 max-md:py-4 dark:shadow-[0_0.25rem_3.4rem_0_rgba(255,255,255,0.10)]"
>
{content}
{isTextSelected && (
<div
className="absolute z-10 bg-white-0 border border-soft-200 rounded-lg shadow-lg p-1 flex items-center gap-1"
style={{
top: `${selectionPosition.top}px`,
left: `${selectionPosition.left}px`,
}}
>
<button
onClick={() => handleFormatText("bold")}
className="flex justify-center items-center size-7 hover:bg-soft-200 rounded transition-colors"
>
<span className="font-bold text-sm">B</span>
</button>
<button
onClick={() => handleFormatText("italic")}
className="flex justify-center items-center size-7 hover:bg-soft-200 rounded transition-colors"
>
<span className="italic text-sm">I</span>
</button>
<button
onClick={() => handleFormatText("underline")}
className="flex justify-center items-center size-7 hover:bg-soft-200 rounded transition-colors"
>
<span className="underline text-sm">U</span>
</button>
<button
onClick={() => handleFormatText("strikethrough")}
className="flex justify-center items-center size-7 hover:bg-soft-200 rounded transition-colors"
>
<span className="line-through text-sm">S</span>
</button>
</div>
)}
</div>
</div>
);
};
export default EditorArticle;

View File

@@ -0,0 +1,89 @@
import { useState } from "react";
import Icon from "@/components/Icon";
type FieldProps = {
className?: string;
classInput?: string;
label?: string;
textarea?: boolean;
type?: string;
required?: boolean;
isSmall?: boolean;
};
const Field = ({
className,
classInput,
label,
textarea,
type,
required,
isSmall,
...inputProps
}: FieldProps &
React.InputHTMLAttributes<HTMLInputElement> &
React.TextareaHTMLAttributes<HTMLTextAreaElement>) => {
const [showPassword, setShowPassword] = useState(false);
const error = false;
return (
<div className={`${className || ""}`}>
{label && (
<div className="mb-1 text-label-md">
{label}
{required && <span className="text-[#E93544]">*</span>}
</div>
)}
<div className={`relative ${textarea ? "flex" : ""}`}>
{textarea ? (
<textarea
className={`w-full border border-stroke-soft-200 text-strong-950 transition-colors resize-none outline-0 focus:!border-blue-500 ${
error ? "!border-[#E93544]" : ""
} ${
isSmall
? "h-30 p-2 rounded-lg text-label-sm"
: "h-37.5 p-4 rounded-xl text-label-md"
} ${classInput || ""}`}
{...inputProps}
></textarea>
) : (
<input
className={`w-full border border-stroke-soft-200 text-strong-950 transition-colors outline-0 focus:!border-blue-500 ${
error ? "!border-[#E93544]" : ""
} ${
isSmall
? "h-9 px-2.5 rounded-lg text-label-sm"
: "h-12 px-4 rounded-xl text-label-md"
} ${classInput || ""}`}
type={
type === "password"
? showPassword
? "text"
: "password"
: type || "text"
}
{...inputProps}
/>
)}
{type === "password" && (
<button
className={`group text-0 absolute right-3 top-1/2 -translate-y-1/2 outline-0 before:absolute before:top-1/2 before:left-1/2 before:w-5.5 before:h-0.75 before:border-t before:border-weak-50 before:-translate-1/2 before:-rotate-45 before:transition-all before:bg-soft-400 hover:before:bg-strong-950 ${
showPassword
? "before:opacity-0"
: "before:opacity-100"
}`}
onClick={() => setShowPassword(!showPassword)}
>
<Icon
className="fill-soft-400 transition-colors group-hover:fill-strong-950"
name="eye"
/>
</button>
)}
</div>
</div>
);
};
export default Field;

View File

@@ -0,0 +1,44 @@
import { FadeLoader } from "react-spinners";
import Image from "@/components/Image";
const Converter = ({}) => {
return (
<div className="border border-stroke-soft-200 rounded-xl p-3.5">
<div className="flex items-center gap-3 p-1.25 shadow-[0_0_3px_0_rgba(0,0,0,0.14)] rounded-xl">
<div className="relative size-[46px] rounded-lg bg-weak-50 max-4xl:size-[36px]">
<div className="absolute -top-[7px] -left-[4px] scale-55 max-4xl:-top-[12px] max-4xl:-left-[10px] max-4xl:scale-35">
<FadeLoader color="var(--strong-950)" />
</div>
</div>
<div className="text-label-md max-md:text-label-sm">
Converting your file .....
</div>
</div>
<div className="mt-3.5 p-3 rounded-xl bg-weak-50">
<div className="flex items-center gap-2">
<div className="flex justify-center items-center size-7 rounded-sm bg-blue-50">
<Image
src="/images/xls-type.svg"
className="w-4.25 opacity-100"
width={15}
height={14}
alt="File"
/>
</div>
<div className="text-label-sm">Docment res.XLS</div>
</div>
<div className="flex items-center gap-2 mt-3">
<div className="relative grow h-1.5 bg-soft-200 rounded-full">
<div
className="absolute top-0 left-0 bottom-0 rounded-full bg-linear-to-r from-[#7D52F4] to-blue-500"
style={{ width: "80%" }}
></div>
</div>
<div className="text-sub-600">80%</div>
</div>
</div>
</div>
);
};
export default Converter;

View File

@@ -0,0 +1,28 @@
import Image from "@/components/Image";
type Props = {
name: string;
size: string;
};
const File = ({ name, size }: Props) => (
<div className="flex items-center gap-3 max-w-97 h-12 border border-stroke-soft-200 bg-weak-50 rounded-lg px-3.5">
<div className="flex justify-center items-center size-7 rounded-sm bg-blue-50">
<Image
src="/images/doc-type.svg"
className="w-4.25 opacity-100"
width={15}
height={14}
alt="File"
/>
</div>
<div className="text-label-sm">{name}</div>
<div className="flex items-center ml-auto text-soft-400">
<div className="text-p-xs">PDF</div>
<div className="size-0.75 mx-1 rounded-full bg-soft-400"></div>
<div className="text-label-xs">{size}</div>
</div>
</div>
);
export default File;

View File

@@ -0,0 +1,26 @@
import Chat from "@/components/Chat";
import Question from "@/components/Question";
import Answer from "@/components/Answer";
import File from "./File";
import Converter from "./Converter";
const FileConverter = () => {
return (
<Chat>
<Question>
<div className="mb-1">
Convert this file from DOC to XLS please
</div>
<File name="Docment res.Doc" size="2.1 Mb" />
</Question>
<Answer>
<div className="mb-2">
Sure converting your document to xls now:
</div>
<Converter />
</Answer>
</Chat>
);
};
export default FileConverter;

View File

@@ -0,0 +1,54 @@
import { useState } from "react";
import Image from "@/components/Image";
import ModalView from "@/components/ModalView";
type Props = {
className?: string;
image: string;
index: number;
};
const Preview = ({ className, image, index }: Props) => {
const [open, setOpen] = useState(false);
return (
<>
<div
className={`absolute cursor-pointer overflow-hidden rounded-xl ${
className || ""
}`}
onClick={() => setOpen(true)}
>
<Image
className="object-cover scale-101"
src={image}
fill
alt=""
/>
{index === 0 && (
<div className="absolute inset-0 rounded-xl bg-overlay backdrop-blur-2xl">
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 flex justify-center items-center gap-1.5 w-full">
<Image
className="w-6 opacity-100"
src="/images/stars-white.svg"
width={24}
height={24}
alt=""
/>
<div className="text-label-sm text-static-white max-md:text-label-xs">
Generating...
</div>
</div>
</div>
)}
</div>
<ModalView
open={open}
onClose={() => setOpen(false)}
image={image}
/>
</>
);
};
export default Preview;

View File

@@ -0,0 +1,22 @@
import Preview from "./Preview";
const Gallery = ({}) => {
return (
<div className="relative h-160 max-4xl:h-106 max-md:h-60">
{[
"/images/image-2.jpg",
"/images/image-3.jpg",
"/images/image-4.jpg",
].map((image, index) => (
<Preview
className="w-[calc(50%-0.25rem)] first:top-0 first:left-0 first:bottom-0 not-first:h-[calc(50%-0.25rem)] not-first:right-0 nth-2:top-0 nth-3:bottom-0"
image={image}
key={index}
index={index}
/>
))}
</div>
);
};
export default Gallery;

View File

@@ -0,0 +1,47 @@
import { useState } from "react";
import Button from "@/components/Button";
import Modal from "@/components/Modal";
import ModalPlan from "@/components/ModalPlan";
const SpecialOffer = ({}) => {
const [open, setOpen] = useState(false);
const [openModalPlan, setOpenModalPlan] = useState(false);
return (
<>
<Button
icon="gift"
isStroke
isCircle
onClick={() => setOpen(true)}
/>
<Modal
classWrapper="max-w-100"
open={open}
onClose={() => setOpen(false)}
>
<div className="mb-2 text-center text-h5">Special Offer</div>
<div className="mb-6 text-p-md text-center text-sub-600">
Upgrade today and get 20% off your first month.
</div>
<Button
className="w-full h-12"
isBlack
icon="gift"
onClick={() => {
setOpen(false);
setOpenModalPlan(true);
}}
>
Claim Offer
</Button>
</Modal>
<ModalPlan
open={openModalPlan}
onClose={() => setOpenModalPlan(false)}
/>
</>
);
};
export default SpecialOffer;

View File

@@ -0,0 +1,57 @@
import { useState } from "react";
import Button from "@/components/Button";
import ModalPlan from "@/components/ModalPlan";
import Icon from "@/components/Icon";
import SpecialOffer from "./SpecialOffer";
type Props = {
onOpenSidebar: () => void;
onToggleTools: () => void;
};
const Header = ({ onOpenSidebar, onToggleTools }: Props) => {
const [open, setOpen] = useState(false);
return (
<>
<div className="flex items-center gap-4 mb-3.5 max-md:gap-2 max-md:mb-3">
<button
className="hidden size-10 mr-2 justify-center items-center max-lg:flex max-md:mr-0"
onClick={onOpenSidebar}
>
<Icon className="!size-6 fill-strong-950" name="burger" />
</button>
<div className="grow">
<div className="text-label-xl max-md:text-label-md">
Chat With AI
</div>
<div className="mt-1 line-clamp-1 text-label-md text-sub-600 max-lg:hidden">
Break down lengthy texts into concise summaries to
grasp.
</div>
</div>
<div className="flex shrink-0 gap-1.5">
<Button
className="max-md:hidden"
icon="flash"
isBlack
onClick={() => setOpen(true)}
>
Upgrade
</Button>
<Button icon="help-circle" isStroke isCircle />
<SpecialOffer />
<Button
icon="wrench"
isStroke
isCircle
onClick={onToggleTools}
/>
</div>
</div>
<ModalPlan open={open} onClose={() => setOpen(false)} />
</>
);
};
export default Header;

View File

@@ -0,0 +1,116 @@
type IconsType = {
[key: string]: string;
};
const icons: IconsType = {
"add-team":
"M7.082 2.71c1.956 0 3.542 1.586 3.542 3.542S9.038 9.793 7.082 9.793 3.54 8.208 3.54 6.252 5.126 2.71 7.082 2.71zm4.166 0c1.956 0 3.542 1.586 3.542 3.542s-1.586 3.542-3.542 3.542c-.345 0-.625-.28-.625-.625s.28-.625.625-.625a2.29 2.29 0 1 0 0-4.583c-.345 0-.625-.28-.625-.625s.28-.625.625-.625zM7.082 3.96a2.29 2.29 0 1 0 0 4.583 2.29 2.29 0 1 0 0-4.583zm1.548 7.083a4.69 4.69 0 0 1 2.678.825c.284.197.354.586.157.87s-.586.354-.87.157c-.56-.388-1.243-.603-1.965-.603H5.534c-1.802 0-3.244 1.331-3.244 2.946 0 .432.402.804.923.804h7.738c.345 0 .625.28.625.625s-.28.625-.625.625H3.213c-1.189 0-2.173-.908-2.173-2.054 0-2.329 2.023-4.196 4.494-4.196h3.095zm7.202 0c.345 0 .625.28.625.625v1.875h1.875c.314 0 .574.231.618.533l.007.092c0 .345-.28.625-.625.625h-1.875v1.875c0 .314-.231.574-.533.618l-.092.007c-.345 0-.625-.28-.625-.625v-1.875h-1.875c-.314 0-.574-.231-.618-.533l-.007-.092c0-.345.28-.625.625-.625h1.875v-1.875c0-.314.231-.574.533-.618l.092-.007z",
"ai-chat":
"M11.859 1.56c3.818.251 6.856 3.304 7.106 7.131a16.83 16.83 0 0 1 0 2.17c-.25 3.828-3.288 6.88-7.106 7.131-1.222.08-2.497.08-3.718 0-.487-.032-1-.147-1.441-.327l-.465-.183.025.009-.008.008c-.038.031-.107.08-.201.148l-.075.054c-.75.547-1.603.822-2.69.796-.933-.022-1.163-.066-1.434-.525-.277-.468-.206-.665.309-1.631.401-.752.501-1.355.307-1.706-.841-1.251-1.35-2.497-1.433-3.774a16.84 16.84 0 0 1 0-2.17c.25-3.828 3.288-6.88 7.106-7.131a28.68 28.68 0 0 1 3.718 0zM8.224 2.801c-3.186.21-5.727 2.762-5.936 5.97-.043.662-.043 1.348 0 2.01.067 1.03.496 2.08 1.252 3.209.474.851.309 1.847-.269 2.932l-.147.278-.025.047.014.001.093.003.11.003c.804.019 1.386-.168 1.914-.554.552-.404.617-.445.879-.476s.323-.013 1.068.291a3.48 3.48 0 0 0 1.046.236c1.165.077 2.385.077 3.551 0 3.186-.21 5.727-2.762 5.936-5.971a15.6 15.6 0 0 0 0-2.01c-.209-3.208-2.75-5.761-5.936-5.97a27.41 27.41 0 0 0-3.551 0zm.102 4.089c.52 0 .982.33 1.147.819l1.542 4.582a.62.62 0 0 1-.397.787.63.63 0 0 1-.794-.393l-.416-1.233H7.244l-.414 1.233a.63.63 0 0 1-.704.416l-.09-.023a.62.62 0 0 1-.397-.787L7.18 7.709c.165-.489.626-.819 1.147-.819zm4.605 0c.347 0 .628.278.628.622v4.976c0 .343-.281.622-.628.622s-.628-.278-.628-.622V7.512c0-.343.281-.622.628-.622zM8.326 8.235l-.664 1.972H8.99l-.664-1.972z",
"ai-programming":
"M9.582 4.373c.345 0 .625.28.625.625s-.28.625-.625.625c-4.469 0-5.51.118-6.355.829-.782.658-.929 1.454-.937 4.892v.64c.008 3.438.155 4.234.937 4.892.823.693 1.831.823 6.009.829h.691c4.178-.006 5.186-.136 6.009-.829.805-.678.937-1.503.937-5.212v-.473l-.023-1.766-.01-.23c-.017-.345.249-.638.593-.655s.638.249.655.593l.035 2.046v.485c0 4.147-.158 5.138-1.382 6.168-1.155.973-2.244 1.117-6.811 1.123h-.698c-4.567-.006-5.656-.15-6.811-1.123-1.19-1.002-1.372-1.966-1.381-5.83v-.338c0-4.147.158-5.138 1.382-6.168 1.184-.997 2.299-1.123 7.16-1.123zm1.031 4.199c.327.109.504.463.395.791l-1.667 5c-.109.327-.463.504-.791.395s-.504-.463-.395-.791l1.667-5c.109-.327.463-.504.791-.395zm2.711.953l1.022.881c.722.622.862.78.862 1.259s-.14.637-.862 1.259l-1.022.881c-.261.225-.656.196-.881-.065s-.196-.656.065-.881l1.022-.881.346-.299.018-.013-.018-.014-.248-.214-.098-.085-1.022-.881c-.261-.225-.291-.62-.065-.881s.62-.291.881-.065zm-6.602.065c.225.261.196.656-.065.881l-1.022.881-.346.299-.019.014.019.013.248.214.098.085 1.022.881c.261.225.291.62.065.881s-.62.291-.881.065l-1.022-.881c-.721-.622-.862-.78-.862-1.259s.14-.637.861-1.259l1.022-.881c.261-.225.656-.196.881.065zm9.28-8.142l.215.581c.315.853.405 1.044.556 1.195s.343.241 1.195.556l.581.215c.544.201.544.971 0 1.172l-.581.215c-.853.315-1.044.405-1.195.556s-.241.342-.556 1.195l-.215.581c-.201.544-.971.544-1.172 0l-.215-.581-.336-.852c-.079-.167-.144-.267-.221-.343-.152-.152-.343-.241-1.195-.556l-.581-.215c-.544-.201-.544-.971 0-1.172l.581-.215c.853-.316 1.044-.405 1.195-.556.076-.076.142-.176.221-.343s.147-.341.336-.852l.215-.581c.201-.544.971-.544 1.172 0zm-.588 1.954l-.004.011c-.136.288-.279.506-.468.695-.178.178-.336.296-.603.426l-.102.047.102.048c.234.114.383.218.537.362l.066.064c.189.189.332.406.468.695l.004.011.049-.103a2 2 0 0 1 .362-.537l.064-.066c.178-.178.336-.296.603-.426l.101-.048-.101-.047a2 2 0 0 1-.536-.362l-.066-.064c-.178-.178-.296-.335-.426-.603l-.049-.103z",
"ai-search":
"M9.168 3.123c.345 0 .625.28.625.625s-.28.625-.625.625A5.21 5.21 0 0 0 3.96 9.582a5.21 5.21 0 0 0 5.208 5.208 5.21 5.21 0 0 0 5.208-5.208c0-.345.28-.625.625-.625s.625.28.625.625a6.43 6.43 0 0 1-1.472 4.104l2.955 2.954c.244.244.244.64 0 .884s-.64.244-.884 0l-2.955-2.954a6.43 6.43 0 0 1-4.103 1.471A6.46 6.46 0 0 1 2.71 9.582a6.46 6.46 0 0 1 6.458-6.458zm4.337-.425l.215.581c.315.853.405 1.044.556 1.195s.343.241 1.195.556l.581.215c.544.201.544.971 0 1.172l-.581.215c-.853.316-1.044.405-1.195.556s-.241.342-.556 1.195l-.215.581c-.201.544-.971.544-1.172 0l-.215-.581-.336-.852c-.079-.167-.144-.267-.221-.343-.152-.152-.343-.241-1.195-.556l-.581-.215c-.544-.201-.544-.971 0-1.172l.581-.215c.853-.316 1.044-.405 1.195-.556s.241-.343.556-1.195l.215-.581c.201-.544.971-.544 1.172 0zm-.587 1.955l-.047.102c-.13.267-.248.424-.426.603s-.336.296-.603.426l-.102.047.102.048c.234.114.383.218.537.362l.066.064c.189.189.332.406.468.695l.004.011.049-.103a2 2 0 0 1 .362-.537l.064-.066c.178-.178.335-.296.603-.426l.101-.048-.101-.047a2 2 0 0 1-.537-.362l-.066-.064c-.178-.178-.296-.335-.426-.603l-.048-.102z",
"alert-circle":
"M10 1a9 9 0 1 1 0 18 9 9 0 1 1 0-18zm0 1.256c-4.277 0-7.744 3.467-7.744 7.744S5.723 17.744 10 17.744s7.744-3.467 7.744-7.744S14.277 2.256 10 2.256zm-.008 9.419a.84.84 0 0 1 .847.837c0 .462-.375.837-.837.837a.84.84 0 0 1-.847-.837c0-.462.375-.837.837-.837zm.007-5.651a.63.63 0 0 1 .628.628V10a.63.63 0 0 1-.628.628.63.63 0 0 1-.628-.628V6.651a.63.63 0 0 1 .628-.628z",
analytic:
"M10.366 1.5c4.542.008 5.645.182 6.798 1.336C18.348 4.02 18.5 5.15 18.5 10v.366c-.008 4.542-.182 5.645-1.335 6.798C15.98 18.348 14.85 18.5 10 18.5h-.366c-4.542-.008-5.645-.182-6.798-1.336S1.508 14.905 1.5 10.348v-.714c.008-4.542.182-5.645 1.336-6.798S5.095 1.508 9.652 1.5h.714zM10 2.744c-4.434 0-5.45.137-6.285.972S2.744 5.566 2.744 10s.137 5.449.972 6.285 1.85.972 6.285.972h.352c4.137-.008 5.12-.159 5.932-.971.835-.835.971-1.851.971-6.285v-.352c-.008-4.137-.159-5.12-.971-5.932-.835-.835-1.851-.972-6.285-.972zm-4.146 9.951a.62.62 0 0 1 .622.622v1.658a.62.62 0 1 1-1.244 0v-1.658a.62.62 0 0 1 .622-.622zM10 11.866a.62.62 0 0 1 .622.622v2.488a.62.62 0 1 1-1.244 0v-2.488a.62.62 0 0 1 .622-.622zm4.146-1.658a.62.62 0 0 1 .622.622v4.146a.62.62 0 1 1-1.244 0v-4.146a.62.62 0 0 1 .622-.622zm.194-5.07l.411 1.359a.62.62 0 0 1-1.191.36l-.13-.431-.118.168c-1.953 2.665-4.861 3.648-7.999 3.61l-.315-.007a.62.62 0 1 1 .041-1.243c2.868.096 5.503-.727 7.24-3.056l.084-.118-.611.099a.62.62 0 0 1-.689-.417l-.024-.098a.62.62 0 0 1 .515-.713l1.576-.253c.47-.06 1.023.248 1.21.741z",
archive:
"M16.971 2.199C18 3.229 18.13 4.194 18.13 8.332v3.333c0 4.137-.13 5.103-1.159 6.132-1.001 1.001-1.941 1.151-5.795 1.159H8.834c-3.854-.008-4.794-.158-5.795-1.159s-1.151-1.941-1.159-5.795V8.332c0-4.137.13-5.103 1.159-6.132C4.04 1.198 4.981 1.048 8.834 1.04h2.004c4.137 0 5.103.13 6.132 1.159zm-.091 8.424H3.13v1.363c.007 3.448.134 4.269.793 4.928s1.479.786 4.928.793h2.308c3.448-.007 4.269-.134 4.928-.793.679-.679.793-1.529.793-5.248v-1.042zm-4.375 2.917c.345 0 .625.28.625.625s-.28.625-.625.625h-5c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h5zM11.159 2.29H8.851c-3.448.007-4.269.134-4.928.793s-.786 1.479-.793 4.928v1.362h13.75V8.332c0-3.719-.114-4.569-.793-5.248-.639-.639-1.43-.778-4.621-.792l-.306-.001zm1.346 2.916c.345 0 .625.28.625.625s-.28.625-.625.625h-5c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h5z",
arrow: "M12.953 5.127l.684.529 1.499 1.248.708.648C16.922 8.581 17.5 9.332 17.5 10s-.578 1.419-1.656 2.448l-.708.648-1.499 1.248-.684.529c-.286.214-.688.152-.899-.138a.66.66 0 0 1 .136-.912l.655-.507 1.447-1.204.67-.613.809-.847H3.143c-.355 0-.643-.292-.643-.652s.288-.652.643-.652h12.63a11.94 11.94 0 0 0-.81-.847l-.67-.613-1.447-1.204-.655-.507a.66.66 0 0 1-.136-.912c.211-.29.613-.352.899-.138z",
api: "M17.201 2.802c1.161 1.161 1.334 2.27 1.342 6.849v.7c-.008 4.579-.181 5.688-1.342 6.849s-2.27 1.334-6.849 1.342h-.7c-4.579-.008-5.688-.181-6.849-1.342s-1.334-2.27-1.342-6.849v-.35c0-4.874.153-6.009 1.342-7.199 1.161-1.161 2.27-1.334 6.849-1.342h.35c4.874 0 6.009.153 7.199 1.342zm-6.863-.092h-.672c-4.172.007-5.162.158-5.979.976-.839.839-.976 1.859-.976 6.315v.336c.007 4.172.158 5.162.976 5.979s1.807.969 5.979.976h.672c4.172-.007 5.162-.158 5.979-.976s.969-1.807.976-5.979v-.672c-.007-4.172-.158-5.162-.976-5.979-.796-.796-1.755-.96-5.657-.975l-.323-.001zM6.829 7.261l1.562 3.75.521 1.25c.133.319-.018.685-.337.817s-.685-.018-.817-.337l-.361-.865H5.451l-.273.823c-.098.295-.395.468-.692.42l-.099-.024c-.327-.109-.504-.463-.395-.791l.417-1.25 1.25-3.75c.183-.548.948-.576 1.17-.043zm5.257-.385c1.035 0 1.875.839 1.875 1.875s-.84 1.875-1.875 1.875h-1.042v1.875c0 .314-.231.574-.533.618l-.092.007c-.345 0-.625-.28-.625-.625V8.085c0-.278.004-.343.051-.488a1.04 1.04 0 0 1 .669-.669c.146-.047.211-.051.488-.051h1.083zm3.333 0c.345 0 .625.28.625.625v5c0 .345-.28.625-.625.625s-.625-.28-.625-.625v-5c0-.345.28-.625.625-.625zM6.316 9.283l-.448 1.344h1.008l-.56-1.344zm5.77-1.156h-1.042v1.25h1.042c.345 0 .625-.28.625-.625s-.28-.625-.625-.625z",
bell: "M10.005 1.04c1.085 0 1.875.517 1.875 1.563 0 .389-.112.787-.306 1.135 2.625.668 4.608 2.98 4.756 5.804l.017.38.005.12c.053 1.276.206 1.8.759 2.214.641.481 1.018 1.235 1.018 2.037 0 1.185-.938 2.164-2.125 2.164h-2.938c-.29 1.426-1.551 2.5-3.062 2.5s-2.773-1.073-3.062-2.5H4.005c-1.187 0-2.125-.98-2.125-2.164 0-.801.377-1.556 1.018-2.037.553-.415.706-.939.759-2.214l.005-.12.017-.38c.149-2.824 2.131-5.136 4.757-5.805-.194-.347-.307-.744-.307-1.133 0-1.045.79-1.562 1.875-1.562zm1.768 15.417H8.237c.257.728.952 1.25 1.768 1.25s1.511-.522 1.768-1.25zM10.019 4.79h-.014l-.014-.001-.21.006c-2.602.113-4.715 2.187-4.853 4.812l-.016.364-.005.122c-.068 1.626-.309 2.45-1.258 3.163-.326.245-.518.629-.518 1.037 0 .506.391.914.875.914h12c.484 0 .875-.408.875-.914 0-.408-.192-.792-.518-1.037-.95-.712-1.191-1.536-1.258-3.163l-.005-.122-.016-.364c-.142-2.696-2.365-4.81-5.063-4.817zm-.014-2.5c-.478 0-.625.096-.625.313 0 .435.366.93.619.937l.012-.001.054-.008c.248-.068.565-.524.565-.929 0-.216-.147-.312-.625-.312z",
browser:
"M17.201 2.802c1.161 1.161 1.334 2.27 1.342 6.849v.7c-.008 4.579-.181 5.688-1.342 6.849s-2.27 1.334-6.849 1.342h-.7c-4.579-.008-5.688-.181-6.849-1.342s-1.334-2.27-1.342-6.849v-.35c0-4.874.153-6.009 1.342-7.199 1.161-1.161 2.27-1.334 6.849-1.342h.35c4.874 0 6.009.153 7.199 1.342zm.08 5.323L2.723 8.125l-.013 1.876v.336c.007 4.172.158 5.162.976 5.979s1.807.969 5.979.976h.672c4.172-.007 5.162-.158 5.979-.976s.969-1.807.976-5.979v-.672l-.012-1.54zM10.338 2.71h-.672c-4.172.007-5.162.158-5.979.976-.592.592-.835 1.274-.926 3.189l14.483.001c-.092-1.915-.334-2.597-.926-3.189-.796-.796-1.755-.96-5.657-.975l-.323-.001zM5.842 4.168c.46 0 .833.373.833.833s-.373.833-.833.833c-.468 0-.841-.373-.841-.833s.373-.833.833-.833zm3.334 0c.46 0 .833.373.833.833s-.373.833-.833.833c-.468 0-.841-.373-.841-.833s.373-.833.833-.833z",
build: "M11.841 17H3.25 1.614C1.275 17 1 16.723 1 16.382s.275-.618.614-.618h1.023v-10.5c0-.546.196-1.072.549-1.482l.11-.119C3.717 3.239 4.29 3 4.886 3h5.318c.597 0 1.169.239 1.591.663s.659 1.001.659 1.601V6.5h2.659a2.24 2.24 0 0 1 1.472.552l.119.111c.422.425.659 1.001.659 1.601v7h1.023c.311 0 .567.232.608.534l.006.084c0 .341-.275.618-.614.618H16.75h-4.909zm.614-1.235h3.682v-7c0-.234-.079-.46-.223-.641l-.077-.087a1.02 1.02 0 0 0-.723-.302h-2.659v8.029zm-2.25-11.529H4.886a1.02 1.02 0 0 0-.723.302c-.192.193-.3.455-.3.728v10.5h7.364V7.118 5.265c0-.273-.108-.535-.3-.728a1.02 1.02 0 0 0-.723-.302zm-1.636 6.176c.339 0 .614.277.614.618s-.275.618-.614.618H6.523c-.339 0-.614-.277-.614-.618s.275-.618.614-.618h2.045zm0-3.294c.339 0 .614.277.614.618s-.275.618-.614.618H6.523c-.339 0-.614-.277-.614-.618s.275-.618.614-.618h2.045z",
burger: "M3.333 5c-.46 0-.833.373-.833.833s.373.833.833.833h13.333c.46 0 .833-.373.833-.833S17.127 5 16.667 5H3.333zm0 4.167c-.46 0-.833.373-.833.833s.373.833.833.833h13.333c.46 0 .833-.373.833-.833s-.373-.833-.833-.833H3.333zm0 4.167c-.46 0-.833.373-.833.833s.373.833.833.833h13.333c.46 0 .833-.373.833-.833s-.373-.833-.833-.833H3.333z",
calendar:
"M13.333.66a1 1 0 0 1 .974.771l.01.063.683-.001a3.5 3.5 0 0 1 3.495 3.308l.005.192v2.5 8.333a3.5 3.5 0 0 1-3.5 3.5H5a3.5 3.5 0 0 1-3.5-3.5V7.493v-2.5a3.5 3.5 0 0 1 3.5-3.5l.682.001.011-.063A1 1 0 0 1 6.55.667L6.667.66a1 1 0 0 1 .974.771l.011.063h4.697l.012-.063a1 1 0 0 1 .857-.764l.117-.007zM16.5 8.493h-13v7.334a1.5 1.5 0 0 0 1.356 1.493l.144.007h10a1.5 1.5 0 0 0 1.5-1.5V8.493zm-2.193-4.937a1 1 0 0 1-1.947 0l-.012-.063H7.651l-.011.063a1 1 0 0 1-.857.764l-.117.007a1 1 0 0 1-.974-.771l-.011-.063H5a1.5 1.5 0 0 0-1.493 1.356l-.007.144v1.5h13v-1.5A1.5 1.5 0 0 0 15.144 3.5L15 3.493h-.683l-.01.063z",
calculator:
"M17.177 2.664c1.21 1.089 1.366 2.132 1.366 6.546v1.583c0 4.414-.156 5.457-1.366 6.546-1.158 1.042-2.254 1.197-6.826 1.204h-.699c-4.572-.007-5.668-.162-6.826-1.204-1.179-1.061-1.357-2.077-1.366-6.205V9.21c0-4.414.156-5.457 1.366-6.546C3.984 1.622 5.08 1.467 9.652 1.46h.349c4.866 0 5.989.136 7.175 1.204zm-6.839.046h-.673c-4.18.007-5.182.144-6.003.883S2.71 5.227 2.71 9.21v1.908c.008 3.708.156 4.576.952 5.292.821.738 1.823.876 6.003.883h.673c4.18-.007 5.182-.144 6.003-.883s.952-1.634.952-5.617V9.21c0-3.983-.134-4.88-.952-5.617-.799-.719-1.77-.869-5.679-.882l-.323-.001zm-1.647 8.444l.087.072c.244.244.244.64 0 .884l-1.017 1.016 1.017 1.017c.217.217.241.554.072.797l-.072.087c-.244.244-.64.244-.884 0L6.877 14.01l-1.016 1.017c-.217.217-.554.241-.797.072l-.087-.072c-.244-.244-.244-.64 0-.884l1.016-1.017-1.016-1.016c-.217-.217-.241-.554-.072-.797l.072-.087c.244-.244.64-.244.884 0l1.016 1.016 1.017-1.016c.217-.217.554-.241.797-.072zm6.311 2.806c.345 0 .625.28.625.625s-.28.625-.625.625h-3.333c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h3.333zm0-2.5c.345 0 .625.28.625.625s-.28.625-.625.625h-3.333c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h3.333zm-1.667-7.083c.345 0 .625.28.625.625l-.001 1.041h1.042c.314 0 .574.231.618.533l.007.092c0 .345-.28.625-.625.625H13.96l.001 1.042c0 .314-.231.574-.533.618l-.092.007c-.345 0-.625-.28-.625-.625l-.001-1.042h-1.041c-.314 0-.574-.231-.618-.533l-.007-.092c0-.345.28-.625.625-.625h1.041l.001-1.041c0-.314.231-.574.533-.618l.092-.007zm-5 1.667c.345 0 .625.28.625.625s-.28.625-.625.625H5.002c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h3.333z",
chat: "M12.998 9.454l1.4.824v4.506c0 1.839-1.522 3.346-3.419 3.346a3.44 3.44 0 0 1-2.579-1.149l.056-.031 4.219-2.39a.48.48 0 0 0 .242-.408l.081-4.698zm-9.957 2.674l.074.043 4.219 2.39a.48.48 0 0 0 .464.004l4.195-2.285-.027 1.584-3.981 2.255c-1.64.929-3.734.374-4.676-1.226-.514-.874-.581-1.88-.268-2.764zm9.395-5.715l3.987 2.259c1.635.926 2.188 2.97 1.25 4.565a3.41 3.41 0 0 1-2.32 1.617v-.034-4.814a.48.48 0 0 0-.236-.412l-4.095-2.411 1.413-.769zm-7.78-1.255l-.001 4.848a.48.48 0 0 0 .235.412l2.42 1.425.032.019 1.644.968-1.413.77-3.987-2.259c-1.635-.926-2.188-2.97-1.25-4.565a3.41 3.41 0 0 1 2.32-1.617zm5.388 2.558l2.007 1.181-.039 2.283-2.047 1.115-1.919-1.13V8.803l1.998-1.088zM9.031 1.88a3.44 3.44 0 0 1 2.578 1.148l-.057.032-4.218 2.39a.48.48 0 0 0-.242.416v4.737l-1.479-.871V5.226c0-1.839 1.522-3.346 3.419-3.346zm7.667 3.238c.514.874.581 1.879.268 2.764l-.072-.042-4.219-2.39a.48.48 0 0 0-.464-.004L8.047 7.715V6.144l3.976-2.252c1.64-.929 3.734-.374 4.676 1.226z",
check: "M15.377 4.75l.904.863-8.75 9.167-.442.463-.452-.452-2.917-2.917.884-.884 2.464 2.464z",
chevron:
"M10.003 10.782l3.712-3.712 1.06 1.06-4.773 4.773L5.23 8.131l1.061-1.06z",
"chevron-circle":
"M10 1a9 9 0 1 1 0 18 9 9 0 1 1 0-18zm0 1.256c-4.277 0-7.744 3.467-7.744 7.744S5.723 17.744 10 17.744s7.744-3.467 7.744-7.744S14.277 2.256 10 2.256zm-.816 3.947l.126.126.279.286c.302.314.603.642.886.969a16.53 16.53 0 0 1 .42.505c.639.802.989 1.398.989 1.911s-.35 1.109-.989 1.911a16.53 16.53 0 0 1-.42.505c-.283.327-.584.655-.886.969l-.279.286-.126.126a.63.63 0 0 1-.888-.008.63.63 0 0 1 .008-.888l.116-.116.263-.27a23.74 23.74 0 0 0 .841-.92 15.29 15.29 0 0 0 .388-.466c.447-.561.715-1.018.715-1.129s-.268-.567-.715-1.129c-.119-.149-.249-.305-.388-.466-.267-.309-.554-.621-.841-.92l-.263-.27-.116-.116a.63.63 0 0 1-.008-.888.63.63 0 0 1 .888-.008z",
close: "M3.684 3.684a.63.63 0 0 1 .89 0l5.425 5.427 5.427-5.427a.63.63 0 0 1 .802-.073l.087.073a.63.63 0 0 1 0 .89l-5.427 5.425 5.427 5.427a.63.63 0 0 1 .073.802l-.073.087a.63.63 0 0 1-.89 0l-5.427-5.427-5.425 5.427a.63.63 0 0 1-.802.073l-.087-.073a.63.63 0 0 1 0-.89l5.427-5.427-5.427-5.425a.63.63 0 0 1-.073-.802l.073-.087z",
comment:
"M17.797 2.599c1.001 1.001 1.152 1.941 1.159 5.795v1.092c-.008 3.854-.158 4.794-1.159 5.795-1.029 1.03-1.995 1.159-6.118 1.159-.449.01-.773.044-1.078.113-.618.142-1.145.365-2.181.88l-.2.1-.227.113-.059.029-.037.018c-1.947.95-2.345 1.078-3.009.595-.531-.395-.671-.987-.575-1.784l.022-.168-.114-.022c-.755-.166-1.379-.459-1.865-.885l-.157-.147C1.198 14.28 1.048 13.339 1.04 9.486v-.754c0-4.137.13-5.103 1.159-6.132C3.2 1.598 4.141 1.448 7.994 1.44h3.671c4.137 0 5.103.13 6.132 1.159zm-5.811.091H8.011c-3.448.007-4.269.134-4.928.793s-.786 1.479-.793 4.928v1.058c.007 3.448.134 4.269.793 4.928.314.314.758.543 1.342.681l.196.041.009-.031.063-.223.02-.07.09-.356c.069-.338.4-.556.738-.487s.556.4.487.738l-.11.439-.134.475-.148.582c-.142.647-.113 1.015-.006 1.095.057.041.412-.074 1.72-.712l.514-.254c1.121-.558 1.713-.808 2.459-.98.397-.09.803-.133 1.343-.145 3.719 0 4.569-.114 5.248-.793.659-.659.786-1.479.793-4.928V8.411c-.007-3.448-.134-4.269-.793-4.928-.639-.639-1.43-.778-4.621-.792l-.306-.001zm1.346 7.916c.345 0 .625.28.625.625s-.28.625-.625.625H6.665c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h6.667zM9.998 6.44c.345 0 .625.28.625.625s-.28.625-.625.625H6.665c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h3.333z",
database:
"M10.002 1.04c3.911 0 7.135 1.174 7.286 2.991l.006.134v11.667c0 1.893-3.285 3.125-7.292 3.125S2.71 17.725 2.71 15.832V4.165c0-1.893 3.285-3.125 7.292-3.125zm0 12.083a17.49 17.49 0 0 1-2.707-.202l.001 1.244c0 .345-.28.625-.625.625s-.625-.28-.625-.625l-.001-1.502c-.824-.215-1.535-.5-2.086-.845l.001 4.014c0 .868 2.685 1.875 6.042 1.875s6.042-1.007 6.042-1.875l.001-4.014c-1.314.824-3.538 1.305-6.043 1.305zm6.043-7.139c-1.314.824-3.537 1.305-6.043 1.305a17.49 17.49 0 0 1-2.707-.202l.001 1.244c0 .345-.28.625-.625.625s-.625-.28-.625-.625L6.045 6.83c-.824-.215-1.535-.5-2.085-.845l-.001 4.013c.001.869 2.686 1.875 6.043 1.875s6.042-1.007 6.042-1.875h.001V5.985zM10.002 2.29c-3.357 0-6.042 1.007-6.042 1.875S6.644 6.04 10.002 6.04s6.042-1.007 6.042-1.875-2.684-1.875-6.042-1.875z",
document:
"M16.517 2.199c1.001 1.001 1.151 1.941 1.159 5.795v.754c0 .345-.28.625-.625.625s-.625-.28-.625-.625v-.737c-.007-3.448-.134-4.269-.793-4.928s-1.479-.786-4.928-.793H9.231c-3.448.007-4.269.134-4.928.793s-.786 1.479-.793 4.928v4.377c.006 2.984.106 3.709.614 4.329.113.137.238.263.375.376.641.526 1.393.615 4.635.615.345 0 .625.28.625.625s-.28.625-.625.625c-3.607 0-4.455-.1-5.428-.899-.2-.165-.384-.348-.549-.549-.773-.942-.891-1.767-.898-5.097v-4.08c0-4.137.13-5.103 1.159-6.132C4.42 1.198 5.361 1.048 9.214 1.04h1.171c4.137 0 5.103.13 6.132 1.159zm-1.966 8.841c1.671 0 3.125 1.53 3.125 3.125v2.5c0 .345-.28.625-.625.625s-.625-.28-.625-.625v-2.5c0-.918-.909-1.875-1.875-1.875s-1.875.957-1.875 1.875v2.917c0 .345.28.625.625.625s.625-.28.625-.625v-2.917c0-.345.28-.625.625-.625s.625.28.625.625v2.917c0 1.035-.84 1.875-1.875 1.875s-1.875-.84-1.875-1.875v-2.917c0-1.595 1.454-3.125 3.125-3.125zm-3.333-1.667c.345 0 .625.28.625.625s-.28.625-.625.625h-5c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h5zm2.5-4.167c.345 0 .625.28.625.625s-.28.625-.625.625h-7.5c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h7.5z",
"document-check":
"M8.354 1.06c.345.015.613.306.598.651s-.306.613-.651.598c-1-.042-1.963-.012-2.834.064l-.785.086C3.253 2.562 2.3 3.443 2.3 5.804v.825.295l-.001.632-.001.701-.001.4-.001.423v.163l-.004 2.069-.001.91v.201.373.087l.001.555v.142l.001.263.008.945c0 1.33.812 2.721 2.146 2.799 2.742.16 7.29.17 9.284 0 .964-.054 1.976-.712 2.125-1.995.167-1.437.209-2.545.197-3.686l-.002-.166v-.06c0-.345.28-.625.625-.625s.625.28.625.625v.052l.002.161c.013 1.189-.032 2.347-.206 3.843-.233 2-1.794 3.014-3.278 3.097-2.044.175-6.649.164-9.446.001-2.148-.125-3.323-2.138-3.323-4.039l-.008-.946-.001-.264v-.07l-.001-1.289.001-.912.001-.526.003-1.549v-.163l.001-.423.001-.399.001-.7.001-.631v-.149l.001-.97c0-3.061 1.497-4.445 3.495-4.586l.813-.09a23.28 23.28 0 0 1 2.996-.068zm4.128 12.495c.345 0 .625.28.625.625s-.28.625-.625.625H5.815c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h6.667zm-3.333-3.333c.345 0 .625.28.625.625s-.28.625-.625.625H5.815c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h3.333zm5.011-9.167c2.648 0 4.796 2.145 4.796 4.792s-2.147 4.792-4.796 4.792-4.796-2.145-4.796-4.792 2.147-4.792 4.796-4.792zm0 1.25c-1.958 0-3.546 1.586-3.546 3.542s1.587 3.542 3.546 3.542 3.546-1.586 3.546-3.542-1.587-3.542-3.546-3.542zm2.244 2.083c.115.325-.055.683-.38.798-.073.026-.155.065-.244.118-.305.183-.657.503-1.022.919a10.85 10.85 0 0 0-.66.836l-.255.373c-.261.41-.869.379-1.087-.056a5.52 5.52 0 0 0-.514-.842l-.202-.245c-.244-.244-.244-.64 0-.884s.64-.244.884 0a3.8 3.8 0 0 1 .387.466l.054.08.197-.249.257-.304c.444-.505.883-.906 1.32-1.167.156-.093.312-.169.468-.224.325-.115.683.055.798.38z",
dots: "M10.005 8.96a1.04 1.04 0 1 1 0 2.083c-.583 0-1.049-.466-1.049-1.042A1.04 1.04 0 0 1 9.998 8.96h.008zm-.006 5a1.04 1.04 0 1 1 0 2.083c-.583 0-1.049-.466-1.049-1.042a1.04 1.04 0 0 1 1.042-1.042h.008zm.013-10a1.04 1.04 0 1 1 0 2.083c-.583 0-1.049-.466-1.049-1.042a1.04 1.04 0 0 1 1.042-1.042h.008z",
edit: "M4.998 11.873c.345 0 .625.28.625.625s-.28.625-.625.625H3.123c-.46 0-.833.373-.833.833s.373.833.833.833h7.917c1.151 0 2.083.933 2.083 2.083s-.933 2.083-2.083 2.083H9.165c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h1.875c.46 0 .833-.373.833-.833s-.373-.833-.833-.833H3.123c-1.151 0-2.083-.933-2.083-2.083s.933-2.083 2.083-2.083h1.875zM16.597 1.218l.125.06c.268.143.429.281.94.788l.274.274c.566.57.707.742.845 1.061a2.17 2.17 0 0 1 0 1.719c-.148.343-.3.516-.983 1.199l-4.185 4.185c-1.108 1.108-1.434 1.41-1.914 1.721a3.84 3.84 0 0 1-.453.254c-.537.254-1.079.37-2.112.484l-.916.094-.657.065c-.392.039-.722-.291-.683-.683l.065-.657.094-.916c.114-1.033.23-1.575.484-2.112.311-.657.627-1.019 1.975-2.367l4.32-4.319c.51-.508.671-.645.94-.788l.125-.06a2.17 2.17 0 0 1 1.719 0zm-1.224 1.148l-.033.016c-.12.064-.248.176-.659.585L10.2 7.447c-1.08 1.084-1.339 1.391-1.551 1.84-.179.379-.271.81-.371 1.714l-.075.732-.008.068.069-.006.566-.057.166-.018c.905-.1 1.335-.192 1.714-.371.104-.049.204-.105.306-.172.375-.243.678-.524 1.711-1.557l4.185-4.185c.54-.54.663-.68.719-.811a.92.92 0 0 0 0-.728c-.052-.121-.16-.249-.602-.693l-.237-.237c-.411-.408-.539-.521-.659-.585l-.033-.016a.92.92 0 0 0-.728 0z",
exchange:
"M5.838 3.143h.017l.034.003.042.004.014.002.015.003.029.008.03.006.013.005.02.007.026.011.024.009.017.01.021.009.016.011.023.013.019.014.023.016.016.014.011.008.018.018.029.027.003.005.012.012c.038.044.07.093.095.147l.031.082.005.016.008.042.008.032v.009l.003.013v.028l.003.042v2.904c0 .345-.28.625-.625.625s-.625-.28-.625-.625V5.076l-.073.07C3.936 6.351 3.206 7.974 3.136 9.727l-.006.278c0 3.797 3.078 6.875 6.875 6.875.519 0 1.031-.058 1.529-.17.337-.076.671.135.748.471s-.135.671-.471.748c-.588.133-1.192.201-1.805.201-4.487 0-8.125-3.638-8.125-8.125 0-2.059.772-3.987 2.102-5.455l.147-.158-1.207.001c-.314 0-.574-.231-.618-.533l-.007-.092c0-.345.28-.625.625-.625h2.917zm4.167-1.263c4.487 0 8.125 3.638 8.125 8.125 0 2.065-.777 3.999-2.115 5.469l-.148.156h1.221c.314 0 .574.231.618.533l.007.092c0 .345-.28.625-.625.625h-2.936l-.018-.001h-.01l-.022-.003-.042-.003-.022-.005-.029-.006-.037-.008-.021-.009-.014-.003-.023-.011-.025-.009-.021-.012-.019-.008-.016-.011-.021-.012-.027-.02-.017-.011-.013-.012-.013-.009-.037-.037-.01-.009-.006-.007-.008-.009-.042-.054-.007-.01a.64.64 0 0 1-.046-.083l-.013-.035-.018-.047-.004-.013-.008-.035-.008-.041-.002-.012-.001-.012-.002-.042-.002-.026v-2.917c0-.345.28-.625.625-.625s.625.28.625.625v1.614l.044-.04c1.235-1.211 1.986-2.859 2.054-4.639l.005-.268c0-3.797-3.078-6.875-6.875-6.875a6.9 6.9 0 0 0-1.528.17c-.337.076-.671-.135-.748-.471s.135-.671.471-.748a8.15 8.15 0 0 1 1.805-.201z",
"eye-hide":
"M2.94 2.063l15 15c.244.244.244.64 0 .884s-.64.244-.884 0l-2.768-2.767c-1.358.826-2.793 1.283-4.29 1.283-3.415 0-6.275-2.233-8.462-5.3-.395-.554-.496-.748-.496-1.159s.101-.604.496-1.159c.865-1.213 1.911-2.365 3.124-3.296L2.057 2.947c-.244-.244-.244-.64 0-.884s.64-.244.884 0zm-.387 7.509c-.238.334-.264.383-.264.433s.026.099.264.433c1.974 2.768 4.545 4.775 7.444 4.775 1.157 0 2.285-.332 3.378-.947l-1.686-1.685c-.507.353-1.114.549-1.751.549-1.693 0-3.066-1.373-3.066-3.066 0-.637.196-1.245.549-1.752l-1.87-1.87c-1.158.864-2.17 1.967-2.999 3.129zm7.444-6.025c3.415 0 6.275 2.232 8.462 5.3.395.554.496.748.496 1.159s-.101.604-.496 1.159c-.518.726-1.127 1.461-1.823 2.151-.245.243-.641.242-.884-.003s-.242-.641.003-.884a15.49 15.49 0 0 0 1.686-1.989c.238-.334.264-.383.264-.433s-.026-.099-.264-.433c-1.974-2.768-4.545-4.775-7.445-4.775-.661 0-1.314.109-1.958.318-.328.107-.681-.073-.788-.401s.073-.681.401-.788c.766-.249 1.549-.379 2.345-.379zm-1.875 6.518c0 1.003.813 1.816 1.816 1.816a1.81 1.81 0 0 0 .843-.207L8.33 9.221a1.81 1.81 0 0 0-.207.843z",
flash: "M9.691 1.575c.841-1.08 2.429-.403 2.429.943v5.794c0 .138.074.221.124.221h2.837c1.194 0 1.785 1.477 1.055 2.414l-5.832 7.486c-.841 1.08-2.429.403-2.429-.943v-5.794c0-.138-.074-.221-.124-.221H4.915c-1.194 0-1.785-1.477-1.055-2.414l5.832-7.486zm1.179.943c0-.214-.122-.266-.193-.175L4.845 9.829c-.122.156-.026.396.069.396h2.837c.777 0 1.374.674 1.374 1.471v5.794c0 .214.122.266.193.175l5.832-7.486c.122-.156.026-.396-.069-.396h-2.837c-.777 0-1.374-.674-1.374-1.471V2.518z",
folder: "M16.971 2.199C18 3.229 18.13 4.194 18.13 8.332v3.333c0 4.137-.13 5.103-1.159 6.132-1.001 1.001-1.941 1.152-5.795 1.159H8.834c-3.854-.008-4.794-.158-5.795-1.159s-1.151-1.941-1.159-5.795V8.332c0-4.137.13-5.103 1.159-6.132C4.04 1.198 4.981 1.048 8.834 1.04h2.004c4.137 0 5.103.13 6.132 1.159zm-.091 8.424H3.13v1.363c.007 3.448.134 4.269.793 4.928s1.479.786 4.928.793h2.308c3.448-.007 4.269-.134 4.928-.793.679-.679.793-1.529.793-5.248v-1.042zm-4.375 2.917c.345 0 .625.28.625.625s-.28.625-.625.625h-5c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h5zM11.159 2.29H8.851c-3.448.007-4.269.134-4.928.793s-.786 1.479-.793 4.928v1.362h13.75V8.332c0-3.719-.114-4.569-.793-5.248-.639-.639-1.43-.778-4.621-.792l-.306-.001zm1.346 2.916c.345 0 .625.28.625.625s-.28.625-.625.625h-5c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h5z",
folders:
"M5.865 2a2.31 2.31 0 0 1 1.786.841l1.328 1.621 4.61.001c1.294.004 1.692.04 2.143.249l.085.041a2.69 2.69 0 0 1 1.185 1.165c.25.483.291.84.295 2.192l.001.598c.685.132 1.022.381 1.344.846.618.894.46 1.545-.688 4.355l-.249.609c-.821 2.011-1.078 2.482-1.787 2.952s-1.25.528-3.46.528H5.435c-2.832-.007-3.482-.127-4.079-.99-.303-.439-.42-.819-.324-1.425l.001-9.491c.003-1.376.039-1.741.253-2.231.316-.725.903-1.302 1.64-1.613.5-.211.869-.246 2.281-.249h.657zm8.398 7.795H7.541c-1.91 0-2.353.048-2.761.318s-.617.654-1.326 2.391l-.249.609c-.934 2.288-1.068 2.837-.814 3.205s.827.452 3.345.452h6.975c1.699-.004 2.12-.06 2.509-.318.407-.27.617-.654 1.326-2.391l.249-.609c.934-2.288 1.068-2.837.814-3.205s-.827-.452-3.345-.452zM5.865 3.231h-.648c-1.203.003-1.514.03-1.796.149a1.86 1.86 0 0 0-.984.968c-.121.278-.149.588-.152 1.78l-.001 5.938.009-.021c.821-2.011 1.078-2.482 1.787-2.952.679-.451 1.205-.523 3.192-.528h7.292l1.483.024v-.229c0-1.312-.026-1.626-.159-1.883a1.45 1.45 0 0 0-.638-.628c-.246-.123-.543-.153-1.689-.156H5.83a.62.62 0 0 1-.626-.615.62.62 0 0 1 .626-.615h1.542l-.694-.848c-.17-.207-.413-.34-.678-.374l-.134-.008z",
gift: "M6.835 1c1.325 0 2.49.695 3.165 1.748C10.674 1.695 11.84 1 13.165 1h.293c1.149 0 2.081.95 2.081 2.123 0 .78-.264 1.497-.705 2.064l.5-.001c1.076 0 1.325.018 1.694.192a1.76 1.76 0 0 1 .691.57c.255.361.281.596.281 1.54s-.026 1.179-.281 1.539a1.73 1.73 0 0 1-.539.49v2.994c0 3.663-.113 4.519-1.021 5.446S14.411 19 10.821 19H9.179c-3.59 0-4.429-.115-5.338-1.042-.881-.899-1.014-1.731-1.021-5.121l-.001-3.32-.01-.006a1.73 1.73 0 0 1-.528-.484C2.044 8.693 2.005 8.466 2 7.68v-.192c0-.943.026-1.179.281-1.54a1.76 1.76 0 0 1 .691-.57c.345-.162.585-.189 1.49-.192l.705.001c-.442-.567-.705-1.284-.705-2.064C4.462 1.95 5.393 1 6.542 1h.293zM4.05 9.786l.002 2.997c.006 3.014.113 3.728.661 4.286.563.575 1.289.674 4.467.674h.205V9.79H4.462l-.412-.004zm11.9 0l-.412.004h-4.923v7.954h.206c3.179 0 3.904-.099 4.467-.674.545-.556.654-1.267.661-4.252l.002-3.032zm-.617-3.344h.16H4.507c-.709.002-.896.019-1.018.077-.094.044-.165.103-.21.165-.033.047-.049.183-.049.805v.135c.002.507.017.626.048.67.044.063.116.121.21.165.121.057.307.074 1.007.076h10.837c.841 0 1.047-.015 1.178-.077.094-.044.165-.103.209-.165.033-.047.049-.183.049-.805s-.015-.757-.049-.805c-.044-.062-.116-.121-.209-.165-.131-.062-.336-.077-1.178-.077zM6.835 2.256h-.293c-.469 0-.85.388-.85.867 0 1.139.905 2.063 2.022 2.063h1.67l-.001-.338C9.385 3.47 8.321 2.336 6.98 2.26l-.145-.004zm6.623 0h-.293c-1.408 0-2.549 1.165-2.549 2.601v.329h1.67c1.072 0 1.949-.851 2.018-1.927l.004-.136c0-.479-.38-.867-.85-.867z",
history:
"M9.998 1.04a8.96 8.96 0 0 1 8.958 8.958 8.96 8.96 0 0 1-8.958 8.958c-.345 0-.625-.28-.625-.625s.28-.625.625-.625a7.71 7.71 0 0 0 7.708-7.708A7.71 7.71 0 0 0 9.998 2.29c-3.018 0-5.678 1.751-6.929 4.396l-.085.187h1.181c.314 0 .574.231.618.533l.007.092c0 .345-.28.625-.625.625H2.081c-.422 0-.723-.41-.596-.812C2.65 3.601 6.063 1.04 9.998 1.04zM6.925 17.436a7.41 7.41 0 0 0 .769.301c.328.108.506.461.399.789s-.461.506-.789.399a8.66 8.66 0 0 1-.898-.352c-.314-.144-.452-.514-.308-.828s.514-.452.828-.308zm-2.443-1.775c.236.254.488.493.754.713s.303.614.083.88-.614.303-.88.083c-.308-.255-.6-.531-.873-.825-.235-.253-.22-.648.033-.883s.648-.22.883.033zm-1.653-2.679a8.27 8.27 0 0 0 .399.894c.159.306.039.684-.267.843s-.684.039-.843-.267a9.52 9.52 0 0 1-.459-1.029c-.122-.323.042-.684.365-.805s.684.042.805.365zM9.998 6.04c.345 0 .625.28.625.625V9.74l1.484 1.483c.217.217.241.554.072.797l-.072.087c-.244.244-.64.244-.884 0L9.556 10.44c-.117-.117-.183-.276-.183-.442V6.665c0-.345.28-.625.625-.625zM1.665 9.373c.345 0 .625.28.625.625 0 .261.012.52.035.777.031.344-.222.648-.566.679s-.648-.222-.679-.566c-.027-.294-.04-.591-.04-.89 0-.345.28-.625.625-.625z",
"help-circle":
"M10 1a9 9 0 1 1 0 18 9 9 0 1 1 0-18zm0 1.256c-4.277 0-7.744 3.467-7.744 7.744S5.723 17.744 10 17.744s7.744-3.467 7.744-7.744S14.277 2.256 10 2.256zm.001 11.093c.462 0 .837.375.837.837s-.375.837-.837.837a.84.84 0 0 1-.845-.837c0-.462.375-.837.837-.837zM10 5.186c1.272 0 2.302 1.031 2.302 2.302 0 .447-.128.876-.365 1.245a5.9 5.9 0 0 1-.307.428l-.277.346c-.543.673-.726.993-.726 1.33v.419a.63.63 0 0 1-.628.628.63.63 0 0 1-.628-.628v-.419c0-.731.279-1.221 1.005-2.119l.262-.327a4.71 4.71 0 0 0 .243-.337 1.04 1.04 0 0 0 .165-.565c0-.578-.469-1.047-1.047-1.047s-1.047.469-1.047 1.047a.63.63 0 0 1-.628.628.63.63 0 0 1-.628-.628c0-1.272 1.031-2.302 2.302-2.302z",
envelope:
"M12.445 2.53c3.479.085 4.336.229 5.33 1.196.973.946 1.139 1.817 1.207 4.852l.001.063.002.087.001.064v2.419l-.001.064-.002.087-.001.063c-.068 3.035-.234 3.906-1.207 4.852s-1.893 1.11-5.058 1.189l-.272.007c-1.635.04-3.254.04-4.889 0l-.272-.007c-3.165-.078-4.088-.246-5.058-1.189s-1.139-1.817-1.207-4.852l-.001-.063-.002-.086-.001-.064V8.791c.072-3.283.219-4.1 1.212-5.065s1.851-1.111 5.33-1.196h4.889zm5.154 3.728L12.894 8.84c-2.326 1.276-3.462 1.276-5.788 0L2.402 6.258c-.073.588-.107 1.394-.133 2.558v2.367l.001.064.002.086.001.062c.06 2.7.192 3.389.841 4.02s1.383.762 4.2.831l.272.007h4.826l.272-.007c2.817-.07 3.555-.204 4.2-.831s.781-1.32.841-4.02l.001-.063.002-.086.001-.064V8.817l-.001-.064-.002-.086-.001-.063-.128-2.346zM7.587 3.746c-3.11.076-3.809.193-4.472.838a2.12 2.12 0 0 0-.372.465l4.982 2.734c1.942 1.065 2.608 1.065 4.55 0l4.98-2.733c-.1-.174-.221-.321-.37-.465-.663-.645-1.362-.762-4.472-.838a98.85 98.85 0 0 0-4.826 0z",
eye: "M10 2.5c3.235 0 6.246 1.833 8.323 5.008.903 1.376.903 3.607.001 4.983C16.246 15.667 13.235 17.5 10 17.5s-6.246-1.833-8.323-5.008c-.903-1.376-.903-3.607-.001-4.983C3.754 4.333 6.765 2.5 10 2.5zm0 1.247c-2.767 0-5.387 1.595-7.243 4.432-.635.967-.635 2.675.001 3.643 1.855 2.836 4.475 4.431 7.242 4.431s5.387-1.595 7.242-4.432c.635-.967.635-2.675-.001-3.643C15.387 5.342 12.767 3.747 10 3.747zm.001 2.659c2.045 0 3.699 1.609 3.699 3.598s-1.654 3.598-3.699 3.598-3.699-1.609-3.699-3.598 1.654-3.598 3.699-3.598zm0 1.247c-1.338 0-2.418 1.051-2.418 2.352s1.08 2.352 2.418 2.352 2.418-1.051 2.418-2.352-1.08-2.352-2.418-2.352z",
image: "M17.201 2.802c1.161 1.161 1.334 2.27 1.342 6.849v.7c-.008 4.579-.181 5.688-1.342 6.849s-2.27 1.334-6.849 1.342h-.7c-4.579-.008-5.688-.181-6.849-1.342s-1.334-2.27-1.342-6.849v-.35c0-4.874.153-6.009 1.342-7.199 1.161-1.161 2.27-1.334 6.849-1.342h.35c4.874 0 6.009.153 7.199 1.342zM3.347 12.296c-.206 0-.411.004-.616.015.063 2.553.286 3.338.955 4.007.818.818 1.807.969 5.979.976h.672l1.929-.02c-.591-1.218-1.524-2.301-2.727-3.148-1.739-1.223-3.93-1.873-6.192-1.829zm11.974 1.664c-1.064-.004-2.126.295-3.152.89a8.99 8.99 0 0 1 1.441 2.365c1.554-.109 2.168-.358 2.707-.898.456-.456.705-.966.837-2.052a5.67 5.67 0 0 0-1.833-.305zM10.338 2.71h-.672c-4.172.007-5.162.158-5.979.976-.839.839-.976 1.859-.976 6.315v.336l.003.722-.253.014.875-.028c2.511-.049 4.963.678 6.923 2.057.363.255.705.53 1.025.823 1.288-.806 2.655-1.22 4.04-1.215.647-.001 1.292.091 1.927.274l.044-2.646v-.672c-.007-4.172-.158-5.162-.976-5.979-.796-.796-1.755-.96-5.657-.975l-.323-.001zm3.414 1.666c1.036 0 1.875.839 1.875 1.875s-.839 1.875-1.875 1.875-1.875-.839-1.875-1.875.839-1.875 1.875-1.875zm0 1.25c-.345 0-.625.28-.625.625s.28.625.625.625.625-.28.625-.625-.28-.625-.625-.625z",
"image-1":
"M18.268 2.336c.672.526.732 1.056.732 3.865v7.598c0 2.809-.06 3.339-.732 3.865-.696.546-1.248.447-4.323-.442a14.23 14.23 0 0 0-7.89 0c-3.076.889-3.627.988-4.323.442-.627-.491-.721-.985-.731-3.333L1 5.919c.005-2.563.083-3.074.732-3.582.696-.546 1.247-.447 4.323.442a14.23 14.23 0 0 0 7.89 0c3.076-.889 3.627-.987 4.323-.442zm-15.75.956c-.198.155-.262.722-.262 2.909v7.598c0 2.187.064 2.754.262 2.909.165.129.771.028 2.86-.569l-.021-.021 1.242-1.289c4.195-4.328 7.214-5.809 11.145-3.864l-.001-5.017c-.005-1.977-.072-2.505-.262-2.654-.173-.135-.832-.018-3.181.661-2.806.811-5.797.811-8.603 0-2.349-.679-3.008-.797-3.181-.661zm5.03 12.341l.398-.059a15.52 15.52 0 0 1 6.356.472c2.349.679 3.008.797 3.181.661.198-.155.262-.722.262-2.909v-1.438c-3.577-2.042-6.163-.879-10.196 3.273zM6.23 5.3c1.04 0 1.884.823 1.884 1.839S7.27 8.978 6.23 8.978s-1.884-.823-1.884-1.839S5.189 5.3 6.23 5.3zm0 1.226a.62.62 0 0 0-.628.613.62.62 0 0 0 .628.613.62.62 0 0 0 .628-.613.62.62 0 0 0-.628-.613z",
language:
"M17.201 2.802c1.161 1.161 1.334 2.27 1.342 6.849v.7c-.008 4.579-.181 5.688-1.342 6.849s-2.27 1.334-6.849 1.342h-.7c-4.579-.008-5.688-.181-6.849-1.342s-1.334-2.27-1.342-6.849v-.35c0-4.874.153-6.009 1.342-7.199 1.161-1.161 2.27-1.334 6.849-1.342h.35c4.874 0 6.009.153 7.199 1.342zm-6.863-.092h-.672c-4.172.007-5.162.158-5.979.976-.839.839-.976 1.859-.976 6.315v.336c.007 4.172.158 5.162.976 5.979s1.807.969 5.979.976h.672c4.172-.007 5.162-.158 5.979-.976s.969-1.807.976-5.979v-.672c-.007-4.172-.158-5.162-.976-5.979-.796-.796-1.755-.96-5.657-.975l-.323-.001zm-.753 2.5c.345 0 .625.28.625.625v.524h1.875 2.083c.345 0 .625.28.625.625s-.28.625-.625.625h-1.624l-.086.252c-.39 1.061-.984 2.119-1.756 3.175l-.202.269 1.232 1.281c.215.224.231.567.05.808l-.067.076c-.249.239-.644.232-.884-.017l-1.125-1.168-.257.296-1.595 1.641-.435.407c-.254.234-.649.217-.883-.037s-.217-.649.037-.883c.848-.779 1.609-1.556 2.284-2.352l.264-.32-.257.31-.108-.136c-.446-.57-.876-1.187-1.086-1.58l-.061-.122c-.146-.313-.01-.685.302-.831s.685-.01.831.302c.117.25.48.782.877 1.301l.044.056.022-.028c.629-.86 1.126-1.712 1.473-2.553l.057-.148H9.585h-3.75c-.345 0-.625-.28-.625-.625s.28-.625.625-.625H8.96v-.524c0-.314.231-.574.533-.618l.092-.007z",
"language-1":
"M11.623 1.5c3.539.366 6.07 2.646 7.005 5.283 1.116 5.32-.324 8.502-3.713 10.349-2.052 1.254-7.047 1.303-9.342.236l-.166-.084-3.35 1.174a.71.71 0 0 1-.926-.835l.034-.099L2.4 14.568l-.114-.184C-.911 9.087 2.177 2.136 7.97 1.596l.261-.02.022-.001 3.37-.075zm-.05 1.24l-3.277.078-.218.017C3.1 3.31.469 9.569 3.626 14.153l.191.278-.13.311-.896 2.139 2.468-.865.294-.103.26.17c1.605 1.046 6.655 1.082 8.469-.025 2.929-1.597 4.121-4.232 3.139-8.941-.76-2.132-2.91-4.067-5.848-4.376zm-.907 3.553l-.001.814h2.692v1.244h-.723l-.08.231c-.34.916-.849 1.828-1.505 2.738l-.187.252 1.055 1.104-.912.855-.935-.979-.282.323c-.388.432-.802.858-1.243 1.282l-.449.424-.857-.909c.728-.674 1.383-1.343 1.966-2.03l.266-.324-.242.293-.138-.178c-.363-.473-.704-.97-.884-1.302l-.067-.132 1.141-.519c.098.211.397.654.732 1.097l.012.017.033-.044c.482-.675.871-1.343 1.157-2.004l.079-.198H6.72V7.108h2.69l.001-.814h1.256z",
link: "M15.037 2.46a4.85 4.85 0 0 1 0 6.855l-.032.033a4.85 4.85 0 0 1-.801 5.801l-2.388 2.388a4.85 4.85 0 0 1-6.855 0 4.85 4.85 0 0 1 0-6.855l.032-.033a4.85 4.85 0 0 1 .801-5.801L8.181 2.46a4.85 4.85 0 0 1 6.855 0zm-5.971.884L6.677 5.732a3.6 3.6 0 0 0 0 5.087 3.6 3.6 0 0 0 5.087 0l.172-.172c.244-.244.64-.244.884 0s.244.64 0 .884l-.172.172a4.85 4.85 0 0 1-6.855 0l-.041-.042-.053.058a3.6 3.6 0 0 0 5.233 4.934l2.389-2.389a3.6 3.6 0 0 0 0-5.087 3.6 3.6 0 0 0-5.087 0l-.172.172c-.244.244-.64.244-.884 0s-.244-.64 0-.884l.172-.172a4.85 4.85 0 0 1 6.855 0l.039.042.055-.058a3.6 3.6 0 0 0-.145-4.934 3.6 3.6 0 0 0-5.087 0z",
"link-1":
"M6.408 7.388c.244.244.244.64 0 .884l-2.175 2.175a3.77 3.77 0 0 0 0 5.329 3.77 3.77 0 0 0 5.329 0l2.175-2.174c.244-.244.64-.244.884 0s.244.64 0 .884l-2.175 2.174a5.02 5.02 0 0 1-7.097 0 5.02 5.02 0 0 1 0-7.097l2.175-2.175c.244-.244.64-.244.884 0zM16.66 3.35a5.02 5.02 0 0 1 0 7.097l-2.175 2.175c-.244.244-.64.244-.884 0s-.244-.64 0-.884l2.175-2.175a3.77 3.77 0 0 0 0-5.329 3.77 3.77 0 0 0-5.329 0L8.272 6.408c-.244.244-.64.244-.884 0s-.244-.64 0-.884L9.563 3.35a5.02 5.02 0 0 1 7.097 0zm-4.13 4.13c.244.244.244.64 0 .884L8.364 12.53c-.244.244-.64.244-.884 0s-.244-.64 0-.884l4.167-4.167c.244-.244.64-.244.884 0z",
plus: "M10.002 2.71c.345 0 .625.28.625.625l-.001 6.041 6.042.001c.314 0 .574.231.618.533l.007.092c0 .345-.28.625-.625.625l-6.042-.001.001 6.042c0 .314-.231.574-.533.618l-.092.007c-.345 0-.625-.28-.625-.625l-.001-6.042-6.041.001c-.314 0-.574-.231-.618-.533l-.007-.092c0-.345.28-.625.625-.625l6.041-.001.001-6.041c0-.314.231-.574.533-.618l.092-.007z",
profile:
"M9.999 10.841c2.968 0 5.385 1.767 6.381 4.509.489 1.345-.587 2.65-1.998 2.65H5.618c-1.412 0-2.486-1.304-1.998-2.65.994-2.74 3.413-4.507 6.38-4.507h-.031l.031-.001zm.018 1.262l-.018.002c-2.441 0-4.401 1.432-5.217 3.682-.167.462.234.95.836.95h8.764c.601 0 1.003-.488.836-.948-.818-2.253-2.777-3.684-5.219-3.684h.018zM10 2c.986 0 1.932.399 2.629 1.11s1.089 1.675 1.089 2.68a3.85 3.85 0 0 1-.283 1.45c-.187.46-.461.878-.806 1.229s-.755.631-1.206.821a3.66 3.66 0 0 1-2.846 0c-.451-.19-.861-.47-1.206-.821s-.619-.77-.806-1.229a3.85 3.85 0 0 1-.283-1.45c0-1.005.392-1.969 1.089-2.68S9.013 2 10 2zm0 1.263c-.658 0-1.288.266-1.753.74a2.55 2.55 0 0 0-.726 1.786 2.57 2.57 0 0 0 .189.967 2.53 2.53 0 0 0 .537.82c.23.235.504.421.804.548a2.44 2.44 0 0 0 1.897 0c.301-.127.574-.313.804-.548a2.53 2.53 0 0 0 .537-.82c.125-.306.189-.635.189-.967a2.55 2.55 0 0 0-.726-1.786c-.465-.474-1.095-.74-1.753-.74z",
python: "M12.529 2.266a2.29 2.29 0 0 1 .632.632c.347.52.386.903.386 2.523v1.041l1.287.001c1.336.004 1.741.051 2.196.334l.081.052a2.29 2.29 0 0 1 .632.632c.329.492.381.862.386 2.277v.491c-.005 1.415-.057 1.785-.386 2.277a2.29 2.29 0 0 1-.632.632c-.492.329-.863.381-2.278.386h-1.287v1.042c0 1.535-.035 1.96-.334 2.442l-.052.081a2.29 2.29 0 0 1-.632.632c-.52.347-.903.386-2.523.386s-2.004-.039-2.523-.386a2.29 2.29 0 0 1-.632-.632c-.329-.492-.381-.862-.386-2.277v-1.288H5.176c-1.336-.004-1.741-.051-2.196-.334l-.081-.052a2.29 2.29 0 0 1-.632-.632c-.329-.492-.381-.862-.386-2.277v-.246c0-1.62.039-2.004.386-2.523a2.29 2.29 0 0 1 .632-.632c.492-.329.862-.381 2.277-.386l1.288-.001V5.176c.004-1.336.051-1.741.334-2.196l.052-.081a2.29 2.29 0 0 1 .632-.632c.492-.329.862-.381 2.277-.386h.246c1.62 0 2.004.039 2.523.386zM7.714 14.809c.004 1.121.039 1.405.175 1.608a1.04 1.04 0 0 0 .287.287c.216.144.523.176 1.829.176s1.613-.031 1.829-.176a1.04 1.04 0 0 0 .287-.288c.136-.203.171-.487.175-1.608v-1.263l-2.29.001c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h4.802c1.122-.004 1.405-.039 1.609-.175a1.04 1.04 0 0 0 .287-.287c.136-.203.171-.487.175-1.608v-.441c-.004-1.122-.039-1.405-.175-1.608a1.04 1.04 0 0 0-.287-.287c-.203-.136-.487-.172-1.608-.175l-1.261-.001v2.292c0 .345-.28.625-.625.625l-5.208-.001-.001 4.179zm3.126-.02c.345 0 .625.28.625.625 0 .353-.28.633-.625.633s-.625-.28-.625-.625c0-.353.28-.633.625-.633zM5.201 7.714c-1.121.004-1.405.039-1.608.175a1.04 1.04 0 0 0-.287.287c-.136.203-.171.487-.175 1.608v.441c.004 1.121.039 1.405.175 1.608a1.04 1.04 0 0 0 .287.287c.203.136.487.172 1.608.175h1.263l.001-2.291c0-.345.28-.625.625-.625l5.208-.001V7.102v-1.9c-.004-1.121-.039-1.405-.175-1.608a1.04 1.04 0 0 0-.287-.287c-.191-.127-.452-.167-1.408-.174l-.201-.001h-.441c-1.122.004-1.405.039-1.608.175a1.04 1.04 0 0 0-.287.287c-.136.203-.171.487-.175 1.608v1.262h2.293c.345 0 .625.28.625.625s-.28.625-.625.625H5.201zm3.973-3.751c.345 0 .625.28.625.625 0 .354-.28.633-.625.633s-.625-.28-.625-.625c0-.354.28-.633.625-.633z",
rocket: "M2.974 13.902a2.39 2.39 0 0 1 2.604.517 2.39 2.39 0 0 1 .518 2.604A2.39 2.39 0 0 1 3.889 18.5H2.99a1.49 1.49 0 0 1-1.49-1.49v-.899a2.39 2.39 0 0 1 1.474-2.208zM16.987 1.505c.202-.017.406.011.596.08s.364.18.507.323.254.316.324.507a1.39 1.39 0 0 1 .081.594c-.242 3.064-1.703 5.603-4.43 8.102l.206-.193.001 2.952a2.34 2.34 0 0 1-.242 1.037l-.086.158a2.34 2.34 0 0 1-.892.861l-2.536 1.383a1.49 1.49 0 0 1-1.474-.026 1.49 1.49 0 0 1-.73-1.281l-.001-1.377-2.937-2.937H4a1.49 1.49 0 0 1-.6-.126l-.138-.069a1.49 1.49 0 0 1-.753-1.269 1.49 1.49 0 0 1 .182-.74l1.382-2.538A2.34 2.34 0 0 1 6.13 5.726h2.951l.066-.07c2.33-2.457 4.71-3.819 7.526-4.122l.315-.029zM4.106 15.019a1.11 1.11 0 0 0-1.142.473 1.11 1.11 0 0 0-.187.618v.899c0 .056.022.111.062.151s.094.062.15.062h.899c.219 0 .434-.066.618-.188a1.11 1.11 0 0 0 .473-1.142 1.11 1.11 0 0 0-.874-.874zm8.889-3.056l-3.406 2.705v1.332c0 .025.004.049.013.072l.015.033c.018.032.044.059.076.078s.068.029.105.03.073-.008.106-.026l2.536-1.383c.168-.092.308-.227.406-.391s.149-.352.149-.543l-.001-1.907zm4.096-9.185c-2.705.213-4.963 1.513-7.261 4.019l.017-.02-3.353 4.223 2.506 2.506 4.223-3.355.244-.227c2.247-2.131 3.458-4.243 3.729-6.729l.027-.288c.001-.017-.001-.034-.007-.051s-.015-.031-.028-.043-.027-.022-.043-.027-.034-.008-.054-.007zM5.33 10.41l2.704-3.406H6.129c-.153 0-.304.033-.442.096l-.101.053c-.165.097-.3.238-.391.405l-1.383 2.538c-.018.033-.027.069-.026.106s.011.073.03.105.046.058.078.076.069.028.106.028H5.33z",
search: "M9.165 1.04c4.487 0 8.125 3.638 8.125 8.125 0 2.019-.736 3.865-1.955 5.286l3.438 3.439c.244.244.244.64 0 .884s-.64.244-.884 0l-3.438-3.439c-1.421 1.219-3.268 1.955-5.287 1.955-4.487 0-8.125-3.638-8.125-8.125S4.678 1.04 9.165 1.04zm0 1.25c-3.797 0-6.875 3.078-6.875 6.875s3.078 6.875 6.875 6.875 6.875-3.078 6.875-6.875S12.962 2.29 9.165 2.29z",
security:
"M9.992 1.04c1.421 0 2.442.379 4.181 1.335l.381.21.372.201c.518.274.92.454 1.312.579 1.162.371 1.351.445 1.605.727.197.219.29.458.371.847l.077.407c1.081 5.993-1.442 11.226-6.73 13.251-.76.291-1.023.36-1.566.36s-.806-.069-1.566-.36C3.14 16.572.615 11.338 1.696 5.346c.141-.783.196-.973.448-1.253s.443-.356 1.605-.727c.393-.125.795-.306 1.313-.58l.371-.2.38-.209C7.55 1.419 8.571 1.04 9.992 1.04zm0 1.25c-1.153 0-2.011.319-3.577 1.181l-.382.21-.387.209c-.578.306-1.042.515-1.518.666-.792.253-1.033.347-1.056.372s-.056.138-.147.639c-.979 5.431 1.256 10.064 5.95 11.862.627.24.769.277 1.119.277s.492-.037 1.119-.277c4.693-1.797 6.927-6.43 5.947-11.861l-.07-.373c-.04-.19-.064-.252-.076-.266-.023-.025-.264-.12-1.056-.372-.475-.151-.939-.36-1.516-.665l-.388-.21-.383-.211c-1.567-.862-2.426-1.181-3.579-1.181zm.002 2.917c2.646 0 4.792 2.145 4.792 4.792S12.64 14.79 9.994 14.79s-4.792-2.145-4.792-4.792 2.145-4.792 4.792-4.792zm0 1.25c-1.956 0-3.542 1.586-3.542 3.542s1.586 3.542 3.542 3.542 3.542-1.586 3.542-3.542-1.586-3.542-3.542-3.542zm2.256 2.083c.115.325-.055.683-.38.798-.073.026-.155.066-.244.118-.305.183-.657.503-1.022.919a10.85 10.85 0 0 0-.66.836 8.85 8.85 0 0 0-.255.373c-.261.41-.869.379-1.087-.056-.136-.272-.261-.422-.361-.489-.012-.008.018.001.086.001-.345 0-.625-.28-.625-.625s.28-.625.625-.625c.259 0 .541.114.813.366l.015.015.252-.318.257-.304c.444-.505.883-.905 1.32-1.167.156-.093.312-.169.468-.224.325-.115.683.055.798.38z",
settings:
"M13.744 1.945l1.437.816c.559.318.667.394.796.699.132.312.113.42-.089 1.197-.331 1.276.48 2.641 1.781 2.992.76.205.866.243 1.071.511.183.239.211.37.215.872v1.936c-.004.502-.031.633-.214.871-.126.165-.285.262-.499.342-.13.049-.237.08-.573.17-1.301.351-2.111 1.716-1.78 2.992.201.775.22.885.089 1.197-.129.305-.237.382-.796.7l-1.437.816c-.55.312-.671.365-.996.321-.333-.045-.419-.117-.969-.666-.973-.971-2.591-.971-3.564 0-.55.549-.636.621-.968.666-.326.044-.447-.01-.996-.321l-1.437-.816c-.559-.317-.667-.394-.796-.7-.132-.312-.113-.42.089-1.197.331-1.276-.48-2.641-1.781-2.992l-.573-.17c-.214-.08-.373-.176-.499-.341-.183-.239-.211-.369-.214-.871V9.194c0-.639.014-.771.215-1.034.205-.268.311-.306 1.072-.511 1.302-.351 2.113-1.716 1.782-2.992-.202-.777-.22-.885-.089-1.197.129-.305.237-.382.796-.699l1.437-.816c.55-.312.671-.365.997-.321.332.045.418.117.968.666.972.97 2.589.97 3.561 0 .551-.549.636-.621.968-.666.326-.044.447.009.996.321zm-.824.968l-.026.031-.158.158-.074.073c-1.46 1.456-3.867 1.456-5.327 0l-.231-.231-.027-.031-.018.012-.188.107-1.437.816-.193.109-.022.01.015.046.036.136.023.09.027.104c.505 1.945-.708 3.985-2.667 4.513l-.321.087-.043.008.002.024V9v1.968.057l-.002.017.027.008.225.063.112.03c1.958.528 3.171 2.568 2.665 4.513l-.085.33-.015.045.022.011.114.064.079.045 1.437.816.188.107.017.011.028-.03.158-.158.074-.073c1.461-1.458 3.869-1.458 5.33 0l.074.073.158.158.026.03.02-.011.111-.063.077-.044 1.437-.816.193-.109.021-.011-.014-.044-.059-.226-.027-.104c-.506-1.945.706-3.985 2.664-4.513l.337-.094.025-.008v-.017-2.05l.001-.023-.042-.009-.219-.059-.102-.028c-1.958-.528-3.171-2.568-2.665-4.513l.049-.194.036-.136.014-.046-.021-.01-.114-.064-.079-.045-1.437-.816-.188-.107-.019-.012zM9.998 6.458c1.956 0 3.542 1.586 3.542 3.542s-1.586 3.542-3.542 3.542-3.542-1.586-3.542-3.542 1.586-3.542 3.542-3.542zm0 1.25a2.29 2.29 0 1 0 0 4.583 2.29 2.29 0 1 0 0-4.583z",
share: "M8.804 1.46c.345-.001.626.279.626.624s-.279.626-.624.626c-3.639.007-4.745.185-5.538.976-.819.818-.971 1.807-.978 5.979v.672c.007 4.172.159 5.161.978 5.979s1.812.969 5.993.977h.674c4.181-.007 5.173-.158 5.993-.976.619-.618.871-1.533.946-3.423.014-.345.304-.613.649-.6s.613.304.6.649c-.086 2.178-.412 3.36-1.312 4.258-1.163 1.16-2.274 1.334-6.862 1.342h-.701c-4.588-.008-5.699-.181-6.862-1.342s-1.337-2.27-1.345-6.85v-.35c0-4.874.153-6.01 1.345-7.2C3.514 1.675 4.799 1.467 8.804 1.46zm4.97 1.667l.15.014c.338.05.63.212.998.508l1.024.893 1.456 1.264c1.062.922 1.306 1.171 1.475 1.649a1.37 1.37 0 0 1 0 .92c-.169.477-.413.727-1.475 1.649l-1.456 1.264-.878.792c-.32.282-.55.444-.817.542a1.41 1.41 0 0 1-.925.019c-.692-.211-.931-.82-.953-1.662l.001-.299.001-.058-.024-.001-.314-.007-.188-.005-.868-.018c-1.099-.013-1.881.042-2.593.21-.784.185-1.407.5-2.602 1.242l-.446.276c-.431.264-.967-.035-.967-.524 0-3.494 1.675-5.918 5.228-6.495.594-.097 1.199-.126 2.023-.119l.605.009.146.003-.001-.043-.003-.151.002-.148c.022-.842.261-1.451.953-1.662.099-.03.199-.049.298-.057l.15-.003zm-.033 1.25c-.014-.002-.026-.001-.051.006-.002.001-.059.145-.068.499a5.89 5.89 0 0 0 .016.538l.02.404c0 .345-.28.625-.625.625-.23 0-.411-.003-.827-.01l-.593-.009c-.759-.007-1.302.02-1.811.103-2.452.399-3.752 1.802-4.089 4.005l-.013.092.192-.113c.779-.452 1.346-.708 2.007-.884l.201-.051c.831-.197 1.704-.258 2.895-.244l.886.019 1.151.02c.345 0 .625.28.625.625 0 .093.001.075-.02.404l-.016.538c.009.354.066.499.068.499.062.019.089.018.128.004.085-.031.211-.12.424-.307l.885-.798 1.456-1.264c.832-.723 1.055-.95 1.116-1.121.006-.017.008-.03.008-.044s-.002-.026-.008-.044c-.06-.171-.283-.398-1.116-1.121l-1.456-1.264-1.385-1.108z",
"share-1":
"M9.259 2c.34-.001.616.274.616.614s-.274.616-.614.616c-3.377.006-4.401.172-5.13.901-.774.774-.901 1.722-.901 5.868v.379c.008 3.826.151 4.739.901 5.489.774.774 1.722.901 5.867.901h.379c3.825-.008 4.738-.151 5.488-.901.729-.729.895-1.753.901-5.131.001-.34.277-.615.616-.614s.615.277.614.617c-.007 3.738-.201 4.939-1.261 5.999C15.618 17.857 14.556 18 9.999 18h-.405c-4.216-.009-5.248-.176-6.333-1.261-1.09-1.09-1.254-2.127-1.261-6.395V10c0-4.557.143-5.619 1.261-6.738C4.321 2.201 5.522 2.007 9.259 2zm5.999.073c1.178.059 1.853.171 2.197.515.317.317.437.917.499 1.935l.015.263.025 1.963c-.005.34-.284.611-.624.607s-.611-.284-.607-.624l.003-.238v-.477l-.057-1.718-.006-.086-7.048 7.09c-.213.214-.545.239-.785.073l-.085-.071c-.241-.24-.242-.629-.003-.87l7.055-7.096-.093-.006-1.715-.057h-.477l-.237.003c-.34.005-.619-.267-.624-.607s.267-.619.607-.624l1.96.025z",
team: "M13.659 12.12l.097.058.856.527c.639.423 1.086.831 1.339 1.317.372.713.235 1.476-.423 2.133-.878.877-1.77 1.345-2.826 1.345H7.297c-1.055 0-1.947-.468-2.826-1.345-.658-.657-.795-1.42-.423-2.133.253-.486.7-.894 1.339-1.317l.477-.298.21-.127.266-.159a6.91 6.91 0 0 1 7.319 0zM7.01 13.214c.016-.01-.703.419-.925.566-.473.313-.783.596-.91.84-.104.2-.077.352.192.621.661.66 1.258.973 1.93.973h5.407c.672 0 1.269-.313 1.93-.973.269-.269.296-.42.192-.621-.127-.244-.437-.527-.91-.84l-.925-.566c-1.829-1.142-4.15-1.142-5.979 0zm8.895-2.998l.094.002c.802.073 1.576.386 2.254.913-.03-.023.471.349.645.493 1.083.893 1.5 1.887.64 2.956-.625.778-1.282 1.207-2.091 1.207a.64.64 0 1 1 0-1.286c.351 0 .694-.224 1.102-.732.28-.348.155-.646-.457-1.15l-.617-.47a3.04 3.04 0 0 0-1.592-.651.64.64 0 0 1-.577-.698.64.64 0 0 1 .597-.584zm-11.214.584a.64.64 0 0 1-.577.698 3.04 3.04 0 0 0-1.592.651 26.62 26.62 0 0 0-.617.47c-.612.504-.736.802-.457 1.15.408.507.751.732 1.102.732a.64.64 0 1 1 0 1.286c-.809 0-1.465-.429-2.091-1.207-.859-1.069-.443-2.063.64-2.956l.645-.493c.678-.528 1.452-.84 2.254-.913a.64.64 0 0 1 .691.582zm10.402-6.585c1.524 0 2.759 1.247 2.759 2.786s-1.235 2.786-2.759 2.786a.64.64 0 1 1 0-1.286c.82 0 1.486-.672 1.486-1.5s-.665-1.5-1.486-1.5a.64.64 0 0 1-.637-.643.64.64 0 0 1 .637-.643zm-10.611 0a.64.64 0 0 1 .637.643.64.64 0 0 1-.637.643c-.82 0-1.486.672-1.486 1.5s.665 1.5 1.486 1.5a.64.64 0 1 1 0 1.286C2.958 9.786 1.723 8.539 1.723 7s1.235-2.786 2.759-2.786zM10 2.5c1.993 0 3.608 1.631 3.608 3.643S11.993 9.786 10 9.786 6.392 8.155 6.392 6.143 8.007 2.5 10 2.5zm0 1.286c-1.289 0-2.335 1.055-2.335 2.357S8.711 8.5 10 8.5s2.335-1.055 2.335-2.357S11.289 3.786 10 3.786z",
template:
"M16.134 2.199c1.03 1.03 1.159 1.995 1.159 6.132v2.786c0 .948-.018 1.191-.174 1.567-.145.35-.292.534-.849 1.095l-4.219 4.219c-.469.465-.627.594-.918.733a2.31 2.31 0 0 1-.226.094c-.303.107-.505.128-1.167.13h-.518c-3.287-.006-4.102-.122-5.038-.877-.22-.177-.42-.377-.597-.597-.755-.936-.87-1.751-.877-5.038V8.332c0-4.137.13-5.103 1.159-6.132C4.87 1.198 5.811 1.048 9.664 1.04h.338c4.137 0 5.103.13 6.132 1.159zm-5.811.091h-.642c-3.448.007-4.269.134-4.928.793s-.786 1.479-.793 4.928v4.41c.006 2.945.103 3.662.6 4.278.121.15.258.287.409.408.616.497 1.332.594 4.278.6h.455l.437-.007.071-.004.001-.197c0-3.05.093-3.842.836-4.628l.079-.081c.789-.789 1.505-.908 4.402-.915l.501-.001.003-.025.012-.566v-2.95c0-3.719-.114-4.569-.793-5.248-.639-.639-1.43-.778-4.621-.792l-.306-.001zm4.835 10.835l-.106.001c-2.081.018-2.625.129-3.042.546s-.528.961-.546 3.042l-.001.104 3.695-3.694zM10.002 8.54c.345 0 .625.28.625.625s-.28.625-.625.625H6.669c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h3.333zm3.333-3.333c.345 0 .625.28.625.625s-.28.625-.625.625H6.669c-.345 0-.625-.28-.625-.625s.28-.625.625-.625h6.667z",
quill: "M17.295 1.35v.625c0 1.198-.269 2.683-.883 3.96-.527 1.097-1.415 1.885-2.451 2.265l-.164.055.797.815c.219.419.239.457.052.786l-.062.108c-1.692 1.66-2.665 2.439-3.764 3.074l-.37.208-.777.422c-1.023.561-3.772 1.359-5.02 1.515a8.76 8.76 0 0 0-.692 3.454H2.71c0-1.477.3-2.835.849-4.076-.017-.122-.025-.263-.029-.42a9.61 9.61 0 0 1 .071-1.196c.09-.832.261-1.891.424-2.669.473-2.261 2.353-4.979 4.326-6.401 2.476-1.784 5.494-2.528 8.319-2.527h.625zm-1.28 1.265l-.088.004c-2.365.119-4.824.816-6.845 2.272-1.729 1.246-3.427 3.7-3.833 5.643l-.301 1.735c.265-.339.551-.665.856-.98.67-.693 1.386-1.28 2.104-1.766.434-.294.777-.491.983-.594l.558 1.118c-.159.079-.454.249-.84.511-.65.44-1.3.974-1.906 1.6-.492.508-.926 1.047-1.296 1.617 1.146-.263 2.759-.758 3.523-1.128l.144-.074.777-.421.345-.194c.892-.516 1.702-1.144 3.022-2.407l.05-.049-1.187-1.215c-.373-.378-.141-.996.352-1.071l.095-.007c1.144-.006 2.21-.672 2.759-1.815.38-.791.609-1.698.706-2.528l.024-.251z",
"web-design":
"M17.84 10.724a2.71 2.71 0 0 1 .599.599c.444.611.512 1.096.517 3.002v.513c-.005 1.906-.073 2.391-.517 3.002a2.71 2.71 0 0 1-.599.599c-.611.444-1.096.512-3.002.517h-4.423c-2.12 0-2.621-.054-3.259-.517a2.71 2.71 0 0 1-.599-.599c-.444-.611-.512-1.096-.517-3.002v-.257c0-2.12.054-2.621.517-3.259a2.71 2.71 0 0 1 .599-.599c.611-.444 1.096-.512 3.002-.517h4.423c2.12 0 2.621.054 3.259.517zm-3.024.733H10.18c-1.588.004-1.982.056-2.289.278a1.46 1.46 0 0 0-.323.323c-.223.307-.274.701-.278 2.289v.47c.004 1.588.055 1.982.278 2.289a1.46 1.46 0 0 0 .323.323c.307.223.701.274 2.289.278h4.402c1.786 0 2.203-.045 2.524-.278a1.46 1.46 0 0 0 .323-.323c.223-.307.274-.701.278-2.289v-.47c-.004-1.588-.056-1.982-.278-2.289a1.46 1.46 0 0 0-.323-.323c-.292-.212-.663-.269-2.07-.277l-.219-.001zm-.661 1.401l.767.661c.587.506.7.634.7 1.063a.9.9 0 0 1-.059.32c-.099.261-.194.357-.641.743l-.767.661c-.261.225-.656.196-.881-.065s-.196-.656.065-.881l.767-.661.078-.068.054-.048-.056-.05-.076-.066-.767-.661c-.261-.225-.291-.62-.065-.881s.62-.291.881-.065zm-2.435.065c.225.261.196.656-.065.881l-.767.661-.076.066-.057.05.055.048.078.068.767.661c.261.225.291.62.065.881s-.62.291-.881.065l-.767-.661c-.447-.385-.542-.481-.641-.743a.9.9 0 0 1-.059-.32c0-.429.113-.556.7-1.063l.767-.661c.261-.225.656-.196.881.065zm4.413-10.705c1.026 1.043 1.156 2.021 1.156 6.22 0 .345-.28.625-.625.625s-.625-.28-.625-.625l-.05-2.816H2.34L2.29 8.112v.653c.007 3.508.134 4.344.796 5.017.292.297.665.485 1.213.607.337.075.55.408.475.745s-.408.55-.745.475c-.771-.171-1.36-.468-1.834-.95-.998-1.014-1.148-1.966-1.156-5.877v-.343c0-4.199.129-5.177 1.156-6.22 1.001-1.017 1.944-1.17 5.798-1.178h2.004c4.138 0 5.106.132 6.136 1.179zm-5.815.072H8.011c-3.447.007-4.266.136-4.924.805-.305.31-.496.654-.615 1.278h13.386c-.119-.624-.31-.968-.615-1.278-.659-.669-1.477-.798-4.924-.805z",
wrench: "M14.664 1.5c.978-.004 1.47 1.178.779 1.869l-2.25 2.25a.84.84 0 1 0 1.187 1.189l2.25-2.249c.691-.691 1.873-.199 1.869.776l-.001 1.264c-.008 3.289-.1 3.757-.767 4.574-.102.124-.217.249-.361.393-.991.99-2.059 1.132-5.824 1.13H11.5l-4.934 4.936a2.97 2.97 0 0 1-4.197 0 2.97 2.97 0 0 1 0-4.197l4.936-4.936V8.16c.006-3.529.166-4.567 1.13-5.532l.393-.361c.817-.667 1.285-.759 4.575-.767l1.261-.001zM7.52 10.041l-4.272 4.273c-.673.673-.673 1.764 0 2.438s1.764.673 2.438 0l4.272-4.271c-1.14-.394-2.044-1.298-2.438-2.439zm-2.903 4.52a.83.83 0 0 1 .829.829.83.83 0 0 1-.829.829.83.83 0 0 1-.837-.829.83.83 0 0 1 .829-.829zm9.691-11.817h-.861c-2.939.007-3.327.075-3.831.487-.088.072-.184.16-.3.277-.641.64-.768 1.599-.766 4.943v.304c.001 1.488 1.208 2.696 2.694 2.696h.585c3.119-.006 4.041-.143 4.663-.766l.277-.3c.412-.504.48-.892.487-3.83v-.863L15.26 7.687c-.779.778-2.02.812-2.839.101l-.109-.102c-.814-.814-.813-2.134.001-2.948l1.995-1.996z",
voice: "M17.221 2.822c1.161 1.161 1.334 2.27 1.342 6.849v.7c-.008 4.579-.181 5.688-1.342 6.849s-2.27 1.334-6.849 1.342h-.7c-4.579-.008-5.688-.181-6.849-1.342s-1.334-2.27-1.342-6.849v-.35c0-4.874.153-6.009 1.342-7.199 1.161-1.161 2.27-1.334 6.849-1.342h.35c4.874 0 6.009.153 7.199 1.342zm-6.863-.092h-.672c-4.172.007-5.162.158-5.979.976-.839.839-.976 1.859-.976 6.315v.336c.007 4.172.158 5.162.976 5.979s1.807.969 5.979.976h.672c4.172-.007 5.162-.158 5.979-.976s.969-1.807.976-5.979v-.672c-.007-4.172-.158-5.162-.976-5.979-.796-.796-1.755-.96-5.657-.975l-.323-.001zm-.336 3.333c.345 0 .625.28.625.625v6.667c0 .345-.28.625-.625.625s-.625-.28-.625-.625V6.688c0-.345.28-.625.625-.625zm-2.5 1.667c.345 0 .625.28.625.625v3.333c0 .345-.28.625-.625.625s-.625-.28-.625-.625V8.355c0-.345.28-.625.625-.625zm5 0c.345 0 .625.28.625.625v3.333c0 .345-.28.625-.625.625s-.625-.28-.625-.625V8.355c0-.345.28-.625.625-.625zm-7.5.833c.345 0 .625.28.625.625v1.667c0 .345-.28.625-.625.625s-.625-.28-.625-.625V9.188c0-.345.28-.625.625-.625zm10 0c.345 0 .625.28.625.625v1.667c0 .345-.28.625-.625.625s-.625-.28-.625-.625V9.188c0-.345.28-.625.625-.625z",
volume: "M6.941 5.311c2.58-2.681 3.131-3.112 4.203-2.651 1.062.457 1.158 1.162 1.158 4.942v5.13c-.006 3.487-.128 4.163-1.158 4.607-1.072.462-1.623.031-4.203-2.65-.818-.85-1.191-1.007-2.378-1.007-1.628 0-2.015-.041-2.538-.404-.75-.521-1.003-1.285-1.023-2.4L1 10.786v-.194l.003-.262.003-.226v-.208l-.003-.226L1 9.408v-.194l.001-.092c.021-1.115.273-1.879 1.023-2.4.523-.363.91-.404 2.538-.404 1.187 0 1.56-.157 2.378-1.007zm3.714-1.473c-.284-.122-.838.311-2.817 2.368-1.056 1.097-1.753 1.391-3.275 1.391-1.309 0-1.615.032-1.831.182-.331.23-.462.626-.475 1.368l-.001.081v.177l.003.246.003.237v.226l-.003.237-.003.246v.177l.001.081c.014.742.145 1.138.475 1.368.215.15.522.182 1.831.182 1.522 0 2.219.293 3.275 1.391 1.979 2.056 2.533 2.49 2.817 2.368.294-.126.391-.845.391-3.765V7.308c-.007-2.674-.108-3.349-.391-3.471zm3.15 3.098a.62.62 0 0 1 .88.12A4.9 4.9 0 0 1 15.651 10a4.9 4.9 0 0 1-.966 2.944.62.62 0 0 1-.88.12c-.276-.214-.329-.615-.118-.896.455-.607.709-1.366.709-2.169s-.253-1.561-.709-2.169c-.21-.281-.157-.682.118-.896zm2.469-1.667a.62.62 0 0 1 .887.04C18.341 6.62 19 8.264 19 10s-.659 3.38-1.839 4.692a.62.62 0 0 1-.887.04c-.256-.238-.273-.643-.039-.903.973-1.082 1.51-2.42 1.51-3.829s-.536-2.747-1.51-3.829c-.234-.26-.217-.665.039-.903z",
zsh: "M16.155 2.795a4.79 4.79 0 0 1 1.06 1.06c.811 1.116.915 2.076.915 6.15s-.104 5.033-.915 6.15a4.79 4.79 0 0 1-1.06 1.06c-1.116.811-2.076.915-6.15.915s-5.033-.104-6.15-.915a4.79 4.79 0 0 1-1.06-1.06c-.787-1.084-.908-2.02-.915-5.798v-.351c0-4.073.104-5.033.915-6.15a4.79 4.79 0 0 1 1.06-1.06c1.084-.787 2.02-.908 5.798-.915h.351c4.073 0 5.033.104 6.15.915zm-5.818.335h-.664c-3.46.006-4.307.112-5.083.676a3.54 3.54 0 0 0-.784.784c-.564.776-.67 1.623-.676 5.083v.664c.006 3.46.112 4.307.676 5.083a3.54 3.54 0 0 0 .784.783c.8.581 1.676.676 5.415.676s4.615-.095 5.415-.676a3.54 3.54 0 0 0 .784-.784c.581-.8.676-1.676.676-5.415s-.095-4.615-.676-5.415a3.54 3.54 0 0 0-.784-.784c-.752-.546-1.571-.663-4.766-.675l-.317-.001zm3.392 2.22c.27.216.313.609.098.878L7.16 14.562c-.216.27-.609.313-.878.098s-.313-.609-.098-.878l6.667-8.333c.216-.27.609-.313.878-.098z",
};
type IconProps = {
className?: string;
name: string;
fill?: string;
};
const Icon = ({ className, name, fill }: IconProps) => (
<svg
className={`inline-flex size-5 ${className || ""}`}
width={20}
height={20}
viewBox="0 0 20 20"
>
<path fill={fill} d={icons[name]}></path>
</svg>
);
export default Icon;

View File

@@ -0,0 +1,18 @@
import { useState } from "react";
import { default as NextImage, ImageProps } from "next/image";
const Image = ({ className, ...props }: ImageProps) => {
const [loaded, setLoaded] = useState(false);
return (
<NextImage
className={`inline-block align-top opacity-0 transition-opacity ${
loaded && "opacity-100"
} ${className || ""}`}
onLoad={() => setLoaded(true)}
{...props}
/>
);
};
export default Image;

View File

@@ -0,0 +1,45 @@
import Chat from "@/components/Chat";
import Question from "@/components/Question";
import Answer from "@/components/Answer";
import Button from "@/components/Button";
const LanguageTranslator = () => {
return (
<Chat>
<Question>
<div className="mb-1">Translate this text to frensh:</div>
<div className="">
Beneath the quiet hum of a late-night city, a single
streetlamp flickered like it was holding on to its last
breath. Somewhere nearby, the scent of fresh rain clung to
the pavement, mixing with the soft sound of footsteps
echoing between empty buildings. It was the kind of night
where time felt slower, and the world seemed to hold its
breath, waiting for something unspoken to happen.
</div>
</Question>
<Answer>
<div className="mb-4">Sure here is the text in frensh:</div>
<div className="text-label-sm">
Sous le doux bourdonnement d&apos;une ville tard dans la
nuit, un seul lampadaire vacillait comme s&apos;il
s&apos;accrochait à son dernier souffle. Non loin de ,
l&apos;odeur de la pluie fraîche s&apos;accrochait au
bitume, se mêlant au léger bruit de pas résonnant entre les
immeubles vides. Cétait le genre de nuit le temps
semblait ralentir, et le monde retenait son souffle,
attendant que quelque chose dindicible se produise.
</div>
<Button
className="!h-9 mt-6 !rounded-lg !bg-weak-50 max-md:mt-4"
icon="language-1"
isStroke
>
Change text language
</Button>
</Answer>
</Chat>
);
};
export default LanguageTranslator;

View File

@@ -0,0 +1,79 @@
import { useState } from "react";
import Sidebar from "@/components/Sidebar";
import Tools from "@/components/Tools";
import Header from "@/components/Header";
import PythonRunner from "@/components/PythonRunner";
import Calculator from "@/components/Calculator";
import Browser from "@/components/Browser";
import WebDesign from "@/components/WebDesign";
import FileConverter from "@/components/FileConverter";
import LanguageTranslator from "@/components/LanguageTranslator";
import ApiIntegrator from "@/components/ApiIntegrator";
type Props = {
children: React.ReactNode;
};
const Layout = ({ children }: Props) => {
const [activeId, setActiveId] = useState<string | null>(null);
const [visibleTools, setVisibleTools] = useState(true);
const [visibleSidebar, setVisibleSidebar] = useState(false);
return (
<div
className={`pl-90 overflow-hidden transition-all max-3xl:pl-75 max-lg:pl-5 max-md:pl-4 ${
visibleTools
? "pr-90 max-3xl:pr-75 max-2xl:pr-5 max-md:pr-4"
: "pr-5 max-md:pr-4"
}`}
>
<Sidebar
visible={visibleSidebar}
onClose={() => setVisibleSidebar(false)}
onClickNewChat={() => setActiveId(null)}
/>
<div className="pt-9.5 pb-5 max-2xl:pt-5 max-md:pt-3 max-md:pb-4">
<Header
onOpenSidebar={() => setVisibleSidebar(true)}
onToggleTools={() => setVisibleTools(!visibleTools)}
/>
{activeId === "python" ? (
<PythonRunner />
) : activeId === "calculator" ? (
<Calculator />
) : activeId === "browser" ? (
<Browser />
) : activeId === "web-design" ? (
<WebDesign />
) : activeId === "exchange" ? (
<FileConverter />
) : activeId === "language" ? (
<LanguageTranslator />
) : activeId === "api" ? (
<ApiIntegrator />
) : (
children
)}
</div>
<Tools
activeId={activeId}
setActiveId={setActiveId}
visible={visibleTools}
onClose={() => setVisibleTools(!visibleTools)}
/>
<div
className={`fixed inset-0 z-10 hidden bg-overlay backdrop-blur-sm transition-all max-lg:block max-md:hidden ${
visibleSidebar || !visibleTools
? "visible opacity-100"
: "invisible opacity-0"
}`}
onClick={() => {
setVisibleSidebar(false);
setVisibleTools(true);
}}
></div>
</div>
);
};
export default Layout;

View File

@@ -0,0 +1,105 @@
import { Swiper, SwiperSlide } from "swiper/react";
import { items } from "./items";
import Image from "@/components/Image";
import { useState } from "react";
import "swiper/css";
import "swiper/css/effect-fade";
import { Autoplay } from "swiper/modules";
const SLIDE_DURATION = 3000;
const Slider = ({}) => {
const [currentSlide, setCurrentSlide] = useState(0);
return (
<div className="absolute bottom-15 left-10 right-10 z-2 max-2xl:bottom-8 max-2xl:left-8 max-2xl:right-8">
<Swiper
className="mySwiper !overflow-visible"
spaceBetween={56}
modules={[Autoplay]}
autoplay={{
delay: SLIDE_DURATION,
disableOnInteraction: false,
}}
onSlideChange={(swiper) => setCurrentSlide(swiper.activeIndex)}
speed={500}
>
{items.map((item, index) => (
<SwiperSlide
className="relative p-5.5 pl-45 border border-stroke-soft-200 rounded-[1.125rem] bg-[#99A0AE]/24 backdrop-blur-[0.625rem] max-xl:pl-5.5"
key={item.id}
>
<div className="absolute -left-1 bottom-3 max-xl:hidden">
<Image
className="w-46.5"
src={item.image}
width={186}
height={170}
alt={item.author}
/>
</div>
<div className="flex items-center gap-3 mb-2 text-label-lg">
<div className="border border-stroke-soft-200 rounded-xl overflow-hidden rotate-[-10deg]">
<Image
className="w-10 h-9.5 object-cover"
src={item.avatar}
width={40}
height={38}
alt={item.author}
/>
</div>
{item.author}
<div className="ml-auto text-label-lg">
{index + 1}/{items.length}
</div>
<div className="w-6.5 h-6.5">
<svg
className="w-full h-full transform -rotate-90"
viewBox="0 0 36 36"
>
<circle
cx="18"
cy="18"
r="16"
fill="none"
stroke="rgba(255, 255, 255, 0.2)"
strokeWidth="3"
/>
<circle
cx="18"
cy="18"
r="16"
fill="none"
stroke="white"
strokeWidth="3"
strokeDasharray={`${2 * Math.PI * 16}`}
strokeDashoffset={`${2 * Math.PI * 16}`}
strokeLinecap="round"
className={`${
index === currentSlide
? "animate-progress"
: "opacity-50"
}`}
style={{
animationDuration: `${SLIDE_DURATION}ms`,
animationPlayState:
index === currentSlide
? "running"
: "paused",
}}
/>
</svg>
</div>
</div>
<div className="min-h-12 line-clamp-2 text-label-sm text-[#CACFD8]">
{item.content}
</div>
</SwiperSlide>
))}
</Swiper>
</div>
);
};
export default Slider;

View File

@@ -0,0 +1,34 @@
export const items = [
{
id: 0,
author: "Suzana Marie",
avatar: "/images/avatar-6.jpg",
content:
"People with link will be able to view conversations and ideas in this board.Changes you make after creating the link will remain private.",
image: "/images/slider-pic-1.png",
},
{
id: 1,
author: "Tyson Martine",
avatar: "/images/avatar-2.png",
content:
"My ideas are in a great place. All the content is well organized and it's easy to find what I need.",
image: "/images/slider-pic-1.png",
},
{
id: 2,
author: "Terrence Jaskolski",
avatar: "/images/avatar-3.jpg",
content:
"I'm amazed at how quickly and easily I can create new ideas with this tool. It's a game-changer!",
image: "/images/slider-pic-1.png",
},
{
id: 3,
author: "Suzana Marie",
avatar: "/images/avatar-6.jpg",
content:
"People with link will be able to view conversations and ideas in this board.Changes you make after creating the link will remain private.",
image: "/images/slider-pic-1.png",
},
];

View File

@@ -0,0 +1,87 @@
import Link from "next/link";
import Image from "@/components/Image";
import Button from "@/components/Button";
import Icon from "@/components/Icon";
import Slider from "./Slider";
type Props = {
children: React.ReactNode;
title: string;
description: React.ReactNode;
};
const LayoutLogin = ({ title, description, children }: Props) => (
<div className="p-5">
<div className="flex min-h-[calc(100svh-2.5rem)]">
<div className="relative w-1/2 text-static-white overflow-hidden max-lg:hidden">
<Image
className="object-cover rounded-3xl"
src="/images/auth-pic.jpg"
fill
sizes="(max-width: 1023px) 100vw, 50vw"
alt=""
/>
<div className="absolute top-19 left-10 right-10 max-2xl:top-8 max-2xl:left-8 max-2xl:right-8">
<div className="mb-4 text-h1 max-2xl:text-h3">
AI Generative Anything you can imagine
</div>
<div className="text-p-lg max-2xl:text-p-md">
Generate your ideas in to reality fast & quick !
</div>
</div>
<Slider />
</div>
<div className="flex flex-col w-1/2 pl-12 max-lg:w-full max-lg:pl-0">
<div className="flex justify-between items-center mb-auto">
<div className="">
<div className="text-[1.125rem] font-bold">Website</div>
<div className="-mt-1 text-soft-400">
Visite Our website
</div>
</div>
<Button
className="!h-10 !gap-3 bg-white-0 rounded-xl"
isStroke
>
LoopiBot.com
<Icon className="!size-4.5" name="chevron-circle" />
</Button>
</div>
<div className="w-full max-w-89 mx-auto my-6">
<div className="mb-7 text-center max-md:mb-4">
<Link className="inline-flex mb-6 max-md:mb-4" href="/">
<Image
className="w-18 opacity-100 max-md:w-14"
src="/images/logo-auth.svg"
width={68}
height={68}
alt=""
/>
</Link>
<div className="text-h3 max-md:text-[1.6rem]">
{title}
</div>
<div className="mt-1.5 text-h6 max-md:text-label-md">
{description}
</div>
</div>
{children}
</div>
<div className="flex justify-between items-center h-15 mt-auto max-2xl:h-auto">
<div className="text-label-sm text-sub-600">
©LoopiBot.io
</div>
<button className="group flex items-center gap-2 text-label-sm text-sub-600 transition-colors hover:text-strong-950">
<Icon
className="!size-4.5 fill-sub-600 transition-colors group-hover:fill-strong-950"
name="envelope"
/>
LoopiBot@chat.io
</button>
</div>
</div>
</div>
</div>
);
export default LayoutLogin;

View File

@@ -0,0 +1,40 @@
import {
Dialog,
DialogPanel,
DialogBackdrop,
CloseButton,
} from "@headlessui/react";
import Icon from "@/components/Icon";
type ModalProps = {
classWrapper?: string;
open: boolean;
onClose: () => void;
children: React.ReactNode;
};
const Modal = ({ classWrapper, open, onClose, children }: ModalProps) => {
return (
<Dialog className="relative z-50" open={open} onClose={onClose}>
<DialogBackdrop
className="fixed inset-0 bg-overlay backdrop-blur-sm duration-300 ease-out data-[closed]:opacity-0"
transition
/>
<div className="fixed inset-0 flex p-4 overflow-y-auto max-lg:py-12">
<DialogPanel
className={`relative w-full m-auto p-5.5 shadow-[0_0_1.25rem_0_rgba(0,0,0,0.02)] rounded-2xl bg-white-0 duration-300 ease-out data-[closed]:opacity-0 max-md:px-4 ${
classWrapper || ""
}`}
transition
>
{children}
<CloseButton className="absolute left-[calc(100%+0.75rem)] top-0 z-15 size-8 bg-strong-950 rounded-full text-0 transition-colors hover:bg-strong-950/90 max-lg:top-auto max-lg:left-auto max-lg:right-0 max-lg:bottom-[calc(100%+0.5rem)]">
<Icon className="!size-4 fill-white-0" name="close" />
</CloseButton>
</DialogPanel>
</div>
</Dialog>
);
};
export default Modal;

View File

@@ -0,0 +1,104 @@
import Modal from "@/components/Modal";
import Button from "@/components/Button";
import Icon from "@/components/Icon";
import { plans } from "./plans";
type GalleryProps = {
open: boolean;
onClose: () => void;
};
const ModalPlan = ({ open, onClose }: GalleryProps) => (
<Modal classWrapper="max-w-200" open={open} onClose={onClose}>
<div className="mb-4 pb-1.5 border-b border-stroke-soft-200">
<div className="text-label-xl">Plans</div>
<div className="text-label-md text-sub-600">
Manage your billing and payment details.
</div>
</div>
<div className="flex gap-3 max-md:gap-1.5 max-md:-mx-4 max-md:px-4 max-md:overflow-auto max-md:scrollbar-none">
{plans.map((plan) => (
<div
className={`relative flex-1 p-3 rounded-2xl overflow-hidden max-md:shrink-0 max-md:flex-auto max-md:w-60 ${
plan.name === "Premium Plan"
? "bg-weak-50 before:absolute before:-top-15 before:-right-32 before:size-60 before:bg-[#476CFF]/5 before:rounded-full before:blur-[3rem]"
: ""
}`}
key={plan.id}
>
<div className="relative z-2">
<div className="flex justify-between items-center mb-4">
<Icon
className="!size-6 fill-strong-950"
name={plan.icon}
/>
{plan.name === "Premium Plan" && (
<div className="flex items-center gap-1 h-6.5 px-2 rounded-full bg-[#476CFF]/10 text-label-xs text-blue-500">
<Icon
className="!size-3.25 fill-blue-500"
name="flash"
/>
Most Popular
</div>
)}
</div>
<div className="mb-1 text-label-lg">{plan.name}</div>
<div className="min-h-10.5 mb-3 text-soft-400">
{plan.description}
</div>
<div className="font-satoshi font-bold text-h5">
{plan.price}
</div>
<div className="mb-6 text-sub-600">{plan.details}</div>
<button
className={`flex justify-center items-center gap-1.5 w-full h-10 mb-6 rounded-[0.625rem] text-label-md transition-opacity hover:opacity-90 ${
plan.name === "Basic Plan"
? "bg-weak-50 [&_svg]:fill-strong-950"
: plan.name === "Premium Plan"
? "border border-blue-500 text-blue-600"
: "bg-blue-600 text-static-white [&_svg]:fill-static-white"
}`}
>
{plan.name === "Basic Plan"
? "Buy now"
: plan.name === "Premium Plan"
? "Switch to this plan"
: "Contact Us"}
{plan.name !== "Premium Plan" && (
<Icon className="-rotate-45" name="arrow" />
)}
</button>
<div className="">
{plan.features.map((feature, index) => (
<div
className="flex items-center gap-2 py-4 border-t border-stroke-soft-200 text-label-sm"
key={index}
>
<svg
className="shrink-0 fill-strong-950"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
>
<path d="M16.611 5.124c.16.431-.06.911-.491 1.071-.882.327-1.85 1.009-2.83 1.909-.971.892-1.906 1.954-2.727 2.985l-2.004 2.739-.569.868-.149.237-.037.06-.009.015-.002.003c-.153.252-.429.406-.724.402s-.566-.164-.713-.42c-.791-1.384-1.444-2.008-1.838-2.283a1.49 1.49 0 0 0-.387-.208l-.03-.009c-.431-.032-.772-.392-.772-.831 0-.46.373-.833.833-.833.098.007.331.033.474.085.228.072.512.203.837.43.466.327 1.014.85 1.61 1.689l.092-.137 2.082-2.846c.852-1.07 1.847-2.204 2.903-3.174 1.047-.962 2.202-1.808 3.378-2.244.431-.16.911.06 1.071.491z" />
</svg>
{feature}
</div>
))}
</div>
</div>
</div>
))}
</div>
<div className="flex justify-end gap-3 mt-4.5">
<Button className="!bg-weak-50" isStroke onClick={onClose}>
Discard
</Button>
<Button isBlack>Upgrade Membership</Button>
</div>
</Modal>
);
export default ModalPlan;

View File

@@ -0,0 +1,44 @@
export const plans = [
{
id: 0,
name: "Basic Plan",
icon: "profile",
description: "per user/email month billed annually",
price: "$9.99",
details: "one-time payment + Local Taxes",
features: [
"1 Image generation",
"File Sharing",
"Limited Integration",
"Limited templates",
],
},
{
id: 1,
name: "Premium Plan",
icon: "rocket",
description: "per user/email month billed annually",
price: "$19.99",
details: "one-time payment + Local Taxes",
features: [
"30 Image generation",
"File Sharing",
"10 Integration",
"20 templates",
],
},
{
id: 2,
name: "Professional Plan",
icon: "build",
description: "per user/email month billed annually",
price: "Custom",
details: "one-time payment + Local Taxes",
features: [
"unlimited Image generation",
"File Sharing",
"unlimited Integration",
"unlimited Integration",
],
},
];

View File

@@ -0,0 +1,63 @@
import { useState } from "react";
import Switch from "@/components/Switch";
import Button from "@/components/Button";
const DataControls = ({}) => {
const [improve, setImprove] = useState(true);
return (
<div className="">
<div className="flex justify-between gap-6 mb-3 pb-3 border-b border-stroke-soft-200">
<div className="max-w-101">
<div className="text-label-md">
Improve the model for everyone
</div>
<div className="text-sub-600">
Allow your content to be used to train our models, which
makes AI better for you and everyone who uses it. We
take steps to protect your privacy.{" "}
<button className="underline text-blue-500 hover:no-underline">
Learn more
</button>
</div>
</div>
<Switch checked={improve} onChange={setImprove} isSmall />
</div>
<div className="flex justify-between items-center gap-6 mb-3 pb-3 border-b border-stroke-soft-200">
<div className="text-label-md">Export Data</div>
<Button
className="!h-10 !rounded-[0.625rem] !bg-weak-50"
isStroke
>
Export
</Button>
</div>
<div className="flex justify-between items-center gap-6 mb-3 pb-3 border-b border-stroke-soft-200">
<div className="text-label-md">Shared Links</div>
<Button
className="!h-10 !rounded-[0.625rem] !bg-weak-50"
isStroke
>
Manage
</Button>
</div>
<div className="flex justify-between items-center gap-6 mb-3 pb-3 border-b border-stroke-soft-200">
<div className="text-label-md">Archive all chats</div>
<Button
className="!h-10 !rounded-[0.625rem] !bg-weak-50"
isStroke
>
Archive all
</Button>
</div>
<div className="flex justify-between items-center gap-6">
<div className="text-label-md">Delete Account</div>
<Button className="!h-10 !rounded-[0.625rem]" isRed>
Delete Account
</Button>
</div>
</div>
);
};
export default DataControls;

View File

@@ -0,0 +1,76 @@
import { useState, useRef } from "react";
import Image from "@/components/Image";
import Button from "@/components/Button";
import Icon from "@/components/Icon";
const UploadImage = ({}) => {
const [preview, setPreview] = useState<string | null>(
"/images/avatar-1.png"
);
const inputRef = useRef<HTMLInputElement>(null);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
const objectUrl = URL.createObjectURL(file);
setPreview(objectUrl);
}
};
const handleRemove = () => {
setPreview(null);
};
return (
<div className="">
<div className="flex items-center gap-3">
<div className="relative flex justify-center items-center bg-soft-200 size-11.5 rounded-full overflow-hidden">
{preview ? (
<Image
className="size-full opacity-100"
src={preview}
width={48}
height={48}
alt="avatar"
/>
) : (
<Icon
className="size-6 fill-strong-950"
name="profile"
/>
)}
</div>
<div className="relative">
<input
className="absolute inset-0 opacity-0 cursor-pointer z-10 object-cover"
ref={inputRef}
type="file"
onChange={handleChange}
accept="image/*"
/>
<Button className="!h-9 rounded-lg" isStroke>
Upload image
</Button>
</div>
<Button
className="!w-9 !h-9 !px-0 rounded-lg"
isStroke
onClick={handleRemove}
>
<Image
className="size-6 opacity-100"
src="/images/trash.svg"
width={24}
height={24}
alt=""
/>
</Button>
</div>
<div className="mt-1.5 text-soft-400 max-md:text-p-xs">
We only support JPG, JPEG, or ,PNG file. 1MB max.
</div>
</div>
);
};
export default UploadImage;

View File

@@ -0,0 +1,80 @@
import { useState } from "react";
import Field from "@/components/Field";
import Icon from "@/components/Icon";
import Button from "@/components/Button";
import UploadImage from "./UploadImage";
const General = ({}) => {
const [fullName, setFullName] = useState("");
const [email, setEmail] = useState("");
const [phoneNumber, setPhoneNumber] = useState("");
return (
<div className="-mt-5 max-md:mt-0">
<div className="flex items-center mb-3 pb-3 border-b border-stroke-soft-200 max-md:flex-col max-md:items-start max-md:gap-3">
<div className="mr-auto">
<div className="text-label-md">Avatar</div>
<div className="text-sub-600">Update full name</div>
</div>
<UploadImage />
</div>
<div className="mb-3 pb-3 border-b border-stroke-soft-200">
<div className="mb-3">
<div className="text-label-md">Personal Information</div>
<div className="text-sub-600">
Edit your personal information
</div>
</div>
<Field
className="mb-3"
label="Full name"
placeholder="Enter full name"
value={fullName}
onChange={(e) => setFullName(e.target.value)}
required
isSmall
/>
<Field
className="mb-1"
label="Email"
placeholder="Enter email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
isSmall
/>
<button className="inline-flex items-center gap-2 text-label-xs text-sub-600 transition-opacity hover:opacity-80">
<Icon className="!size-4" name="plus" />
Add another
</button>
</div>
<div className="mb-3">
<div className="mb-3">
<div className="text-label-md">Phone number</div>
<div className="text-sub-600">Update your phone number</div>
</div>
<Field
className=""
label="Phone number"
placeholder="Enter phone number"
type="tel"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value)}
required
isSmall
/>
</div>
<div className="flex justify-end gap-3">
<Button className="!h-10 !px-4.5 !bg-weak-50" isStroke>
Discard
</Button>
<Button className="!h-10 !px-4.5" isBlack>
Save Changes
</Button>
</div>
</div>
);
};
export default General;

View File

@@ -0,0 +1,34 @@
import { useState } from "react";
import Switch from "@/components/Switch";
const Speech = ({}) => {
const [responses, setResponses] = useState(true);
const [push, setPush] = useState(true);
const [email, setEmail] = useState(false);
return (
<div className="">
<div className="flex items-center mb-3 pb-3 border-b border-stroke-soft-200">
<div className="mr-auto">
<div className="text-label-md">Responses</div>
<div className="text-sub-600">Ai response Push</div>
</div>
<Switch checked={responses} onChange={setResponses} isSmall />
</div>
<div className="flex items-center mb-3 pb-3 border-b border-stroke-soft-200">
<div className="mr-auto">
<div className="text-label-md">Push</div>
</div>
<Switch checked={push} onChange={setPush} isSmall />
</div>
<div className="flex items-center mb-3 pb-3 border-b border-stroke-soft-200">
<div className="mr-auto">
<div className="text-label-md">Email</div>
</div>
<Switch checked={email} onChange={setEmail} isSmall />
</div>
</div>
);
};
export default Speech;

View File

@@ -0,0 +1,47 @@
import { useState } from "react";
import Switch from "@/components/Switch";
import Button from "@/components/Button";
const Security = ({}) => {
const [authentication, setAuthentication] = useState(true);
return (
<div className="">
<div className="flex justify-between gap-6 mb-3 pb-3 border-b border-stroke-soft-200">
<div className="max-w-101">
<div className="text-label-md">
Mlti-factor authentication
</div>
<div className="text-sub-600">
Require an extra security challenge when logging in. If
you are unable to pass this challenge, you will have the
option to recover your account via email.
</div>
</div>
<Switch
checked={authentication}
onChange={setAuthentication}
isSmall
/>
</div>
<div className="flex justify-between gap-6 max-md:flex-col max-md:gap-3">
<div className="max-w-101">
<div className="text-label-md">Log out of all devices</div>
<div className="text-sub-600">
Log out of all active sessions across all devices,
including your current session. It may take up to 30
minutes for other devices to be logged out.
</div>
</div>
<Button
className="shrink-0 !h-10 !rounded-[0.625rem] !bg-weak-50"
isStroke
>
Log out all
</Button>
</div>
</div>
);
};
export default Security;

View File

@@ -0,0 +1,62 @@
import { useState } from "react";
import Button from "@/components/Button";
import Icon from "@/components/Icon";
import Select from "@/components/Select";
const voiceOptions = [
{ id: 1, name: "Default" },
{ id: 2, name: "Echo" },
{ id: 3, name: "Tempo" },
];
const languages = [
{ id: 1, name: "English" },
{ id: 2, name: "French" },
{ id: 3, name: "Spanish" },
];
const Notifications = ({}) => {
const [voice, setVoice] = useState(voiceOptions[0]);
const [language, setLanguage] = useState(languages[0]);
return (
<div className="">
<div className="flex items-center mb-3 pb-3 border-b border-stroke-soft-200">
<div className="mr-auto">
<div className="text-label-md">Voice</div>
<div className="text-sub-600">Choose your ai voice.</div>
</div>
<div className="flex gap-1.5">
<Button
className="w-10 !h-10 !px-0 !rounded-[0.625rem] !bg-weak-50"
isStroke
>
<Icon className="fill-icon-sub-600" name="volume" />
</Button>
<Select
classButton="h-10 bg-weak-50 rounded-[0.625rem]"
value={voice}
onChange={setVoice}
options={voiceOptions}
/>
</div>
</div>
<div className="flex items-center gap-4">
<div className="mr-auto">
<div className="text-label-md">Main Language</div>
<div className="text-sub-600">
For best results, select the language you mainly speak.
</div>
</div>
<Select
classButton="h-10 bg-weak-50 rounded-[0.625rem]"
value={language}
onChange={setLanguage}
options={languages}
/>
</div>
</div>
);
};
export default Notifications;

View File

@@ -0,0 +1,97 @@
import { useTheme } from "next-themes";
import Image from "@/components/Image";
const Theme = ({}) => {
const { theme, setTheme } = useTheme();
const themes = [
{
id: 1,
name: "Light mode",
image: "/images/theme-light.png",
value: "light",
},
{
id: 2,
name: "Dark mode",
image: "/images/theme-dark.png",
value: "dark",
},
{
id: 3,
name: "System Preference",
image: "/images/theme-preference.png",
value: "system",
},
];
return (
<div className="">
<div className="text-label-md">Themes</div>
<div className="mb-3 text-sub-600">
Choose your style or customize your theme
</div>
<div className="flex gap-3 max-md:flex-col">
{themes.map((button) => (
<div
className={`flex flex-col flex-1 border rounded-lg bg-white-0 overflow-hidden cursor-pointer transition-colors hover:border-blue-500 ${
theme === button.value
? "border-blue-500 dark:text-static-black"
: "border-stroke-soft-200"
}`}
key={button.id}
onClick={() => setTheme(button.value)}
>
<div className="p-2.5 py-4">
<div className="border border-stroke-soft-200 rounded-lg overflow-hidden">
<Image
className="w-full opacity-100"
src={button.image}
width={152}
height={95}
alt=""
/>
</div>
</div>
<div
className={`flex items-center grow gap-2.5 h-11 px-3 transition-colors ${
theme === button.value
? "bg-blue-50"
: "bg-weak-50"
}`}
>
<div
className={`flex justify-center items-center size-4.5 rounded-full border ${
theme === button.value
? "border-blue-500 bg-blue-500"
: "border-stroke-soft-200 bg-white-0"
}`}
>
<svg
className={`fill-static-white transition-opacity ${
theme === button.value
? "opacity-100"
: "opacity-0"
}`}
xmlns="http://www.w3.org/2000/svg"
width="9"
height="8"
fill="none"
viewBox="0 0 9 8"
>
<path
fillRule="evenodd"
d="M8.252 2.16a.79.79 0 1 0-1.167-1.07l-3.795 4.14-1.394-1.394a.79.79 0 1 0-1.12 1.12l1.979 1.979a.79.79 0 0 0 1.143-.025l4.354-4.75z"
/>
</svg>
</div>
<div className="text-label-sm">{button.name}</div>
</div>
</div>
))}
</div>
</div>
);
};
export default Theme;

View File

@@ -0,0 +1,90 @@
import { useState } from "react";
import Modal from "@/components/Modal";
import Icon from "@/components/Icon";
import General from "./General";
import Notifications from "./Notifications";
import Speech from "./Speech";
import Theme from "./Theme";
import Security from "./Security";
import DataControls from "./DataControls";
import { menu } from "./menu";
type Props = {
open: boolean;
onClose: () => void;
};
const Settings = ({ open, onClose }: Props) => {
const [activeId, setActiveId] = useState(0);
return (
<>
<Modal
classWrapper="flex flex-col max-w-199.5 min-h-180 max-md:min-h-[calc(100svh-6rem)]"
open={open}
onClose={onClose}
>
<div className="mb-4.5 pb-2 border-b border-stroke-soft-200 max-md:mb-3">
<div className="text-label-xl max-md:text-label-lg">
Settings
</div>
<div className="mt-2 text-label-md text-sub-600 max-md:hidden">
People with link will be able to view conversations and
ideas in this board.Changes you make after creating the
link will remain private.
</div>
</div>
<div className="flex grow max-md:block">
<div className="flex flex-col gap-2 shrink-0 w-50 pr-4 border-r border-stroke-soft-200 max-lg:w-40 max-md:flex-row max-md:gap-4 max-md:w-auto max-md:mb-4 max-md:overflow-auto max-md:scrollbar-none max-md:-mx-4 max-md:px-4 max-md:border-0">
{menu.map((item) => (
<button
className={`group flex items-center gap-2 h-10 transition-colors hover:text-strong-950 max-md:shrink-0 ${
activeId === item.id
? "!text-blue-500"
: "text-sub-600"
}`}
onClick={() => setActiveId(item.id)}
key={item.id}
>
<Icon
className={`transition-colors group-hover:fill-strong-950 ${
activeId === item.id
? "!fill-blue-500"
: "fill-sub-600"
}`}
name={item.icon}
/>
{item.name}
</button>
))}
</div>
<div className="grow pl-4 max-md:pl-0">
{menu
.filter((item) => item.id === activeId)
.map((item) => (
<div
className="flex items-center gap-2.5 mb-8 text-label-lg max-md:hidden"
key={item.id}
>
<Icon
className="!size-7 fill-strong-950"
name={item.icon}
/>
{item.name}
</div>
))}
{activeId === 0 && <General />}
{activeId === 1 && <Notifications />}
{activeId === 2 && <Speech />}
{activeId === 3 && <Theme />}
{activeId === 4 && <Security />}
{activeId === 5 && <DataControls />}
</div>
</div>
</Modal>
</>
);
};
export default Settings;

View File

@@ -0,0 +1,32 @@
export const menu = [
{
id: 0,
name: "General",
icon: "folder",
},
{
id: 1,
name: "Notifications",
icon: "bell",
},
{
id: 2,
name: "Speech",
icon: "voice",
},
{
id: 3,
name: "Theme",
icon: "document",
},
{
id: 4,
name: "Security",
icon: "security",
},
{
id: 5,
name: "Data Controls",
icon: "database",
},
];

View File

@@ -0,0 +1,56 @@
import { useState } from "react";
import Image from "@/components/Image";
const tabs = [
{ id: 0, name: "Generate Images" },
{ id: 1, name: "3 Media" },
];
const Images = ({}) => {
const [activeTab, setActiveTab] = useState(1);
return (
<>
<div className="absolute bottom-[calc(100%-0.75rem)] left-1/2 -translate-x-1/2">
{(activeTab === 0
? ["/images/image-5.jpg"]
: [
"/images/image-6.jpg",
"/images/image-5.jpg",
"/images/image-7.jpg",
]
).map((image, index) => (
<div
key={index}
className="relative w-17 rounded-lg overflow-hidden first:z-3 not-first:absolute not-first:bottom-1.5 nth-2:right-1/4 nth-2:-rotate-15 nth-3:left-1/4 nth-3:rotate-15"
>
<Image
className="w-full scale-105 opacity-100"
src={image}
width={68}
height={94}
alt="Image"
/>
</div>
))}
</div>
<div className="flex justify-center mb-4.5">
<div className="flex justify-center gap-2 p-0.25 bg-weak-50 rounded-lg border border-stroke-soft-200">
{tabs.map((tab) => (
<button
className={`p-2 rounded-lg text-label-xs transition-colors ${
activeTab === tab.id ? "bg-soft-200" : ""
}`}
key={tab.id}
onClick={() => setActiveTab(tab.id)}
>
{tab.name}
</button>
))}
</div>
</div>
</>
);
};
export default Images;

View File

@@ -0,0 +1,68 @@
import Modal from "@/components/Modal";
import Button from "@/components/Button";
import Images from "./Images";
type Props = {
open: boolean;
onClose: () => void;
};
const ModalShare = ({ open, onClose }: Props) => {
const link =
"Https://AIchat.ai/share/board/2ca927-2028c-2028-20nc-AA2ca927-2028c-2028-20nc-AA";
const handleCopyCode = async () => {
try {
await navigator.clipboard.writeText(link);
console.log("Code copied to clipboard!");
} catch (err) {
console.error("Failed to copy code: ", err);
}
};
return (
<Modal classWrapper="max-w-138" open={open} onClose={onClose}>
<Images />
<div className="mb-2 text-center text-label-xl">Share Board</div>
<div className="mb-4.5 text-center text-label-md text-sub-600 max-md:text-label-sm">
People with link will be able to view conversations and ideas in
this board.Changes you make after creating the link will remain
private.
</div>
<div className="flex items-center gap-2 p-2 pl-3 border border-stroke-soft-200 rounded-lg bg-weak-50">
<div className="truncate">{link}</div>
<button
className="group flex justify-center items-center shrink-0 size-9 border border-stroke-soft-200 rounded-lg"
onClick={handleCopyCode}
>
<svg
className="size-4.5 fill-icon-soft-400 transition-colors group-hover:fill-strong-950"
xmlns="http://www.w3.org/2000/svg"
width="13"
height="13"
viewBox="0 0 13 13"
>
<path
opacity=".4"
d="M9.108 4.32l1.94.068c.523.07.964.221 1.314.571s.501.791.571 1.314C13 6.776 13 7.417 13 8.212h0v.645h0l-.068 1.94c-.07.523-.221.964-.571 1.314s-.791.501-1.314.571c-.504.068-1.145.068-1.94.068h0-.645 0c-.795 0-1.436 0-1.94-.068-.523-.07-.964-.221-1.314-.571s-.5-.791-.571-1.314c-.068-.504-.068-1.145-.068-1.94h0v-.645h0l.068-1.94c.07-.523.221-.964.571-1.314s.791-.5 1.314-.571c.504-.068 1.145-.068 1.94-.068h0 .645 0z"
/>
<path d="M3.698 8.161l.076-2.005c.083-.62.279-1.275.818-1.814s1.195-.735 1.814-.818c.562-.076 1.253-.076 2.005-.076h1.3c.172 0 .258 0 .31-.056s.046-.14.033-.307l-.042-.413c-.075-.558-.226-1.021-.546-1.412a2.76 2.76 0 0 0-.383-.383C8.671.539 8.178.39 7.578.319 6.995.25 6.257.25 5.325.25h-.057c-.932 0-1.67 0-2.253.069-.599.071-1.093.22-1.505.558a2.76 2.76 0 0 0-.383.383c-.338.412-.487.906-.558 1.505C.5 3.349.5 4.087.5 5.018v.057l.069 2.253c.071.599.22 1.093.558 1.505a2.76 2.76 0 0 0 .383.383c.391.321.854.471 1.412.546l.413.042c.168.012.251.019.307-.033s.056-.138.056-.31v-1.3z" />
</svg>
</button>
</div>
<div className="flex justify-between items-center mt-4.5">
<button
className="text-label-md text-strong-950 transition-colors hover:text-blue-500"
onClick={onClose}
>
Cancel
</button>
<Button className="px-6.5" isBlack onClick={handleCopyCode}>
Update & Copy Link
</Button>
</div>
</Modal>
);
};
export default ModalShare;

View File

@@ -0,0 +1,64 @@
import Modal from "@/components/Modal";
import Image from "@/components/Image";
import Button from "@/components/Button";
type GalleryProps = {
open: boolean;
onClose: () => void;
image: string;
};
const ModalView = ({ open, onClose, image }: GalleryProps) => (
<Modal classWrapper="max-w-220" open={open} onClose={onClose}>
<div className="flex items-center gap-3 mb-4.5 p-3 bg-weak-50 rounded-xl max-md:flex-wrap">
<div className="flex justify-center items-center shrink-0 size-9 rounded-lg bg-white-0">
<Image
className="w-4.5 opacity-100"
src="/images/stars.svg"
width={18}
height={18}
alt=""
/>
</div>
<div className="grow">
<div className="text-label-sm">
You Are still on the free plan
</div>
<div className="mt-1 text-p-xs">
Upgrade your free plan into premium plan. Get premium Ai
features now and generate multiple Images !
</div>
</div>
<Button
className="shrink-0 !h-10 px-7 text-p-xs max-md:w-full"
isBlack
>
Go Premium
</Button>
</div>
<div className="relative h-110 rounded-xl overflow-hidden max-md:h-60">
<Image className="object-cover scale-101" src={image} fill alt="" />
<Button
className="absolute right-3.5 bottom-4.5 max-md:right-2 max-md:bottom-2 max-md:gap-0 max-md:w-10 max-md:h-10 max-md:px-0 max-md:text-0"
isWhite
>
<svg
className="!size-5 fill-strong-950"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
>
<path d="M10.84 11.042c0-.46-.373-.833-.833-.833s-.833.373-.833.833v3.75h-.495c-.146 0-.326 0-.473.018h-.003c-.105.013-.585.073-.814.544s.022.889.077.979l.002.003a4.16 4.16 0 0 0 .281.387l.02.025.881 1.043c.158.163.339.33.532.463.171.118.463.287.822.287s.651-.169.822-.287c.192-.132.373-.3.532-.463.317-.326.635-.729.881-1.043l.02-.025.281-.387.002-.003c.054-.09.306-.507.077-.979s-.709-.531-.814-.544h-.003c-.147-.018-.327-.018-.473-.018h-.486v-3.75z" />
<path
opacity=".4"
d="M1.043 10.417c0-2.201 1.484-4.056 3.507-4.617.163-.045.245-.068.292-.116s.068-.13.109-.293a5.21 5.21 0 0 1 10.227.693c.023.204.034.307.086.366s.153.084.356.133c1.917.465 3.34 2.192 3.34 4.252 0 2.416-1.959 4.375-4.375 4.375h-.05c-.183 0-.274 0-.337-.042s-.106-.144-.191-.348c-.105-.251-.262-.486-.469-.687-.276-.268-.608-.445-.957-.53-.259-.063-.389-.095-.442-.163s-.053-.178-.053-.397v-2c0-1.151-.933-2.083-2.083-2.083s-2.083.933-2.083 2.083v2c0 .219 0 .329-.053.397s-.183.1-.442.163c-.349.085-.681.262-.957.53-.208.203-.384.438-.502.691-.097.208-.146.312-.214.352s-.153.032-.324.018c-2.455-.207-4.383-2.266-4.383-4.774z"
/>
</svg>
Download Image
</Button>
</div>
</Modal>
);
export default ModalView;

View File

@@ -0,0 +1,53 @@
import Image from "@/components/Image";
const HotKeys = ({}) => (
<div className="flex items-center gap-3 py-3.5 border-b border-stroke-soft-200 text-label-sm max-md:hidden">
<div className="flex items-center gap-1.5">
<div className="">
<Image
className="w-5 opacity-100"
src="/images/key-square-arrow-down.svg"
width={20}
height={20}
alt=""
/>
</div>
<div className="rotate-180">
<Image
className="w-5 opacity-100"
src="/images/key-square-arrow-down.svg"
width={20}
height={20}
alt=""
/>
</div>
To Navigate
</div>
<div className="flex items-center gap-1.5 mr-auto">
<div className="rotate-180">
<Image
className="w-5 opacity-100"
src="/images/key-square-enter.svg"
width={20}
height={20}
alt=""
/>
</div>
To Select
</div>
<div className="flex items-center gap-1.5">
<div className="rotate-180">
<Image
className="w-5 opacity-100"
src="/images/key-square-enter.svg"
width={20}
height={20}
alt=""
/>
</div>
To Close
</div>
</div>
);
export default HotKeys;

View File

@@ -0,0 +1,75 @@
import { useState, useRef } from "react";
import TextareaAutosize from "react-textarea-autosize";
import { motion } from "framer-motion";
import { useClickAway } from "react-use";
import Icon from "@/components/Icon";
import HotKeys from "./HotKeys";
import { items } from "./items";
const Menu = ({}) => {
const [visible, setVisible] = useState(false);
const [message, setMessage] = useState("");
const ref = useRef(null);
useClickAway(ref, () => {
setVisible(false);
});
return (
<div className="flex" ref={ref}>
<button className="group" onClick={() => setVisible(!visible)}>
<Icon
className={`transition-colors group-hover:fill-blue-500 ${
visible ? "fill-blue-500" : "fill-icon-soft-400"
}`}
name="zsh"
/>
</button>
{visible && (
<motion.div
className="absolute -left-0.5 bottom-[calc(100%+0.75rem)] -right-0.5 px-3.5 shadow-[0_0_4.6rem_0_rgba(0,0,0,0.17)] bg-white-0 rounded-xl border border-stroke-soft-200"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{
delay: 0.2,
}}
>
<HotKeys />
<div className="py-2">
{items.map((item, index) => (
<div
className="flex items-center gap-2 px-3 py-2 rounded-xl transition-colors cursor-pointer hover:bg-weak-50"
key={index}
>
<Icon
className="shrink-0 fill-strong-950"
name="arrow"
/>
<div className="grow">
<div className="text-label-sm">
{item.title}
</div>
<div className="text-soft-400">
{item.description}
</div>
</div>
</div>
))}
</div>
<div className="-mx-3.5 px-3.5 py-3 bg-weak-50 rounded-b-xl border-t border-stroke-soft-200">
<TextareaAutosize
className="w-full text-p-md text-strong-950 outline-none resize-none placeholder:text-sub-600"
maxRows={5}
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Ask a question about this answer"
/>
</div>
</motion.div>
)}
</div>
);
};
export default Menu;

View File

@@ -0,0 +1,18 @@
export const items = [
{
title: "Ask",
description: "Ask the AI anything (general chat)",
},
{
title: "Summarize",
description: "Summarize pasted text or a document",
},
{
title: "/Rewrite",
description: "Rewrite a sentence for clarity or tone",
},
{
title: "/Call-api",
description: "Call a connected external API and show the result",
},
];

View File

@@ -0,0 +1,25 @@
import { useState } from "react";
import Icon from "@/components/Icon";
const Note = ({}) => {
const [isVisible, setIsVisible] = useState(true);
return isVisible ? (
<div className="flex items-center gap-1.5 min-h-8 px-3 py-1.5 rounded-t-xl bg-blue-50 border-b border-stroke-soft-200 text-label-xs text-blue-500">
<div className="shrink-0 text-0">
<Icon className="!size-4 fill-blue-500" name="alert-circle" />
</div>
<div className="grow">
By select a feature, it will make you goal easily to achieve
</div>
<button className="group" onClick={() => setIsVisible(false)}>
<Icon
className="!size-4 fill-strong-950 transition-colors group-hover:fill-red-500"
name="close"
/>
</button>
</div>
) : null;
};
export default Note;

View File

@@ -0,0 +1,144 @@
import { useState, useEffect, useRef } from "react";
import { motion } from "framer-motion";
import { FadeLoader } from "react-spinners";
import { useClickAway } from "react-use";
import Icon from "@/components/Icon";
import { items } from "./items";
const Search = ({}) => {
const [itemStates, setItemStates] = useState<
Array<"pending" | "loading" | "active">
>(new Array(items.length).fill("pending"));
const [visible, setVisible] = useState(false);
const ref = useRef(null);
useClickAway(ref, () => {
setVisible(false);
});
useEffect(() => {
if (!visible) {
setItemStates(new Array(items.length).fill("pending"));
return;
}
const timeouts: NodeJS.Timeout[] = [];
const animateItems = () => {
items.forEach((_, index) => {
const loadingTimeout = setTimeout(() => {
setItemStates((prev) => {
const newStates = [...prev];
newStates[index] = "loading";
return newStates;
});
}, 3000 * index);
const activeTimeout = setTimeout(() => {
setItemStates((prev) => {
const newStates = [...prev];
newStates[index] = "active";
return newStates;
});
}, 3000 * index + 3000);
timeouts.push(loadingTimeout, activeTimeout);
});
};
animateItems();
// Cleanup function - очищаємо всі таймери при зміні visible або unmount
return () => {
timeouts.forEach((timeout) => clearTimeout(timeout));
};
}, [visible]);
return (
<div className="flex mr-auto" ref={ref}>
<button className="group" onClick={() => setVisible(!visible)}>
<Icon
className={`transition-colors group-hover:fill-blue-500 ${
visible ? "fill-blue-500" : "fill-icon-soft-400"
}`}
name="ai-search"
/>
</button>
{visible && (
<motion.div
className="absolute -left-0.5 bottom-[calc(100%+0.75rem)] -right-0.5 p-3.5 shadow-[0_0_4.6rem_0_rgba(0,0,0,0.17)] bg-white-0 rounded-xl border border-stroke-soft-200"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{
delay: 0.2,
}}
>
<div className="flex items-center p-1.25 pr-3 rounded-xl shadow-[0_0_0.1875rem_0_rgba(0,0,0,0.14)]">
<div className="flex items-center gap-3 mr-auto text-label-md">
<div className="flex justify-center items-center size-9 rounded-lg bg-weak-50">
<Icon className="fill-strong-950" name="chat" />
</div>
Results
</div>
<button className="group flex items-center gap-1.5 text-sub-600 transition-colors hover:text-strong-950">
<Icon
className="fill-sub-600 transition-colors group-hover:fill-strong-950"
name="eye-hide"
/>
Hide Steps
</button>
</div>
<div className="mt-3.5 p-3 bg-weak-50 rounded-xl">
{items.map((item, index) => {
const state = itemStates[index];
const isPending = state === "pending";
const isLoading = state === "loading";
const isActive = state === "active";
return (
<div
className={`flex items-center gap-2 not-last:mb-3 ${
isPending
? "opacity-28"
: isLoading
? "opacity-100 text-blue-500"
: isActive
? "opacity-100"
: "opacity-28"
}`}
key={index}
>
<div className="relative shrink-0 size-[20px] text-0">
{isLoading ? (
<div className="absolute -top-[20px] -left-[18px] scale-40">
<FadeLoader color="var(--blue-500)" />
</div>
) : (
<Icon
className="!size-[20px] fill-strong-950"
name={item.icon}
/>
)}
</div>
<div className="text-label-sm">
{item.title}
{isActive && (
<Icon
className="-my-0.5 ml-2 fill-[#1DAF61]"
name="check"
/>
)}
</div>
</div>
);
})}
</div>
</motion.div>
)}
</div>
);
};
export default Search;

View File

@@ -0,0 +1,14 @@
export const items = [
{
title: "Searching for examples and definitions",
icon: "search",
},
{
title: "Considering Sources",
icon: "archive",
},
{
title: "Writing and running query",
icon: "quill",
},
];

View File

@@ -0,0 +1,28 @@
import { useState } from "react";
import Image from "@/components/Image";
import { Switch as HeadlessSwitch } from "@headlessui/react";
const Speed = ({}) => {
const [checked, setChecked] = useState(false);
return (
<HeadlessSwitch
className="relative group inline-flex w-16 h-6 p-0.75 rounded-full bg-soft-200 overflow-hidden"
checked={checked}
onChange={setChecked}
>
<Image
className="absolute top-0 left-0 w-full opacity-100"
src="/images/bg-toggle.png"
width={64}
height={24}
alt="Speed"
/>
<span className="relative z-2 flex items-center justify-center w-12 h-4.5 bg-white-0 rounded-full text-p-xs text-sub-600 transition-transform group-data-[checked]:translate-x-2.5">
Speed
</span>
</HeadlessSwitch>
);
};
export default Speed;

View File

@@ -0,0 +1,73 @@
import { useState } from "react";
import Link from "next/link";
import TextareaAutosize from "react-textarea-autosize";
import Icon from "@/components/Icon";
import Image from "@/components/Image";
import Note from "./Note";
import Speed from "./Speed";
import Search from "./Search";
import Menu from "./Menu";
type ButtonProps = {
className?: string;
icon: string;
onClick: () => void;
};
const Button = ({ className, icon, onClick }: ButtonProps) => {
return (
<button className={`group text-0 ${className || ""}`} onClick={onClick}>
<Icon
className="fill-icon-soft-400 transition-colors group-hover:fill-blue-500"
name={icon}
/>
</button>
);
};
const PanelMessage = ({}) => {
const [message, setMessage] = useState("");
return (
<div className="relative z-3 mx-7.5 mb-5.5 shrink-0 rounded-xl border border-stroke-soft-200 bg-white-0 max-md:m-0">
<Note />
<div className="px-3 py-3.5 max-md:px-4 max-md:py-2.5">
<div className="min-h-12 text-0 mb-3">
<TextareaAutosize
className="w-full h-12 text-p-md text-strong-950 outline-none resize-none placeholder:text-soft-400"
maxRows={5}
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Write your message ..."
/>
</div>
<div className="flex items-center gap-2.5">
<Speed />
<div className="w-0.25 h-5 bg-stroke-soft-200"></div>
<Menu />
<Button icon="link" onClick={() => {}} />
<Search />
<Button icon="image" onClick={() => {}} />
<Link className="group text-0" href="/research">
<Icon
className="fill-icon-soft-400 transition-colors group-hover:fill-blue-500"
name="voice"
/>
</Link>
<div className="w-0.25 h-5 bg-stroke-soft-200"></div>
<button className="group text-0" onClick={() => {}}>
<Image
className="w-5 opacity-100"
src="/images/sent.svg"
width={20}
height={20}
alt="Sent"
/>
</button>
</div>
</div>
</div>
);
};
export default PanelMessage;

View File

@@ -0,0 +1,61 @@
import { useState } from "react";
import Chat from "@/components/Chat";
import Question from "@/components/Question";
import Answer from "@/components/Answer";
import CodeEditor from "@/components/CodeEditor";
const PythonRunner = () => {
const [isGenerating, setIsGenerating] = useState(false);
const [code, setCode] = useState(`# Def
def is_prime(n):
# condition
if n <= 1:
return False
# for
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True`);
const handleGenerateCode = async () => {
setIsGenerating(true);
setTimeout(() => {
setCode(`# Def
def is_prime(n):
# condition
if n <= 1:
return False
# for
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
# Test the function
print(is_prime(17)) # True
print(is_prime(15)) # False`);
setIsGenerating(false);
}, 2000);
};
return (
<Chat>
<Question>
Write a Python function that checks if a number is prime.
</Question>
<Answer>
<div className="mb-1">Sure here is the response:</div>
<CodeEditor
title="Contact Form"
language="python"
initialCode={code}
onCodeChange={setCode}
onGenerate={handleGenerateCode}
isGenerating={isGenerating}
/>
</Answer>
</Chat>
);
};
export default PythonRunner;

View File

@@ -0,0 +1,28 @@
import Image from "@/components/Image";
type Props = {
children: React.ReactNode;
};
const Question = ({ children }: Props) => (
<div className="flex">
<div className="shrink-0">
<Image
className="size-9 opacity-100 rounded-full object-cover"
src="/images/avatar-2.png"
width={36}
height={36}
alt="Avatar"
/>
</div>
<div className="w-[calc(100%-2.25rem)] pl-3">
<div className="flex items-center gap-2 mb-1">
<div className="text-label-sm">James Brown</div>
<div className="-mb-0.75 text-p-xs text-soft-400">1min ago</div>
</div>
{children}
</div>
</div>
);
export default Question;

View File

@@ -0,0 +1,67 @@
import {
Listbox,
ListboxButton,
ListboxOption,
ListboxOptions,
} from "@headlessui/react";
import Icon from "@/components/Icon";
type SelectOption = {
id: number;
name: string;
};
type SelectProps = {
className?: string;
classButton?: string;
value: SelectOption;
onChange: (value: SelectOption) => void;
options: SelectOption[];
};
const Select = ({
className,
classButton,
value,
onChange,
options,
}: SelectProps) => {
return (
<Listbox
className={`${className || ""}`}
value={value}
onChange={onChange}
as="div"
>
<ListboxButton
className={`group flex justify-between items-center w-full h-10 pl-4 pr-3 border border-stroke-soft-200 rounded-xl text-label-sm text-sub-600 fill-sub-600 transition-all outline-0 data-[hover]:border-stroke-sub-300 data-[hover]:text-strong-950 data-[hover]:fill-strong-950 data-[open]:fill-strong-950 data-[open]:text-strong-950 data-[open]:border-stroke-sub-300 ${
classButton || ""
}`}
>
{value.name}
<Icon
className="shrink-0 ml-2 fill-inherit transition-transform group-[[data-open]]:rotate-180"
name="chevron"
/>
</ListboxButton>
<ListboxOptions
className="z-100 [--anchor-gap:0.25rem] w-[var(--button-width)] bg-white-0 border border-stroke-soft-200 shadow-lg rounded-xl overflow-hidden origin-top transition duration-200 ease-out outline-none data-[closed]:scale-95 data-[closed]:opacity-0"
anchor="bottom"
transition
modal={false}
>
{options.map((option) => (
<ListboxOption
className="group relative px-4 py-2 truncate text-label-sm text-strong-950 cursor-pointer transition-colors data-[focus]:bg-weak-50 data-[selected]:bg-weak-50 not-last:mb-0.25"
key={option.id}
value={option}
>
{option.name}
</ListboxOption>
))}
</ListboxOptions>
</Listbox>
);
};
export default Select;

View File

@@ -0,0 +1,24 @@
import Icon from "@/components/Icon";
type Props = {
title: string;
icon: string;
onClick: () => void;
};
const Button = ({ title, icon, onClick }: Props) => {
return (
<button
className="group relative flex items-center shrink-0 gap-2 w-full h-10 px-3 text-label-sm transition-colors text-sub-600 hover:text-strong-950 not-last:mb-2"
onClick={onClick}
>
<Icon
className="fill-sub-600 transition-colors group-hover:fill-strong-950"
name={icon}
/>
{title}
</button>
);
};
export default Button;

View File

@@ -0,0 +1,34 @@
export const folders = [
{
id: 0,
name: "Business plan",
items: [
{
id: 0,
name: "Buiness analysis",
type: "analytic",
},
{
id: 1,
name: "Journey map",
type: "voice",
},
],
},
{
id: 1,
name: "Images Ideas",
items: [
{
id: 0,
name: "Images Generations",
type: "image",
},
{
id: 1,
name: "3 ideas for gallery",
type: "voice",
},
],
},
];

View File

@@ -0,0 +1,89 @@
import { useState } from "react";
import AnimateHeight from "react-animate-height";
import Icon from "@/components/Icon";
import Actions from "@/components/Actions";
const actions = [
{ name: "Rename", onClick: () => {} },
{ name: "Delete", onClick: () => {} },
{ name: "Copy", onClick: () => {} },
];
import { folders } from "./folders";
const Folders = ({}) => {
const [active, setActive] = useState(false);
return (
<div className="mb-2">
<div
className={`group flex items-center gap-2 h-10 px-3 text-label-sm transition-colors cursor-pointer hover:text-strong-950 ${
active ? "!text-blue-500" : "text-sub-600"
}`}
onClick={() => setActive(!active)}
>
<Icon
className={`group-hover:fill-strong-950 transition-colors ${
active ? "!fill-blue-500" : "fill-sub-600"
}`}
name="folder"
/>
Folders
<Icon
className={`ml-auto transition-transform ${
active ? "rotate-180 fill-blue-500" : "fill-strong-950"
}`}
name="chevron"
/>
</div>
<AnimateHeight duration={500} height={active ? "auto" : 0}>
<div className="pl-5">
{folders.map((folder) => (
<div className="" key={folder.id}>
<div className="flex items-center gap-2 h-9 text-sub-600">
<Icon
className="fill-icon-sub-600"
name="folders"
/>
{folder.name}
</div>
<div className="relative pt-2 pl-5 before:absolute before:top-0 before:left-0.5 before:bottom-4 before:w-0.25 before:bg-stroke-soft-200">
{folder.items.map((item) => (
<div
className="flex items-center h-8 gap-2 text-sub-600 [&_svg]:fill-icon-sub-600 not-last:mb-2 max-md:pr-3"
key={item.id}
>
{item.type === "analytic" ? (
<div className="flex items-center">
<Icon name="template" />
<Icon
className="!size-3"
name="plus"
/>
<Icon name="analytic" />
</div>
) : item.type === "image" ? (
<Icon name="image-1" />
) : (
<Icon name="voice" />
)}
<div className="truncate transition-colors cursor-pointer hover:text-strong-950">
{item.name}
</div>
<Actions
className="ml-auto"
classNameButton="rotate-90"
items={actions}
/>
</div>
))}
</div>
</div>
))}
</div>
</AnimateHeight>
</div>
);
};
export default Folders;

View File

@@ -0,0 +1,31 @@
import { useState } from "react";
import Field from "@/components/Field";
import Button from "@/components/Button";
const InvitePeople = ({}) => {
const [email, setEmail] = useState("");
return (
<>
<div className="mb-2 text-center text-h5">Invite people</div>
<div className="mb-4 text-p-md text-center text-sub-600">
Send an invite to your teammates and start collaborating
together.
</div>
<Field
className="mb-6"
label="Email"
placeholder="Enter email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<Button className="w-full h-12" isBlack icon="plus">
Invite
</Button>
</>
);
};
export default InvitePeople;

View File

@@ -0,0 +1,50 @@
import { useState } from "react";
import {
Listbox,
ListboxButton,
ListboxOption,
ListboxOptions,
} from "@headlessui/react";
import Icon from "@/components/Icon";
const options = [
{ id: 1, name: "My Workspace" },
{ id: 2, name: "Team Workspace" },
{ id: 3, name: "Sandbox" },
];
const MyWorkspace = ({}) => {
const [value, setValue] = useState(options[0]);
return (
<Listbox value={value} onChange={setValue}>
<ListboxButton className="group flex items-center gap-2 w-full p-1.25 pr-3 rounded-xl cursor-pointer outline-0 max-lg:w-[calc(100%-1.75rem)] dark:shadow-[0_0_0.1875rem_0_rgba(255,255,255,0.16)]">
<div className="flex justify-center items-center bg-weak-50 rounded-lg size-8.75 font-medium">
P
</div>
<div className="truncate text-label-sm">{value.name}</div>
<Icon
className="shrink-0 ml-auto fill-strong-950 transition-transform group-[[data-open]]:rotate-180"
name="chevron"
/>
</ListboxButton>
<ListboxOptions
className="z-100 [--anchor-gap:0.25rem] w-[var(--button-width)] bg-white-0 border border-stroke-soft-200 shadow-lg rounded-xl origin-top overflow-hidden transition duration-200 ease-out outline-none data-[closed]:scale-95 data-[closed]:opacity-0"
anchor="bottom"
transition
modal={false}
>
{options.map((option) => (
<ListboxOption
className="group relative px-4 py-2 truncate text-label-sm text-strong-950 cursor-pointer transition-colors data-[focus]:bg-weak-50 data-[selected]:bg-weak-50 not-last:mb-0.25"
key={option.id}
value={option}
>
{option.name}
</ListboxOption>
))}
</ListboxOptions>
</Listbox>
);
};
export default MyWorkspace;

View File

@@ -0,0 +1,38 @@
import Link from "next/link";
import { usePathname } from "next/navigation";
import Icon from "@/components/Icon";
type Props = {
href: string;
title: string;
icon: string;
};
const NavLink = ({ href, title, icon }: Props) => {
const pathname = usePathname();
const isActive = pathname === href;
return (
<Link
className={`group relative flex items-center shrink-0 gap-2 h-10 px-3 text-label-sm transition-colors hover:text-strong-950 not-last:mb-2 ${
isActive ? "!text-blue-500" : "text-sub-600"
}`}
href={href}
>
<Icon
className={`transition-colors group-hover:fill-strong-950 ${
isActive ? "!fill-blue-500" : "fill-sub-600"
}`}
name={icon}
/>
<div className="">{title}</div>
{title === "Templates" && (
<div className="ml-auto px-2 py-0.5 bg-strong-950 rounded-md text-[0.6875rem] leading-[1rem] text-white-0">
Beta
</div>
)}
</Link>
);
};
export default NavLink;

View File

@@ -0,0 +1,53 @@
import { useState } from "react";
import Image from "@/components/Image";
import Icon from "@/components/Icon";
import ModalPlan from "@/components/ModalPlan";
const Upgrade = ({}) => {
const [open, setOpen] = useState(false);
return (
<>
<div className="mt-8 max-md:mt-6">
<div className="">
<Image
className="w-full opacity-100 dark:hidden"
src="/images/upgrade-pic-light.png"
width={220}
height={140}
alt="Upgrade"
priority
/>
<Image
className="!hidden w-full opacity-100 dark:!block"
src="/images/upgrade-pic-dark.png"
width={220}
height={140}
alt="Upgrade"
priority
/>
</div>
<div className="p-3 rounded-b-xl dark:shadow-[inset_0_0_0.1875rem_0_rgba(255,255,255,0.16)]">
<div
className="group flex items-center gap-1 text-label-md cursor-pointer"
onClick={() => setOpen(true)}
>
Upgrade to Premium{" "}
<Icon
className="fill-blue-500 transition-transform group-hover:translate-x-0.5"
name="arrow"
/>
</div>
<div className="mt-0.75 text-label-xs text-sub-600">
Want to reach{" "}
<span className="text-strong-950">more features</span>{" "}
and grow much bigger?
</div>
</div>
</div>
<ModalPlan open={open} onClose={() => setOpen(false)} />
</>
);
};
export default Upgrade;

View File

@@ -0,0 +1,30 @@
import Link from "next/link";
import Image from "@/components/Image";
import Icon from "@/components/Icon";
const User = ({}) => (
<Link
className="group flex items-center shrink-0 gap-2 mx-5 pt-3 px-3 pb-5 border-t border-stroke-soft-200"
href="/auth/sign-in"
>
<div className="">
<Image
className="size-10 rounded-full opacity-100"
src="/images/avatar-1.png"
width={40}
height={40}
alt="User"
/>
</div>
<div className="text-label-sm">
<div className="">Emillia Caitin</div>
<div className="text-sub-600">hey@agency.com</div>
</div>
<Icon
className="ml-auto fill-sub-600 -rotate-90 transition-transform group-hover:translate-x-0.5"
name="chevron"
/>
</Link>
);
export default User;

View File

@@ -0,0 +1,131 @@
import { useState } from "react";
import Link from "next/link";
import Icon from "@/components/Icon";
import Modal from "@/components/Modal";
import ModalShare from "@/components/ModalShare";
import ModalSettings from "@/components/ModalSettings";
import NavLink from "./NavLink";
import MyWorkspace from "./MyWorkspace";
import Upgrade from "./Upgrade";
import Button from "./Button";
import User from "./User";
import Folders from "./Folders";
import InvitePeople from "./InvitePeople";
type Props = {
visible: boolean;
onClose: () => void;
onClickNewChat: () => void;
};
const Sidebar = ({ visible, onClose, onClickNewChat }: Props) => {
const [open, setOpen] = useState(false);
const [openModalShare, setOpenModalShare] = useState(false);
const [openModalInvite, setOpenModalInvite] = useState(false);
return (
<>
<div
className={`fixed top-5 left-5 bottom-5 flex flex-col w-80 bg-white-0 rounded-3xl shadow-[0_0_1.25rem_0_rgba(0,0,0,0.03)] max-3xl:w-65 max-lg:top-0 max-lg:left-0 max-lg:bottom-0 max-lg:z-20 max-lg:w-75 max-lg:shadow-2xl max-lg:rounded-none max-lg:transition-transform max-md:w-full max-md:p-4 ${
visible
? "max-lg:translate-x-0"
: "max-lg:-translate-x-full"
}`}
>
<div className="grow overflow-auto scrollbar-none p-5">
<div className="flex items-center gap-2 mb-5 max-lg:pr-2 max-md:mb-3">
<MyWorkspace />
<button
className="group hidden ml-4 max-lg:flex"
onClick={onClose}
>
<Icon
className="text-label-sm fill-strong-950 transition-colors group-hover:fill-blue-500"
name="close"
/>
</button>
</div>
<Link
className="group relative flex items-center shrink-0 gap-2 h-10 px-3 rounded-xl text-label-sm transition-colors hover:text-blue-500 not-last:mb-2 dark:shadow-[0_0_0.1875rem_0_rgba(255,255,255,0.16)]"
href="/"
onClick={onClickNewChat}
>
<Icon
className="fill-strong-950 transition-colors group-hover:fill-blue-500"
name="chat"
/>
Chat With AI
</Link>
<div className="mb-auto">
<div className="mb-2 text-label-xs text-soft-400">
Today
</div>
<Link
className="flex items-center gap-2 h-10 mb-2 px-3 rounded-xl bg-weak-50 dark:shadow-[0_0_0.1875rem_0_rgba(255,255,255,0.16)]"
href="/write-copy"
>
<Icon className="fill-strong-950" name="document" />
<div className="text-label-sm">
mental health problems
</div>
</Link>
<Folders />
<NavLink
href="/documents"
title="Documents"
icon="document"
/>
<Button
title="Shared With Me"
icon="share"
onClick={() => setOpenModalShare(true)}
/>
<NavLink
href="/templates"
title="Templates"
icon="template"
/>
<NavLink
href="/history"
title="History"
icon="history"
/>
</div>
<Upgrade />
<div className="mt-7 max-md:mt-4">
<Button
title="Feedback"
icon="comment"
onClick={() => {}}
/>
<Button
title="Invite People"
icon="add-team"
onClick={() => setOpenModalInvite(true)}
/>
<Button
title="Settings"
icon="settings"
onClick={() => setOpen(true)}
/>
</div>
</div>
<User />
</div>
<ModalSettings open={open} onClose={() => setOpen(false)} />
<ModalShare
open={openModalShare}
onClose={() => setOpenModalShare(false)}
/>
<Modal
classWrapper="max-w-100"
open={openModalInvite}
onClose={() => setOpenModalInvite(false)}
>
<InvitePeople />
</Modal>
</>
);
};
export default Sidebar;

View File

@@ -0,0 +1,28 @@
import { useState } from "react";
import Image from "@/components/Image";
import ModalView from "@/components/ModalView";
const SoloImage = ({}) => {
const [open, setOpen] = useState(false);
return (
<>
<div className="cursor-pointer" onClick={() => setOpen(true)}>
<Image
className="w-full rounded-xl"
src="/images/image-1.jpg"
width={731}
height={418}
alt=""
/>
</div>
<ModalView
open={open}
onClose={() => setOpen(false)}
image="/images/image-1.jpg"
/>
</>
);
};
export default SoloImage;

View File

@@ -0,0 +1,28 @@
import { Switch as HeadlessSwitch } from "@headlessui/react";
type SwitchProps = {
className?: string;
checked: boolean;
onChange: (checked: boolean) => void;
isSmall?: boolean;
};
const Switch = ({ className, checked, onChange, isSmall }: SwitchProps) => (
<HeadlessSwitch
className={`group shrink-0 inline-flex p-0.5 rounded-full bg-soft-200 data-[checked]:!bg-blue-500 ${
isSmall ? "w-7 h-4" : "w-9 h-5"
} ${className || ""}`}
checked={checked}
onChange={onChange}
>
<span
className={`relative rounded-full bg-white-0 shadow-[0_0.0625rem_0.125rem_0_rgba(16,24,40,0.06),0_0.0625rem_0.125rem_0_rgba(16,24,40,0.10)] transition-transform ${
isSmall
? "size-3 group-data-[checked]:translate-x-3 before:absolute before:inset-1 before:rounded-full before:transition-colors before:bg-soft-200 group-data-[checked]:before:bg-blue-500 dark:bg-static-white"
: "size-4 group-data-[checked]:translate-x-4"
}`}
/>
</HeadlessSwitch>
);
export default Switch;

View File

@@ -0,0 +1,44 @@
type TabItem = {
id: number;
name: string;
onClick?: () => void;
};
type Props = {
className?: string;
items: TabItem[];
value: TabItem;
setValue: (value: TabItem) => void;
};
const Tabs = ({ className, items, value, setValue }: Props) => {
return (
<div className={`p-1 bg-weak-50 rounded-full ${className || ""}`}>
<div className="relative flex">
<div
className={`absolute top-0 left-0 bottom-0 rounded-full bg-white-0 transition-transform ${
items.length === 3 ? "w-1/3" : "w-1/2"
} ${value.id === items[1].id ? "translate-x-full" : ""}`}
></div>
{items.map((item) => (
<button
className={`relative z-1 flex-1 h-9 text-label-sm transition-colors hover:text-strong-950 ${
value.id === item.id
? "text-strong-950"
: "text-sub-600"
}`}
key={item.id}
onClick={() => {
setValue(item);
item.onClick?.();
}}
>
{item.name}
</button>
))}
</div>
</div>
);
};
export default Tabs;

View File

@@ -0,0 +1,3 @@
const Test = ({}) => <div className=""></div>;
export default Test;

View File

@@ -0,0 +1,60 @@
import Icon from "@/components/Icon";
import Switch from "@/components/Switch";
type Props = {
group: {
id: string;
title: string;
items: {
id: string;
title: string;
description: string;
icon: string;
}[];
};
activeId: string | null;
setActiveId: (id: string | null) => void;
};
const Group = ({ group, activeId, setActiveId }: Props) => {
return (
<div
className="not-last:mb-5 not-last:pb-5 not-last:border-b border-stroke-soft-200"
key={group.id}
>
<div className="mb-2 py-1 text-label-xs text-soft-400 max-md:mb-1">
{group.title}
</div>
<div className="">
{group.items.map((item) => (
<div
className="py-2 not-last:mb-1 max-md:py-1.5"
key={item.id}
>
<div className="flex items-center gap-2">
<Icon
className="fill-strong-950"
name={item.icon}
/>
<div className="text-label-md">{item.title}</div>
<Switch
className="ml-auto"
checked={activeId === item.id}
onChange={() => {
setActiveId(
activeId === item.id ? null : item.id
);
}}
/>
</div>
<div className="mt-0.5 text-sub-600">
{item.description}
</div>
</div>
))}
</div>
</div>
);
};
export default Group;

Some files were not shown because too many files have changed in this diff Show More