feat: add artifacts preview

This commit is contained in:
kangfenmao 2024-10-14 14:17:40 +08:00
parent 2ebcec9f59
commit c68ad4febb
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",
"message.new.context": "New Context",
"message.new.branch": "New Branch",
"assistant.search.placeholder": "Search"
"assistant.search.placeholder": "Search",
"artifacts.button.preview": "Preview",
"artifacts.button.download": "Download"
},
"assistants": {
"title": "Assistants",

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ import styled from 'styled-components'
import Inputbar from './Inputbar/Inputbar'
import Messages from './Messages/Messages'
import RightSidebar from './Tabs'
import Tabs from './Tabs'
interface Props {
assistant: Assistant
@ -29,7 +29,7 @@ const Chat: FC<Props> = (props) => {
<Inputbar assistant={assistant} setActiveTopic={props.setActiveTopic} />
</Main>
{topicPosition === 'right' && showTopics && (
<RightSidebar
<Tabs
activeAssistant={assistant}
activeTopic={props.activeTopic}
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 styled from 'styled-components'
import Artifacts from './Artifacts'
import Mermaid from './Mermaid'
interface CodeBlockProps {
@ -21,8 +22,9 @@ const CodeBlock: React.FC<CodeBlockProps> = ({ children, className }) => {
const match = /language-(\w+)/.exec(className || '')
const showFooterCopyButton = children && children.length > 500
const { theme } = useTheme()
const language = match?.[1]
if (match && match[1] === 'mermaid') {
if (language === 'mermaid') {
initMermaid(theme)
return <Mermaid chart={children} />
}
@ -50,6 +52,7 @@ const CodeBlock: React.FC<CodeBlockProps> = ({ children, className }) => {
<CopyButton text={children} style={{ marginTop: -40, marginRight: 10 }} />
</CodeFooter>
)}
{language === 'html' && children?.includes('</html>') && <Artifacts html={children} />}
</div>
) : (
<code className={className}>{children}</code>

View File

@ -32,3 +32,14 @@ $$
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
}