feat: add artifacts preview
This commit is contained in:
parent
2ebcec9f59
commit
c68ad4febb
File diff suppressed because one or more lines are too long
@ -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",
|
||||
|
||||
@ -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": "助手",
|
||||
|
||||
@ -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": "助手",
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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}
|
||||
|
||||
51
src/renderer/src/pages/home/Markdown/Artifacts.tsx
Normal file
51
src/renderer/src/pages/home/Markdown/Artifacts.tsx
Normal 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
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user