diff --git a/src/renderer/src/pages/home/Messages/Message.tsx b/src/renderer/src/pages/home/Messages/Message.tsx
index a2e6633f77..0c20f0d842 100644
--- a/src/renderer/src/pages/home/Messages/Message.tsx
+++ b/src/renderer/src/pages/home/Messages/Message.tsx
@@ -2,6 +2,7 @@ import { loggerService } from '@logger'
import Scrollbar from '@renderer/components/Scrollbar'
import { useMessageEditing } from '@renderer/context/MessageEditingContext'
import { useAssistant } from '@renderer/hooks/useAssistant'
+import { useChatContext } from '@renderer/hooks/useChatContext'
import { useMessageOperations } from '@renderer/hooks/useMessageOperations'
import { useModel } from '@renderer/hooks/useModel'
import { useSettings } from '@renderer/hooks/useSettings'
@@ -38,6 +39,16 @@ interface Props {
const logger = loggerService.withContext('MessageItem')
+const WrapperContainer = ({
+ isMultiSelectMode,
+ children
+}: {
+ isMultiSelectMode: boolean
+ children: React.ReactNode
+}) => {
+ return isMultiSelectMode ? : children
+}
+
const MessageItem: FC = ({
message,
topic,
@@ -49,6 +60,7 @@ const MessageItem: FC = ({
}) => {
const { t } = useTranslation()
const { assistant, setModel } = useAssistant(message.assistantId)
+ const { isMultiSelectMode } = useChatContext(topic)
const model = useModel(getMessageModelId(message), message.model?.provider) || message.model
const { messageFont, fontSize, messageStyle } = useSettings()
const { editMessageBlocks, resendUserMessageWithEdit, editMessage } = useMessageOperations(topic)
@@ -122,7 +134,15 @@ const MessageItem: FC = ({
if (message.type === 'clear') {
return (
- EventEmitter.emit(EVENT_NAMES.NEW_CONTEXT)}>
+ {
+ if (isMultiSelectMode) {
+ return
+ }
+ EventEmitter.emit(EVENT_NAMES.NEW_CONTEXT)
+ }}>
{t('chat.message.new.context')}
@@ -131,56 +151,64 @@ const MessageItem: FC = ({
}
return (
-
-
- {isEditing && (
-
+
+
- )}
- {!isEditing && (
- <>
-
-
-
-
-
- {showMenubar && (
-
- }
- setModel={setModel}
- />
-
- )}
- >
- )}
-
+ {isEditing && (
+
+ )}
+ {!isEditing && (
+ <>
+
+
+
+
+
+ {showMenubar && (
+
+ }
+ setModel={setModel}
+ />
+
+ )}
+ >
+ )}
+
+
)
}
@@ -232,9 +260,11 @@ const MessageFooter = styled.div<{ $isLastMessage: boolean; $messageStyle: 'plai
margin-top: 8px;
`
-const NewContextMessage = styled.div`
+const NewContextMessage = styled.div<{ isMultiSelectMode: boolean }>`
cursor: pointer;
flex: 1;
+
+ ${({ isMultiSelectMode }) => isMultiSelectMode && 'cursor: default;'}
`
export default memo(MessageItem)
diff --git a/src/renderer/src/pages/home/Messages/SelectionBox.tsx b/src/renderer/src/pages/home/Messages/SelectionBox.tsx
index ab48b69a8e..2d12f0305c 100644
--- a/src/renderer/src/pages/home/Messages/SelectionBox.tsx
+++ b/src/renderer/src/pages/home/Messages/SelectionBox.tsx
@@ -17,9 +17,14 @@ const SelectionBox: React.FC = ({
const [isDragging, setIsDragging] = useState(false)
const [dragStart, setDragStart] = useState({ x: 0, y: 0 })
const [dragCurrent, setDragCurrent] = useState({ x: 0, y: 0 })
+ const [isMouseDown, setIsMouseDown] = useState(false)
const dragSelectedIds = useRef>(new Set())
+ // 拖拽阈值,只有移动距离超过这个值才开始框选
+ // 避免触控板点击触发拖拽
+ const DRAG_THRESHOLD = 5
+
useEffect(() => {
if (!isMultiSelectMode) return
@@ -39,20 +44,30 @@ const SelectionBox: React.FC = ({
e.preventDefault()
- setIsDragging(true)
+ setIsMouseDown(true)
const pos = updateDragPos(e)
setDragStart(pos)
setDragCurrent(pos)
dragSelectedIds.current.clear()
- document.body.classList.add('no-select')
}
const handleMouseMove = (e: MouseEvent) => {
+ if (!isMouseDown) return
+
+ const pos = updateDragPos(e)
+
+ const deltaX = Math.abs(pos.x - dragStart.x)
+ const deltaY = Math.abs(pos.y - dragStart.y)
+ const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
+
+ if (!isDragging && distance > DRAG_THRESHOLD) {
+ setIsDragging(true)
+ document.body.classList.add('no-select')
+ }
+
if (!isDragging) return
e.preventDefault()
-
- const pos = updateDragPos(e)
setDragCurrent(pos)
// 计算当前框选矩形
@@ -69,6 +84,9 @@ const SelectionBox: React.FC = ({
const checkbox = el.querySelector('input[type="checkbox"]') as HTMLInputElement | null
const isAlreadySelected = checkbox?.checked || false
+ // 清除上下文这类消息也会被选中,所以需要跳过
+ if (!checkbox) return
+
// 如果已经被记录为拖动选中,跳过
if (dragSelectedIds.current.has(id)) return
@@ -94,9 +112,11 @@ const SelectionBox: React.FC = ({
}
const handleMouseUp = () => {
- if (!isDragging) return
- setIsDragging(false)
- document.body.classList.remove('no-select')
+ setIsMouseDown(false)
+ if (isDragging) {
+ setIsDragging(false)
+ document.body.classList.remove('no-select')
+ }
}
const container = scrollContainerRef.current!
@@ -110,7 +130,7 @@ const SelectionBox: React.FC = ({
window.removeEventListener('mouseup', handleMouseUp)
document.body.classList.remove('no-select')
}
- }, [isMultiSelectMode, isDragging, dragStart, scrollContainerRef, messageElements, handleSelectMessage])
+ }, [isMultiSelectMode, isDragging, isMouseDown, dragStart, scrollContainerRef, messageElements, handleSelectMessage])
if (!isDragging || !isMultiSelectMode) return null