refactor: enhance preference management and update sidebar icon handling

- Updated preferences to include new confirmation settings for message actions, allowing users to customize action confirmations.
- Refactored components to utilize the usePreference hook for improved preference handling, replacing previous useSettings references.
- Introduced new types for AssistantIconType and SidebarIcon to enhance type safety.
- Cleaned up unused code and comments related to previous settings for better maintainability.
- Updated auto-generated preference mappings to reflect recent changes in preference structure.
This commit is contained in:
fullex 2025-09-02 17:24:38 +08:00
parent 820d6a6e96
commit 5d789ef394
24 changed files with 220 additions and 189 deletions

View File

@ -42,3 +42,16 @@ export type WindowStyle = 'transparent' | 'opaque'
export type SendMessageShortcut = 'Enter' | 'Shift+Enter' | 'Ctrl+Enter' | 'Command+Enter' | 'Alt+Enter'
export type AssistantTabSortType = 'tags' | 'list'
export type SidebarIcon =
| 'assistants'
| 'agents'
| 'paintings'
| 'translate'
| 'minapp'
| 'knowledge'
| 'files'
| 'code_tools'
| 'notes'
export type AssistantIconType = 'model' | 'emoji' | 'none'

View File

@ -1,6 +1,6 @@
/**
* Auto-generated preferences configuration
* Generated at: 2025-09-02T06:55:00.341Z
* Generated at: 2025-09-02T08:08:51.242Z
*
* This file is automatically generated from classification.json
* To update this file, modify classification.json and run:
@ -11,11 +11,13 @@
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import type {
AssistantIconType,
AssistantTabSortType,
SelectionActionItem,
SelectionFilterMode,
SelectionTriggerMode,
SendMessageShortcut,
SidebarIcon,
WindowStyle
} from '@shared/data/preferenceTypes'
import { LanguageVarious, ThemeMode } from '@shared/data/preferenceTypes'
@ -74,7 +76,7 @@ export interface PreferencesType {
// redux/settings/clickAssistantToShowTopic
'assistant.click_to_show_topic': boolean
// redux/settings/assistantIconType
'assistant.icon_type': string
'assistant.icon_type': AssistantIconType
// redux/settings/showAssistants
'assistant.tab.show': boolean
// redux/settings/assistantsTabSortType
@ -127,6 +129,10 @@ export interface PreferencesType {
'chat.input.translate.auto_translate_with_space': boolean
// redux/settings/showTranslateConfirm
'chat.input.translate.show_confirm': boolean
// redux/settings/confirmDeleteMessage
'chat.message.confirm_delete': boolean
// redux/settings/confirmRegenerateMessage
'chat.message.confirm_regenerate': boolean
// redux/settings/messageFont
'chat.message.font': string
// redux/settings/fontSize
@ -362,7 +368,7 @@ export interface PreferencesType {
// redux/settings/enableTopicNaming
'topic.naming.enabled': boolean
// redux/settings/topicNamingPrompt
'topic.naming.prompt': string
'topic.naming_prompt': string
// redux/settings/topicPosition
'topic.position': string
// redux/settings/pinTopicsToTop
@ -375,6 +381,10 @@ export interface PreferencesType {
'ui.custom_css': string
// redux/settings/navbarPosition
'ui.navbar.position': 'left' | 'top'
// redux/settings/sidebarIcons.disabled
'ui.sidebar.icons.invisible': SidebarIcon[]
// redux/settings/sidebarIcons.visible
'ui.sidebar.icons.visible': SidebarIcon[]
// redux/settings/theme
'ui.theme_mode': ThemeMode
// redux/settings/userTheme.colorPrimary
@ -437,6 +447,8 @@ export const DefaultPreferences: PreferencesType = {
'chat.input.show_estimated_tokens': false,
'chat.input.translate.auto_translate_with_space': false,
'chat.input.translate.show_confirm': true,
'chat.message.confirm_delete': true,
'chat.message.confirm_regenerate': true,
'chat.message.font': 'system',
'chat.message.font_size': 14,
'chat.message.math_engine': 'KaTeX',
@ -601,13 +613,25 @@ export const DefaultPreferences: PreferencesType = {
'shortcut.selection.toggle_enabled': { editable: true, enabled: false, key: [], system: true },
'shortcut.topic.new': { editable: true, enabled: true, key: ['CommandOrControl', 'N'], system: false },
'topic.naming.enabled': true,
'topic.naming.prompt': '',
'topic.naming_prompt': '',
'topic.position': 'left',
'topic.tab.pin_to_top': false,
'topic.tab.show': true,
'topic.tab.show_time': false,
'ui.custom_css': '',
'ui.navbar.position': 'top',
'ui.sidebar.icons.invisible': [],
'ui.sidebar.icons.visible': [
'assistants',
'agents',
'paintings',
'translate',
'minapp',
'knowledge',
'files',
'code_tools',
'notes'
],
'ui.theme_mode': ThemeMode.system,
'ui.theme_user.color_primary': '#00b96b',
'ui.window_style': 'opaque'
@ -618,8 +642,8 @@ export const DefaultPreferences: PreferencesType = {
/**
* :
* - 总配置项: 177
* - 总配置项: 181
* - electronStore项: 1
* - redux项: 176
* - redux项: 180
* - localStorage项: 0
*/

View File

@ -1,5 +1,5 @@
import { usePreference } from '@data/hooks/usePreference'
import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import { useSettings } from '@renderer/hooks/useSettings'
import { WebviewTag } from 'electron'
import { memo, useEffect, useRef } from 'react'
@ -23,7 +23,7 @@ const WebviewContainer = memo(
onNavigateCallback: (appid: string, url: string) => void
}) => {
const webviewRef = useRef<WebviewTag | null>(null)
const { enableSpellCheck } = useSettings()
const [enableSpellCheck] = usePreference('app.spell_check.enabled')
const { isLeftNavbar } = useNavbarPosition()
const setRef = (appid: string) => {

View File

@ -1,7 +1,7 @@
import { usePreference } from '@data/hooks/usePreference'
import DefaultAvatar from '@renderer/assets/images/avatar.png'
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import useAvatar from '@renderer/hooks/useAvatar'
import { useSettings } from '@renderer/hooks/useSettings'
import ImageStorage from '@renderer/services/ImageStorage'
import { useAppDispatch } from '@renderer/store'
import { setAvatar } from '@renderer/store/runtime'
@ -25,7 +25,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
const [emojiPickerOpen, setEmojiPickerOpen] = useState(false)
const [dropdownOpen, setDropdownOpen] = useState(false)
const { t } = useTranslation()
const { userName } = useSettings()
const [userName] = usePreference('app.user.name')
const dispatch = useAppDispatch()
const avatar = useAvatar()

View File

@ -1,3 +1,4 @@
import { usePreference } from '@data/hooks/usePreference'
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import { isMac } from '@renderer/config/constant'
import { UserAvatar } from '@renderer/config/env'
@ -39,8 +40,8 @@ import { SidebarOpenedMinappTabs, SidebarPinnedApps } from './PinnedMinapps'
const Sidebar: FC = () => {
const { hideMinappPopup } = useMinappPopup()
const { minappShow } = useRuntime()
const { sidebarIcons } = useSettings()
const { pinned } = useMinapps()
const [visibleSidebarIcons] = usePreference('ui.sidebar.icons.visible')
const { pathname } = useLocation()
const navigate = useNavigate()
@ -53,7 +54,7 @@ const Sidebar: FC = () => {
const backgroundColor = useNavBackgroundColor()
const showPinnedApps = pinned.length > 0 && sidebarIcons.visible.includes('minapp')
const showPinnedApps = pinned.length > 0 && visibleSidebarIcons.includes('minapp')
const to = async (path: string) => {
await modelGenerating()
@ -122,7 +123,8 @@ const Sidebar: FC = () => {
const MainMenus: FC = () => {
const { hideMinappPopup } = useMinappPopup()
const { pathname } = useLocation()
const { sidebarIcons, defaultPaintingProvider } = useSettings()
const [visibleSidebarIcons] = usePreference('ui.sidebar.icons.visible')
const { defaultPaintingProvider } = useSettings()
const { minappShow } = useRuntime()
const navigate = useNavigate()
const { theme } = useTheme()
@ -154,7 +156,7 @@ const MainMenus: FC = () => {
notes: '/notes'
}
return sidebarIcons.visible.map((icon) => {
return visibleSidebarIcons.map((icon) => {
const path = pathMap[icon]
const isActive = path === '/' ? isRoute(path) : isRoutes(path)

View File

@ -1,20 +1,22 @@
import { SidebarIcon } from '@renderer/types'
import { SidebarIcon } from '@shared/data/preferenceTypes'
//TODO 这个文件是否还有存在的价值? fullex @ data refactor
/**
*
*
*/
export const DEFAULT_SIDEBAR_ICONS: SidebarIcon[] = [
'assistants',
'agents',
'paintings',
'translate',
'minapp',
'knowledge',
'files',
'code_tools',
'notes'
]
// export const DEFAULT_SIDEBAR_ICONS: SidebarIcon[] = [
// 'assistants',
// 'agents',
// 'paintings',
// 'translate',
// 'minapp',
// 'knowledge',
// 'files',
// 'code_tools',
// 'notes'
// ]
/**
*

View File

@ -1,25 +1,18 @@
import store, { useAppDispatch, useAppSelector } from '@renderer/store'
import {
AssistantIconType,
setAssistantIconType,
setAutoCheckUpdate as _setAutoCheckUpdate,
// setDisableHardwareAcceleration,
setEnableDeveloperMode,
setLaunchOnBoot,
setLaunchToTray,
setPinTopicsToTop,
setSendMessageShortcut as _setSendMessageShortcut,
setSidebarIcons,
setTargetLanguage,
setTestChannel as _setTestChannel,
setTestPlan as _setTestPlan,
SettingsState,
setTopicPosition,
setTray as _setTray,
setTrayOnClose
// setWindowStyle
} from '@renderer/store/settings'
import { SidebarIcon, TranslateLanguageCode } from '@renderer/types'
import { TranslateLanguageCode } from '@renderer/types'
import { UpgradeChannel } from '@shared/config/constant'
import type { SendMessageShortcut } from '@shared/data/preferenceTypes'
@ -79,25 +72,25 @@ export function useSettings() {
// },
setTargetLanguage(targetLanguage: TranslateLanguageCode) {
dispatch(setTargetLanguage(targetLanguage))
},
setTopicPosition(topicPosition: 'left' | 'right') {
dispatch(setTopicPosition(topicPosition))
},
setPinTopicsToTop(pinTopicsToTop: boolean) {
dispatch(setPinTopicsToTop(pinTopicsToTop))
},
updateSidebarIcons(icons: { visible: SidebarIcon[]; disabled: SidebarIcon[] }) {
dispatch(setSidebarIcons(icons))
},
updateSidebarVisibleIcons(icons: SidebarIcon[]) {
dispatch(setSidebarIcons({ visible: icons }))
},
updateSidebarDisabledIcons(icons: SidebarIcon[]) {
dispatch(setSidebarIcons({ disabled: icons }))
},
setAssistantIconType(assistantIconType: AssistantIconType) {
dispatch(setAssistantIconType(assistantIconType))
}
// setTopicPosition(topicPosition: 'left' | 'right') {
// dispatch(setTopicPosition(topicPosition))
// },
// setPinTopicsToTop(pinTopicsToTop: boolean) {
// dispatch(setPinTopicsToTop(pinTopicsToTop))
// }
// updateSidebarIcons(icons: { visible: SidebarIcon[]; disabled: SidebarIcon[] }) {
// dispatch(setSidebarIcons(icons))
// },
// updateSidebarVisibleIcons(icons: SidebarIcon[]) {
// dispatch(setSidebarIcons({ visible: icons }))
// },
// updateSidebarDisabledIcons(icons: SidebarIcon[]) {
// dispatch(setSidebarIcons({ disabled: icons }))
// },
// setAssistantIconType(assistantIconType: AssistantIconType) {
// dispatch(setAssistantIconType(assistantIconType))
// }
// setDisableHardwareAcceleration(disableHardwareAcceleration: boolean) {
// dispatch(setDisableHardwareAcceleration(disableHardwareAcceleration))
// window.api.setDisableHardwareAcceleration(disableHardwareAcceleration)
@ -118,18 +111,18 @@ export const getStoreSetting = (key: keyof SettingsState) => {
return store.getState().settings[key]
}
export const useEnableDeveloperMode = () => {
const enableDeveloperMode = useAppSelector((state) => state.settings.enableDeveloperMode)
const dispatch = useAppDispatch()
// export const useEnableDeveloperMode = () => {
// const enableDeveloperMode = useAppSelector((state) => state.settings.enableDeveloperMode)
// const dispatch = useAppDispatch()
return {
enableDeveloperMode,
setEnableDeveloperMode: (enableDeveloperMode: boolean) => {
dispatch(setEnableDeveloperMode(enableDeveloperMode))
window.api.config.set('enableDeveloperMode', enableDeveloperMode)
}
}
}
// return {
// enableDeveloperMode,
// setEnableDeveloperMode: (enableDeveloperMode: boolean) => {
// dispatch(setEnableDeveloperMode(enableDeveloperMode))
// window.api.config.set('enableDeveloperMode', enableDeveloperMode)
// }
// }
// }
export const getEnableDeveloperMode = () => {
return store.getState().settings.enableDeveloperMode

View File

@ -1,8 +1,7 @@
import { SidebarIcon } from '@renderer/types'
import { useSettings } from './useSettings'
import { usePreference } from '@data/hooks/usePreference'
import { SidebarIcon } from '@shared/data/preferenceTypes'
export function useSidebarIconShow(icon: SidebarIcon) {
const { sidebarIcons } = useSettings()
return sidebarIcons.visible.includes(icon)
const [visibleSidebarIcons] = usePreference('ui.sidebar.icons.visible')
return visibleSidebarIcons.includes(icon)
}

View File

@ -219,7 +219,7 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
logger.info('Starting to send message')
const parent = spanManagerService.startTrace(
const parent = await spanManagerService.startTrace(
{ topicId: topic.id, name: 'sendMessage', inputs: text },
mentionedModels && mentionedModels.length > 0 ? mentionedModels : [assistant.model]
)

View File

@ -1,3 +1,4 @@
import { usePreference } from '@data/hooks/usePreference'
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import { HStack } from '@renderer/components/Layout'
import UserPopup from '@renderer/components/Popups/UserPopup'
@ -7,7 +8,8 @@ import { useTheme } from '@renderer/context/ThemeProvider'
import useAvatar from '@renderer/hooks/useAvatar'
import { useChatContext } from '@renderer/hooks/useChatContext'
import { useMinappPopup } from '@renderer/hooks/useMinappPopup'
import { useMessageStyle, useSettings } from '@renderer/hooks/useSettings'
import { useMessageStyle } from '@renderer/hooks/useSettings'
import { useSidebarIconShow } from '@renderer/hooks/useSidebarIcon'
import { getMessageModelId } from '@renderer/services/MessagesService'
import { getModelName } from '@renderer/services/ModelService'
import type { Assistant, Model, Topic } from '@renderer/types'
@ -36,7 +38,8 @@ const getAvatarSource = (isLocalAi: boolean, modelId: string | undefined) => {
const MessageHeader: FC<Props> = memo(({ assistant, model, message, topic, isGroupContextMessage }) => {
const avatar = useAvatar()
const { theme } = useTheme()
const { userName, sidebarIcons } = useSettings()
const [userName] = usePreference('app.user.name')
const showMinappIcon = useSidebarIconShow('minapp')
const { t } = useTranslation()
const { isBubbleStyle } = useMessageStyle()
const { openMinappById } = useMinappPopup()
@ -61,7 +64,6 @@ const MessageHeader: FC<Props> = memo(({ assistant, model, message, topic, isGro
const isAssistantMessage = message.role === 'assistant'
const isUserMessage = message.role === 'user'
const showMinappIcon = sidebarIcons.visible.includes('minapp')
const avatarName = useMemo(() => firstLetter(assistant?.name).toUpperCase(), [assistant?.name])
const username = useMemo(() => removeLeadingEmoji(getUserName()), [getUserName])

View File

@ -1,4 +1,5 @@
// import { InfoCircleOutlined } from '@ant-design/icons'
import { usePreference } from '@data/hooks/usePreference'
import { loggerService } from '@logger'
import { CopyIcon, DeleteIcon, EditIcon, RefreshIcon } from '@renderer/components/Icons'
import ObsidianExportPopup from '@renderer/components/Popups/ObsidianExportPopup'
@ -9,7 +10,7 @@ import { useMessageEditing } from '@renderer/context/MessageEditingContext'
import { useChatContext } from '@renderer/hooks/useChatContext'
import { useMessageOperations } from '@renderer/hooks/useMessageOperations'
import { useNotesSettings } from '@renderer/hooks/useNotesSettings'
import { useEnableDeveloperMode, useMessageStyle, useSettings } from '@renderer/hooks/useSettings'
import { useMessageStyle } from '@renderer/hooks/useSettings'
import { useTemporaryValue } from '@renderer/hooks/useTemporaryValue'
import useTranslate from '@renderer/hooks/useTranslate'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
@ -98,8 +99,9 @@ const MessageMenubar: FC<Props> = (props) => {
} = useMessageOperations(topic)
const { isBubbleStyle } = useMessageStyle()
const { enableDeveloperMode } = useEnableDeveloperMode()
const { confirmDeleteMessage, confirmRegenerateMessage } = useSettings()
const [enableDeveloperMode] = usePreference('app.developer_mode.enabled')
const [confirmDeleteMessage] = usePreference('chat.message.confirm_delete')
const [confirmRegenerateMessage] = usePreference('chat.message.confirm_regenerate')
// const loading = useTopicLoading(topic)

View File

@ -1,3 +1,4 @@
import { usePreference } from '@data/hooks/usePreference'
import { DraggableVirtualList } from '@renderer/components/DraggableList'
import { CopyIcon, DeleteIcon, EditIcon } from '@renderer/components/Icons'
import ObsidianExportPopup from '@renderer/components/Popups/ObsidianExportPopup'
@ -8,7 +9,6 @@ import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant'
import { useInPlaceEdit } from '@renderer/hooks/useInPlaceEdit'
import { useNotesSettings } from '@renderer/hooks/useNotesSettings'
import { modelGenerating } from '@renderer/hooks/useRuntime'
import { useSettings } from '@renderer/hooks/useSettings'
import { finishTopicRenaming, startTopicRenaming, TopicManager } from '@renderer/hooks/useTopic'
import { fetchMessagesSummary } from '@renderer/services/ApiService'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
@ -61,11 +61,14 @@ interface Props {
}
const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic, position }) => {
const [topicPosition, setTopicPosition] = usePreference('topic.position')
const [showTopicTime] = usePreference('topic.tab.show_time')
const [pinTopicsToTop] = usePreference('topic.tab.pin_to_top')
const { t } = useTranslation()
const { notesPath } = useNotesSettings()
const { assistants } = useAssistants()
const { assistant, removeTopic, moveTopic, updateTopic, updateTopics } = useAssistant(_assistant.id)
const { showTopicTime, pinTopicsToTop, setTopicPosition, topicPosition } = useSettings()
const renamingTopics = useSelector((state: RootState) => state.runtime.chat.renamingTopics)
const topicLoadingQuery = useSelector((state: RootState) => state.messages.loadingByTopic)

View File

@ -1,9 +1,9 @@
import { usePreference } from '@data/hooks/usePreference'
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import EmojiIcon from '@renderer/components/EmojiIcon'
import { CopyIcon, DeleteIcon, EditIcon } from '@renderer/components/Icons'
import PromptPopup from '@renderer/components/Popups/PromptPopup'
import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant'
import { useSettings } from '@renderer/hooks/useSettings'
import { useTags } from '@renderer/hooks/useTags'
import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings'
import { getDefaultModel } from '@renderer/services/AssistantService'
@ -57,10 +57,13 @@ const AssistantItem: FC<AssistantItemProps> = ({
copyAssistant,
handleSortByChange
}) => {
const [assistantIconType, setAssistantIconType] = usePreference('assistant.icon_type')
const [clickAssistantToShowTopic] = usePreference('assistant.click_to_show_topic')
const [topicPosition] = usePreference('topic.position')
const { t } = useTranslation()
const { allTags } = useTags()
const { removeAllTopics } = useAssistant(assistant.id)
const { clickAssistantToShowTopic, topicPosition, assistantIconType, setAssistantIconType } = useSettings()
const defaultModel = getDefaultModel()
const { assistants, updateAssistants } = useAssistants()

View File

@ -4,20 +4,11 @@ import { ResetIcon } from '@renderer/components/Icons'
import { HStack } from '@renderer/components/Layout'
import TextBadge from '@renderer/components/TextBadge'
import { isMac, THEME_COLOR_PRESETS } from '@renderer/config/constant'
import { DEFAULT_SIDEBAR_ICONS } from '@renderer/config/sidebar'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import { useSettings } from '@renderer/hooks/useSettings'
import useUserTheme from '@renderer/hooks/useUserTheme'
import { useAppDispatch } from '@renderer/store'
import {
AssistantIconType,
setAssistantIconType,
setClickAssistantToShowTopic,
setPinTopicsToTop,
setShowTopicTime,
setSidebarIcons
} from '@renderer/store/settings'
import { DefaultPreferences } from '@shared/data/preferences'
import { AssistantIconType } from '@shared/data/preferenceTypes'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { Button, ColorPicker, Segmented, Switch } from 'antd'
import { Minus, Monitor, Moon, Plus, Sun } from 'lucide-react'
@ -56,29 +47,23 @@ const ColorCircle = styled.div<{ color: string; isActive?: boolean }>`
`
const DisplaySettings: FC = () => {
const {
// windowStyle,
// setWindowStyle,
topicPosition,
setTopicPosition,
clickAssistantToShowTopic,
showTopicTime,
pinTopicsToTop,
// customCss,
sidebarIcons,
assistantIconType
} = useSettings()
const [windowStyle, setWindowStyle] = usePreference('ui.window_style')
const [customCss, setCustomCss] = usePreference('ui.custom_css')
const [visibleIcons, setVisibleIcons] = usePreference('ui.sidebar.icons.visible')
const [invisibleIcons, setInvisibleIcons] = usePreference('ui.sidebar.icons.invisible')
const [topicPosition, setTopicPosition] = usePreference('topic.position')
const [clickAssistantToShowTopic, setClickAssistantToShowTopic] = usePreference('assistant.click_to_show_topic')
const [pinTopicsToTop, setPinTopicsToTop] = usePreference('topic.tab.pin_to_top')
const [showTopicTime, setShowTopicTime] = usePreference('topic.tab.show_time')
const [assistantIconType, setAssistantIconType] = usePreference('assistant.icon_type')
const { navbarPosition, setNavbarPosition } = useNavbarPosition()
const { theme, settedTheme, setTheme } = useTheme()
const { t } = useTranslation()
const dispatch = useAppDispatch()
const [currentZoom, setCurrentZoom] = useState(1.0)
const { userTheme, setUserTheme } = useUserTheme()
const [visibleIcons, setVisibleIcons] = useState(sidebarIcons?.visible || DEFAULT_SIDEBAR_ICONS)
const [disabledIcons, setDisabledIcons] = useState(sidebarIcons?.disabled || [])
// const [visibleIcons, setVisibleIcons] = useState(sidebarIcons?.visible || DEFAULT_SIDEBAR_ICONS)
// const [disabledIcons, setDisabledIcons] = useState(sidebarIcons?.disabled || [])
const handleWindowStyleChange = useCallback(
(checked: boolean) => {
@ -98,10 +83,9 @@ const DisplaySettings: FC = () => {
)
const handleReset = useCallback(() => {
setVisibleIcons([...DEFAULT_SIDEBAR_ICONS])
setDisabledIcons([])
dispatch(setSidebarIcons({ visible: DEFAULT_SIDEBAR_ICONS, disabled: [] }))
}, [dispatch])
setVisibleIcons(DefaultPreferences.default['ui.sidebar.icons.visible'])
setInvisibleIcons(DefaultPreferences.default['ui.sidebar.icons.invisible'])
}, [setVisibleIcons, setInvisibleIcons])
const themeOptions = useMemo(
() => [
@ -278,7 +262,7 @@ const DisplaySettings: FC = () => {
<SettingRowTitle>{t('settings.advanced.auto_switch_to_topics')}</SettingRowTitle>
<Switch
checked={clickAssistantToShowTopic}
onChange={(checked) => dispatch(setClickAssistantToShowTopic(checked))}
onChange={(checked) => setClickAssistantToShowTopic(checked)}
/>
</SettingRow>
<SettingDivider />
@ -286,12 +270,12 @@ const DisplaySettings: FC = () => {
)}
<SettingRow>
<SettingRowTitle>{t('settings.topic.show.time')}</SettingRowTitle>
<Switch checked={showTopicTime} onChange={(checked) => dispatch(setShowTopicTime(checked))} />
<Switch checked={showTopicTime} onChange={(checked) => setShowTopicTime(checked)} />
</SettingRow>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.topic.pin_to_top')}</SettingRowTitle>
<Switch checked={pinTopicsToTop} onChange={(checked) => dispatch(setPinTopicsToTop(checked))} />
<Switch checked={pinTopicsToTop} onChange={(checked) => setPinTopicsToTop(checked)} />
</SettingRow>
</SettingGroup>
<SettingGroup theme={theme}>
@ -302,7 +286,7 @@ const DisplaySettings: FC = () => {
<Segmented
value={assistantIconType}
shape="round"
onChange={(value) => dispatch(setAssistantIconType(value as AssistantIconType))}
onChange={(value) => setAssistantIconType(value as AssistantIconType)}
options={assistantIconTypeOptions}
/>
</SettingRow>
@ -319,9 +303,9 @@ const DisplaySettings: FC = () => {
<SettingDivider />
<SidebarIconsManager
visibleIcons={visibleIcons}
disabledIcons={disabledIcons}
invisibleIcons={invisibleIcons}
setVisibleIcons={setVisibleIcons}
setDisabledIcons={setDisabledIcons}
setInvisibleIcons={setInvisibleIcons}
/>
</SettingGroup>
)}

View File

@ -8,9 +8,9 @@ import {
DropResult
} from '@hello-pangea/dnd'
import { getSidebarIconLabel } from '@renderer/i18n/label'
import { useAppDispatch } from '@renderer/store'
import { setSidebarIcons } from '@renderer/store/settings'
import { SidebarIcon } from '@renderer/types'
// import { useAppDispatch } from '@renderer/store'
// import { setSidebarIcons } from '@renderer/store/settings'
import { SidebarIcon } from '@shared/data/preferenceTypes'
import { message } from 'antd'
import {
Code,
@ -29,20 +29,20 @@ import styled from 'styled-components'
interface SidebarIconsManagerProps {
visibleIcons: SidebarIcon[]
disabledIcons: SidebarIcon[]
invisibleIcons: SidebarIcon[]
setVisibleIcons: (icons: SidebarIcon[]) => void
setDisabledIcons: (icons: SidebarIcon[]) => void
setInvisibleIcons: (icons: SidebarIcon[]) => void
}
const SidebarIconsManager: FC<SidebarIconsManagerProps> = ({
visibleIcons,
disabledIcons,
invisibleIcons,
setVisibleIcons,
setDisabledIcons
setInvisibleIcons
}) => {
const { t } = useTranslation()
const dispatch = useAppDispatch()
// const dispatch = useAppDispatch()
const onDragEnd = useCallback(
(result: DropResult) => {
@ -51,42 +51,39 @@ const SidebarIconsManager: FC<SidebarIconsManagerProps> = ({
const { source, destination } = result
// 如果是chat图标且目标是disabled区域,则不允许移动并提示
const draggedItem = source.droppableId === 'visible' ? visibleIcons[source.index] : disabledIcons[source.index]
const draggedItem = source.droppableId === 'visible' ? visibleIcons[source.index] : invisibleIcons[source.index]
if (draggedItem === 'assistants' && destination.droppableId === 'disabled') {
message.warning(t('settings.display.sidebar.chat.hiddenMessage'))
return
}
if (source.droppableId === destination.droppableId) {
const list = source.droppableId === 'visible' ? [...visibleIcons] : [...disabledIcons]
const list = source.droppableId === 'visible' ? [...visibleIcons] : [...invisibleIcons]
const [removed] = list.splice(source.index, 1)
list.splice(destination.index, 0, removed)
if (source.droppableId === 'visible') {
setVisibleIcons(list)
dispatch(setSidebarIcons({ visible: list, disabled: disabledIcons }))
} else {
setDisabledIcons(list)
dispatch(setSidebarIcons({ visible: visibleIcons, disabled: list }))
setInvisibleIcons(list)
}
return
}
const sourceList = source.droppableId === 'visible' ? [...visibleIcons] : [...disabledIcons]
const destList = destination.droppableId === 'visible' ? [...visibleIcons] : [...disabledIcons]
const sourceList = source.droppableId === 'visible' ? [...visibleIcons] : [...invisibleIcons]
const destList = destination.droppableId === 'visible' ? [...visibleIcons] : [...invisibleIcons]
const [removed] = sourceList.splice(source.index, 1)
const targetList = destList.filter((icon) => icon !== removed)
targetList.splice(destination.index, 0, removed)
const newVisibleIcons = destination.droppableId === 'visible' ? targetList : sourceList
const newDisabledIcons = destination.droppableId === 'disabled' ? targetList : sourceList
const newInvisibleIcons = destination.droppableId === 'disabled' ? targetList : sourceList
setVisibleIcons(newVisibleIcons)
setDisabledIcons(newDisabledIcons)
dispatch(setSidebarIcons({ visible: newVisibleIcons, disabled: newDisabledIcons }))
setInvisibleIcons(newInvisibleIcons)
},
[visibleIcons, disabledIcons, dispatch, setVisibleIcons, setDisabledIcons, t]
[visibleIcons, invisibleIcons, setVisibleIcons, setInvisibleIcons, t]
)
const onMoveIcon = useCallback(
@ -99,21 +96,19 @@ const SidebarIconsManager: FC<SidebarIconsManagerProps> = ({
if (fromList === 'visible') {
const newVisibleIcons = visibleIcons.filter((i) => i !== icon)
const newDisabledIcons = disabledIcons.some((i) => i === icon) ? disabledIcons : [...disabledIcons, icon]
const newInvisibleIcons = invisibleIcons.some((i) => i === icon) ? invisibleIcons : [...invisibleIcons, icon]
setVisibleIcons(newVisibleIcons)
setDisabledIcons(newDisabledIcons)
dispatch(setSidebarIcons({ visible: newVisibleIcons, disabled: newDisabledIcons }))
setInvisibleIcons(newInvisibleIcons)
} else {
const newDisabledIcons = disabledIcons.filter((i) => i !== icon)
const newInvisibleIcons = invisibleIcons.filter((i) => i !== icon)
const newVisibleIcons = visibleIcons.some((i) => i === icon) ? visibleIcons : [...visibleIcons, icon]
setDisabledIcons(newDisabledIcons)
setInvisibleIcons(newInvisibleIcons)
setVisibleIcons(newVisibleIcons)
dispatch(setSidebarIcons({ visible: newVisibleIcons, disabled: newDisabledIcons }))
}
},
[t, visibleIcons, disabledIcons, setVisibleIcons, setDisabledIcons, dispatch]
[t, visibleIcons, invisibleIcons, setVisibleIcons, setInvisibleIcons]
)
// 使用useMemo缓存图标映射
@ -169,10 +164,10 @@ const SidebarIconsManager: FC<SidebarIconsManagerProps> = ({
<Droppable droppableId="disabled">
{(provided: DroppableProvided) => (
<IconList ref={provided.innerRef} {...provided.droppableProps}>
{disabledIcons.length === 0 ? (
{invisibleIcons.length === 0 ? (
<EmptyPlaceholder>{t('settings.display.sidebar.empty')}</EmptyPlaceholder>
) : (
disabledIcons.map((icon, index) => (
invisibleIcons.map((icon, index) => (
<Draggable key={icon} draggableId={icon} index={index}>
{(provided: DraggableProvided) => (
<IconItem ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>

View File

@ -4,7 +4,7 @@ import InfoTooltip from '@renderer/components/InfoTooltip'
import { HStack } from '@renderer/components/Layout'
import Selector from '@renderer/components/Selector'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useEnableDeveloperMode, useSettings } from '@renderer/hooks/useSettings'
import { useSettings } from '@renderer/hooks/useSettings'
import { useTimer } from '@renderer/hooks/useTimer'
import i18n from '@renderer/i18n'
import { RootState, useAppDispatch } from '@renderer/store'
@ -42,16 +42,16 @@ const GeneralSettings: FC = () => {
enableDataCollection,
enableSpellCheck
} = useSettings()
const [proxyUrl, setProxyUrl] = useState<string | undefined>(storeProxyUrl)
const [proxyBypassRules, setProxyBypassRules] = useState<string | undefined>(storeProxyBypassRules)
const { theme } = useTheme()
const { enableDeveloperMode, setEnableDeveloperMode } = useEnableDeveloperMode()
const { setTimeoutTimer } = useTimer()
const [language, setLanguage] = usePreference('app.language')
const [disableHardwareAcceleration, setDisableHardwareAcceleration] = usePreference(
'app.disable_hardware_acceleration'
)
const [enableDeveloperMode, setEnableDeveloperMode] = usePreference('app.developer_mode.enabled')
const [proxyUrl, setProxyUrl] = useState<string | undefined>(storeProxyUrl)
const [proxyBypassRules, setProxyBypassRules] = useState<string | undefined>(storeProxyBypassRules)
const { theme } = useTheme()
const { setTimeoutTimer } = useTimer()
const updateTray = (isShowTray: boolean) => {
setTray(isShowTray)

View File

@ -3,7 +3,6 @@ import InfoTooltip from '@renderer/components/InfoTooltip'
import { HStack } from '@renderer/components/Layout'
import ModelSelector from '@renderer/components/ModelSelector'
import { isEmbeddingModel, isRerankModel, isTextToImageModel } from '@renderer/config/models'
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useDefaultModel } from '@renderer/hooks/useAssistant'
import { useProviders } from '@renderer/hooks/useProvider'
@ -12,6 +11,7 @@ import { getModelUniqId, hasModel } from '@renderer/services/ModelService'
import { useAppDispatch } from '@renderer/store'
import { setTranslateModelPrompt } from '@renderer/store/settings'
import { Model } from '@renderer/types'
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import { Button, Tooltip } from 'antd'
import { find } from 'lodash'
import { Languages, MessageSquareMore, Rocket, Settings2 } from 'lucide-react'

View File

@ -1,10 +1,10 @@
import { RedoOutlined } from '@ant-design/icons'
import { HStack } from '@renderer/components/Layout'
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useSettings } from '@renderer/hooks/useSettings'
import { useAppDispatch } from '@renderer/store'
import { setTranslateModelPrompt } from '@renderer/store/settings'
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import { Input, Tooltip } from 'antd'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'

View File

@ -127,7 +127,7 @@ export const searchKnowledgeBase = async (
const threshold = base.threshold || DEFAULT_KNOWLEDGE_THRESHOLD
if (topicId) {
currentSpan = addSpan({
currentSpan = await addSpan({
topicId,
name: `${base.name}-search`,
inputs: {
@ -225,7 +225,7 @@ export const processKnowledgeSearch = async (
return []
}
const span = addSpan({
const span = await addSpan({
topicId,
name: 'knowledgeSearch',
inputs: {

View File

@ -1,11 +1,11 @@
import { MessageStream } from '@anthropic-ai/sdk/resources/messages/messages'
import { preferenceService } from '@data/PreferenceService'
import { loggerService } from '@logger'
import { SpanEntity, TokenUsage } from '@mcp-trace/trace-core'
import { cleanContext, endContext, getContext, startContext } from '@mcp-trace/trace-web'
import { Context, context, Span, SpanStatusCode, trace } from '@opentelemetry/api'
import { isAsyncIterable } from '@renderer/aiCore/middleware/utils'
import { db } from '@renderer/databases'
import { getEnableDeveloperMode } from '@renderer/hooks/useSettings'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { handleAsyncIterable } from '@renderer/trace/dataHandler/AsyncIterableHandler'
import { handleResult } from '@renderer/trace/dataHandler/CommonResultHandler'
@ -23,6 +23,10 @@ const logger = loggerService.withContext('SpanManagerService')
class SpanManagerService {
private spanMap: Map<string, ModelSpanEntity[]> = new Map()
async getEnableDeveloperMode() {
return await preferenceService.get('app.developer_mode.enabled')
}
getModelSpanEntity(topicId: string, modelName?: string) {
const entities = this.spanMap.get(topicId)
if (!entities) {
@ -38,8 +42,8 @@ class SpanManagerService {
return entity
}
startTrace(params: StartSpanParams, models?: Model[]) {
if (!getEnableDeveloperMode()) {
async startTrace(params: StartSpanParams, models?: Model[]) {
if (!(await this.getEnableDeveloperMode())) {
return
}
@ -64,7 +68,7 @@ class SpanManagerService {
}
async restartTrace(message: Message, text?: string) {
if (!getEnableDeveloperMode()) {
if (!(await this.getEnableDeveloperMode())) {
return
}
@ -98,7 +102,7 @@ class SpanManagerService {
}
async appendTrace(message: Message, model: Model) {
if (!getEnableDeveloperMode()) {
if (!(await this.getEnableDeveloperMode())) {
return
}
if (!message.traceId) {
@ -190,8 +194,8 @@ class SpanManagerService {
window.api.trace.saveData(params.topicId)
}
addSpan(params: StartSpanParams) {
if (!getEnableDeveloperMode()) {
async addSpan(params: StartSpanParams) {
if (!(await this.getEnableDeveloperMode())) {
return
}
const entity = this.getModelSpanEntity(params.topicId, params.modelName)
@ -289,15 +293,15 @@ class SpanManagerService {
* @param getTopicId Function to get topicId from arguments.
* @returns The result of the executed function.
*/
export function withSpanResult<F extends (...args: any) => any>(
export async function withSpanResult<F extends (...args: any) => any>(
fn: F,
params: StartSpanParams,
...args: Parameters<F>
): ReturnType<F> {
): Promise<ReturnType<F>> {
if (!params.topicId || params.topicId === '') {
return fn(...args)
}
const span = addSpan({
const span = await addSpan({
topicId: params.topicId,
name: params.name,
tag: params.tag,

View File

@ -443,7 +443,7 @@ class WebSearchService {
const signal = this.getRequestState(requestId).signal || this.signal
const span = webSearchProvider.topicId
? addSpan({
? await addSpan({
topicId: webSearchProvider.topicId,
name: `WebSearch`,
inputs: {

View File

@ -15,7 +15,7 @@ import {
isSupportStreamOptionsProvider,
SYSTEM_PROVIDERS
} from '@renderer/config/providers'
import { DEFAULT_SIDEBAR_ICONS } from '@renderer/config/sidebar'
// import { DEFAULT_SIDEBAR_ICONS } from '@renderer/config/sidebar'
import db from '@renderer/databases'
import i18n from '@renderer/i18n'
import { DEFAULT_ASSISTANT_SETTINGS } from '@renderer/services/AssistantService'
@ -33,6 +33,7 @@ import {
import { getDefaultGroupName, getLeadingEmoji, runAsyncFunction, uuid } from '@renderer/utils'
import { defaultByPassRules, UpgradeChannel } from '@shared/config/constant'
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import { DefaultPreferences } from '@shared/data/preferences'
import { isEmpty } from 'lodash'
import { createMigrate } from 'redux-persist'
@ -45,7 +46,6 @@ import { initialState as notesInitialState } from './note'
import { initialState as settingsInitialState } from './settings'
import { initialState as shortcutsInitialState } from './shortcuts'
import { defaultWebSearchProviders } from './websearch'
const logger = loggerService.withContext('Migrate')
// remove logo base64 data to reduce the size of the state
@ -804,7 +804,7 @@ const migrateConfig = {
})
}
state.settings.sidebarIcons = {
visible: DEFAULT_SIDEBAR_ICONS,
visible: DefaultPreferences.default['ui.sidebar.icons.visible'],
disabled: []
}
return state
@ -816,7 +816,7 @@ const migrateConfig = {
try {
if (!state.settings.sidebarIcons) {
state.settings.sidebarIcons = {
visible: DEFAULT_SIDEBAR_ICONS,
visible: DefaultPreferences.default['ui.sidebar.icons.visible'],
disabled: []
}
}
@ -2181,10 +2181,10 @@ const migrateConfig = {
'136': (state: RootState) => {
try {
state.settings.sidebarIcons.visible = [...new Set(state.settings.sidebarIcons.visible)].filter((icon) =>
DEFAULT_SIDEBAR_ICONS.includes(icon)
DefaultPreferences.default['ui.sidebar.icons.visible'].includes(icon)
)
state.settings.sidebarIcons.disabled = [...new Set(state.settings.sidebarIcons.disabled)].filter((icon) =>
DEFAULT_SIDEBAR_ICONS.includes(icon)
DefaultPreferences.default['ui.sidebar.icons.visible'].includes(icon)
)
return state
} catch (error) {

View File

@ -1,6 +1,5 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { isMac } from '@renderer/config/constant'
import { DEFAULT_SIDEBAR_ICONS } from '@renderer/config/sidebar'
import {
ApiServerConfig,
CodeStyleVarious,
@ -9,15 +8,20 @@ import {
OpenAISummaryText,
PaintingProvider,
S3Config,
SidebarIcon,
TranslateLanguageCode
} from '@renderer/types'
import { uuid } from '@renderer/utils'
import { UpgradeChannel } from '@shared/config/constant'
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import type { SendMessageShortcut } from '@shared/data/preferenceTypes'
import type { AssistantTabSortType } from '@shared/data/preferenceTypes'
import { LanguageVarious, ThemeMode } from '@shared/data/preferenceTypes'
import { DefaultPreferences } from '@shared/data/preferences'
import type {
AssistantIconType,
AssistantTabSortType,
LanguageVarious,
SendMessageShortcut,
SidebarIcon
} from '@shared/data/preferenceTypes'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { OpenAIVerbosity } from '@types'
import { RemoteSyncState } from './backup'
@ -25,11 +29,11 @@ import { RemoteSyncState } from './backup'
// export type SendMessageShortcut = 'Enter' | 'Shift+Enter' | 'Ctrl+Enter' | 'Command+Enter' | 'Alt+Enter'
// Re-export for backward compatibility
export { DEFAULT_SIDEBAR_ICONS }
// export { DEFAULT_SIDEBAR_ICONS }
export interface NutstoreSyncRuntime extends RemoteSyncState {}
export type AssistantIconType = 'model' | 'emoji' | 'none'
// export type AssistantIconType = 'model' | 'emoji' | 'none'
export type UserTheme = {
colorPrimary: string
@ -310,7 +314,7 @@ export const initialState: SettingsState = {
customCss: '',
topicNamingPrompt: '',
sidebarIcons: {
visible: DEFAULT_SIDEBAR_ICONS,
visible: DefaultPreferences.default['ui.sidebar.icons.visible'],
disabled: []
},
narrowMode: false,

View File

@ -722,16 +722,17 @@ export const isAutoDetectionMethod = (method: string): method is AutoDetectionMe
return Object.hasOwn(AutoDetectionMethods, method)
}
export type SidebarIcon =
| 'assistants'
| 'agents'
| 'paintings'
| 'translate'
| 'minapp'
| 'knowledge'
| 'files'
| 'code_tools'
| 'notes'
// by fullex @ data refactor
// export type SidebarIcon =
// | 'assistants'
// | 'agents'
// | 'paintings'
// | 'translate'
// | 'minapp'
// | 'knowledge'
// | 'files'
// | 'code_tools'
// | 'notes'
export type ExternalToolResult = {
mcpTools?: MCPTool[]