From 13abc2d65351a9edc3eb79dd084e3a65462f364e Mon Sep 17 00:00:00 2001 From: suyao Date: Mon, 1 Dec 2025 03:23:46 +0800 Subject: [PATCH] Backport: PR 11558 to v2 --- .../home/Inputbar/AgentSessionInputbar.tsx | 23 ++++--------- .../src/pages/home/Inputbar/Inputbar.tsx | 33 ++++++++++++++++--- .../home/Inputbar/components/InputbarCore.tsx | 7 ++-- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/renderer/src/pages/home/Inputbar/AgentSessionInputbar.tsx b/src/renderer/src/pages/home/Inputbar/AgentSessionInputbar.tsx index 722697be78..524d642aa1 100644 --- a/src/renderer/src/pages/home/Inputbar/AgentSessionInputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/AgentSessionInputbar.tsx @@ -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() -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 = ({ 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(draftCacheKey) ?? '', + onChange: (value) => cacheService.set(draftCacheKey, value, DRAFT_CACHE_TTL) }) const { textareaRef, diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index 81507ab596..d136954751 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -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(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 = ({ 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 = ({ assistant: initialAssistant, se const { setFiles, setMentionedModels, setSelectedKnowledgeBases } = useInputbarToolsDispatch() const { setCouldAddImageFile } = useInputbarToolsInternalDispatch() - const { text, setText } = useInputText() + const { text, setText } = useInputText({ + initialValue: cacheService.get(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 = ({ 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', { diff --git a/src/renderer/src/pages/home/Inputbar/components/InputbarCore.tsx b/src/renderer/src/pages/home/Inputbar/components/InputbarCore.tsx index e70a7473e5..923222f39a 100644 --- a/src/renderer/src/pages/home/Inputbar/components/InputbarCore.tsx +++ b/src/renderer/src/pages/home/Inputbar/components/InputbarCore.tsx @@ -151,11 +151,8 @@ export const InputbarCore: FC = ({ const setText = useCallback>>( (value) => { - if (typeof value === 'function') { - onTextChange(value(textRef.current)) - } else { - onTextChange(value) - } + const newText = typeof value === 'function' ? value(textRef.current) : value + onTextChange(newText) }, [onTextChange] )