feat: add export to .md button

This commit is contained in:
Yifei Zhang
2023-03-15 17:24:03 +00:00
parent 64e331a3e3
commit bab470d000
11 changed files with 181 additions and 18 deletions

View File

@@ -328,4 +328,8 @@
position: absolute;
right: 30px;
bottom: 10px;
}
.export-content {
white-space: break-spaces;
}

View File

@@ -23,9 +23,13 @@ import DeleteIcon from "../icons/delete.svg";
import LoadingIcon from "../icons/three-dots.svg";
import MenuIcon from "../icons/menu.svg";
import CloseIcon from "../icons/close.svg";
import CopyIcon from "../icons/copy.svg";
import DownloadIcon from "../icons/download.svg";
import { Message, SubmitKey, useChatStore, Theme } from "../store";
import { Settings } from "./settings";
import { showModal } from "./ui-lib";
import { copyToClipboard, downloadAs } from "../utils";
export function Markdown(props: { content: string }) {
return (
@@ -208,7 +212,10 @@ export function Chat(props: { showSideBar?: () => void }) {
<IconButton
icon={<ExportIcon />}
bordered
title="导出聊天记录为 Markdown开发中"
title="导出聊天记录"
onClick={() => {
exportMessages(session.messages, session.topic)
}}
/>
</div>
</div>
@@ -294,6 +301,22 @@ function useSwitchTheme() {
}, [config.theme]);
}
function exportMessages(messages: Message[], topic: string) {
const mdText = `# ${topic}\n\n` + messages.map(m => {
return m.role === 'user' ? `## ${m.content}` : m.content.trim()
}).join('\n\n')
const filename = `${topic}.md`
showModal({
title: "导出聊天记录为 Markdown", children: <div className="markdown-body">
<pre className={styles['export-content']}>{mdText}</pre>
</div>, actions: [
<IconButton icon={<CopyIcon />} bordered text="全部复制" onClick={() => copyToClipboard(mdText)} />,
<IconButton icon={<DownloadIcon />} bordered text="下载文件" onClick={() => downloadAs(mdText, filename)} />
]
})
}
export function Home() {
const [createNewSession] = useChatStore((state) => [state.newSession]);
const loading = !useChatStore?.persist?.hasHydrated();

View File

@@ -29,6 +29,7 @@
transform: translateY(10px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
@@ -56,3 +57,68 @@
.list .list-item:last-child {
border: 0;
}
.modal-container {
box-shadow: var(--card-shadow);
background-color: var(--white);
border-radius: 12px;
width: 50vw;
--modal-padding: 20px;
.modal-header {
padding: var(--modal-padding);
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: var(--border-in-light);
.modal-title {
font-weight: bolder;
font-size: 16px;
}
.modal-close-btn {
cursor: pointer;
&:hover {
filter: brightness(1.2);
}
}
}
.modal-content {
height: 40vh;
padding: var(--modal-padding);
overflow: auto;
}
.modal-footer {
padding: var(--modal-padding);
display: flex;
justify-content: flex-end;
.modal-actions {
display: flex;
align-items: center;
.modal-action {
&:not(:last-child) {
margin-right: 20px;
}
}
}
}
}
@media only screen and (max-width: 600px) {
.modal-container {
width: 90vw;
.modal-content {
height: 50vh;
}
}
}

View File

@@ -1,5 +1,8 @@
import styles from "./ui-lib.module.scss";
import LoadingIcon from "../icons/three-dots.svg";
import CloseIcon from "../icons/close.svg";
import { createRoot } from 'react-dom/client'
import { IconButton } from "./button";
export function Popover(props: {
children: JSX.Element;
@@ -46,4 +49,43 @@ export function Loading() {
alignItems: "center",
justifyContent: "center"
}}><LoadingIcon /></div>
}
interface ModalProps {
title: string,
children?: JSX.Element,
actions?: JSX.Element[],
onClose?: () => void,
}
export function Modal(props: ModalProps) {
return <div className={styles['modal-container']}>
<div className={styles['modal-header']}>
<div className={styles['modal-title']}>{props.title}</div>
<div className={styles['modal-close-btn']} onClick={props.onClose}>
<CloseIcon />
</div>
</div>
<div className={styles['modal-content']}>{props.children}</div>
<div className={styles['modal-footer']}>
<div className={styles['modal-actions']}>
{props.actions?.map(action => <div className={styles['modal-action']}>{action}</div>)}
</div>
</div>
</div>
}
export function showModal(props: ModalProps) {
const div = document.createElement('div')
div.className = "modal-mask";
document.body.appendChild(div)
const root = createRoot(div)
root.render(<Modal {...props} onClose={() => {
props.onClose?.();
root.unmount();
div.remove();
}}></Modal>)
}