feat: add artifacts preview

This commit is contained in:
kangfenmao 2024-10-14 14:17:40 +08:00
parent 0026293e69
commit 21a13d54c8
9 changed files with 111 additions and 22 deletions

File diff suppressed because one or more lines are too long

View File

@ -107,7 +107,9 @@
"add.assistant.title": "Add Assistant", "add.assistant.title": "Add Assistant",
"message.new.context": "New Context", "message.new.context": "New Context",
"message.new.branch": "New Branch", "message.new.branch": "New Branch",
"assistant.search.placeholder": "Search" "assistant.search.placeholder": "Search",
"artifacts.button.preview": "Preview",
"artifacts.button.download": "Download"
}, },
"assistants": { "assistants": {
"title": "Assistants", "title": "Assistants",

View File

@ -107,7 +107,9 @@
"add.assistant.title": "添加助手", "add.assistant.title": "添加助手",
"message.new.context": "清除上下文", "message.new.context": "清除上下文",
"message.new.branch": "新分支", "message.new.branch": "新分支",
"assistant.search.placeholder": "搜索" "assistant.search.placeholder": "搜索",
"artifacts.button.preview": "预览",
"artifacts.button.download": "下载"
}, },
"assistants": { "assistants": {
"title": "助手", "title": "助手",

View File

@ -107,7 +107,9 @@
"add.assistant.title": "添加助手", "add.assistant.title": "添加助手",
"message.new.context": "新上下文", "message.new.context": "新上下文",
"message.new.branch": "新分支", "message.new.branch": "新分支",
"assistant.search.placeholder": "搜尋" "assistant.search.placeholder": "搜尋",
"artifacts.button.preview": "預覽",
"artifacts.button.download": "下載"
}, },
"assistants": { "assistants": {
"title": "助手", "title": "助手",

View File

@ -61,22 +61,24 @@ const AppsPage: FC = () => {
{agents.length > 0 && <ManageIcon onClick={ManageAgentsPopup.show} />} {agents.length > 0 && <ManageIcon onClick={ManageAgentsPopup.show} />}
</HStack> </HStack>
<UserAgents onAdd={onAddAgentConfirm} /> <UserAgents onAdd={onAddAgentConfirm} />
{Object.keys(agentGroups).map((group) => ( {Object.keys(agentGroups)
<div key={group}> .reverse()
<Title level={4} key={group} style={{ marginBottom: 16 }}> .map((group) => (
{group} <div key={group}>
</Title> <Title level={4} key={group} style={{ marginBottom: 16 }}>
<Row gutter={16}> {group}
{agentGroups[group].map((agent, index) => { </Title>
return ( <Row gutter={16}>
<Col span={8} key={group + index}> {agentGroups[group].map((agent, index) => {
<AgentCard onClick={() => onAddAgentConfirm(agent)} agent={agent as any} /> return (
</Col> <Col span={8} key={group + index}>
) <AgentCard onClick={() => onAddAgentConfirm(agent)} agent={agent as any} />
})} </Col>
</Row> )
</div> })}
))} </Row>
</div>
))}
<div style={{ minHeight: 20 }} /> <div style={{ minHeight: 20 }} />
</AssistantsContainer> </AssistantsContainer>
</ContentContainer> </ContentContainer>

View File

@ -8,7 +8,7 @@ import styled from 'styled-components'
import Inputbar from './Inputbar/Inputbar' import Inputbar from './Inputbar/Inputbar'
import Messages from './Messages/Messages' import Messages from './Messages/Messages'
import RightSidebar from './Tabs' import Tabs from './Tabs'
interface Props { interface Props {
assistant: Assistant assistant: Assistant
@ -29,7 +29,7 @@ const Chat: FC<Props> = (props) => {
<Inputbar assistant={assistant} setActiveTopic={props.setActiveTopic} /> <Inputbar assistant={assistant} setActiveTopic={props.setActiveTopic} />
</Main> </Main>
{topicPosition === 'right' && showTopics && ( {topicPosition === 'right' && showTopics && (
<RightSidebar <Tabs
activeAssistant={assistant} activeAssistant={assistant}
activeTopic={props.activeTopic} activeTopic={props.activeTopic}
setActiveAssistant={props.setActiveAssistant} setActiveAssistant={props.setActiveAssistant}

View File

@ -0,0 +1,51 @@
import MinApp from '@renderer/components/MinApp'
import { AppLogo } from '@renderer/config/env'
import { extractTitle } from '@renderer/utils/formula'
import { Button } from 'antd'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
interface Props {
html: string
}
const Artifacts: FC<Props> = ({ html }) => {
const { t } = useTranslation()
const title = extractTitle(html) || 'Artifacts' + ' ' + t('chat.artifacts.button.preview')
const onPreview = async () => {
const path = await window.api.file.create('artifacts-preview.html')
await window.api.file.write(path, html)
MinApp.start({
name: title,
logo: AppLogo,
url: `file://${path}`
})
}
const onDownload = () => {
window.api.file.save(`${title}.html`, html)
}
return (
<Container>
<Button type="primary" size="middle" onClick={onPreview}>
{t('chat.artifacts.button.preview')}
</Button>
<Button size="middle" onClick={onDownload}>
{t('chat.artifacts.button.download')}
</Button>
</Container>
)
}
const Container = styled.div`
margin: 10px;
display: flex;
flex-direction: row;
gap: 8px;
`
export default Artifacts

View File

@ -9,6 +9,7 @@ import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
import { atomDark, oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism' import { atomDark, oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism'
import styled from 'styled-components' import styled from 'styled-components'
import Artifacts from './Artifacts'
import Mermaid from './Mermaid' import Mermaid from './Mermaid'
interface CodeBlockProps { interface CodeBlockProps {
@ -21,8 +22,9 @@ const CodeBlock: React.FC<CodeBlockProps> = ({ children, className }) => {
const match = /language-(\w+)/.exec(className || '') const match = /language-(\w+)/.exec(className || '')
const showFooterCopyButton = children && children.length > 500 const showFooterCopyButton = children && children.length > 500
const { theme } = useTheme() const { theme } = useTheme()
const language = match?.[1]
if (match && match[1] === 'mermaid') { if (language === 'mermaid') {
initMermaid(theme) initMermaid(theme)
return <Mermaid chart={children} /> return <Mermaid chart={children} />
} }
@ -50,6 +52,7 @@ const CodeBlock: React.FC<CodeBlockProps> = ({ children, className }) => {
<CopyButton text={children} style={{ marginTop: -40, marginRight: 10 }} /> <CopyButton text={children} style={{ marginTop: -40, marginRight: 10 }} />
</CodeFooter> </CodeFooter>
)} )}
{language === 'html' && children?.includes('</html>') && <Artifacts html={children} />}
</div> </div>
) : ( ) : (
<code className={className}>{children}</code> <code className={className}>{children}</code>

View File

@ -32,3 +32,14 @@ $$
return match return match
}) })
} }
export function extractTitle(html: string): string | null {
const titleRegex = /<title>(.*?)<\/title>/i
const match = html.match(titleRegex)
if (match && match[1]) {
return match[1].trim()
}
return null
}