diff --git a/src/renderer/src/components/ContextMenu/index.tsx b/src/renderer/src/components/ContextMenu/index.tsx index 62b3e99fe5..d0eace1dbf 100644 --- a/src/renderer/src/components/ContextMenu/index.tsx +++ b/src/renderer/src/components/ContextMenu/index.tsx @@ -2,6 +2,7 @@ import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { Dropdown } from 'antd' import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' +import styled from 'styled-components' interface ContextMenuProps { children: React.ReactNode @@ -73,7 +74,7 @@ const ContextMenu: React.FC = ({ children, onContextMenu }) => ] return ( -
+ {contextMenuPosition && ( = ({ children, onContextMenu }) => )} {children} -
+ ) } +const ContextContainer = styled.div`` + export default ContextMenu diff --git a/src/renderer/src/pages/home/Messages/ChatContext.tsx b/src/renderer/src/pages/home/Messages/ChatContext.tsx index 13e06a0720..733cdeaa2c 100644 --- a/src/renderer/src/pages/home/Messages/ChatContext.tsx +++ b/src/renderer/src/pages/home/Messages/ChatContext.tsx @@ -4,7 +4,6 @@ import { RootState } from '@renderer/store' import { messageBlocksSelectors } from '@renderer/store/messageBlock' import { selectMessagesForTopic } from '@renderer/store/newMessage' import { Topic } from '@renderer/types' -import { Modal } from 'antd' import { createContext, FC, ReactNode, use, useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useStore } from 'react-redux' @@ -41,8 +40,6 @@ export const ChatProvider: FC = ({ children, activeTopic }) = const { deleteMessage } = useMessageOperations(activeTopic) const [isMultiSelectMode, setIsMultiSelectMode] = useState(false) const [selectedMessageIds, setSelectedMessageIds] = useState([]) - const [confirmDeleteVisible, setConfirmDeleteVisible] = useState(false) - const [messagesToDelete, setMessagesToDelete] = useState([]) const [messageRefs, setMessageRefs] = useState>(new Map()) const store = useStore() @@ -118,8 +115,22 @@ export const ChatProvider: FC = ({ children, activeTopic }) = switch (actionType) { case 'delete': - setMessagesToDelete(messageIds) - setConfirmDeleteVisible(true) + window.modal.confirm({ + title: t('message.delete.confirm.title'), + content: t('message.delete.confirm.content', { count: messageIds.length }), + okButtonProps: { danger: true }, + centered: true, + onOk: async () => { + try { + await Promise.all(messageIds.map((messageId) => deleteMessage(messageId))) + window.message.success(t('message.delete.success')) + toggleMultiSelectMode(false) + } catch (error) { + console.error('Failed to delete messages:', error) + window.message.error(t('message.delete.failed')) + } + } + }) break case 'save': { const assistantMessages = messages.filter((msg) => messageIds.includes(msg.id)) @@ -173,25 +184,6 @@ export const ChatProvider: FC = ({ children, activeTopic }) = } } - const confirmDelete = async () => { - try { - await Promise.all(messagesToDelete.map((messageId) => deleteMessage(messageId))) - window.message.success(t('message.delete.success')) - setMessagesToDelete([]) - toggleMultiSelectMode(false) - } catch (error) { - console.error('Failed to delete messages:', error) - window.message.error(t('message.delete.failed')) - } finally { - setConfirmDeleteVisible(false) - } - } - - const cancelDelete = () => { - setConfirmDeleteVisible(false) - setMessagesToDelete([]) - } - const value = { isMultiSelectMode, selectedMessageIds, @@ -204,20 +196,5 @@ export const ChatProvider: FC = ({ children, activeTopic }) = registerMessageElement } - return ( - - {children} - -

{t('message.delete.confirm.content', { count: messagesToDelete.length })}

-
-
- ) + return {children} } diff --git a/src/renderer/src/pages/home/Messages/Message.tsx b/src/renderer/src/pages/home/Messages/Message.tsx index c2ff982226..484d212bb5 100644 --- a/src/renderer/src/pages/home/Messages/Message.tsx +++ b/src/renderer/src/pages/home/Messages/Message.tsx @@ -51,7 +51,7 @@ const MessageItem: FC = ({ const { assistant, setModel } = useAssistant(message.assistantId) const model = useModel(getMessageModelId(message), message.model?.provider) || message.model const { isBubbleStyle } = useMessageStyle() - const { showMessageDivider, messageFont, fontSize, narrowMode } = useSettings() + const { showMessageDivider, messageFont, fontSize, narrowMode, messageStyle } = useSettings() const { editMessageBlocks, resendUserMessageWithEdit } = useMessageOperations(topic) const messageContainerRef = useRef(null) const { editingMessageId, stopEditing } = useMessageEditing() @@ -134,6 +134,22 @@ const MessageItem: FC = ({ ) } + if (isEditing) { + return ( + + +
+ +
+
+ ) + } + return ( = ({ overflowY: 'visible', maxWidth: narrowMode ? 760 : undefined }}> - {isEditing ? ( - - ) : ( - - - - )} + + + {showMenubar && ( = ({ message, onSave, onResend, onCancel }) } return ( - <> - e.preventDefault()} onDrop={handleDrop}> - {editedBlocks - .filter((block) => block.type === MessageBlockType.MAIN_TEXT) - .map((block) => ( - + e.preventDefault()} onDrop={handleDrop}> + {editedBlocks + .filter((block) => block.type === MessageBlockType.MAIN_TEXT) + .map((block) => ( + + ))} + {(editedBlocks.some((block) => block.type === MessageBlockType.FILE || block.type === MessageBlockType.IMAGE) || + files.length > 0) && ( + + {editedBlocks + .filter((block) => block.type === MessageBlockType.FILE || block.type === MessageBlockType.IMAGE) + .map( + (block) => + block.file && ( + handleFileRemove(block.id)}> + + + ) + )} + + {files.map((file) => ( + setFiles((prevFiles) => prevFiles.filter((f) => f.id !== file.id))}> + + ))} - {(editedBlocks.some((block) => block.type === MessageBlockType.FILE || block.type === MessageBlockType.IMAGE) || - files.length > 0) && ( - - {editedBlocks - .filter((block) => block.type === MessageBlockType.FILE || block.type === MessageBlockType.IMAGE) - .map( - (block) => - block.file && ( - handleFileRemove(block.id)}> - - - ) - )} + + )} - {files.map((file) => ( - setFiles((prevFiles) => prevFiles.filter((f) => f.id !== file.id))}> - - - ))} - - )} - - - - - - - - - - - - - - handleClick()}> - - - - - handleClick(true)}> - - - - - - - + + + + + + + + + + + + + handleClick()}> + + + + + handleClick(true)}> + + + + + + ) } -const FileBlocksContainer = styled.div` - display: flex; - flex-wrap: wrap; - gap: 8px; - padding: 0 15px; - margin: 8px 0; - background: transparent; - border-radius: 4px; -` - const EditorContainer = styled.div` padding: 8px 0; border: 1px solid var(--color-border); transition: all 0.2s ease; border-radius: 15px; - margin-top: 0; + margin-top: 5px; background-color: var(--color-background-opacity); + width: 100%; &.file-dragging { border: 2px dashed #2ecc71; @@ -304,6 +293,16 @@ const EditorContainer = styled.div` } ` +const FileBlocksContainer = styled.div` + display: flex; + flex-wrap: wrap; + gap: 8px; + padding: 0 15px; + margin: 8px 0; + background: transparent; + border-radius: 4px; +` + const Textarea = styled(TextArea)` padding: 0; border-radius: 0;