feat: add message and modal api to window object
This commit is contained in:
parent
a2ec18d9be
commit
f8d9b437c2
@ -1,5 +1,6 @@
|
|||||||
|
import { message, Modal } from 'antd'
|
||||||
import { findIndex, pullAt } from 'lodash'
|
import { findIndex, pullAt } from 'lodash'
|
||||||
import React, { useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
|
|
||||||
let id = 0
|
let id = 0
|
||||||
let onPop = () => {}
|
let onPop = () => {}
|
||||||
@ -17,6 +18,8 @@ type ElementItem = {
|
|||||||
|
|
||||||
const TopViewContainer: React.FC<Props> = ({ children }) => {
|
const TopViewContainer: React.FC<Props> = ({ children }) => {
|
||||||
const [elements, setElements] = useState<ElementItem[]>([])
|
const [elements, setElements] = useState<ElementItem[]>([])
|
||||||
|
const [messageApi, messageContextHolder] = message.useMessage()
|
||||||
|
const [modal, modalContextHolder] = Modal.useModal()
|
||||||
|
|
||||||
onPop = () => {
|
onPop = () => {
|
||||||
const views = [...elements]
|
const views = [...elements]
|
||||||
@ -34,9 +37,16 @@ const TopViewContainer: React.FC<Props> = ({ children }) => {
|
|||||||
setElements(views)
|
setElements(views)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.message = messageApi
|
||||||
|
window.modal = modal
|
||||||
|
}, [messageApi, modal])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{children}
|
{children}
|
||||||
|
{messageContextHolder}
|
||||||
|
{modalContextHolder}
|
||||||
{elements.length > 0 && (
|
{elements.length > 0 && (
|
||||||
<div style={{ display: 'flex', flex: 1, position: 'absolute', width: '100%', height: '100%' }}>
|
<div style={{ display: 'flex', flex: 1, position: 'absolute', width: '100%', height: '100%' }}>
|
||||||
<div style={{ position: 'absolute', width: '100%', height: '100%' }} onClick={onPop} />
|
<div style={{ position: 'absolute', width: '100%', height: '100%' }} onClick={onPop} />
|
||||||
|
|||||||
10
src/renderer/src/env.d.ts
vendored
10
src/renderer/src/env.d.ts
vendored
@ -1 +1,11 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
import { MessageInstance } from 'antd/es/message/interface'
|
||||||
|
import { HookAPI } from 'antd/es/modal/useModal'
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
message: MessageInstance
|
||||||
|
modal: HookAPI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
|
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
|
||||||
import { SYSTEM_ASSISTANTS } from '@renderer/config/assistant'
|
import { SYSTEM_ASSISTANTS } from '@renderer/config/assistant'
|
||||||
import { Button, Col, message, Row, Tooltip, Typography } from 'antd'
|
import { Button, Col, Row, Tooltip, Typography } from 'antd'
|
||||||
import { find, groupBy } from 'lodash'
|
import { find, groupBy } from 'lodash'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
@ -14,26 +14,21 @@ const { Title } = Typography
|
|||||||
const AppsPage: FC = () => {
|
const AppsPage: FC = () => {
|
||||||
const { assistants, addAssistant } = useAssistants()
|
const { assistants, addAssistant } = useAssistants()
|
||||||
const assistantGroups = groupBy(SYSTEM_ASSISTANTS, 'group')
|
const assistantGroups = groupBy(SYSTEM_ASSISTANTS, 'group')
|
||||||
const [messageApi, contextHolder] = message.useMessage()
|
|
||||||
|
|
||||||
const onAddAssistant = (assistant: SystemAssistant) => {
|
const onAddAssistant = (assistant: SystemAssistant) => {
|
||||||
addAssistant({
|
addAssistant({
|
||||||
...getDefaultAssistant(),
|
...getDefaultAssistant(),
|
||||||
...assistant
|
...assistant
|
||||||
})
|
})
|
||||||
messageApi.destroy()
|
window.message.success({
|
||||||
messageApi.open({
|
|
||||||
type: 'success',
|
|
||||||
content: 'Assistant added successfully',
|
content: 'Assistant added successfully',
|
||||||
style: {
|
key: 'assistant-added',
|
||||||
marginTop: '5vh'
|
style: { marginTop: '5vh' }
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
{contextHolder}
|
|
||||||
<Navbar>
|
<Navbar>
|
||||||
<NavbarCenter>Assistant Market</NavbarCenter>
|
<NavbarCenter>Assistant Market</NavbarCenter>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
|
|||||||
@ -5,14 +5,44 @@ import { FC } from 'react'
|
|||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import Logo from '@renderer/assets/images/logo.png'
|
import Logo from '@renderer/assets/images/logo.png'
|
||||||
import useAvatar from '@renderer/hooks/useAvatar'
|
import useAvatar from '@renderer/hooks/useAvatar'
|
||||||
|
import { CopyOutlined, DeleteOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
const MessageItem: FC<{ message: Message }> = ({ message }) => {
|
interface Props {
|
||||||
|
message: Message
|
||||||
|
showMenu?: boolean
|
||||||
|
onDeleteMessage?: (message: Message) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const MessageItem: FC<Props> = ({ message, showMenu, onDeleteMessage }) => {
|
||||||
const avatar = useAvatar()
|
const avatar = useAvatar()
|
||||||
|
|
||||||
|
const onCopy = () => {
|
||||||
|
navigator.clipboard.writeText(message.content)
|
||||||
|
window.message.success({ content: 'Copied!', key: 'copy-message' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDelete = async () => {
|
||||||
|
const confirmed = await window.modal.confirm({
|
||||||
|
title: 'Delete Message',
|
||||||
|
content: 'Are you sure you want to delete this message?',
|
||||||
|
okText: 'Delete',
|
||||||
|
okType: 'danger'
|
||||||
|
})
|
||||||
|
confirmed && onDeleteMessage?.(message)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageContainer key={message.id}>
|
<MessageContainer key={message.id}>
|
||||||
<AvatarWrapper>{message.role === 'assistant' ? <Avatar src={Logo} /> : <Avatar src={avatar} />}</AvatarWrapper>
|
<AvatarWrapper>{message.role === 'assistant' ? <Avatar src={Logo} /> : <Avatar src={avatar} />}</AvatarWrapper>
|
||||||
<div className="markdown" dangerouslySetInnerHTML={{ __html: marked(message.content) }} />
|
<MessageContent>
|
||||||
|
<div className="markdown" dangerouslySetInnerHTML={{ __html: marked(message.content) }} />
|
||||||
|
{showMenu && (
|
||||||
|
<MenusBar className="menubar">
|
||||||
|
<CopyOutlined onClick={onCopy} />
|
||||||
|
<DeleteOutlined onClick={onDelete} />
|
||||||
|
</MenusBar>
|
||||||
|
)}
|
||||||
|
</MessageContent>
|
||||||
</MessageContainer>
|
</MessageContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -28,4 +58,34 @@ const AvatarWrapper = styled.div`
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const MessageContent = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
.menubar {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
.menubar {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const MenusBar = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 6px;
|
||||||
|
.anticon {
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 15px;
|
||||||
|
color: var(--color-icon);
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export default MessageItem
|
export default MessageItem
|
||||||
|
|||||||
@ -52,12 +52,21 @@ const Messages: FC<Props> = ({ assistant, topic }) => {
|
|||||||
}
|
}
|
||||||
}, [assistant, messages, topic, updateTopic])
|
}, [assistant, messages, topic, updateTopic])
|
||||||
|
|
||||||
|
const onDeleteMessage = (message: Message) => {
|
||||||
|
const _messages = messages.filter((m) => m.id !== message.id)
|
||||||
|
setMessages(_messages)
|
||||||
|
localforage.setItem(`topic:${topic.id}`, {
|
||||||
|
...topic,
|
||||||
|
messages: _messages
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribes = [
|
const unsubscribes = [
|
||||||
EventEmitter.on(EVENT_NAMES.SEND_MESSAGE, async (msg: Message) => {
|
EventEmitter.on(EVENT_NAMES.SEND_MESSAGE, async (msg: Message) => {
|
||||||
console.debug({ assistant, provider, message: msg, topic })
|
console.debug({ assistant, provider, message: msg, topic })
|
||||||
onSendMessage(msg)
|
onSendMessage(msg)
|
||||||
fetchChatCompletion({ assistant, messages: [messages, msg], topic, onResponse: setLastMessage })
|
fetchChatCompletion({ assistant, messages: [...messages, msg], topic, onResponse: setLastMessage })
|
||||||
}),
|
}),
|
||||||
EventEmitter.on(EVENT_NAMES.AI_CHAT_COMPLETION, async (msg: Message) => {
|
EventEmitter.on(EVENT_NAMES.AI_CHAT_COMPLETION, async (msg: Message) => {
|
||||||
setLastMessage(null)
|
setLastMessage(null)
|
||||||
@ -84,10 +93,10 @@ const Messages: FC<Props> = ({ assistant, topic }) => {
|
|||||||
useEffect(() => hljs.highlightAll())
|
useEffect(() => hljs.highlightAll())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container id="topics">
|
<Container id="messages">
|
||||||
{lastMessage && <MessageItem message={lastMessage} />}
|
{lastMessage && <MessageItem message={lastMessage} />}
|
||||||
{reverse([...messages]).map((message) => (
|
{reverse([...messages]).map((message) => (
|
||||||
<MessageItem message={message} key={message.id} />
|
<MessageItem message={message} key={message.id} showMenu onDeleteMessage={onDeleteMessage} />
|
||||||
))}
|
))}
|
||||||
<MessageItem message={assistantDefaultMessage} />
|
<MessageItem message={assistantDefaultMessage} />
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user