From de7f806bbcb81078541b5b50e0bee1d2f343472b Mon Sep 17 00:00:00 2001 From: Teo Date: Sun, 13 Apr 2025 21:07:00 +0800 Subject: [PATCH] =?UTF-8?q?hotfix:=20=E4=BC=98=E5=8C=96=E4=B8=80=E4=BA=9Bi?= =?UTF-8?q?ssue=E5=8F=8D=E9=A6=88=20(#4758)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat(Inputbar, Settings): add backspace delete model functionality and localization updates - Implemented a new setting to enable backspace key functionality for deleting models/attachments in the Inputbar. - Added corresponding localization strings for English, Japanese, Russian, Chinese (Simplified and Traditional) in the i18n files. - Updated the QuickPanelBody styling to inherit border-radius. - Migrated the new setting to the state management for persistence. Co-authored-by: 亢奋猫 --- .../src/components/QuickPanel/view.tsx | 1 + src/renderer/src/i18n/locales/en-us.json | 1 + src/renderer/src/i18n/locales/ja-jp.json | 1 + src/renderer/src/i18n/locales/ru-ru.json | 1 + src/renderer/src/i18n/locales/zh-cn.json | 1 + src/renderer/src/i18n/locales/zh-tw.json | 1 + .../src/pages/home/Inputbar/Inputbar.tsx | 16 +-- .../home/Inputbar/MentionModelsButton.tsx | 104 ++++++++++++------ .../src/pages/home/Tabs/SettingsTab.tsx | 13 ++- src/renderer/src/store/migrate.ts | 8 ++ src/renderer/src/store/settings.ts | 8 +- 11 files changed, 106 insertions(+), 49 deletions(-) diff --git a/src/renderer/src/components/QuickPanel/view.tsx b/src/renderer/src/components/QuickPanel/view.tsx index b431c69b..f70c4f7e 100644 --- a/src/renderer/src/components/QuickPanel/view.tsx +++ b/src/renderer/src/components/QuickPanel/view.tsx @@ -545,6 +545,7 @@ const QuickPanelBody = styled.div` background-color: rgba(240, 240, 240, 0.5); backdrop-filter: blur(35px) saturate(150%); z-index: -1; + border-radius: inherit; body[theme-mode='dark'] & { background-color: rgba(40, 40, 40, 0.4); diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 74cc35f9..438a4d1f 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -1132,6 +1132,7 @@ "messages.input.show_estimated_tokens": "Show estimated tokens", "messages.input.title": "Input Settings", "messages.input.enable_quick_triggers": "Enable '/' and '@' triggers", + "messages.input.enable_delete_model": "Enable the backspace key to delete models/attachments.", "messages.markdown_rendering_input_message": "Markdown render input message", "messages.math_engine": "Math engine", "messages.metrics": "{{time_first_token_millsec}}ms to first token | {{token_speed}} tok/sec", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index a39d193d..602bbe85 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -1131,6 +1131,7 @@ "messages.input.show_estimated_tokens": "推定トークン数を表示", "messages.input.title": "入力設定", "messages.input.enable_quick_triggers": "'/' と '@' を有効にしてクイックメニューを表示します。", + "messages.input.enable_delete_model": "バックスペースキーでモデル/添付ファイルを削除します。", "messages.markdown_rendering_input_message": "Markdownで入力メッセージをレンダリング", "messages.math_engine": "数式エンジン", "messages.metrics": "最初のトークンまでの時間 {{time_first_token_millsec}}ms | トークン速度 {{token_speed}} tok/sec", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 075f5048..4e42ba17 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -1131,6 +1131,7 @@ "messages.input.show_estimated_tokens": "Показывать затраты токенов", "messages.input.title": "Настройки ввода", "messages.input.enable_quick_triggers": "Включите '/' и '@', чтобы вызвать быстрое меню.", + "messages.input.enable_delete_model": "Включите удаление модели/вложения с помощью клавиши Backspace", "messages.markdown_rendering_input_message": "Отображение ввода в формате Markdown", "messages.math_engine": "Математический движок", "messages.metrics": "{{time_first_token_millsec}}ms до первого токена | {{token_speed}} tok/sec", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index db3fe830..07e63538 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -1132,6 +1132,7 @@ "messages.input.show_estimated_tokens": "显示预估 Token 数", "messages.input.title": "输入设置", "messages.input.enable_quick_triggers": "启用 '/' 和 '@' 触发快捷菜单", + "messages.input.enable_delete_model": "启用删除键删除输入的模型/附件", "messages.markdown_rendering_input_message": "Markdown 渲染输入消息", "messages.math_engine": "数学公式引擎", "messages.metrics": "首字时延 {{time_first_token_millsec}}ms | 每秒 {{token_speed}} tokens", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 1f70ddd3..1c03bad1 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -1131,6 +1131,7 @@ "messages.input.show_estimated_tokens": "顯示預估 Token 數", "messages.input.title": "輸入設定", "messages.input.enable_quick_triggers": "啟用 '/' 和 '@' 觸發快捷選單", + "messages.input.enable_delete_model": "啟用刪除鍵刪除模型/附件", "messages.markdown_rendering_input_message": "Markdown 渲染輸入訊息", "messages.math_engine": "Markdown 渲染輸入訊息", "messages.metrics": "首字延遲 {{time_first_token_millsec}}ms | 每秒 {{token_speed}} tokens", diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index b64e37bc..a170ece1 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -80,7 +80,8 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = pasteLongTextThreshold, showInputEstimatedTokens, autoTranslateWithSpace, - enableQuickPanelTriggers + enableQuickPanelTriggers, + enableBackspaceDeleteModel } = useSettings() const [expended, setExpend] = useState(false) const [estimateTokenCount, setEstimateTokenCount] = useState(0) @@ -467,21 +468,12 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = return event.preventDefault() } - if (event.key === 'Backspace' && text.trim() === '' && mentionModels.length > 0) { + if (enableBackspaceDeleteModel && event.key === 'Backspace' && text.trim() === '' && mentionModels.length > 0) { setMentionModels((prev) => prev.slice(0, -1)) return event.preventDefault() } - if (event.key === 'Backspace' && text.trim() === '' && selectedKnowledgeBases.length > 0) { - setSelectedKnowledgeBases((prev) => { - const newSelectedKnowledgeBases = prev.slice(0, -1) - updateAssistant({ ...assistant, knowledge_bases: newSelectedKnowledgeBases }) - return newSelectedKnowledgeBases - }) - return event.preventDefault() - } - - if (event.key === 'Backspace' && text.trim() === '' && files.length > 0) { + if (enableBackspaceDeleteModel && event.key === 'Backspace' && text.trim() === '' && files.length > 0) { setFiles((prev) => prev.slice(0, -1)) return event.preventDefault() } diff --git a/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx b/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx index 2328c002..df4fafea 100644 --- a/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx @@ -8,11 +8,13 @@ import { useProviders } from '@renderer/hooks/useProvider' import { getModelUniqId } from '@renderer/services/ModelService' import { Model } from '@renderer/types' import { Avatar, Tooltip } from 'antd' +import { useLiveQuery } from 'dexie-react-hooks' import { first, sortBy } from 'lodash' import { AtSign } from 'lucide-react' -import { FC, useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react' +import { FC, useCallback, useImperativeHandle, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router' +import styled from 'styled-components' export interface MentionModelsButtonRef { openQuickPanel: () => void @@ -27,47 +29,84 @@ interface Props { const MentionModelsButton: FC = ({ ref, mentionModels, onMentionModel, ToolbarButton }) => { const { providers } = useProviders() - const [pinnedModels, setPinnedModels] = useState([]) const { t } = useTranslation() const navigate = useNavigate() const quickPanel = useQuickPanel() + const pinnedModels = useLiveQuery( + async () => { + const setting = await db.settings.get('pinned:models') + return setting?.value || [] + }, + [], + [] + ) + const modelItems = useMemo(() => { - // Get all models from providers - const allModels = providers - .filter((p) => p.models && p.models.length > 0) - .flatMap((p) => + const items: QuickPanelListItem[] = [] + + if (pinnedModels.length > 0) { + const pinnedItems = providers.flatMap((p) => p.models - .filter((m) => !isEmbeddingModel(m)) - .filter((m) => !isRerankModel(m)) + .filter((m) => !isEmbeddingModel(m) && !isRerankModel(m)) + .filter((m) => pinnedModels.includes(getModelUniqId(m))) .map((m) => ({ - model: m, - provider: p, - isPinned: pinnedModels.includes(getModelUniqId(m)) + label: ( + <> + {p.isSystem ? t(`provider.${p.id}`) : p.name} + | {m.name} + + ), + description: , + icon: ( + + {first(m.name)} + + ), + action: () => onMentionModel(m), + isSelected: mentionModels.some((selected) => getModelUniqId(selected) === getModelUniqId(m)) })) ) - // Sort by pinned status and name - const newList: QuickPanelListItem[] = sortBy(allModels, ['isPinned', 'model.name']) - .reverse() - .map((item) => ({ - label: `${item.provider.isSystem ? t(`provider.${item.provider.id}`) : item.provider.name} | ${item.model.name}`, - description: , - icon: ( - - {first(item.model.name)} - - ), - action: () => onMentionModel(item.model), - isSelected: mentionModels.some((selected) => getModelUniqId(selected) === getModelUniqId(item.model)) - })) - newList.push({ + if (pinnedItems.length > 0) { + items.push(...sortBy(pinnedItems, ['label'])) + } + } + + providers.forEach((p) => { + const providerModels = p.models + .filter((m) => !isEmbeddingModel(m) && !isRerankModel(m)) + .filter((m) => !pinnedModels.includes(getModelUniqId(m))) + .map((m) => ({ + label: ( + <> + {p.isSystem ? t(`provider.${p.id}`) : p.name} + | {m.name} + + ), + description: , + icon: ( + + {first(m.name)} + + ), + action: () => onMentionModel(m), + isSelected: mentionModels.some((selected) => getModelUniqId(selected) === getModelUniqId(m)) + })) + + if (providerModels.length > 0) { + items.push(...sortBy(providerModels, ['label'])) + } + }) + + items.push({ label: t('settings.models.add.add_model') + '...', icon: , action: () => navigate('/settings/provider'), isSelected: false }) - return newList + + return items }, [providers, t, pinnedModels, mentionModels, onMentionModel, navigate]) const openQuickPanel = useCallback(() => { @@ -90,14 +129,6 @@ const MentionModelsButton: FC = ({ ref, mentionModels, onMentionModel, To } }, [openQuickPanel, quickPanel]) - useEffect(() => { - const loadPinnedModels = async () => { - const setting = await db.settings.get('pinned:models') - setPinnedModels(setting?.value || []) - } - loadPinnedModels() - }, []) - useImperativeHandle(ref, () => ({ openQuickPanel })) @@ -112,3 +143,6 @@ const MentionModelsButton: FC = ({ ref, mentionModels, onMentionModel, To } export default MentionModelsButton +const ProviderName = styled.span` + font-weight: 500; +` diff --git a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx index 80a29bbf..a2ba0868 100644 --- a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx +++ b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx @@ -27,6 +27,7 @@ import { setCodeShowLineNumbers, setCodeStyle, setCodeWrappable, + setEnableBackspaceDeleteModel, setEnableQuickPanelTriggers, setFontSize, setMathEngine, @@ -91,7 +92,8 @@ const SettingsTab: FC = (props) => { multiModelMessageStyle, thoughtAutoCollapse, messageNavigation, - enableQuickPanelTriggers + enableQuickPanelTriggers, + enableBackspaceDeleteModel } = useSettings() const onUpdateAssistantSettings = (settings: Partial) => { @@ -608,6 +610,15 @@ const SettingsTab: FC = (props) => { /> + + {t('settings.messages.input.enable_delete_model')} + dispatch(setEnableBackspaceDeleteModel(checked))} + /> + + {t('settings.input.target_language')} { + try { + state.settings.enableBackspaceDeleteModel = true + return state + } catch (error) { + return state + } } } diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index b7b4be71..ece6d8cd 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -113,6 +113,7 @@ export interface SettingsState { // 隐私设置 enableDataCollection: boolean enableQuickPanelTriggers: boolean + enableBackspaceDeleteModel: boolean exportMenuOptions: { image: boolean markdown: boolean @@ -212,6 +213,7 @@ export const initialState: SettingsState = { showOpenedMinappsInSidebar: true, enableDataCollection: false, enableQuickPanelTriggers: false, + enableBackspaceDeleteModel: true, exportMenuOptions: { image: true, markdown: true, @@ -483,6 +485,9 @@ const settingsSlice = createSlice({ }, setEnableQuickPanelTriggers: (state, action: PayloadAction) => { state.enableQuickPanelTriggers = action.payload + }, + setEnableBackspaceDeleteModel: (state, action: PayloadAction) => { + state.enableBackspaceDeleteModel = action.payload } } }) @@ -570,7 +575,8 @@ export const { setShowOpenedMinappsInSidebar, setEnableDataCollection, setEnableQuickPanelTriggers, - setExportMenuOptions + setExportMenuOptions, + setEnableBackspaceDeleteModel } = settingsSlice.actions export default settingsSlice.reducer