Backport: PR 11558 to v2

This commit is contained in:
suyao 2025-12-01 03:23:46 +08:00
parent 4f0cba15a0
commit 13abc2d653
No known key found for this signature in database
3 changed files with 38 additions and 25 deletions

View File

@ -2,6 +2,7 @@ import { loggerService } from '@logger'
import type { QuickPanelTriggerInfo } from '@renderer/components/QuickPanel'
import { QuickPanelReservedSymbol, useQuickPanel } from '@renderer/components/QuickPanel'
import { isGenerateImageModel, isVisionModel } from '@renderer/config/models'
import { cacheService } from '@renderer/data/CacheService'
import { useSession } from '@renderer/hooks/agents/useSession'
import { useInputText } from '@renderer/hooks/useInputText'
import { selectNewTopicLoading } from '@renderer/hooks/useMessageOperations'
@ -41,19 +42,10 @@ import { getInputbarConfig } from './registry'
import { TopicType } from './types'
const logger = loggerService.withContext('AgentSessionInputbar')
const agentSessionDraftCache = new Map<string, string>()
const readDraftFromCache = (key: string): string => {
return agentSessionDraftCache.get(key) ?? ''
}
const DRAFT_CACHE_TTL = 24 * 60 * 60 * 1000 // 24 hours
const writeDraftToCache = (key: string, value: string) => {
if (!value) {
agentSessionDraftCache.delete(key)
} else {
agentSessionDraftCache.set(key, value)
}
}
const getAgentDraftCacheKey = (agentId: string) => `agent.session.draft.${agentId}`
type Props = {
agentId: string
@ -170,16 +162,15 @@ const AgentSessionInputbarInner: FC<InnerProps> = ({ assistant, agentId, session
const scope = TopicType.Session
const config = getInputbarConfig(scope)
// Use shared hooks for text and textarea management
const initialDraft = useMemo(() => readDraftFromCache(agentId), [agentId])
const persistDraft = useCallback((next: string) => writeDraftToCache(agentId, next), [agentId])
// Use shared hooks for text and textarea management with draft persistence
const draftCacheKey = getAgentDraftCacheKey(agentId)
const {
text,
setText,
isEmpty: inputEmpty
} = useInputText({
initialValue: initialDraft,
onChange: persistDraft
initialValue: cacheService.get<string>(draftCacheKey) ?? '',
onChange: (value) => cacheService.set(draftCacheKey, value, DRAFT_CACHE_TTL)
})
const {
textareaRef,

View File

@ -9,6 +9,7 @@ import {
isVisionModels,
isWebSearchModel
} from '@renderer/config/models'
import { cacheService } from '@renderer/data/CacheService'
import db from '@renderer/databases'
import { useAssistant } from '@renderer/hooks/useAssistant'
import { useInputText } from '@renderer/hooks/useInputText'
@ -39,7 +40,7 @@ import { getSendMessageShortcutLabel } from '@renderer/utils/input'
import { documentExts, imageExts, textExts } from '@shared/config/constant'
import { debounce } from 'lodash'
import type { FC } from 'react'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import React, { useCallback, useEffect, useEffectEvent, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { InputbarCore } from './components/InputbarCore'
@ -51,6 +52,17 @@ import TokenCount from './TokenCount'
const logger = loggerService.withContext('Inputbar')
const INPUTBAR_DRAFT_CACHE_KEY = 'inputbar.draft.text'
const DRAFT_CACHE_TTL = 24 * 60 * 60 * 1000 // 24 hours
const getMentionedModelsCacheKey = (assistantId: string) => `inputbar.mentioned.models.${assistantId}`
const getValidatedCachedModels = (assistantId: string): Model[] => {
const cached = cacheService.get<Model[]>(getMentionedModelsCacheKey(assistantId))
if (!Array.isArray(cached)) return []
return cached.filter((model) => model?.id && model?.name)
}
interface Props {
assistant: Assistant
setActiveTopic: (topic: Topic) => void
@ -80,16 +92,18 @@ const Inputbar: FC<Props> = ({ assistant: initialAssistant, setActiveTopic, topi
toggleExpanded: () => {}
})
const [initialMentionedModels] = useState(() => getValidatedCachedModels(initialAssistant.id))
const initialState = useMemo(
() => ({
files: [] as FileType[],
mentionedModels: [] as Model[],
mentionedModels: initialMentionedModels,
selectedKnowledgeBases: initialAssistant.knowledge_bases ?? [],
isExpanded: false,
couldAddImageFile: false,
extensions: [] as string[]
}),
[initialAssistant.knowledge_bases]
[initialMentionedModels, initialAssistant.knowledge_bases]
)
return (
@ -121,7 +135,10 @@ const InputbarInner: FC<InputbarInnerProps> = ({ assistant: initialAssistant, se
const { setFiles, setMentionedModels, setSelectedKnowledgeBases } = useInputbarToolsDispatch()
const { setCouldAddImageFile } = useInputbarToolsInternalDispatch()
const { text, setText } = useInputText()
const { text, setText } = useInputText({
initialValue: cacheService.get<string>(INPUTBAR_DRAFT_CACHE_KEY) ?? '',
onChange: (value) => cacheService.set(INPUTBAR_DRAFT_CACHE_KEY, value, DRAFT_CACHE_TTL)
})
const {
textareaRef,
resize: resizeTextArea,
@ -192,6 +209,14 @@ const InputbarInner: FC<InputbarInnerProps> = ({ assistant: initialAssistant, se
setCouldAddImageFile(canAddImageFile)
}, [canAddImageFile, setCouldAddImageFile])
const onUnmount = useEffectEvent((id: string) => {
cacheService.set(getMentionedModelsCacheKey(id), mentionedModels, DRAFT_CACHE_TTL)
})
useEffect(() => {
return () => onUnmount(assistant.id)
}, [assistant.id, onUnmount])
const placeholderText = enableQuickPanelTriggers
? t('chat.input.placeholder', { key: getSendMessageShortcutLabel(sendMessageShortcut) })
: t('chat.input.placeholder_without_triggers', {

View File

@ -151,11 +151,8 @@ export const InputbarCore: FC<InputbarCoreProps> = ({
const setText = useCallback<React.Dispatch<React.SetStateAction<string>>>(
(value) => {
if (typeof value === 'function') {
onTextChange(value(textRef.current))
} else {
onTextChange(value)
}
const newText = typeof value === 'function' ? value(textRef.current) : value
onTextChange(newText)
},
[onTextChange]
)