feat: support clipboard images in mini assistant

This commit is contained in:
who is 2025-12-10 00:44:38 +00:00
parent 7507443d8b
commit 4a7986f041
2 changed files with 35 additions and 4 deletions

View File

@ -7,12 +7,14 @@ import i18n from '@renderer/i18n'
import { fetchChatCompletion } from '@renderer/services/ApiService' import { fetchChatCompletion } from '@renderer/services/ApiService'
import { getDefaultTopic } from '@renderer/services/AssistantService' import { getDefaultTopic } from '@renderer/services/AssistantService'
import { ConversationService } from '@renderer/services/ConversationService' import { ConversationService } from '@renderer/services/ConversationService'
import FileManager from '@renderer/services/FileManager'
import { getAssistantMessage, getUserMessage } from '@renderer/services/MessagesService' import { getAssistantMessage, getUserMessage } from '@renderer/services/MessagesService'
import PasteService from '@renderer/services/PasteService'
import store, { useAppSelector } from '@renderer/store' import store, { useAppSelector } from '@renderer/store'
import { updateOneBlock, upsertManyBlocks, upsertOneBlock } from '@renderer/store/messageBlock' import { updateOneBlock, upsertManyBlocks, upsertOneBlock } from '@renderer/store/messageBlock'
import { newMessagesActions, selectMessagesForTopic } from '@renderer/store/newMessage' import { newMessagesActions, selectMessagesForTopic } from '@renderer/store/newMessage'
import { cancelThrottledBlockUpdate, throttledBlockUpdate } from '@renderer/store/thunk/messageThunk' import { cancelThrottledBlockUpdate, throttledBlockUpdate } from '@renderer/store/thunk/messageThunk'
import type { Topic } from '@renderer/types' import type { FileMetadata, Topic } from '@renderer/types'
import { ThemeMode } from '@renderer/types' import { ThemeMode } from '@renderer/types'
import type { Chunk } from '@renderer/types/chunk' import type { Chunk } from '@renderer/types/chunk'
import { ChunkType } from '@renderer/types/chunk' import { ChunkType } from '@renderer/types/chunk'
@ -51,6 +53,7 @@ const HomeWindow: FC<{ draggable?: boolean }> = ({ draggable = true }) => {
const [isFirstMessage, setIsFirstMessage] = useState(true) const [isFirstMessage, setIsFirstMessage] = useState(true)
const [userInputText, setUserInputText] = useState('') const [userInputText, setUserInputText] = useState('')
const [files, setFiles] = useState<FileMetadata[]>([])
const [clipboardText, setClipboardText] = useState('') const [clipboardText, setClipboardText] = useState('')
const lastClipboardTextRef = useRef<string | null>(null) const lastClipboardTextRef = useRef<string | null>(null)
@ -73,6 +76,8 @@ const HomeWindow: FC<{ draggable?: boolean }> = ({ draggable = true }) => {
const inputBarRef = useRef<HTMLDivElement>(null) const inputBarRef = useRef<HTMLDivElement>(null)
const featureMenusRef = useRef<FeatureMenusRef>(null) const featureMenusRef = useRef<FeatureMenusRef>(null)
const supportedImageExts = useMemo(() => ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp'], [])
const referenceText = useMemo(() => clipboardText || userInputText, [clipboardText, userInputText]) const referenceText = useMemo(() => clipboardText || userInputText, [clipboardText, userInputText])
const userContent = useMemo(() => { const userContent = useMemo(() => {
@ -213,6 +218,23 @@ const HomeWindow: FC<{ draggable?: boolean }> = ({ draggable = true }) => {
setUserInputText(e.target.value) setUserInputText(e.target.value)
} }
const handlePaste = useCallback(
async (event: React.ClipboardEvent<HTMLInputElement>) => {
await PasteService.handlePaste(
event.nativeEvent,
supportedImageExts,
setFiles,
setUserInputText,
false,
undefined,
userInputText,
undefined,
t
)
},
[supportedImageExts, t, userInputText]
)
const handleError = (error: Error) => { const handleError = (error: Error) => {
setIsLoading(false) setIsLoading(false)
setError(error.message) setError(error.message)
@ -227,10 +249,13 @@ const HomeWindow: FC<{ draggable?: boolean }> = ({ draggable = true }) => {
try { try {
const topicId = currentTopic.current.id const topicId = currentTopic.current.id
const uploadedFiles = files.length ? await FileManager.uploadFiles(files) : []
const { message: userMessage, blocks } = getUserMessage({ const { message: userMessage, blocks } = getUserMessage({
content: [prompt, userContent].filter(Boolean).join('\n\n'), content: [prompt, userContent].filter(Boolean).join('\n\n'),
assistant: currentAssistant, assistant: currentAssistant,
topic: currentTopic.current topic: currentTopic.current,
files: uploadedFiles
}) })
store.dispatch(newMessagesActions.addMessage({ topicId, message: userMessage })) store.dispatch(newMessagesActions.addMessage({ topicId, message: userMessage }))
@ -272,6 +297,7 @@ const HomeWindow: FC<{ draggable?: boolean }> = ({ draggable = true }) => {
setIsFirstMessage(false) setIsFirstMessage(false)
setUserInputText('') setUserInputText('')
setFiles([])
const newAssistant = cloneDeep(currentAssistant) const newAssistant = cloneDeep(currentAssistant)
if (!newAssistant.settings) { if (!newAssistant.settings) {
@ -452,7 +478,7 @@ const HomeWindow: FC<{ draggable?: boolean }> = ({ draggable = true }) => {
currentAskId.current = '' currentAskId.current = ''
} }
}, },
[userContent, currentAssistant] [files, userContent, currentAssistant]
) )
const handlePause = useCallback(() => { const handlePause = useCallback(() => {
@ -546,6 +572,7 @@ const HomeWindow: FC<{ draggable?: boolean }> = ({ draggable = true }) => {
loading={isLoading} loading={isLoading}
handleKeyDown={handleKeyDown} handleKeyDown={handleKeyDown}
handleChange={handleChange} handleChange={handleChange}
handlePaste={handlePaste}
ref={inputBarRef} ref={inputBarRef}
/> />
<Divider style={{ margin: '10px 0' }} /> <Divider style={{ margin: '10px 0' }} />
@ -590,6 +617,7 @@ const HomeWindow: FC<{ draggable?: boolean }> = ({ draggable = true }) => {
loading={isLoading} loading={isLoading}
handleKeyDown={handleKeyDown} handleKeyDown={handleKeyDown}
handleChange={handleChange} handleChange={handleChange}
handlePaste={handlePaste}
ref={inputBarRef} ref={inputBarRef}
/> />
<Divider style={{ margin: '10px 0' }} /> <Divider style={{ margin: '10px 0' }} />

View File

@ -14,6 +14,7 @@ interface InputBarProps {
loading: boolean loading: boolean
handleKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => void handleKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => void
handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void
handlePaste: (e: React.ClipboardEvent<HTMLInputElement>) => void
} }
const InputBar = ({ const InputBar = ({
@ -23,7 +24,8 @@ const InputBar = ({
placeholder, placeholder,
loading, loading,
handleKeyDown, handleKeyDown,
handleChange handleChange,
handlePaste
}: InputBarProps & { ref?: React.RefObject<HTMLDivElement | null> }) => { }: InputBarProps & { ref?: React.RefObject<HTMLDivElement | null> }) => {
const inputRef = useRef<InputRef>(null) const inputRef = useRef<InputRef>(null)
const { setTimeoutTimer } = useTimer() const { setTimeoutTimer } = useTimer()
@ -40,6 +42,7 @@ const InputBar = ({
autoFocus autoFocus
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
onChange={handleChange} onChange={handleChange}
onPaste={handlePaste}
ref={inputRef} ref={inputRef}
/> />
</InputWrapper> </InputWrapper>