diff --git a/src/components/Main.astro b/src/components/Main.astro index d027d0eb..339d62f5 100644 --- a/src/components/Main.astro +++ b/src/components/Main.astro @@ -12,7 +12,7 @@ import Conversation from './main/Conversation'
- +
diff --git a/src/components/Markdown.tsx b/src/components/Markdown.tsx index e8ae73be..d85abc24 100644 --- a/src/components/Markdown.tsx +++ b/src/components/Markdown.tsx @@ -11,6 +11,7 @@ import 'katex/dist/katex.min.css' interface Props { class?: string text: string + showRawCode?: boolean } const parseMarkdown = (raw: string) => { @@ -27,11 +28,11 @@ const parseMarkdown = (raw: string) => { } export default (props: Props) => { - const htmlString = () => parseMarkdown(props.text) + const htmlString = () => props.showRawCode ? props.text : parseMarkdown(props.text) return (
) diff --git a/src/components/StreamableText.tsx b/src/components/StreamableText.tsx index 7abd1f1d..02a4bec6 100644 --- a/src/components/StreamableText.tsx +++ b/src/components/StreamableText.tsx @@ -7,6 +7,7 @@ import Markdown from './Markdown' interface Props { class?: string text: string + showRawCode?: boolean streamInfo?: () => { conversationId: string messageId: string @@ -47,6 +48,7 @@ export default (props: Props) => { ) } diff --git a/src/components/main/MessageItem.tsx b/src/components/main/MessageItem.tsx index 7f53cab7..f3e58c16 100644 --- a/src/components/main/MessageItem.tsx +++ b/src/components/main/MessageItem.tsx @@ -1,6 +1,9 @@ import { For } from 'solid-js/web' +import { createSignal } from 'solid-js' +import { useClipboardCopy } from '@/hooks' +import { deleteMessageByConversationId } from '@/stores/messages' import StreamableText from '../StreamableText' -import { DropDownMenu, Tooltip } from '../ui/base' +import { Tooltip } from '../ui/base' import type { MenuItem } from '../ui/base' import type { MessageInstance } from '@/types/message' @@ -18,18 +21,29 @@ export default (props: Props) => { assistant: 'bg-gradient-to-b from-[#fccb90] to-[#d57eeb]', } - const handleCopyMessage = () => { + const [copied, copy] = useClipboardCopy(props.message.content) + const [showRawCode, setShowRawCode] = createSignal(false) + const handleDeleteMessageItem = () => { + deleteMessageByConversationId(props.conversationId, props.message) } - const menuList: MenuItem[] = [ - { id: 'retry', label: 'Retry message', icon: 'i-ion:refresh-outline' }, - { id: 'show', label: 'Show raw code', icon: 'i-carbon-code' }, - { id: 'share', label: 'Share message', icon: 'i-ion:ios-share-alt' }, - { id: 'edit', label: 'Edit message', icon: 'i-ion:md-create' }, - { id: 'copy', label: 'Copy message', icon: 'i-carbon-copy' }, - { id: 'delete', label: 'Delete message', icon: 'i-carbon-trash-can' }, - ] + const [menuList, setMenuList] = createSignal([ + // TODO: Retry send message + // { id: 'retry', label: 'Retry send', icon: 'i-ion:refresh-outline', role: 'all' }, + { id: 'raw', label: 'Show raw code', icon: 'i-carbon-code', role: 'system', action: () => setShowRawCode(!showRawCode()) }, + // TODO: Share message + // { id: 'share', label: 'Share message', icon: 'i-ion:ios-share-alt' }, + // TODO: Edit message + // { id: 'edit', label: 'Edit message', icon: 'i-ion:md-create', role: 'user' }, + { id: 'copy', label: 'Copy message', icon: 'i-carbon-copy', role: 'all', action: copy }, + { id: 'delete', label: 'Delete message', icon: 'i-carbon-trash-can', role: 'all', action: handleDeleteMessageItem }, + ]) + + if (props.message.role === 'user') + setMenuList(menuList().filter(item => ['all', 'user'].includes(item.role!))) + else + setMenuList(menuList().filter(item => ['all', 'system'].includes(item.role!))) return (
{
- - {item => (
)} + + {item => ( + + { + item.id === 'copy' + ?
+ :
+ } + )}
@@ -63,6 +84,7 @@ export default (props: Props) => { handleStreaming: props.handleStreaming, }) : undefined} + showRawCode={showRawCode()} />
diff --git a/src/components/ui/base/DropdownMenu.tsx b/src/components/ui/base/DropdownMenu.tsx index d99d74f5..de4ce9a1 100644 --- a/src/components/ui/base/DropdownMenu.tsx +++ b/src/components/ui/base/DropdownMenu.tsx @@ -10,6 +10,8 @@ export interface MenuItem { icon?: string // TODO: nested menu children?: MenuItem[] + role?: string + action?: (params?: any) => void } interface Props { diff --git a/src/components/ui/base/Tooltip.tsx b/src/components/ui/base/Tooltip.tsx index 20af22d0..54cff90a 100644 --- a/src/components/ui/base/Tooltip.tsx +++ b/src/components/ui/base/Tooltip.tsx @@ -9,6 +9,7 @@ interface Props { children: JSX.Element openDelay?: number closeDelay?: number + handleChildClick?: () => void placement?: 'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end' | 'right-start' | 'right-end' } @@ -30,9 +31,9 @@ export const Tooltip = (props: Props) => { const resolvedChild = () => { const child = children(() => props.children) createEffect(() => { - spread(child() as Element, { ...api().triggerProps }) + spread(child() as Element, { ...api().triggerProps, onClick: props.handleChildClick }) }) - return child + return child() } return ( diff --git a/src/stores/messages.ts b/src/stores/messages.ts index b287280a..b7a22703 100644 --- a/src/stores/messages.ts +++ b/src/stores/messages.ts @@ -60,3 +60,16 @@ export const clearMessagesByConversationId = action( }) }, ) + +export const deleteMessageByConversationId = action( + conversationMessagesMap, + 'deleteMessageByConversationId', + (map, id: string, payload: MessageInstance) => { + const oldMessages = map.get()[id] || [] + map.setKey(id, [...oldMessages.filter(message => message.id !== payload.id)]) + db.setItem(id, [...oldMessages.filter(message => message.id !== payload.id)]) + updateConversationById(id, { + lastUseTime: Date.now(), + }) + }, +)