feature: customizable sidebar module #644

close #644
This commit is contained in:
kangfenmao 2025-01-06 16:59:10 +08:00
parent ac9017c031
commit d0948e6f8a
14 changed files with 124 additions and 56 deletions

View File

@ -21,7 +21,8 @@ const Sidebar: FC = () => {
const { minappShow } = useRuntime()
const { t } = useTranslation()
const navigate = useNavigate()
const { windowStyle, showMinappIcon, showFilesIcon } = useSettings()
const { windowStyle, showTranslateIcon, showPaintingIcon, showMinappIcon, showKnowledgeIcon, showFilesIcon } =
useSettings()
const { theme, toggleTheme } = useTheme()
const isRoute = (path: string): string => (pathname === path ? 'active' : '')
@ -61,6 +62,7 @@ const Sidebar: FC = () => {
</Icon>
</StyledLink>
</Tooltip>
{showPaintingIcon && (
<Tooltip title={t('paintings.title')} mouseEnterDelay={0.8} placement="right">
<StyledLink onClick={() => to('/paintings')}>
<Icon className={isRoute('/paintings')}>
@ -68,6 +70,8 @@ const Sidebar: FC = () => {
</Icon>
</StyledLink>
</Tooltip>
)}
{showTranslateIcon && (
<Tooltip title={t('translate.title')} mouseEnterDelay={0.8} placement="right">
<StyledLink onClick={() => to('/translate')}>
<Icon className={isRoute('/translate')}>
@ -75,6 +79,7 @@ const Sidebar: FC = () => {
</Icon>
</StyledLink>
</Tooltip>
)}
{showMinappIcon && (
<Tooltip title={t('minapp.title')} mouseEnterDelay={0.8} placement="right">
<StyledLink onClick={() => to('/apps')}>
@ -84,6 +89,7 @@ const Sidebar: FC = () => {
</StyledLink>
</Tooltip>
)}
{showKnowledgeIcon && (
<Tooltip title={t('knowledge_base.title')} mouseEnterDelay={0.5} placement="right">
<StyledLink onClick={() => to('/knowledge')}>
<Icon className={isRoute('/knowledge')}>
@ -91,6 +97,7 @@ const Sidebar: FC = () => {
</Icon>
</StyledLink>
</Tooltip>
)}
{showFilesIcon && (
<Tooltip title={t('files.title')} mouseEnterDelay={0.8} placement="right">
<StyledLink onClick={() => to('/files')}>

View File

@ -2,7 +2,6 @@ import { isMac } from '@renderer/config/constant'
import { isLocalAi } from '@renderer/config/env'
import db from '@renderer/databases'
import i18n from '@renderer/i18n'
import { startAutoSync, stopAutoSync } from '@renderer/services/BackupService'
import { useAppDispatch } from '@renderer/store'
import { setAvatar, setFilesPath, setUpdateState } from '@renderer/store/runtime'
import { delay, runAsyncFunction } from '@renderer/utils'
@ -16,16 +15,7 @@ import useUpdateHandler from './useUpdateHandler'
export function useAppInit() {
const dispatch = useAppDispatch()
const {
proxyUrl,
language,
windowStyle,
manualUpdateCheck,
proxyMode,
webdavAutoSync,
webdavSyncInterval,
customCss
} = useSettings()
const { proxyUrl, language, windowStyle, manualUpdateCheck, proxyMode, customCss } = useSettings()
const { minappShow } = useRuntime()
const { setDefaultModel, setTopicNamingModel, setTranslateModel } = useDefaultModel()
const avatar = useLiveQuery(() => db.settings.get('image://avatar'))
@ -84,10 +74,6 @@ export function useAppInit() {
})
}, [dispatch])
useEffect(() => {
webdavAutoSync ? startAutoSync() : stopAutoSync()
}, [webdavAutoSync, webdavSyncInterval])
useEffect(() => {
import('@renderer/queue/KnowledgeQueue')
}, [])

View File

@ -394,7 +394,10 @@
"general.user_name.placeholder": "Enter your name",
"general.view_webdav_settings": "View WebDAV settings",
"general.display.title": "Display Settings",
"display.sidebar.translate.icon": "Show Translate icon",
"display.sidebar.painting.icon": "Show Painting icon",
"display.sidebar.minapp.icon": "Show MinApp icon",
"display.sidebar.knowledge.icon": "Show Knowledge icon",
"display.sidebar.files.icon": "Show Files icon",
"display.sidebar.title": "Sidebar Settings",
"display.topic.title": "Topic Settings",

View File

@ -392,7 +392,10 @@
"general.user_name.placeholder": "ユーザー名を入力",
"general.view_webdav_settings": "WebDAV設定を表示",
"general.display.title": "表示設定",
"display.sidebar.translate.icon": "翻訳のアイコンを表示",
"display.sidebar.painting.icon": "絵画のアイコンを表示",
"display.sidebar.minapp.icon": "ミニアプリのアイコンを表示",
"display.sidebar.knowledge.icon": "ナレッジのアイコンを表示",
"display.sidebar.files.icon": "ファイルのアイコンを表示",
"display.sidebar.title": "サイドバー設定",
"display.topic.title": "トピック設定",

View File

@ -394,7 +394,10 @@
"general.user_name.placeholder": "Введите ваше имя",
"general.view_webdav_settings": "Просмотр настроек WebDAV",
"general.display.title": "Настройки отображения",
"display.sidebar.translate.icon": "Показывать иконку перевода",
"display.sidebar.painting.icon": "Показывать иконку рисования",
"display.sidebar.minapp.icon": "Показывать иконку мини-приложения",
"display.sidebar.knowledge.icon": "Показывать иконку знаний",
"display.sidebar.files.icon": "Показывать иконку файлов",
"display.sidebar.title": "Настройки боковой панели",
"display.topic.title": "Настройки топиков",

View File

@ -395,7 +395,10 @@
"general.user_name.placeholder": "请输入用户名",
"general.view_webdav_settings": "查看 WebDAV 设置",
"general.display.title": "显示设置",
"display.sidebar.translate.icon": "显示翻译图标",
"display.sidebar.painting.icon": "显示绘画图标",
"display.sidebar.minapp.icon": "显示小程序图标",
"display.sidebar.knowledge.icon": "显示知识图标",
"display.sidebar.files.icon": "显示文件图标",
"display.sidebar.title": "侧边栏设置",
"display.topic.title": "话题设置",

View File

@ -394,7 +394,10 @@
"general.user_name.placeholder": "輸入您的名稱",
"general.view_webdav_settings": "查看 WebDAV 設定",
"general.display.title": "顯示設定",
"display.sidebar.translate.icon": "顯示翻譯圖示",
"display.sidebar.painting.icon": "顯示繪圖圖示",
"display.sidebar.minapp.icon": "顯示小程序圖示",
"display.sidebar.knowledge.icon": "顯示知識圖示",
"display.sidebar.files.icon": "顯示文件圖示",
"display.sidebar.title": "側邊欄設定",
"display.topic.title": "話題設定",

View File

@ -1,8 +1,19 @@
import KeyvStorage from '@kangfenmao/keyv-storage'
function init() {
import { startAutoSync } from './services/BackupService'
import store from './store'
function initKeyv() {
window.keyv = new KeyvStorage()
window.keyv.init()
}
init()
function initAutoSync() {
const { webdavAutoSync } = store.getState().settings
if (webdavAutoSync) {
startAutoSync()
}
}
initKeyv()
initAutoSync()

View File

@ -62,7 +62,8 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic }) => {
showInputEstimatedTokens,
clickAssistantToShowTopic,
language,
autoTranslateWithSpace
autoTranslateWithSpace,
showKnowledgeIcon
} = useSettings()
const [expended, setExpend] = useState(false)
const [estimateTokenCount, setEstimateTokenCount] = useState(0)
@ -450,12 +451,14 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic }) => {
<ControlOutlined />
</ToolbarButton>
</Tooltip>
{showKnowledgeIcon && (
<KnowledgeBaseButton
selectedBase={selectedKnowledgeBase}
onSelect={handleKnowledgeBaseSelect}
ToolbarButton={ToolbarButton}
disabled={files.length > 0}
/>
)}
<AttachmentButton
model={model}
files={files}

View File

@ -27,7 +27,7 @@ const getAvatarSource = (isLocalAi: boolean, modelId: string | undefined) => {
const MessageHeader: FC<Props> = memo(({ assistant, model, message }) => {
const avatar = useAvatar()
const { theme } = useTheme()
const { userName } = useSettings()
const { userName, showMinappIcon } = useSettings()
const { t } = useTranslation()
const { isBubbleStyle } = useMessageStyle()
@ -44,7 +44,9 @@ const MessageHeader: FC<Props> = memo(({ assistant, model, message }) => {
const avatarName = useMemo(() => firstLetter(assistant?.name).toUpperCase(), [assistant?.name])
const username = useMemo(() => removeLeadingEmoji(getUserName()), [getUserName])
const showMiniApp = useCallback(() => model?.provider && startMinAppById(model.provider), [model?.provider])
const showMiniApp = useCallback(() => {
showMinappIcon && model?.provider && startMinAppById(model.provider)
}, [model?.provider, showMinappIcon])
const avatarStyle: CSSProperties | undefined = isBubbleStyle
? {
@ -62,7 +64,7 @@ const MessageHeader: FC<Props> = memo(({ assistant, model, message }) => {
size={35}
style={{
borderRadius: '20%',
cursor: 'pointer',
cursor: showMinappIcon ? 'pointer' : 'default',
border: isLocalAi ? '1px solid var(--color-border-soft)' : 'none',
filter: theme === 'dark' ? 'invert(0.05)' : undefined
}}

View File

@ -25,7 +25,7 @@ interface Props {
const HeaderNavbar: FC<Props> = ({ activeAssistant }) => {
const { assistant } = useAssistant(activeAssistant.id)
const { showAssistants, toggleShowAssistants } = useShowAssistants()
const { topicPosition } = useSettings()
const { topicPosition, showMinappIcon } = useSettings()
const { showTopics, toggleShowTopics } = useShowTopics()
useShortcut('toggle_show_assistants', () => {
@ -79,11 +79,13 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant }) => {
<NavbarIcon onClick={() => SearchPopup.show()}>
<SearchOutlined />
</NavbarIcon>
{showMinappIcon && (
<AppStorePopover>
<NavbarIcon style={{ marginLeft: isMac ? 5 : 10 }}>
<i className="iconfont icon-appstore" />
</NavbarIcon>
</AppStorePopover>
)}
{topicPosition === 'right' && (
<NavbarIcon onClick={toggleShowTopics} style={{ marginLeft: isMac ? 5 : 10 }}>
<i className={`iconfont icon-${showTopics ? 'show' : 'hide'}-sidebar`} />

View File

@ -6,8 +6,11 @@ import {
setClickAssistantToShowTopic,
setCustomCss,
setShowFilesIcon,
setShowKnowledgeIcon,
setShowMinappIcon,
setShowTopicTime
setShowPaintingIcon,
setShowTopicTime,
setShowTranslateIcon
} from '@renderer/store/settings'
import { ThemeMode } from '@renderer/types'
import { Input, Select, Switch } from 'antd'
@ -22,7 +25,10 @@ const DisplaySettings: FC = () => {
theme,
windowStyle,
setWindowStyle,
showTranslateIcon,
showPaintingIcon,
showMinappIcon,
showKnowledgeIcon,
showFilesIcon,
topicPosition,
setTopicPosition,
@ -103,11 +109,26 @@ const DisplaySettings: FC = () => {
<SettingGroup theme={theme}>
<SettingTitle>{t('settings.display.sidebar.title')}</SettingTitle>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.display.sidebar.translate.icon')}</SettingRowTitle>
<Switch checked={showTranslateIcon} onChange={(value) => dispatch(setShowTranslateIcon(value))} />
</SettingRow>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.display.sidebar.painting.icon')}</SettingRowTitle>
<Switch checked={showPaintingIcon} onChange={(value) => dispatch(setShowPaintingIcon(value))} />
</SettingRow>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.display.sidebar.minapp.icon')}</SettingRowTitle>
<Switch checked={showMinappIcon} onChange={(value) => dispatch(setShowMinappIcon(value))} />
</SettingRow>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.display.sidebar.knowledge.icon')}</SettingRowTitle>
<Switch checked={showKnowledgeIcon} onChange={(value) => dispatch(setShowKnowledgeIcon(value))} />
</SettingRow>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.display.sidebar.files.icon')}</SettingRowTitle>
<Switch checked={showFilesIcon} onChange={(value) => dispatch(setShowFilesIcon(value))} />

View File

@ -786,6 +786,9 @@ const migrateConfig = {
system: false
})
}
state.settings.showTranslateIcon = true
state.settings.showPaintingIcon = true
state.settings.showKnowledgeIcon = true
return state
}
}

View File

@ -42,7 +42,10 @@ export interface SettingsState {
autoTranslateWithSpace: boolean
enableTopicNaming: boolean
// Sidebar icons
showTranslateIcon: boolean
showPaintingIcon: boolean
showMinappIcon: boolean
showKnowledgeIcon: boolean
showFilesIcon: boolean
customCss: string
topicNamingPrompt: string
@ -84,7 +87,10 @@ const initialState: SettingsState = {
translateModelPrompt: TRANSLATE_PROMPT,
autoTranslateWithSpace: false,
enableTopicNaming: true,
showTranslateIcon: true,
showPaintingIcon: true,
showMinappIcon: true,
showKnowledgeIcon: true,
showFilesIcon: true,
customCss: '',
topicNamingPrompt: ''
@ -203,9 +209,18 @@ const settingsSlice = createSlice({
setEnableTopicNaming: (state, action: PayloadAction<boolean>) => {
state.enableTopicNaming = action.payload
},
setShowTranslateIcon: (state, action: PayloadAction<boolean>) => {
state.showTranslateIcon = action.payload
},
setShowPaintingIcon: (state, action: PayloadAction<boolean>) => {
state.showPaintingIcon = action.payload
},
setShowMinappIcon: (state, action: PayloadAction<boolean>) => {
state.showMinappIcon = action.payload
},
setShowKnowledgeIcon: (state, action: PayloadAction<boolean>) => {
state.showKnowledgeIcon = action.payload
},
setShowFilesIcon: (state, action: PayloadAction<boolean>) => {
state.showFilesIcon = action.payload
},
@ -258,7 +273,10 @@ export const {
setTranslateModelPrompt,
setAutoTranslateWithSpace,
setEnableTopicNaming,
setShowTranslateIcon,
setShowPaintingIcon,
setShowMinappIcon,
setShowKnowledgeIcon,
setShowFilesIcon,
setPasteLongTextThreshold,
setCustomCss,