hotfix: 优化一些issue反馈 (#4758)
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: 亢奋猫 <kangfenmao@qq.com>
This commit is contained in:
parent
5f7d8652bc
commit
de7f806bbc
@ -545,6 +545,7 @@ const QuickPanelBody = styled.div`
|
|||||||
background-color: rgba(240, 240, 240, 0.5);
|
background-color: rgba(240, 240, 240, 0.5);
|
||||||
backdrop-filter: blur(35px) saturate(150%);
|
backdrop-filter: blur(35px) saturate(150%);
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
|
border-radius: inherit;
|
||||||
|
|
||||||
body[theme-mode='dark'] & {
|
body[theme-mode='dark'] & {
|
||||||
background-color: rgba(40, 40, 40, 0.4);
|
background-color: rgba(40, 40, 40, 0.4);
|
||||||
|
|||||||
@ -1132,6 +1132,7 @@
|
|||||||
"messages.input.show_estimated_tokens": "Show estimated tokens",
|
"messages.input.show_estimated_tokens": "Show estimated tokens",
|
||||||
"messages.input.title": "Input Settings",
|
"messages.input.title": "Input Settings",
|
||||||
"messages.input.enable_quick_triggers": "Enable '/' and '@' triggers",
|
"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.markdown_rendering_input_message": "Markdown render input message",
|
||||||
"messages.math_engine": "Math engine",
|
"messages.math_engine": "Math engine",
|
||||||
"messages.metrics": "{{time_first_token_millsec}}ms to first token | {{token_speed}} tok/sec",
|
"messages.metrics": "{{time_first_token_millsec}}ms to first token | {{token_speed}} tok/sec",
|
||||||
|
|||||||
@ -1131,6 +1131,7 @@
|
|||||||
"messages.input.show_estimated_tokens": "推定トークン数を表示",
|
"messages.input.show_estimated_tokens": "推定トークン数を表示",
|
||||||
"messages.input.title": "入力設定",
|
"messages.input.title": "入力設定",
|
||||||
"messages.input.enable_quick_triggers": "'/' と '@' を有効にしてクイックメニューを表示します。",
|
"messages.input.enable_quick_triggers": "'/' と '@' を有効にしてクイックメニューを表示します。",
|
||||||
|
"messages.input.enable_delete_model": "バックスペースキーでモデル/添付ファイルを削除します。",
|
||||||
"messages.markdown_rendering_input_message": "Markdownで入力メッセージをレンダリング",
|
"messages.markdown_rendering_input_message": "Markdownで入力メッセージをレンダリング",
|
||||||
"messages.math_engine": "数式エンジン",
|
"messages.math_engine": "数式エンジン",
|
||||||
"messages.metrics": "最初のトークンまでの時間 {{time_first_token_millsec}}ms | トークン速度 {{token_speed}} tok/sec",
|
"messages.metrics": "最初のトークンまでの時間 {{time_first_token_millsec}}ms | トークン速度 {{token_speed}} tok/sec",
|
||||||
|
|||||||
@ -1131,6 +1131,7 @@
|
|||||||
"messages.input.show_estimated_tokens": "Показывать затраты токенов",
|
"messages.input.show_estimated_tokens": "Показывать затраты токенов",
|
||||||
"messages.input.title": "Настройки ввода",
|
"messages.input.title": "Настройки ввода",
|
||||||
"messages.input.enable_quick_triggers": "Включите '/' и '@', чтобы вызвать быстрое меню.",
|
"messages.input.enable_quick_triggers": "Включите '/' и '@', чтобы вызвать быстрое меню.",
|
||||||
|
"messages.input.enable_delete_model": "Включите удаление модели/вложения с помощью клавиши Backspace",
|
||||||
"messages.markdown_rendering_input_message": "Отображение ввода в формате Markdown",
|
"messages.markdown_rendering_input_message": "Отображение ввода в формате Markdown",
|
||||||
"messages.math_engine": "Математический движок",
|
"messages.math_engine": "Математический движок",
|
||||||
"messages.metrics": "{{time_first_token_millsec}}ms до первого токена | {{token_speed}} tok/sec",
|
"messages.metrics": "{{time_first_token_millsec}}ms до первого токена | {{token_speed}} tok/sec",
|
||||||
|
|||||||
@ -1132,6 +1132,7 @@
|
|||||||
"messages.input.show_estimated_tokens": "显示预估 Token 数",
|
"messages.input.show_estimated_tokens": "显示预估 Token 数",
|
||||||
"messages.input.title": "输入设置",
|
"messages.input.title": "输入设置",
|
||||||
"messages.input.enable_quick_triggers": "启用 '/' 和 '@' 触发快捷菜单",
|
"messages.input.enable_quick_triggers": "启用 '/' 和 '@' 触发快捷菜单",
|
||||||
|
"messages.input.enable_delete_model": "启用删除键删除输入的模型/附件",
|
||||||
"messages.markdown_rendering_input_message": "Markdown 渲染输入消息",
|
"messages.markdown_rendering_input_message": "Markdown 渲染输入消息",
|
||||||
"messages.math_engine": "数学公式引擎",
|
"messages.math_engine": "数学公式引擎",
|
||||||
"messages.metrics": "首字时延 {{time_first_token_millsec}}ms | 每秒 {{token_speed}} tokens",
|
"messages.metrics": "首字时延 {{time_first_token_millsec}}ms | 每秒 {{token_speed}} tokens",
|
||||||
|
|||||||
@ -1131,6 +1131,7 @@
|
|||||||
"messages.input.show_estimated_tokens": "顯示預估 Token 數",
|
"messages.input.show_estimated_tokens": "顯示預估 Token 數",
|
||||||
"messages.input.title": "輸入設定",
|
"messages.input.title": "輸入設定",
|
||||||
"messages.input.enable_quick_triggers": "啟用 '/' 和 '@' 觸發快捷選單",
|
"messages.input.enable_quick_triggers": "啟用 '/' 和 '@' 觸發快捷選單",
|
||||||
|
"messages.input.enable_delete_model": "啟用刪除鍵刪除模型/附件",
|
||||||
"messages.markdown_rendering_input_message": "Markdown 渲染輸入訊息",
|
"messages.markdown_rendering_input_message": "Markdown 渲染輸入訊息",
|
||||||
"messages.math_engine": "Markdown 渲染輸入訊息",
|
"messages.math_engine": "Markdown 渲染輸入訊息",
|
||||||
"messages.metrics": "首字延遲 {{time_first_token_millsec}}ms | 每秒 {{token_speed}} tokens",
|
"messages.metrics": "首字延遲 {{time_first_token_millsec}}ms | 每秒 {{token_speed}} tokens",
|
||||||
|
|||||||
@ -80,7 +80,8 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
|
|||||||
pasteLongTextThreshold,
|
pasteLongTextThreshold,
|
||||||
showInputEstimatedTokens,
|
showInputEstimatedTokens,
|
||||||
autoTranslateWithSpace,
|
autoTranslateWithSpace,
|
||||||
enableQuickPanelTriggers
|
enableQuickPanelTriggers,
|
||||||
|
enableBackspaceDeleteModel
|
||||||
} = useSettings()
|
} = useSettings()
|
||||||
const [expended, setExpend] = useState(false)
|
const [expended, setExpend] = useState(false)
|
||||||
const [estimateTokenCount, setEstimateTokenCount] = useState(0)
|
const [estimateTokenCount, setEstimateTokenCount] = useState(0)
|
||||||
@ -467,21 +468,12 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
|
|||||||
return event.preventDefault()
|
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))
|
setMentionModels((prev) => prev.slice(0, -1))
|
||||||
return event.preventDefault()
|
return event.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === 'Backspace' && text.trim() === '' && selectedKnowledgeBases.length > 0) {
|
if (enableBackspaceDeleteModel && event.key === 'Backspace' && text.trim() === '' && files.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) {
|
|
||||||
setFiles((prev) => prev.slice(0, -1))
|
setFiles((prev) => prev.slice(0, -1))
|
||||||
return event.preventDefault()
|
return event.preventDefault()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,11 +8,13 @@ import { useProviders } from '@renderer/hooks/useProvider'
|
|||||||
import { getModelUniqId } from '@renderer/services/ModelService'
|
import { getModelUniqId } from '@renderer/services/ModelService'
|
||||||
import { Model } from '@renderer/types'
|
import { Model } from '@renderer/types'
|
||||||
import { Avatar, Tooltip } from 'antd'
|
import { Avatar, Tooltip } from 'antd'
|
||||||
|
import { useLiveQuery } from 'dexie-react-hooks'
|
||||||
import { first, sortBy } from 'lodash'
|
import { first, sortBy } from 'lodash'
|
||||||
import { AtSign } from 'lucide-react'
|
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 { useTranslation } from 'react-i18next'
|
||||||
import { useNavigate } from 'react-router'
|
import { useNavigate } from 'react-router'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
export interface MentionModelsButtonRef {
|
export interface MentionModelsButtonRef {
|
||||||
openQuickPanel: () => void
|
openQuickPanel: () => void
|
||||||
@ -27,47 +29,84 @@ interface Props {
|
|||||||
|
|
||||||
const MentionModelsButton: FC<Props> = ({ ref, mentionModels, onMentionModel, ToolbarButton }) => {
|
const MentionModelsButton: FC<Props> = ({ ref, mentionModels, onMentionModel, ToolbarButton }) => {
|
||||||
const { providers } = useProviders()
|
const { providers } = useProviders()
|
||||||
const [pinnedModels, setPinnedModels] = useState<string[]>([])
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const quickPanel = useQuickPanel()
|
const quickPanel = useQuickPanel()
|
||||||
|
|
||||||
|
const pinnedModels = useLiveQuery(
|
||||||
|
async () => {
|
||||||
|
const setting = await db.settings.get('pinned:models')
|
||||||
|
return setting?.value || []
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
const modelItems = useMemo(() => {
|
const modelItems = useMemo(() => {
|
||||||
// Get all models from providers
|
const items: QuickPanelListItem[] = []
|
||||||
const allModels = providers
|
|
||||||
.filter((p) => p.models && p.models.length > 0)
|
if (pinnedModels.length > 0) {
|
||||||
.flatMap((p) =>
|
const pinnedItems = providers.flatMap((p) =>
|
||||||
p.models
|
p.models
|
||||||
.filter((m) => !isEmbeddingModel(m))
|
.filter((m) => !isEmbeddingModel(m) && !isRerankModel(m))
|
||||||
.filter((m) => !isRerankModel(m))
|
.filter((m) => pinnedModels.includes(getModelUniqId(m)))
|
||||||
.map((m) => ({
|
.map((m) => ({
|
||||||
model: m,
|
label: (
|
||||||
provider: p,
|
<>
|
||||||
isPinned: pinnedModels.includes(getModelUniqId(m))
|
<ProviderName>{p.isSystem ? t(`provider.${p.id}`) : p.name}</ProviderName>
|
||||||
|
<span style={{ opacity: 0.8 }}> | {m.name}</span>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
description: <ModelTagsWithLabel model={m} showLabel={false} size={10} style={{ opacity: 0.8 }} />,
|
||||||
|
icon: (
|
||||||
|
<Avatar src={getModelLogo(m.id)} size={20}>
|
||||||
|
{first(m.name)}
|
||||||
|
</Avatar>
|
||||||
|
),
|
||||||
|
action: () => onMentionModel(m),
|
||||||
|
isSelected: mentionModels.some((selected) => getModelUniqId(selected) === getModelUniqId(m))
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sort by pinned status and name
|
if (pinnedItems.length > 0) {
|
||||||
const newList: QuickPanelListItem[] = sortBy(allModels, ['isPinned', 'model.name'])
|
items.push(...sortBy(pinnedItems, ['label']))
|
||||||
.reverse()
|
}
|
||||||
.map((item) => ({
|
}
|
||||||
label: `${item.provider.isSystem ? t(`provider.${item.provider.id}`) : item.provider.name} | ${item.model.name}`,
|
|
||||||
description: <ModelTagsWithLabel model={item.model} showLabel={false} size={10} style={{ opacity: 0.8 }} />,
|
providers.forEach((p) => {
|
||||||
icon: (
|
const providerModels = p.models
|
||||||
<Avatar src={getModelLogo(item.model.id)} size={20}>
|
.filter((m) => !isEmbeddingModel(m) && !isRerankModel(m))
|
||||||
{first(item.model.name)}
|
.filter((m) => !pinnedModels.includes(getModelUniqId(m)))
|
||||||
</Avatar>
|
.map((m) => ({
|
||||||
),
|
label: (
|
||||||
action: () => onMentionModel(item.model),
|
<>
|
||||||
isSelected: mentionModels.some((selected) => getModelUniqId(selected) === getModelUniqId(item.model))
|
<ProviderName>{p.isSystem ? t(`provider.${p.id}`) : p.name}</ProviderName>
|
||||||
}))
|
<span style={{ opacity: 0.8 }}> | {m.name}</span>
|
||||||
newList.push({
|
</>
|
||||||
|
),
|
||||||
|
description: <ModelTagsWithLabel model={m} showLabel={false} size={10} style={{ opacity: 0.8 }} />,
|
||||||
|
icon: (
|
||||||
|
<Avatar src={getModelLogo(m.id)} size={20}>
|
||||||
|
{first(m.name)}
|
||||||
|
</Avatar>
|
||||||
|
),
|
||||||
|
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') + '...',
|
label: t('settings.models.add.add_model') + '...',
|
||||||
icon: <PlusOutlined />,
|
icon: <PlusOutlined />,
|
||||||
action: () => navigate('/settings/provider'),
|
action: () => navigate('/settings/provider'),
|
||||||
isSelected: false
|
isSelected: false
|
||||||
})
|
})
|
||||||
return newList
|
|
||||||
|
return items
|
||||||
}, [providers, t, pinnedModels, mentionModels, onMentionModel, navigate])
|
}, [providers, t, pinnedModels, mentionModels, onMentionModel, navigate])
|
||||||
|
|
||||||
const openQuickPanel = useCallback(() => {
|
const openQuickPanel = useCallback(() => {
|
||||||
@ -90,14 +129,6 @@ const MentionModelsButton: FC<Props> = ({ ref, mentionModels, onMentionModel, To
|
|||||||
}
|
}
|
||||||
}, [openQuickPanel, quickPanel])
|
}, [openQuickPanel, quickPanel])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const loadPinnedModels = async () => {
|
|
||||||
const setting = await db.settings.get('pinned:models')
|
|
||||||
setPinnedModels(setting?.value || [])
|
|
||||||
}
|
|
||||||
loadPinnedModels()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
openQuickPanel
|
openQuickPanel
|
||||||
}))
|
}))
|
||||||
@ -112,3 +143,6 @@ const MentionModelsButton: FC<Props> = ({ ref, mentionModels, onMentionModel, To
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default MentionModelsButton
|
export default MentionModelsButton
|
||||||
|
const ProviderName = styled.span`
|
||||||
|
font-weight: 500;
|
||||||
|
`
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import {
|
|||||||
setCodeShowLineNumbers,
|
setCodeShowLineNumbers,
|
||||||
setCodeStyle,
|
setCodeStyle,
|
||||||
setCodeWrappable,
|
setCodeWrappable,
|
||||||
|
setEnableBackspaceDeleteModel,
|
||||||
setEnableQuickPanelTriggers,
|
setEnableQuickPanelTriggers,
|
||||||
setFontSize,
|
setFontSize,
|
||||||
setMathEngine,
|
setMathEngine,
|
||||||
@ -91,7 +92,8 @@ const SettingsTab: FC<Props> = (props) => {
|
|||||||
multiModelMessageStyle,
|
multiModelMessageStyle,
|
||||||
thoughtAutoCollapse,
|
thoughtAutoCollapse,
|
||||||
messageNavigation,
|
messageNavigation,
|
||||||
enableQuickPanelTriggers
|
enableQuickPanelTriggers,
|
||||||
|
enableBackspaceDeleteModel
|
||||||
} = useSettings()
|
} = useSettings()
|
||||||
|
|
||||||
const onUpdateAssistantSettings = (settings: Partial<AssistantSettings>) => {
|
const onUpdateAssistantSettings = (settings: Partial<AssistantSettings>) => {
|
||||||
@ -608,6 +610,15 @@ const SettingsTab: FC<Props> = (props) => {
|
|||||||
/>
|
/>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
<SettingDivider />
|
<SettingDivider />
|
||||||
|
<SettingRow>
|
||||||
|
<SettingRowTitleSmall>{t('settings.messages.input.enable_delete_model')}</SettingRowTitleSmall>
|
||||||
|
<Switch
|
||||||
|
size="small"
|
||||||
|
checked={enableBackspaceDeleteModel}
|
||||||
|
onChange={(checked) => dispatch(setEnableBackspaceDeleteModel(checked))}
|
||||||
|
/>
|
||||||
|
</SettingRow>
|
||||||
|
<SettingDivider />
|
||||||
<SettingRow>
|
<SettingRow>
|
||||||
<SettingRowTitleSmall>{t('settings.input.target_language')}</SettingRowTitleSmall>
|
<SettingRowTitleSmall>{t('settings.input.target_language')}</SettingRowTitleSmall>
|
||||||
<StyledSelect
|
<StyledSelect
|
||||||
|
|||||||
@ -1224,6 +1224,14 @@ const migrateConfig = {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'97': (state: RootState) => {
|
||||||
|
try {
|
||||||
|
state.settings.enableBackspaceDeleteModel = true
|
||||||
|
return state
|
||||||
|
} catch (error) {
|
||||||
|
return state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -113,6 +113,7 @@ export interface SettingsState {
|
|||||||
// 隐私设置
|
// 隐私设置
|
||||||
enableDataCollection: boolean
|
enableDataCollection: boolean
|
||||||
enableQuickPanelTriggers: boolean
|
enableQuickPanelTriggers: boolean
|
||||||
|
enableBackspaceDeleteModel: boolean
|
||||||
exportMenuOptions: {
|
exportMenuOptions: {
|
||||||
image: boolean
|
image: boolean
|
||||||
markdown: boolean
|
markdown: boolean
|
||||||
@ -212,6 +213,7 @@ export const initialState: SettingsState = {
|
|||||||
showOpenedMinappsInSidebar: true,
|
showOpenedMinappsInSidebar: true,
|
||||||
enableDataCollection: false,
|
enableDataCollection: false,
|
||||||
enableQuickPanelTriggers: false,
|
enableQuickPanelTriggers: false,
|
||||||
|
enableBackspaceDeleteModel: true,
|
||||||
exportMenuOptions: {
|
exportMenuOptions: {
|
||||||
image: true,
|
image: true,
|
||||||
markdown: true,
|
markdown: true,
|
||||||
@ -483,6 +485,9 @@ const settingsSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setEnableQuickPanelTriggers: (state, action: PayloadAction<boolean>) => {
|
setEnableQuickPanelTriggers: (state, action: PayloadAction<boolean>) => {
|
||||||
state.enableQuickPanelTriggers = action.payload
|
state.enableQuickPanelTriggers = action.payload
|
||||||
|
},
|
||||||
|
setEnableBackspaceDeleteModel: (state, action: PayloadAction<boolean>) => {
|
||||||
|
state.enableBackspaceDeleteModel = action.payload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -570,7 +575,8 @@ export const {
|
|||||||
setShowOpenedMinappsInSidebar,
|
setShowOpenedMinappsInSidebar,
|
||||||
setEnableDataCollection,
|
setEnableDataCollection,
|
||||||
setEnableQuickPanelTriggers,
|
setEnableQuickPanelTriggers,
|
||||||
setExportMenuOptions
|
setExportMenuOptions,
|
||||||
|
setEnableBackspaceDeleteModel
|
||||||
} = settingsSlice.actions
|
} = settingsSlice.actions
|
||||||
|
|
||||||
export default settingsSlice.reducer
|
export default settingsSlice.reducer
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user