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

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

@@ -1,6 +1,6 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
if command -v npx >/dev/null 2>&1; then
npx lint-staged
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
if command -v npx >/dev/null 2>&1; then
npx lint-staged
fi

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';
const map = {
model: dynamic(() => import('./icons/model.svg')),
share: dynamic(() => import('./icons/share.svg')),
home: dynamic(() => import('./icons/home.svg'))
model: require('./icons/model.svg').default,
share: require('./icons/share.svg').default,
home: require('./icons/home.svg').default,
menu: require('./icons/menu.svg').default
};
const MyIcon = ({
name,
w = 'auto',
h = 'auto',
...props
}: { name: keyof typeof map } & IconProps) => {
return map[name] ? <Icon as={map[name]} w={w} h={h} {...props} /> : null;
export type IconName = keyof typeof map;
const MyIcon = ({ name, w = 'auto', h = 'auto', ...props }: { name: IconName } & IconProps) => {
return map[name] ? (
<Icon as={map[name]} w={w} h={h} boxSizing={'content-box'} verticalAlign={'top'} {...props} />
) : null;
};
export default MyIcon;

View File

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

View File

@@ -4,7 +4,7 @@
width: 4px;
height: 14px;
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 {
@@ -14,7 +14,7 @@
width: 4px;
height: 14px;
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;
}
}
@@ -65,7 +65,6 @@
.markdown h4 .mini-icon-link,
.markdown h5 .mini-icon-link,
.markdown h6 .mini-icon-link {
color: #000000;
display: none;
}
.markdown h1:hover a.anchor,
@@ -103,11 +102,9 @@
font-size: inherit;
}
.markdown h1 {
color: #000000;
font-size: 28px;
}
.markdown h2 {
color: #000000;
font-size: 24px;
}
.markdown h3 {
@@ -120,8 +117,7 @@
font-size: 14px;
}
.markdown h6 {
color: #777777;
font-size: 14px;
font-size: 12px;
}
.markdown p,
.markdown blockquote,
@@ -132,14 +128,6 @@
.markdown pre {
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 > h1:first-child,
.markdown > h1:first-child + h2,
@@ -321,7 +309,6 @@
}
.markdown code,
.markdown tt {
background-color: #f0f0f0;
border: 1px solid #eaeaea;
border-radius: 3px 3px 3px 3px;
margin: 0 2px;
@@ -336,7 +323,6 @@
}
.markdown .highlight pre,
.markdown pre {
background-color: #f0f0f0;
border: 1px solid #cccccc;
border-radius: 3px 3px 3px 3px;
font-size: max(0.9em, 14px);
@@ -369,11 +355,11 @@
border-radius: 0;
background-color: #222 !important;
overflow-x: auto;
color: #fff;
}
pre code {
background-color: #222 !important;
color: #fff;
width: 100%;
}
@@ -385,6 +371,7 @@
table {
border-collapse: separate;
border-spacing: 0px;
color: var(--chakra-colors-gray-700);
thead tr:first-child th {
border-bottom-width: 1px;

View File

@@ -1,7 +1,7 @@
import React, { memo, useMemo } from 'react';
import ReactMarkdown from 'react-markdown';
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 Icon from '@/components/Iconfont';
import remarkGfm from 'remark-gfm';
@@ -34,7 +34,7 @@ const Markdown = ({ source, isChatting = false }: { source: string; isChatting?:
<Flex
py={2}
px={5}
backgroundColor={'#323641'}
backgroundColor={useColorModeValue('#323641', 'gray.600')}
color={'#fff'}
fontSize={'sm'}
userSelect={'none'}

View File

@@ -90,6 +90,13 @@ export const theme = extendTheme({
fonts: {
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: {
Modal: ModalTheme,
Button

View File

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

View File

@@ -33,6 +33,7 @@ import { streamFetch } from '@/api/fetch';
import SlideBar from './components/SlideBar';
import Empty from './components/Empty';
import { getToken } from '@/utils/user';
import MyIcon from '@/components/Icon';
const Markdown = dynamic(() => import('@/components/Markdown'));
@@ -364,7 +365,7 @@ const Chat = ({ chatId }: { chatId: string }) => {
<Flex
h={'100%'}
flexDirection={media('row', 'column')}
backgroundColor={useColorModeValue('blackAlpha.50', 'rgba(52,53,65)')}
backgroundColor={useColorModeValue('white', '')}
>
{isPc ? (
<Box flex={'0 0 250px'} w={0} h={'100%'}>
@@ -382,12 +383,18 @@ const Chat = ({ chatId }: { chatId: string }) => {
alignItems={'center'}
h={'100%'}
justifyContent={'space-between'}
backgroundColor={'white'}
backgroundColor={useColorModeValue('white', 'gray.700')}
color={useColorModeValue('blackAlpha.700', 'white')}
position={'relative'}
px={7}
>
<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>{chatData?.name}</Box>
</Flex>
@@ -418,7 +425,9 @@ const Chat = ({ chatId }: { chatId: string }) => {
key={index}
py={media(9, 6)}
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')}
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} />}
</Box>
{/* 发送区 */}
<Box
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)')}
>
<Box m={media('20px auto', '0 auto')} w={'100%'} maxW={media('min(750px, 100%)', 'auto')}>
{!!chatWindowError ? (
<Box textAlign={'center'}>
<Box color={'red'}>{chatWindowError.text}</Box>
@@ -471,9 +474,11 @@ const Chat = ({ chatId }: { chatId: string }) => {
<Box
py={5}
position={'relative'}
boxShadow={'base'}
overflow={'hidden'}
borderRadius={media('md', 'none')}
boxShadow={`0 0 15px rgba(0,0,0,0.1)`}
border={media('1px solid', '0')}
borderColor={useColorModeValue('gray.200', 'gray.700')}
borderRadius={['none', 'md']}
backgroundColor={useColorModeValue('white', 'gray.700')}
>
{/* 输入框 */}
<Textarea

View File

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

View File

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