feat: 聊天页暗夜模式

This commit is contained in:
archer
2023-03-20 21:34:12 +08:00
parent 3aeb510f43
commit dc467c26b5
11 changed files with 76 additions and 61 deletions

0
.husky/pre-commit Normal file → Executable file
View File

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1679316084227" class="icon" viewBox="0 0 1305 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1173" xmlns:xlink="http://www.w3.org/1999/xlink" width="61.171875" height="48"><path d="M0.837818 75.218317c0 19.642164 8.098902 39.191237 21.969435 53.06177 13.963624 13.963624 33.512697 22.062525 53.247951 22.062525a76.055204 76.055204 0 0 0 53.06177-21.969434c13.963624-13.963624 22.062525-33.512697 22.062526-53.154861A76.055204 76.055204 0 0 0 129.303156 21.970365 76.055204 76.055204 0 0 0 76.055204 0.000931a76.055204 76.055204 0 0 0-53.247951 21.969434A76.706839 76.706839 0 0 0 0.837818 75.218317M0.837818 476.160498c0 19.642164 8.005811 39.377419 21.969435 53.247952 13.963624 13.963624 33.419606 21.969435 53.247951 21.969434a76.241385 76.241385 0 0 0 53.154861-21.969434 75.962113 75.962113 0 0 0 21.969435-53.247952 76.241385 76.241385 0 0 0-21.969435-53.154861 75.962113 75.962113 0 0 0-53.154861-21.969434 76.241385 76.241385 0 0 0-53.247951 21.969434 75.962113 75.962113 0 0 0-21.969435 53.154861M0.837818 877.19577c0 19.642164 8.005811 39.284328 21.969435 53.247951 13.963624 13.963624 33.419606 21.969435 53.247951 21.969435a76.241385 76.241385 0 0 0 53.154861-21.969435 75.962113 75.962113 0 0 0 21.969435-53.247951 76.241385 76.241385 0 0 0-21.969435-53.247952 75.962113 75.962113 0 0 0-53.154861-21.969434 76.241385 76.241385 0 0 0-53.247951 21.969434 76.520658 76.520658 0 0 0-21.969435 53.247952M1304.109361 75.218317c0 41.518508-32.395607 75.124295-72.331571 75.124295H373.945843c-40.029055 0-72.331571-33.512697-72.331571-75.124295C301.521181 33.513628 333.916788 0.000931 373.945843 0.000931h857.831947c40.029055 0 72.331571 33.605788 72.331571 75.217386M1231.77779 551.377884H373.945843c-40.029055 0-72.331571-33.605788-72.331571-75.217386 0-41.518508 32.302516-75.124295 72.331571-75.124295h857.831947c40.029055-0.186182 72.331571 33.512697 72.331571 75.124295 0 41.425417-32.395607 75.217386-72.331571 75.217386zM1304.109361 877.102679c0 41.611599-32.395607 75.310477-72.331571 75.310477H373.945843c-40.029055 0-72.331571-33.698878-72.331571-75.310477 0-41.425417 32.302516-75.124295 72.331571-75.124295h857.831947c40.029055-0.093091 72.331571 33.698878 72.331571 75.124295" p-id="1174"></path></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -4,18 +4,18 @@ import { Icon } from '@chakra-ui/react';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
const map = { const map = {
model: dynamic(() => import('./icons/model.svg')), model: require('./icons/model.svg').default,
share: dynamic(() => import('./icons/share.svg')), share: require('./icons/share.svg').default,
home: dynamic(() => import('./icons/home.svg')) home: require('./icons/home.svg').default,
menu: require('./icons/menu.svg').default
}; };
const MyIcon = ({ export type IconName = keyof typeof map;
name,
w = 'auto', const MyIcon = ({ name, w = 'auto', h = 'auto', ...props }: { name: IconName } & IconProps) => {
h = 'auto', return map[name] ? (
...props <Icon as={map[name]} w={w} h={h} boxSizing={'content-box'} verticalAlign={'top'} {...props} />
}: { name: keyof typeof map } & IconProps) => { ) : null;
return map[name] ? <Icon as={map[name]} w={w} h={h} {...props} /> : null;
}; };
export default MyIcon; export default MyIcon;

View File

@@ -1,6 +1,5 @@
import React from 'react'; import React, { useEffect } from 'react';
import { Box } from '@chakra-ui/react'; import { Box, useColorMode } from '@chakra-ui/react';
import Link from 'next/link';
import Navbar from './navbar'; import Navbar from './navbar';
import NavbarPhone from './navbarPhone'; import NavbarPhone from './navbarPhone';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
@@ -44,9 +43,16 @@ const navbarList = [
const Layout = ({ children }: { children: JSX.Element }) => { const Layout = ({ children }: { children: JSX.Element }) => {
const { isPc } = useScreen(); const { isPc } = useScreen();
const router = useRouter(); const router = useRouter();
const { colorMode, setColorMode } = useColorMode();
const { Loading } = useLoading({ defaultLoading: true }); const { Loading } = useLoading({ defaultLoading: true });
const { loading } = useGlobalStore(); const { loading } = useGlobalStore();
useEffect(() => {
if (colorMode === 'dark' && router.pathname !== '/chat') {
setColorMode('light');
}
}, [colorMode, router.pathname, setColorMode]);
return ( return (
<> <>
{!unShowLayoutRoute[router.pathname] ? ( {!unShowLayoutRoute[router.pathname] ? (

View File

@@ -4,7 +4,7 @@
width: 4px; width: 4px;
height: 14px; height: 14px;
transform: translate(4px, 2px) scaleY(1.3); transform: translate(4px, 2px) scaleY(1.3);
background-color: rgba(0, 0, 0, 0.7); background-color: var(--chakra-colors-chakra-body-text);
animation: blink 0.6s infinite; animation: blink 0.6s infinite;
} }
.animation { .animation {
@@ -14,7 +14,7 @@
width: 4px; width: 4px;
height: 14px; height: 14px;
transform: translate(4px, 2px) scaleY(1.3); transform: translate(4px, 2px) scaleY(1.3);
background-color: rgba(0, 0, 0, 0.7); background-color: var(--chakra-colors-chakra-body-text);
animation: blink 0.6s infinite; animation: blink 0.6s infinite;
} }
} }
@@ -65,7 +65,6 @@
.markdown h4 .mini-icon-link, .markdown h4 .mini-icon-link,
.markdown h5 .mini-icon-link, .markdown h5 .mini-icon-link,
.markdown h6 .mini-icon-link { .markdown h6 .mini-icon-link {
color: #000000;
display: none; display: none;
} }
.markdown h1:hover a.anchor, .markdown h1:hover a.anchor,
@@ -103,11 +102,9 @@
font-size: inherit; font-size: inherit;
} }
.markdown h1 { .markdown h1 {
color: #000000;
font-size: 28px; font-size: 28px;
} }
.markdown h2 { .markdown h2 {
color: #000000;
font-size: 24px; font-size: 24px;
} }
.markdown h3 { .markdown h3 {
@@ -120,8 +117,7 @@
font-size: 14px; font-size: 14px;
} }
.markdown h6 { .markdown h6 {
color: #777777; font-size: 12px;
font-size: 14px;
} }
.markdown p, .markdown p,
.markdown blockquote, .markdown blockquote,
@@ -132,14 +128,6 @@
.markdown pre { .markdown pre {
margin: 10px 0; margin: 10px 0;
} }
.markdown hr {
background: url('https://a248.e.akamai.net/assets.github.com/assets/primer/markdown/dirty-shade-350cca8f57223ebd53603021b2e670f4f319f1b7.png')
repeat-x scroll 0 0 transparent;
border: 0 none;
color: #cccccc;
height: 4px;
padding: 0;
}
.markdown > h2:first-child, .markdown > h2:first-child,
.markdown > h1:first-child, .markdown > h1:first-child,
.markdown > h1:first-child + h2, .markdown > h1:first-child + h2,
@@ -321,7 +309,6 @@
} }
.markdown code, .markdown code,
.markdown tt { .markdown tt {
background-color: #f0f0f0;
border: 1px solid #eaeaea; border: 1px solid #eaeaea;
border-radius: 3px 3px 3px 3px; border-radius: 3px 3px 3px 3px;
margin: 0 2px; margin: 0 2px;
@@ -336,7 +323,6 @@
} }
.markdown .highlight pre, .markdown .highlight pre,
.markdown pre { .markdown pre {
background-color: #f0f0f0;
border: 1px solid #cccccc; border: 1px solid #cccccc;
border-radius: 3px 3px 3px 3px; border-radius: 3px 3px 3px 3px;
font-size: max(0.9em, 14px); font-size: max(0.9em, 14px);
@@ -369,11 +355,11 @@
border-radius: 0; border-radius: 0;
background-color: #222 !important; background-color: #222 !important;
overflow-x: auto; overflow-x: auto;
color: #fff;
} }
pre code { pre code {
background-color: #222 !important; background-color: #222 !important;
color: #fff;
width: 100%; width: 100%;
} }
@@ -385,6 +371,7 @@
table { table {
border-collapse: separate; border-collapse: separate;
border-spacing: 0px; border-spacing: 0px;
color: var(--chakra-colors-gray-700);
thead tr:first-child th { thead tr:first-child th {
border-bottom-width: 1px; border-bottom-width: 1px;

View File

@@ -1,7 +1,7 @@
import React, { memo, useMemo } from 'react'; import React, { memo, useMemo } from 'react';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { Box, Flex } from '@chakra-ui/react'; import { Box, Flex, useColorModeValue } from '@chakra-ui/react';
import { useCopyData } from '@/utils/tools'; import { useCopyData } from '@/utils/tools';
import Icon from '@/components/Iconfont'; import Icon from '@/components/Iconfont';
import remarkGfm from 'remark-gfm'; import remarkGfm from 'remark-gfm';
@@ -34,7 +34,7 @@ const Markdown = ({ source, isChatting = false }: { source: string; isChatting?:
<Flex <Flex
py={2} py={2}
px={5} px={5}
backgroundColor={'#323641'} backgroundColor={useColorModeValue('#323641', 'gray.600')}
color={'#fff'} color={'#fff'}
fontSize={'sm'} fontSize={'sm'}
userSelect={'none'} userSelect={'none'}

View File

@@ -90,6 +90,13 @@ export const theme = extendTheme({
fonts: { fonts: {
body: '-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"' body: '-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"'
}, },
breakpoints: {
sm: '900px',
md: '1200px',
lg: '1500px',
xl: '1800',
'2xl': '2100'
},
components: { components: {
Modal: ModalTheme, Modal: ModalTheme,
Button Button

View File

@@ -19,7 +19,8 @@ import {
ModalBody, ModalBody,
ModalCloseButton, ModalCloseButton,
useDisclosure, useDisclosure,
useColorMode useColorMode,
useColorModeValue
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useUserStore } from '@/store/user'; import { useUserStore } from '@/store/user';
import { useChatStore } from '@/store/chat'; import { useChatStore } from '@/store/chat';
@@ -100,6 +101,9 @@ const SlideBar = ({
size={'xs'} size={'xs'}
onClick={(e) => { onClick={(e) => {
removeChatHistoryByWindowId(item.chatId); removeChatHistoryByWindowId(item.chatId);
if (item.chatId === chatId) {
resetChat();
}
e.stopPropagation(); e.stopPropagation();
}} }}
/> />
@@ -132,7 +136,7 @@ const SlideBar = ({
w={'100%'} w={'100%'}
h={'100%'} h={'100%'}
py={3} py={3}
backgroundColor={'rgba(32,33,35,1)'} backgroundColor={useColorModeValue('blackAlpha.800', 'blackAlpha.500')}
color={'white'} color={'white'}
> >
{/* 新对话 */} {/* 新对话 */}
@@ -152,7 +156,7 @@ const SlideBar = ({
{/* 我的模型 & 历史记录 折叠框*/} {/* 我的模型 & 历史记录 折叠框*/}
<Box flex={'1 0 0'} px={3} h={0} overflowY={'auto'}> <Box flex={'1 0 0'} px={3} h={0} overflowY={'auto'}>
<Accordion defaultIndex={[0]} allowToggle> <Accordion defaultIndex={[0]} allowToggle allowMultiple>
{isSuccess && ( {isSuccess && (
<AccordionItem borderTop={0} borderBottom={0}> <AccordionItem borderTop={0} borderBottom={0}>
<AccordionButton borderRadius={'md'} pl={1}> <AccordionButton borderRadius={'md'} pl={1}>
@@ -210,7 +214,7 @@ const SlideBar = ({
</Accordion> </Accordion>
</Box> </Box>
<Divider my={4} /> <Divider my={4} colorScheme={useColorModeValue('gray', 'white')} />
<RenderButton onClick={() => router.push('/')}> <RenderButton onClick={() => router.push('/')}>
<> <>
@@ -243,7 +247,7 @@ const SlideBar = ({
{/* 分享提示modal */} {/* 分享提示modal */}
<Modal isOpen={isOpenShare} onClose={onCloseShare}> <Modal isOpen={isOpenShare} onClose={onCloseShare}>
<ModalOverlay /> <ModalOverlay />
<ModalContent> <ModalContent color={useColorModeValue('blackAlpha.700', 'white')}>
<ModalHeader></ModalHeader> <ModalHeader></ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody> <ModalBody>
@@ -252,7 +256,7 @@ const SlideBar = ({
<ModalFooter> <ModalFooter>
<Button colorScheme="gray" variant={'outline'} mr={3} onClick={onCloseShare}> <Button colorScheme="gray" variant={'outline'} mr={3} onClick={onCloseShare}>
Close
</Button> </Button>
{getToken() && ( {getToken() && (
<Button <Button

View File

@@ -33,6 +33,7 @@ import { streamFetch } from '@/api/fetch';
import SlideBar from './components/SlideBar'; import SlideBar from './components/SlideBar';
import Empty from './components/Empty'; import Empty from './components/Empty';
import { getToken } from '@/utils/user'; import { getToken } from '@/utils/user';
import MyIcon from '@/components/Icon';
const Markdown = dynamic(() => import('@/components/Markdown')); const Markdown = dynamic(() => import('@/components/Markdown'));
@@ -364,7 +365,7 @@ const Chat = ({ chatId }: { chatId: string }) => {
<Flex <Flex
h={'100%'} h={'100%'}
flexDirection={media('row', 'column')} flexDirection={media('row', 'column')}
backgroundColor={useColorModeValue('blackAlpha.50', 'rgba(52,53,65)')} backgroundColor={useColorModeValue('white', '')}
> >
{isPc ? ( {isPc ? (
<Box flex={'0 0 250px'} w={0} h={'100%'}> <Box flex={'0 0 250px'} w={0} h={'100%'}>
@@ -382,12 +383,18 @@ const Chat = ({ chatId }: { chatId: string }) => {
alignItems={'center'} alignItems={'center'}
h={'100%'} h={'100%'}
justifyContent={'space-between'} justifyContent={'space-between'}
backgroundColor={'white'} backgroundColor={useColorModeValue('white', 'gray.700')}
color={useColorModeValue('blackAlpha.700', 'white')}
position={'relative'} position={'relative'}
px={7} px={7}
> >
<Box onClick={onOpenSlider}> <Box onClick={onOpenSlider}>
<Icon name="icon-caidan" width={20} height={20}></Icon> <MyIcon
name={'menu'}
w={'20px'}
h={'20px'}
fill={useColorModeValue('blackAlpha.700', 'white')}
/>
</Box> </Box>
<Box>{chatData?.name}</Box> <Box>{chatData?.name}</Box>
</Flex> </Flex>
@@ -418,7 +425,9 @@ const Chat = ({ chatId }: { chatId: string }) => {
key={index} key={index}
py={media(9, 6)} py={media(9, 6)}
px={media(4, 2)} px={media(4, 2)}
backgroundColor={index % 2 === 0 ? useColorModeValue('white', 'rgba(68,70,84)') : ''} backgroundColor={
index % 2 !== 0 ? useColorModeValue('blackAlpha.50', 'gray.700') : ''
}
color={useColorModeValue('blackAlpha.700', 'white')} color={useColorModeValue('blackAlpha.700', 'white')}
borderBottom={'1px solid rgba(0,0,0,0.1)'} borderBottom={'1px solid rgba(0,0,0,0.1)'}
> >
@@ -447,13 +456,7 @@ const Chat = ({ chatId }: { chatId: string }) => {
{chatData.history.length === 0 && <Empty intro={chatData.intro} />} {chatData.history.length === 0 && <Empty intro={chatData.intro} />}
</Box> </Box>
{/* 发送区 */} {/* 发送区 */}
<Box <Box m={media('20px auto', '0 auto')} w={'100%'} maxW={media('min(750px, 100%)', 'auto')}>
m={media('20px auto', '0 auto')}
w={'100%'}
maxW={media('min(750px, 100%)', 'auto')}
boxShadow={`0 -14px 30px ${useColorModeValue('rgba(255,255,255,0.5)', 'blackAlpha.200')}`}
borderTop={media('none', '1px solid rgba(0,0,0,0.1)')}
>
{!!chatWindowError ? ( {!!chatWindowError ? (
<Box textAlign={'center'}> <Box textAlign={'center'}>
<Box color={'red'}>{chatWindowError.text}</Box> <Box color={'red'}>{chatWindowError.text}</Box>
@@ -471,9 +474,11 @@ const Chat = ({ chatId }: { chatId: string }) => {
<Box <Box
py={5} py={5}
position={'relative'} position={'relative'}
boxShadow={'base'} boxShadow={`0 0 15px rgba(0,0,0,0.1)`}
overflow={'hidden'} border={media('1px solid', '0')}
borderRadius={media('md', 'none')} borderColor={useColorModeValue('gray.200', 'gray.700')}
borderRadius={['none', 'md']}
backgroundColor={useColorModeValue('white', 'gray.700')}
> >
{/* 输入框 */} {/* 输入框 */}
<Textarea <Textarea

View File

@@ -6,9 +6,8 @@ import { useScreen } from '@/hooks/useScreen';
import type { ResLogin } from '@/api/response/user'; import type { ResLogin } from '@/api/response/user';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useUserStore } from '@/store/user'; import { useUserStore } from '@/store/user';
import LoginForm from './components/LoginForm';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
const LoginForm = dynamic(() => import('./components/LoginForm'));
const RegisterForm = dynamic(() => import('./components/RegisterForm')); const RegisterForm = dynamic(() => import('./components/RegisterForm'));
const ForgetPasswordForm = dynamic(() => import('./components/ForgetPasswordForm')); const ForgetPasswordForm = dynamic(() => import('./components/ForgetPasswordForm'));

View File

@@ -9,6 +9,7 @@ type Props = {
pushChatHistory: (e: HistoryItem) => void; pushChatHistory: (e: HistoryItem) => void;
updateChatHistory: (chatId: string, title: string) => void; updateChatHistory: (chatId: string, title: string) => void;
removeChatHistoryByWindowId: (chatId: string) => void; removeChatHistoryByWindowId: (chatId: string) => void;
clearHistory: () => void;
generateChatWindow: (modelId: string) => Promise<string>; generateChatWindow: (modelId: string) => Promise<string>;
}; };
export const useChatStore = create<Props>()( export const useChatStore = create<Props>()(
@@ -18,7 +19,7 @@ export const useChatStore = create<Props>()(
chatHistory: [], chatHistory: [],
pushChatHistory(item: HistoryItem) { pushChatHistory(item: HistoryItem) {
set((state) => { set((state) => {
state.chatHistory = [item, ...state.chatHistory]; state.chatHistory = [item, ...state.chatHistory].slice(0, 20);
}); });
}, },
updateChatHistory(chatId: string, title: string) { updateChatHistory(chatId: string, title: string) {
@@ -34,6 +35,11 @@ export const useChatStore = create<Props>()(
state.chatHistory = state.chatHistory.filter((item) => item.chatId !== chatId); state.chatHistory = state.chatHistory.filter((item) => item.chatId !== chatId);
}); });
}, },
clearHistory() {
set((state) => {
state.chatHistory = [];
});
},
generateChatWindow(modelId: string) { generateChatWindow(modelId: string) {
return getChatSiteId(modelId); return getChatSiteId(modelId);
} }