feat: add quick assistant settings panel and management functionality (#6201)

* feat: add quick assistant settings panel and management functionality

- Create QuickAssistantSettings component for UI
- Extend useAssistant hook with quick assistant controls
- Add settings button in ModelSettings page
- Implement temperature, context count, max tokens, and other parameters
- Connect settings to store via updateQuickAssistant action

Separate quick assistant preferences from default assistant settings for better customization.

* refactor(QuickAssistantSettings): remove maxTokens and refine UI layout

- Removed maxTokens related state, logic, and UI elements
- Simplified settings page by eliminating unused configuration
- Adjusted layout for Slider and InputNumber for better usability
- Removed fixed width from Modal to enable responsive behavior

* refactor(HomeWindow): optimize message building logic

- Removed redundant quickAssistant fetching logic
- Use `useQuickAssistant` hook directly for cleaner code
- Simplified message content concatenation method

* style(QuickAssistantSettings): Adjust spacing in settings page layout

Change the column width of sliders and input fields from 20/4 to 21/3 for a more reasonable layout
Also set the popup width to 800px to improve user experience

* feat(Quick Assistant): Add option to select assistant or model, and optimize Quick Assistant logic

- Added functionality to choose between using models or referencing other assistants
- Optimized model selection logic to automatically select based on settings
- Added relevant internationalization texts

* fix(HomeWindow): Dynamically display input box placeholder text based on quick assistant states

* refactor(QuickAssistant): remove the implement of the quick assistant feature and restructure related logic

- Remove code related to the quick assistant feature, including the useQuickAssistant hook, QuickAssistantSettings component, and associated store logic.
- Restructure the HomeWindow component to use default or specified assistants instead of the quick assistant functionality, simplifying the code structure.

* refactor(QuickAssistant): Remove custom default model for quick assistant and switch to default assistant

- Refactor quick assistant functionality, remove independent model settings, change to select via assistant ID
- Update multilingual translation text to match new features

* refactor(QuickAssistant): Remove quick assistant-related states and simplify logic

- Remove unused quick assistant states and toggle functionality, simplifying related logic
- Update multilingual files to match the new default model and assistant labels

* refactor(i18n): Unify translation keys for input field placeholders

Unify the placeholder translation keys from `model_empty` and `assistant_empty` into empty across different scenarios, streamlining code logic

* refactor(settings): simplify quick helper selection logic by directly using the preset helper

- Removed redundant helper filtering logic, directly using the preset helper as the quick helper
This commit is contained in:
jwcrystal 2025-06-16 18:13:35 +08:00 committed by GitHub
parent 9faa45b571
commit 477b5d2449
11 changed files with 192 additions and 65 deletions

View File

@ -15,7 +15,7 @@ import {
updateTopic, updateTopic,
updateTopics updateTopics
} from '@renderer/store/assistants' } from '@renderer/store/assistants'
import { setDefaultModel, setQuickAssistantModel, setTopicNamingModel, setTranslateModel } from '@renderer/store/llm' import { setDefaultModel, setTopicNamingModel, setTranslateModel } from '@renderer/store/llm'
import { Assistant, AssistantSettings, Model, Topic } from '@renderer/types' import { Assistant, AssistantSettings, Model, Topic } from '@renderer/types'
import { useCallback, useMemo } from 'react' import { useCallback, useMemo } from 'react'
@ -103,17 +103,15 @@ export function useDefaultAssistant() {
} }
export function useDefaultModel() { export function useDefaultModel() {
const { defaultModel, topicNamingModel, translateModel, quickAssistantModel } = useAppSelector((state) => state.llm) const { defaultModel, topicNamingModel, translateModel } = useAppSelector((state) => state.llm)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
return { return {
defaultModel, defaultModel,
topicNamingModel, topicNamingModel,
translateModel, translateModel,
quickAssistantModel,
setDefaultModel: (model: Model) => dispatch(setDefaultModel({ model })), setDefaultModel: (model: Model) => dispatch(setDefaultModel({ model })),
setTopicNamingModel: (model: Model) => dispatch(setTopicNamingModel({ model })), setTopicNamingModel: (model: Model) => dispatch(setTopicNamingModel({ model })),
setTranslateModel: (model: Model) => dispatch(setTranslateModel({ model })), setTranslateModel: (model: Model) => dispatch(setTranslateModel({ model }))
setQuickAssistantModel: (model: Model) => dispatch(setQuickAssistantModel({ model }))
} }
} }

View File

@ -1592,6 +1592,10 @@
"models.translate_model_prompt_title": "Translate Model Prompt", "models.translate_model_prompt_title": "Translate Model Prompt",
"models.quick_assistant_model": "Quick Assistant Model", "models.quick_assistant_model": "Quick Assistant Model",
"models.quick_assistant_model_description": "Default model used by Quick Assistant", "models.quick_assistant_model_description": "Default model used by Quick Assistant",
"models.quick_assistant_selection": "Select Assistant",
"models.quick_assistant_default_tag": "Default",
"models.use_model": "Default Model",
"models.use_assistant": "Use Assistant",
"moresetting": "More Settings", "moresetting": "More Settings",
"moresetting.check.confirm": "Confirm Selection", "moresetting.check.confirm": "Confirm Selection",
"moresetting.check.warn": "Please be cautious when selecting this option. Incorrect selection may cause the model to malfunction!", "moresetting.check.warn": "Please be cautious when selecting this option. Incorrect selection may cause the model to malfunction!",

View File

@ -1586,6 +1586,10 @@
"models.translate_model_prompt_title": "翻訳モデルのプロンプト", "models.translate_model_prompt_title": "翻訳モデルのプロンプト",
"models.quick_assistant_model": "クイックアシスタントモデル", "models.quick_assistant_model": "クイックアシスタントモデル",
"models.quick_assistant_model_description": "クイックアシスタントで使用されるデフォルトモデル", "models.quick_assistant_model_description": "クイックアシスタントで使用されるデフォルトモデル",
"models.quick_assistant_selection": "アシスタントを選択します",
"models.quick_assistant_default_tag": "デフォルト",
"models.use_model": "デフォルトモデル",
"models.use_assistant": "アシスタントの活用",
"moresetting": "詳細設定", "moresetting": "詳細設定",
"moresetting.check.confirm": "選択を確認", "moresetting.check.confirm": "選択を確認",
"moresetting.check.warn": "このオプションを選択する際は慎重に行ってください。誤った選択はモデルの誤動作を引き起こす可能性があります!", "moresetting.check.warn": "このオプションを選択する際は慎重に行ってください。誤った選択はモデルの誤動作を引き起こす可能性があります!",

View File

@ -1586,6 +1586,10 @@
"models.translate_model_prompt_title": "Модель перевода", "models.translate_model_prompt_title": "Модель перевода",
"models.quick_assistant_model": "Модель быстрого помощника", "models.quick_assistant_model": "Модель быстрого помощника",
"models.quick_assistant_model_description": "Модель по умолчанию, используемая быстрым помощником", "models.quick_assistant_model_description": "Модель по умолчанию, используемая быстрым помощником",
"models.quick_assistant_selection": "Выберите помощника",
"models.quick_assistant_default_tag": "умолчанию",
"models.use_model": "модель по умолчанию",
"models.use_assistant": "Использование ассистентов",
"moresetting": "Дополнительные настройки", "moresetting": "Дополнительные настройки",
"moresetting.check.confirm": "Подтвердить выбор", "moresetting.check.confirm": "Подтвердить выбор",
"moresetting.check.warn": "Пожалуйста, будьте осторожны при выборе этой опции. Неправильный выбор может привести к сбою в работе модели!", "moresetting.check.warn": "Пожалуйста, будьте осторожны при выборе этой опции. Неправильный выбор может привести к сбою в работе модели!",

View File

@ -1592,6 +1592,10 @@
"models.translate_model_prompt_title": "翻译模型提示词", "models.translate_model_prompt_title": "翻译模型提示词",
"models.quick_assistant_model": "快捷助手模型", "models.quick_assistant_model": "快捷助手模型",
"models.quick_assistant_model_description": "快捷助手使用的默认模型", "models.quick_assistant_model_description": "快捷助手使用的默认模型",
"models.quick_assistant_selection": "选择助手",
"models.quick_assistant_default_tag": "默认",
"models.use_model": "默认模型",
"models.use_assistant": "使用助手",
"moresetting": "更多设置", "moresetting": "更多设置",
"moresetting.check.confirm": "确认勾选", "moresetting.check.confirm": "确认勾选",
"moresetting.check.warn": "请慎重勾选此选项,勾选错误会导致模型无法正常使用!!!", "moresetting.check.warn": "请慎重勾选此选项,勾选错误会导致模型无法正常使用!!!",

View File

@ -1589,6 +1589,10 @@
"models.translate_model_prompt_title": "翻譯模型提示詞", "models.translate_model_prompt_title": "翻譯模型提示詞",
"models.quick_assistant_model": "快捷助手模型", "models.quick_assistant_model": "快捷助手模型",
"models.quick_assistant_model_description": "快捷助手使用的預設模型", "models.quick_assistant_model_description": "快捷助手使用的預設模型",
"models.quick_assistant_selection": "選擇助手",
"models.quick_assistant_default_tag": "預設",
"models.use_model": "預設模型",
"models.use_assistant": "使用助手",
"moresetting": "更多設定", "moresetting": "更多設定",
"moresetting.check.confirm": "確認勾選", "moresetting.check.confirm": "確認勾選",
"moresetting.check.warn": "請謹慎勾選此選項,勾選錯誤會導致模型無法正常使用!!!", "moresetting.check.warn": "請謹慎勾選此選項,勾選錯誤會導致模型無法正常使用!!!",
@ -1957,7 +1961,7 @@
}, },
"opacity": { "opacity": {
"title": "透明度", "title": "透明度",
"description": "設置視窗的默認透明度100%為完全不透明" "description": "設置視窗的預設透明度100%為完全不透明"
} }
}, },
"actions": { "actions": {

View File

@ -1,37 +1,35 @@
import { RedoOutlined } from '@ant-design/icons' import { RedoOutlined } from '@ant-design/icons'
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import { HStack } from '@renderer/components/Layout' import { HStack } from '@renderer/components/Layout'
import PromptPopup from '@renderer/components/Popups/PromptPopup' import PromptPopup from '@renderer/components/Popups/PromptPopup'
import { isEmbeddingModel } from '@renderer/config/models' import { isEmbeddingModel } from '@renderer/config/models'
import { TRANSLATE_PROMPT } from '@renderer/config/prompts' import { TRANSLATE_PROMPT } from '@renderer/config/prompts'
import { useTheme } from '@renderer/context/ThemeProvider' import { useTheme } from '@renderer/context/ThemeProvider'
import { useDefaultModel } from '@renderer/hooks/useAssistant' import { useAssistants, useDefaultAssistant, useDefaultModel } from '@renderer/hooks/useAssistant'
import { useProviders } from '@renderer/hooks/useProvider' import { useProviders } from '@renderer/hooks/useProvider'
import { useSettings } from '@renderer/hooks/useSettings' import { useSettings } from '@renderer/hooks/useSettings'
import { getModelUniqId, hasModel } from '@renderer/services/ModelService' import { getModelUniqId, hasModel } from '@renderer/services/ModelService'
import { useAppSelector } from '@renderer/store'
import { useAppDispatch } from '@renderer/store' import { useAppDispatch } from '@renderer/store'
import { setQuickAssistantId } from '@renderer/store/llm'
import { setTranslateModelPrompt } from '@renderer/store/settings' import { setTranslateModelPrompt } from '@renderer/store/settings'
import { Model } from '@renderer/types' import { Model } from '@renderer/types'
import { Button, Select, Tooltip } from 'antd' import { Button, Select, Tooltip } from 'antd'
import { find, sortBy } from 'lodash' import { find, sortBy } from 'lodash'
import { FolderPen, Languages, MessageSquareMore, Rocket, Settings2 } from 'lucide-react' import { CircleHelp, FolderPen, Languages, MessageSquareMore, Rocket, Settings2 } from 'lucide-react'
import { FC, useMemo } from 'react' import { FC, useMemo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import { SettingContainer, SettingDescription, SettingGroup, SettingTitle } from '..' import { SettingContainer, SettingDescription, SettingGroup, SettingTitle } from '..'
import DefaultAssistantSettings from './DefaultAssistantSettings' import DefaultAssistantSettings from './DefaultAssistantSettings'
import TopicNamingModalPopup from './TopicNamingModalPopup' import TopicNamingModalPopup from './TopicNamingModalPopup'
const ModelSettings: FC = () => { const ModelSettings: FC = () => {
const { const { defaultModel, topicNamingModel, translateModel, setDefaultModel, setTopicNamingModel, setTranslateModel } =
defaultModel, useDefaultModel()
topicNamingModel, const { defaultAssistant } = useDefaultAssistant()
translateModel, const { assistants } = useAssistants()
quickAssistantModel,
setDefaultModel,
setTopicNamingModel,
setTranslateModel,
setQuickAssistantModel
} = useDefaultModel()
const { providers } = useProviders() const { providers } = useProviders()
const allModels = providers.map((p) => p.models).flat() const allModels = providers.map((p) => p.models).flat()
const { theme } = useTheme() const { theme } = useTheme()
@ -39,6 +37,7 @@ const ModelSettings: FC = () => {
const { translateModelPrompt } = useSettings() const { translateModelPrompt } = useSettings()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { quickAssistantId } = useAppSelector((state) => state.llm)
const selectOptions = providers const selectOptions = providers
.filter((p) => p.models.length > 0) .filter((p) => p.models.length > 0)
@ -68,11 +67,6 @@ const ModelSettings: FC = () => {
[translateModel] [translateModel]
) )
const defaultQuickAssistantModel = useMemo(
() => (hasModel(quickAssistantModel) ? getModelUniqId(quickAssistantModel) : undefined),
[quickAssistantModel]
)
const onUpdateTranslateModel = async () => { const onUpdateTranslateModel = async () => {
const prompt = await PromptPopup.show({ const prompt = await PromptPopup.show({
title: t('settings.models.translate_model_prompt_title'), title: t('settings.models.translate_model_prompt_title'),
@ -163,27 +157,118 @@ const ModelSettings: FC = () => {
<SettingDescription>{t('settings.models.translate_model_description')}</SettingDescription> <SettingDescription>{t('settings.models.translate_model_description')}</SettingDescription>
</SettingGroup> </SettingGroup>
<SettingGroup theme={theme}> <SettingGroup theme={theme}>
<SettingTitle style={{ marginBottom: 12 }}> <HStack alignItems="center" style={{ marginBottom: 12 }}>
<HStack alignItems="center" gap={10}> <SettingTitle>
<Rocket size={18} color="var(--color-text)" /> <HStack alignItems="center" gap={10}>
{t('settings.models.quick_assistant_model')} <Rocket size={18} color="var(--color-text)" />
</HStack> {t('settings.models.quick_assistant_model')}
</SettingTitle> <Tooltip title={t('selection.settings.user_modal.model.tooltip')} arrow>
<HStack alignItems="center"> <QuestionIcon size={12} />
<Select </Tooltip>
value={defaultQuickAssistantModel} <Spacer />
defaultValue={defaultQuickAssistantModel} </HStack>
style={{ width: 360 }} <HStack alignItems="center" gap={0}>
onChange={(value) => setQuickAssistantModel(find(allModels, JSON.parse(value)) as Model)} <StyledButton
options={selectOptions} type={!quickAssistantId ? 'primary' : 'default'}
showSearch onClick={() => dispatch(setQuickAssistantId(null))}
placeholder={t('settings.models.empty')} selected={!quickAssistantId}>
/> {t('settings.models.use_model')}
</StyledButton>
<StyledButton
type={quickAssistantId ? 'primary' : 'default'}
onClick={() => {
dispatch(setQuickAssistantId(defaultAssistant.id))
}}
selected={!!quickAssistantId}>
{t('settings.models.use_assistant')}
</StyledButton>
</HStack>
</SettingTitle>
</HStack> </HStack>
{!quickAssistantId ? null : (
<HStack alignItems="center" style={{ marginTop: 12 }}>
<Select
value={quickAssistantId}
style={{ width: 360 }}
onChange={(value) => dispatch(setQuickAssistantId(value))}
placeholder={t('settings.models.quick_assistant_selection')}>
{assistants.map((a) => (
<Select.Option key={a.id} value={a.id}>
<AssistantItem>
<ModelAvatar model={a.model || defaultModel} size={18} />
<AssistantName>{a.name}</AssistantName>
<Spacer />
{a.id === defaultAssistant.id && (
<DefaultTag isCurrent={true}>{t('settings.models.quick_assistant_default_tag')}</DefaultTag>
)}
</AssistantItem>
</Select.Option>
))}
</Select>
</HStack>
)}
<SettingDescription>{t('settings.models.quick_assistant_model_description')}</SettingDescription> <SettingDescription>{t('settings.models.quick_assistant_model_description')}</SettingDescription>
</SettingGroup> </SettingGroup>
</SettingContainer> </SettingContainer>
) )
} }
const QuestionIcon = styled(CircleHelp)`
cursor: pointer;
color: var(--color-text-3);
`
const StyledButton = styled(Button)<{ selected: boolean }>`
border-radius: ${(props) => (props.selected ? '6px' : '6px')};
z-index: ${(props) => (props.selected ? 1 : 0)};
min-width: 80px;
&:first-child {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-right-width: 0px; // No right border for the first button when not selected
}
&:last-child {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-left-width: 1px; // Ensure left border for the last button
}
// Override Ant Design's default hover and focus styles for a cleaner look
&:hover,
&:focus {
z-index: 1;
border-color: ${(props) => (props.selected ? 'var(--ant-primary-color)' : 'var(--ant-primary-color-hover)')};
box-shadow: ${(props) =>
props.selected ? '0 0 0 2px var(--ant-primary-color-outline)' : '0 0 0 2px var(--ant-primary-color-outline)'};
}
`
const AssistantItem = styled.div`
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
height: 28px;
`
const AssistantName = styled.span`
max-width: calc(100% - 60px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`
const Spacer = styled.div`
flex: 1;
`
const DefaultTag = styled.span<{ isCurrent: boolean }>`
color: ${(props) => (props.isCurrent ? 'var(--color-primary)' : 'var(--color-text-3)')};
font-size: 12px;
padding: 2px 4px;
border-radius: 4px;
`
export default ModelSettings export default ModelSettings

View File

@ -21,7 +21,7 @@ export interface LlmState {
defaultModel: Model defaultModel: Model
topicNamingModel: Model topicNamingModel: Model
translateModel: Model translateModel: Model
quickAssistantModel: Model quickAssistantId: string | null
settings: LlmSettings settings: LlmSettings
} }
@ -514,7 +514,7 @@ const initialState: LlmState = {
defaultModel: SYSTEM_MODELS.defaultModel[0], defaultModel: SYSTEM_MODELS.defaultModel[0],
topicNamingModel: SYSTEM_MODELS.defaultModel[1], topicNamingModel: SYSTEM_MODELS.defaultModel[1],
translateModel: SYSTEM_MODELS.defaultModel[2], translateModel: SYSTEM_MODELS.defaultModel[2],
quickAssistantModel: SYSTEM_MODELS.defaultModel[3], quickAssistantId: null,
providers: INITIAL_PROVIDERS, providers: INITIAL_PROVIDERS,
settings: { settings: {
ollama: { ollama: {
@ -621,8 +621,9 @@ const llmSlice = createSlice({
setTranslateModel: (state, action: PayloadAction<{ model: Model }>) => { setTranslateModel: (state, action: PayloadAction<{ model: Model }>) => {
state.translateModel = action.payload.model state.translateModel = action.payload.model
}, },
setQuickAssistantModel: (state, action: PayloadAction<{ model: Model }>) => {
state.quickAssistantModel = action.payload.model setQuickAssistantId: (state, action: PayloadAction<string | null>) => {
state.quickAssistantId = action.payload
}, },
setOllamaKeepAliveTime: (state, action: PayloadAction<number>) => { setOllamaKeepAliveTime: (state, action: PayloadAction<number>) => {
state.settings.ollama.keepAliveTime = action.payload state.settings.ollama.keepAliveTime = action.payload
@ -661,7 +662,7 @@ export const {
setDefaultModel, setDefaultModel,
setTopicNamingModel, setTopicNamingModel,
setTranslateModel, setTranslateModel,
setQuickAssistantModel, setQuickAssistantId,
setOllamaKeepAliveTime, setOllamaKeepAliveTime,
setLMStudioKeepAliveTime, setLMStudioKeepAliveTime,
setGPUStackKeepAliveTime, setGPUStackKeepAliveTime,

View File

@ -1462,8 +1462,6 @@ const migrateConfig = {
searchMessageShortcut.shortcut = [isMac ? 'Command' : 'Ctrl', 'Shift', 'F'] searchMessageShortcut.shortcut = [isMac ? 'Command' : 'Ctrl', 'Shift', 'F']
} }
} }
// Quick assistant model
state.llm.quickAssistantModel = state.llm.defaultModel || SYSTEM_MODELS.silicon[1]
return state return state
} catch (error) { } catch (error) {
return state return state

View File

@ -1,5 +1,4 @@
import Scrollbar from '@renderer/components/Scrollbar' import Scrollbar from '@renderer/components/Scrollbar'
import { getDefaultModel } from '@renderer/services/AssistantService'
import { Assistant } from '@renderer/types' import { Assistant } from '@renderer/types'
import { FC } from 'react' import { FC } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
@ -11,11 +10,9 @@ interface Props {
} }
const ChatWindow: FC<Props> = ({ route, assistant }) => { const ChatWindow: FC<Props> = ({ route, assistant }) => {
// const { defaultAssistant } = useDefaultAssistant()
return ( return (
<Main className="bubble"> <Main className="bubble">
<Messages assistant={{ ...assistant, model: getDefaultModel() }} route={route} /> <Messages assistant={{ ...assistant }} route={route} />
</Main> </Main>
) )
} }

View File

@ -4,13 +4,13 @@ import { useDefaultAssistant, useDefaultModel } from '@renderer/hooks/useAssista
import { useSettings } from '@renderer/hooks/useSettings' import { useSettings } from '@renderer/hooks/useSettings'
import i18n from '@renderer/i18n' import i18n from '@renderer/i18n'
import { fetchChatCompletion } from '@renderer/services/ApiService' import { fetchChatCompletion } from '@renderer/services/ApiService'
import { getDefaultAssistant, getDefaultModel } from '@renderer/services/AssistantService' import { getAssistantById } from '@renderer/services/AssistantService'
import { getAssistantMessage, getUserMessage } from '@renderer/services/MessagesService' import { getAssistantMessage, getUserMessage } from '@renderer/services/MessagesService'
import store from '@renderer/store' import store, { useAppSelector } from '@renderer/store'
import { upsertManyBlocks } from '@renderer/store/messageBlock' import { upsertManyBlocks } from '@renderer/store/messageBlock'
import { updateOneBlock, upsertOneBlock } from '@renderer/store/messageBlock' import { updateOneBlock, upsertOneBlock } from '@renderer/store/messageBlock'
import { newMessagesActions } from '@renderer/store/newMessage' import { newMessagesActions } from '@renderer/store/newMessage'
import { ThemeMode } from '@renderer/types' import { Assistant, ThemeMode } from '@renderer/types'
import { Chunk, ChunkType } from '@renderer/types/chunk' import { Chunk, ChunkType } from '@renderer/types/chunk'
import { AssistantMessageStatus } from '@renderer/types/newMessage' import { AssistantMessageStatus } from '@renderer/types/newMessage'
import { MessageBlockStatus } from '@renderer/types/newMessage' import { MessageBlockStatus } from '@renderer/types/newMessage'
@ -37,14 +37,14 @@ const HomeWindow: FC = () => {
const [isFirstMessage, setIsFirstMessage] = useState(true) const [isFirstMessage, setIsFirstMessage] = useState(true)
const [clipboardText, setClipboardText] = useState('') const [clipboardText, setClipboardText] = useState('')
const [selectedText, setSelectedText] = useState('') const [selectedText, setSelectedText] = useState('')
const [currentAssistant, setCurrentAssistant] = useState<Assistant>({} as Assistant)
const [text, setText] = useState('') const [text, setText] = useState('')
const [lastClipboardText, setLastClipboardText] = useState<string | null>(null) const [lastClipboardText, setLastClipboardText] = useState<string | null>(null)
const textChange = useState(() => {})[1] const textChange = useState(() => {})[1]
const { defaultAssistant } = useDefaultAssistant() const { defaultAssistant } = useDefaultAssistant()
const topic = defaultAssistant.topics[0] const topic = defaultAssistant.topics[0]
const { defaultModel, quickAssistantModel } = useDefaultModel() const { defaultModel } = useDefaultModel()
// 如果 quickAssistantModel 未設定,則使用 defaultModel const model = currentAssistant.model || defaultModel
const model = quickAssistantModel || defaultModel
const { language, readClipboardAtStartup, windowStyle } = useSettings() const { language, readClipboardAtStartup, windowStyle } = useSettings()
const { theme } = useTheme() const { theme } = useTheme()
const { t } = useTranslation() const { t } = useTranslation()
@ -54,6 +54,8 @@ const HomeWindow: FC = () => {
const content = isFirstMessage ? (referenceText === text ? text : `${referenceText}\n\n${text}`).trim() : text.trim() const content = isFirstMessage ? (referenceText === text ? text : `${referenceText}\n\n${text}`).trim() : text.trim()
const { quickAssistantId } = useAppSelector((state) => state.llm)
const readClipboard = useCallback(async () => { const readClipboard = useCallback(async () => {
if (!readClipboardAtStartup) return if (!readClipboardAtStartup) return
@ -158,16 +160,36 @@ const HomeWindow: FC = () => {
setText(e.target.value) setText(e.target.value)
} }
useEffect(() => {
const defaultCurrentAssistant = {
...defaultAssistant,
model: defaultModel
}
if (quickAssistantId) {
// 獲取指定助手,如果不存在則使用默認助手
const assistantFromId = getAssistantById(quickAssistantId)
const currentAssistant = assistantFromId || defaultCurrentAssistant
// 如果助手本身沒有設定模型,則使用預設模型
if (!currentAssistant.model) {
currentAssistant.model = defaultModel
}
setCurrentAssistant(currentAssistant)
} else {
setCurrentAssistant(defaultCurrentAssistant)
}
}, [quickAssistantId, defaultAssistant, defaultModel])
const onSendMessage = useCallback( const onSendMessage = useCallback(
async (prompt?: string) => { async (prompt?: string) => {
if (isEmpty(content)) { if (isEmpty(content)) {
return return
} }
const topic = currentAssistant.topics[0]
const messageParams = { const messageParams = {
role: 'user', role: 'user',
content: prompt ? `${prompt}\n\n${content}` : content, content: [prompt, content].filter(Boolean).join('\n\n'),
assistant: defaultAssistant, assistant: currentAssistant,
topic, topic,
createdAt: dayjs().format('YYYY-MM-DD HH:mm:ss'), createdAt: dayjs().format('YYYY-MM-DD HH:mm:ss'),
status: 'success' status: 'success'
@ -178,7 +200,7 @@ const HomeWindow: FC = () => {
store.dispatch(newMessagesActions.addMessage({ topicId, message: userMessage })) store.dispatch(newMessagesActions.addMessage({ topicId, message: userMessage }))
store.dispatch(upsertManyBlocks(blocks)) store.dispatch(upsertManyBlocks(blocks))
const assistant = getDefaultAssistant() const assistant = currentAssistant
let blockId: string | null = null let blockId: string | null = null
let blockContent: string = '' let blockContent: string = ''
@ -187,7 +209,7 @@ const HomeWindow: FC = () => {
fetchChatCompletion({ fetchChatCompletion({
messages: [userMessage], messages: [userMessage],
assistant: { ...assistant, model: quickAssistantModel || getDefaultModel(), settings: { streamOutput: true } }, assistant: { ...assistant, settings: { streamOutput: true } },
onChunkReceived: (chunk: Chunk) => { onChunkReceived: (chunk: Chunk) => {
if (chunk.type === ChunkType.TEXT_DELTA) { if (chunk.type === ChunkType.TEXT_DELTA) {
blockContent += chunk.text blockContent += chunk.text
@ -224,7 +246,7 @@ const HomeWindow: FC = () => {
setIsFirstMessage(false) setIsFirstMessage(false)
setText('') // ✅ 清除输入框内容 setText('') // ✅ 清除输入框内容
}, },
[content, defaultAssistant, topic, quickAssistantModel] [content, currentAssistant, topic]
) )
const clearClipboard = () => { const clearClipboard = () => {
@ -277,7 +299,11 @@ const HomeWindow: FC = () => {
text={text} text={text}
model={model} model={model}
referenceText={referenceText} referenceText={referenceText}
placeholder={t('miniwindow.input.placeholder.empty', { model: model.name })} placeholder={
quickAssistantId
? t('miniwindow.input.placeholder.empty', { model: currentAssistant.name })
: t('miniwindow.input.placeholder.empty', { model: model.name })
}
handleKeyDown={handleKeyDown} handleKeyDown={handleKeyDown}
handleChange={handleChange} handleChange={handleChange}
ref={inputBarRef} ref={inputBarRef}
@ -290,7 +316,7 @@ const HomeWindow: FC = () => {
<ClipboardPreview referenceText={referenceText} clearClipboard={clearClipboard} t={t} /> <ClipboardPreview referenceText={referenceText} clearClipboard={clearClipboard} t={t} />
</div> </div>
)} )}
<ChatWindow route={route} assistant={defaultAssistant} /> <ChatWindow route={route} assistant={currentAssistant ?? defaultAssistant} />
<Divider style={{ margin: '10px 0' }} /> <Divider style={{ margin: '10px 0' }} />
<Footer route={route} onExit={() => setRoute('home')} /> <Footer route={route} onExit={() => setRoute('home')} />
</Container> </Container>
@ -316,7 +342,9 @@ const HomeWindow: FC = () => {
placeholder={ placeholder={
referenceText && route === 'home' referenceText && route === 'home'
? t('miniwindow.input.placeholder.title') ? t('miniwindow.input.placeholder.title')
: t('miniwindow.input.placeholder.empty', { model: model.name }) : quickAssistantId
? t('miniwindow.input.placeholder.empty', { model: currentAssistant.name })
: t('miniwindow.input.placeholder.empty', { model: model.name })
} }
handleKeyDown={handleKeyDown} handleKeyDown={handleKeyDown}
handleChange={handleChange} handleChange={handleChange}