feat: add basic ui

This commit is contained in:
Yidadaa
2023-03-10 01:01:40 +08:00
parent 0decdb3e43
commit d49b2aa2c3
26 changed files with 4500 additions and 463 deletions

View File

@@ -0,0 +1,35 @@
.icon-button {
background-color: var(--white);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
padding: 10px;
box-shadow: var(--card-shadow);
cursor: pointer;
transition: all .3s ease;
overflow: hidden;
user-select: none;
}
.border {
border: var(--border-in-light);
}
.icon-button:hover {
filter: brightness(0.9) hue-rotate(0.01turn);
}
.icon-button-icon {
width: 16px;
height: 16px;
display: flex;
justify-content: center;
align-items: center;
}
.icon-button-text {
margin-left: 5px;
font-size: 12px;
}

25
app/components/button.tsx Normal file
View File

@@ -0,0 +1,25 @@
import * as React from "react";
import styles from "./button.module.css";
export function IconButton(props: {
onClick?: () => void;
icon: JSX.Element;
text?: string;
bordered?: boolean;
className?: string;
}) {
return (
<div
className={
styles["icon-button"] +
` ${props.bordered && styles.border} ${props.className ?? ""}`
}
>
<div className={styles["icon-button-icon"]}>{props.icon}</div>
{props.text && (
<div className={styles["icon-button-text"]}>{props.text}</div>
)}
</div>
);
}

View File

@@ -0,0 +1,222 @@
.container {
max-width: 1080px;
max-height: 780px;
min-width: 600px;
width: 90vw;
height: 90vh;
background-color: var(--white);
border: var(--border-in-light);
border-radius: 20px;
box-shadow: var(--shadow);
display: flex;
overflow: hidden;
}
.sidebar {
max-width: 300px;
padding: 20px;
background-color: var(--second);
display: flex;
flex-direction: column;
box-shadow:inset -2px 0px 2px 0px rgb(0, 0, 0, 0.05);
}
.sidebar-header {
position: relative;
padding-top: 20px;
padding-bottom: 20px;
}
.sidebar-logo {
position: absolute;
right: 0;
bottom: 18px;
}
.sidebar-title {
font-size: 20px;
font-weight: bold;
}
.sidebar-sub-title {
font-size: 12px;
font-weight: 400px;
}
.sidebar-body {
flex: 1;
overflow: auto;
}
.chat-list {
width: 260px;
}
.chat-item {
padding: 10px 14px;
background-color: var(--white);
border-radius: 10px;
margin-bottom: 10px;
box-shadow: var(--card-shadow);
transition: all .3s ease;
cursor: pointer;
user-select: none;
}
.chat-item:hover {
background-color: var(--hover-color);
}
.chat-item-selected {
border: 2px solid var(--primary);
}
.chat-item-title {
font-size: 14px;
font-weight: bolder;
}
.chat-item-info {
display: flex;
justify-content: space-between;
color: rgb(166, 166, 166);
font-size: 12px;
margin-top: 8px;
}
.chat-item-count {}
.chat-item-date {}
.sidebar-tail {
display: flex;
justify-content: space-between;
padding-top: 20px;
}
.sidebar-actions {
display: inline-flex;
}
.sidebar-action:last-child {
margin-left: 15px;
}
.chat {
display: flex;
flex-direction: column;
width: 100%;
position: relative;
}
.chat-header {
padding: 14px 20px;
border-bottom: rgba(0, 0, 0, 0.1) 1px solid;
display: flex;
justify-content: space-between;
align-items: center;
}
.chat-header-title {
font-size: 20px;
font-weight: bolder;
}
.chat-header-sub-title {
font-size: 14px;
margin-top: 5px;
}
.chat-actions {
display: inline-flex;
}
.chat-action-button {
margin-left: 10px;
}
.chat-body {
flex: 1;
overflow: auto;
padding: 20px;
margin-bottom: 100px;
}
.chat-message {
display: flex;
flex-direction: row;
}
.chat-message-reverse {
display: flex;
flex-direction: row-reverse;
}
.chat-message-container {
width: 60%;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.chat-message-reverse > .chat-message-container {
align-items: flex-end;
}
.chat-message-avtar {}
.chat-message-item {
border-radius: 10px;
background-color: rgba(0, 0, 0, 0.05);
padding: 10px;
font-size: 14px;
margin-top: 5px;
user-select: text;
}
.chat-message-actions{
display: flex;
flex-direction: row-reverse;
width: 100%;
padding: 5px 10px;
box-sizing: border-box;
}
.chat-message-action-date{
font-size: 12px;
color: #aaa;
}
.chat-message-action-button{}
.chat-input-panel {
position: absolute;
bottom: 20px;
display: flex;
width: 100%;
padding: 20px;
box-sizing: border-box;
}
.chat-input-panel-inner {
display: flex;
flex: 1;
}
.chat-input-panel-multi {}
.chat-input {
height: 100%;
width: 100%;
border-radius: 10px;
border: var(--border-in-light);
box-shadow: var(--card-shadow);
font-family: inherit;
padding: 10px 14px;
resize: none;
outline: none;
color: #333;
}
.chat-input:focus {
border: 1px solid var(--primary);
}
.chat-input-send{
background-color: var(--primary);
color: white;
position: absolute;
right: 30px;
bottom: 10px;
}

177
app/components/home.tsx Normal file
View File

@@ -0,0 +1,177 @@
"use client";
import { IconButton } from "./button";
import styles from "./home.module.css";
import SettingsIcon from "../icons/settings.svg";
import GithubIcon from "../icons/github.svg";
import ChatGptIcon from "../icons/chatgpt.svg";
import SendWhiteIcon from "../icons/send-white.svg";
import BrainIcon from "../icons/brain.svg";
import ExportIcon from "../icons/export.svg";
import BotIcon from "../icons/bot.svg";
import UserIcon from "../icons/user.svg";
import AddIcon from "../icons/add.svg";
export function ChatItem(props: {
onClick?: () => void;
title: string;
count: number;
time: string;
selected: boolean;
}) {
return (
<div
className={`${styles["chat-item"]} ${
props.selected && styles["chat-item-selected"]
}`}
>
<div className={styles["chat-item-title"]}>{props.title}</div>
<div className={styles["chat-item-info"]}>
<div className={styles["chat-item-count"]}>{props.count} </div>
<div className={styles["chat-item-date"]}>{props.time}</div>
</div>
</div>
);
}
export function ChatList() {
const listData = new Array(5).fill({
title: "这是一个标题",
count: 10,
time: new Date().toLocaleString(),
});
const selectedIndex = 0;
return (
<div className={styles["chat-list"]}>
{listData.map((item, i) => (
<ChatItem {...item} key={i} selected={i === selectedIndex} />
))}
</div>
);
}
export function Chat() {
const messages = [
{
role: "user",
content: "这是一条消息",
date: new Date().toLocaleString(),
},
{
role: "bot",
content: "这是一条回复".repeat(10),
date: new Date().toLocaleString(),
},
];
const title = "这是一个标题";
const count = 10;
return (
<div className={styles.chat}>
<div className={styles["chat-header"]}>
<div>
<div className={styles["chat-header-title"]}>{title}</div>
<div className={styles["chat-header-sub-title"]}>
ChatGPT {count}
</div>
</div>
<div className={styles["chat-actions"]}>
<div className={styles["chat-action-button"]}>
<IconButton icon={<BrainIcon />} bordered />
</div>
<div className={styles["chat-action-button"]}>
<IconButton icon={<ExportIcon />} bordered />
</div>
</div>
</div>
<div className={styles["chat-body"]}>
{messages.map((message, i) => {
const isUser = message.role === "user";
return (
<div
key={i}
className={
isUser ? styles["chat-message-reverse"] : styles["chat-message"]
}
>
<div className={styles["chat-message-container"]}>
<div className={styles["chat-message-avtar"]}>
{message.role === "user" ? <UserIcon /> : <BotIcon />}
</div>
<div className={styles["chat-message-item"]}>
{message.content}
</div>
{!isUser && (
<div className={styles["chat-message-actions"]}>
<div className={styles["chat-message-action-date"]}>
{message.date}
</div>
</div>
)}
</div>
</div>
);
})}
</div>
<div className={styles["chat-input-panel"]}>
<div className={styles["chat-input-panel-inner"]}>
<textarea
className={styles["chat-input"]}
placeholder="输入消息"
rows={3}
/>
<IconButton
icon={<SendWhiteIcon />}
text={"发送"}
className={styles["chat-input-send"]}
/>
</div>
</div>
</div>
);
}
export function Home() {
return (
<div className={styles.container}>
<div className={styles.sidebar}>
<div className={styles["sidebar-header"]}>
<div className={styles["sidebar-title"]}>ChatGPT Next</div>
<div className={styles["sidebar-sub-title"]}>
Build your own AI assistant.
</div>
<div className={styles["sidebar-logo"]}>
<ChatGptIcon />
</div>
</div>
<div className={styles["sidebar-body"]}>
<ChatList />
</div>
<div className={styles["sidebar-tail"]}>
<div className={styles["sidebar-actions"]}>
<div className={styles["sidebar-action"]}>
<IconButton icon={<SettingsIcon />} />
</div>
<div className={styles["sidebar-action"]}>
<IconButton icon={<GithubIcon />} />
</div>
</div>
<div>
<IconButton icon={<AddIcon />} text={"新的聊天"} />
</div>
</div>
</div>
<Chat key="chat" />
</div>
);
}