refactor: sidebar minapps
This commit is contained in:
parent
7633d70435
commit
49a5bc7900
@ -47,19 +47,22 @@ const DragableList: FC<Props<any>> = ({
|
||||
<Droppable droppableId="droppable" {...droppableProps}>
|
||||
{(provided) => (
|
||||
<div {...provided.droppableProps} ref={provided.innerRef} style={{ ...style }}>
|
||||
{list.map((item, index) => (
|
||||
<Draggable key={`draggable_${item.id}_${index}`} draggableId={item.id} index={index} {...droppableProps}>
|
||||
{(provided) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
style={{ ...provided.draggableProps.style, marginBottom: 8, ...listStyle }}>
|
||||
{children(item, index)}
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
))}
|
||||
{list.map((item, index) => {
|
||||
const id = item.id || item
|
||||
return (
|
||||
<Draggable key={`draggable_${id}_${index}`} draggableId={id} index={index} {...droppableProps}>
|
||||
{(provided) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
style={{ ...provided.draggableProps.style, marginBottom: 8, ...listStyle }}>
|
||||
{children(item, index)}
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
|
||||
@ -5,6 +5,7 @@ import { useBridge } from '@renderer/hooks/useBridge'
|
||||
import store from '@renderer/store'
|
||||
import { setMinappShow } from '@renderer/store/runtime'
|
||||
import { MinAppType } from '@renderer/types'
|
||||
import { delay } from '@renderer/utils'
|
||||
import { Avatar, Drawer } from 'antd'
|
||||
import { WebviewTag } from 'electron'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
@ -28,9 +29,10 @@ const PopupContainer: React.FC<Props> = ({ app, resolve }) => {
|
||||
|
||||
const canOpenExternalLink = app.url.startsWith('http://') || app.url.startsWith('https://')
|
||||
|
||||
const onClose = () => {
|
||||
const onClose = async (_delay = 0.3) => {
|
||||
setOpen(false)
|
||||
setTimeout(() => resolve({}), 300)
|
||||
await delay(_delay)
|
||||
resolve({})
|
||||
}
|
||||
|
||||
MinApp.onClose = onClose
|
||||
@ -58,7 +60,7 @@ const PopupContainer: React.FC<Props> = ({ app, resolve }) => {
|
||||
<ExportOutlined />
|
||||
</Button>
|
||||
)}
|
||||
<Button onClick={onClose}>
|
||||
<Button onClick={() => onClose()}>
|
||||
<CloseOutlined />
|
||||
</Button>
|
||||
</ButtonsGroup>
|
||||
@ -99,7 +101,7 @@ const PopupContainer: React.FC<Props> = ({ app, resolve }) => {
|
||||
<Drawer
|
||||
title={<Title />}
|
||||
placement="bottom"
|
||||
onClose={onClose}
|
||||
onClose={() => onClose()}
|
||||
open={open}
|
||||
mask={true}
|
||||
rootClassName="minapp-drawer"
|
||||
@ -202,40 +204,39 @@ const EmptyView = styled.div`
|
||||
export default class MinApp {
|
||||
static topviewId = 0
|
||||
static onClose = () => {}
|
||||
static isOpening = false
|
||||
static app: MinAppType | null = null
|
||||
|
||||
static async start(app: MinAppType) {
|
||||
if (this.isOpening) return
|
||||
this.isOpening = true
|
||||
|
||||
try {
|
||||
// 先关闭现有的小程序
|
||||
await this.close()
|
||||
|
||||
// 确保 webview 完全卸载
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
store.dispatch(setMinappShow(true))
|
||||
return new Promise<any>((resolve) => {
|
||||
TopView.show(
|
||||
<PopupContainer
|
||||
app={app}
|
||||
resolve={(v) => {
|
||||
resolve(v)
|
||||
this.close()
|
||||
}}
|
||||
/>,
|
||||
'MinApp'
|
||||
)
|
||||
})
|
||||
} finally {
|
||||
this.isOpening = false
|
||||
if (MinApp.app?.id === app.id) {
|
||||
return
|
||||
}
|
||||
|
||||
if (MinApp.app) {
|
||||
// @ts-ignore delay params
|
||||
await MinApp.onClose(0)
|
||||
await delay(0)
|
||||
}
|
||||
|
||||
MinApp.app = app
|
||||
store.dispatch(setMinappShow(true))
|
||||
|
||||
return new Promise<any>((resolve) => {
|
||||
TopView.show(
|
||||
<PopupContainer
|
||||
app={app}
|
||||
resolve={(v) => {
|
||||
resolve(v)
|
||||
this.close()
|
||||
}}
|
||||
/>,
|
||||
'MinApp'
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
static close() {
|
||||
if (!this.isOpening) return
|
||||
TopView.hide('MinApp')
|
||||
store.dispatch(setMinappShow(false))
|
||||
MinApp.app = null
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,21 @@
|
||||
import { FileSearchOutlined, FolderOutlined, PictureOutlined, TranslationOutlined } from '@ant-design/icons'
|
||||
import { isMac } from '@renderer/config/constant'
|
||||
import { isLocalAi, UserAvatar } from '@renderer/config/env'
|
||||
import { getAllMinApps } from '@renderer/config/minapps'
|
||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||
import useAvatar from '@renderer/hooks/useAvatar'
|
||||
import { useMinapps } from '@renderer/hooks/useMinapps'
|
||||
import { modelGenerating, useRuntime } from '@renderer/hooks/useRuntime'
|
||||
import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import type { MenuProps } from 'antd'
|
||||
import { Tooltip } from 'antd'
|
||||
import { Avatar } from 'antd'
|
||||
import { Dropdown } from 'antd'
|
||||
import { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import DragableList from '../DragableList'
|
||||
import MinApp from '../MinApp'
|
||||
import UserPopup from '../Popups/UserPopup'
|
||||
|
||||
@ -22,79 +25,22 @@ const Sidebar: FC = () => {
|
||||
const { minappShow } = useRuntime()
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const { windowStyle, sidebarIcons, miniAppIcons } = useSettings()
|
||||
const { windowStyle, sidebarIcons } = useSettings()
|
||||
const { theme, toggleTheme } = useTheme()
|
||||
const allApps = getAllMinApps()
|
||||
|
||||
const isRoute = (path: string): string => (pathname === path ? 'active' : '')
|
||||
const isRoutes = (path: string): string => (pathname.startsWith(path) ? 'active' : '')
|
||||
const { pinned } = useMinapps()
|
||||
|
||||
const onEditUser = () => UserPopup.show()
|
||||
|
||||
const macTransparentWindow = isMac && windowStyle === 'transparent'
|
||||
const sidebarBgColor = macTransparentWindow ? 'transparent' : 'var(--navbar-background)'
|
||||
|
||||
const showPinnedApps = pinned.length > 0 && sidebarIcons.visible.includes('minapp')
|
||||
|
||||
const to = async (path: string) => {
|
||||
await modelGenerating()
|
||||
navigate(path)
|
||||
}
|
||||
|
||||
const renderMainMenus = () => {
|
||||
return sidebarIcons.visible.map((icon) => {
|
||||
const iconMap = {
|
||||
assistants: <i className="iconfont icon-chat" />,
|
||||
agents: <i className="iconfont icon-business-smart-assistant" />,
|
||||
paintings: <PictureOutlined style={{ fontSize: 16 }} />,
|
||||
translate: <TranslationOutlined />,
|
||||
minapp: <i className="iconfont icon-appstore" />,
|
||||
knowledge: <FileSearchOutlined />,
|
||||
files: <FolderOutlined />
|
||||
}
|
||||
|
||||
const pathMap = {
|
||||
assistants: '/',
|
||||
agents: '/agents',
|
||||
paintings: '/paintings',
|
||||
translate: '/translate',
|
||||
minapp: '/apps',
|
||||
knowledge: '/knowledge',
|
||||
files: '/files'
|
||||
}
|
||||
|
||||
const path = pathMap[icon]
|
||||
const isActive = path === '/' ? isRoute(path) : isRoutes(path)
|
||||
|
||||
return (
|
||||
<Tooltip key={icon} title={t(`${icon}.title`)} mouseEnterDelay={0.8} placement="right">
|
||||
<StyledLink onClick={() => to(path)}>
|
||||
<Icon className={isActive}>{iconMap[icon]}</Icon>
|
||||
</StyledLink>
|
||||
</Tooltip>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const renderPinnedApps = () => {
|
||||
if (!miniAppIcons?.pinned) return null
|
||||
const pinnedApps = allApps.filter((app) => miniAppIcons.pinned.includes(app.id))
|
||||
return pinnedApps.map((app) => (
|
||||
<Tooltip key={app.id} title={app.name} mouseEnterDelay={0.8} placement="right">
|
||||
<StyledLink>
|
||||
<Icon onClick={() => MinApp.start(app)}>
|
||||
<AppIcon
|
||||
src={app.logo}
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
border: app.bodered ? '0.5px solid var(--color-border)' : 'none'
|
||||
}}
|
||||
/>
|
||||
</Icon>
|
||||
</StyledLink>
|
||||
</Tooltip>
|
||||
))
|
||||
}
|
||||
|
||||
return (
|
||||
<Container
|
||||
id="app-sidebar"
|
||||
@ -103,14 +49,19 @@ const Sidebar: FC = () => {
|
||||
zIndex: minappShow ? 10000 : 'initial'
|
||||
}}>
|
||||
<AvatarImg src={avatar || UserAvatar} draggable={false} className="nodrag" onClick={onEditUser} />
|
||||
<MainMenus>
|
||||
<ScrollContainer>
|
||||
<Menus onClick={MinApp.onClose}>
|
||||
{renderMainMenus()}
|
||||
{renderPinnedApps()}
|
||||
</Menus>
|
||||
</ScrollContainer>
|
||||
</MainMenus>
|
||||
<MainMenusContainer>
|
||||
<Menus onClick={MinApp.onClose}>
|
||||
<MainMenus />
|
||||
</Menus>
|
||||
{showPinnedApps && (
|
||||
<AppsContainer>
|
||||
<Divider />
|
||||
<Menus>
|
||||
<PinnedApps />
|
||||
</Menus>
|
||||
</AppsContainer>
|
||||
)}
|
||||
</MainMenusContainer>
|
||||
<Menus onClick={MinApp.onClose}>
|
||||
<Tooltip title={t('settings.theme.title')} mouseEnterDelay={0.8} placement="right">
|
||||
<Icon onClick={() => toggleTheme()}>
|
||||
@ -133,6 +84,89 @@ const Sidebar: FC = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const MainMenus: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const { pathname } = useLocation()
|
||||
const { sidebarIcons } = useSettings()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const isRoute = (path: string): string => (pathname === path ? 'active' : '')
|
||||
const isRoutes = (path: string): string => (pathname.startsWith(path) ? 'active' : '')
|
||||
|
||||
const iconMap = {
|
||||
assistants: <i className="iconfont icon-chat" />,
|
||||
agents: <i className="iconfont icon-business-smart-assistant" />,
|
||||
paintings: <PictureOutlined style={{ fontSize: 16 }} />,
|
||||
translate: <TranslationOutlined />,
|
||||
minapp: <i className="iconfont icon-appstore" />,
|
||||
knowledge: <FileSearchOutlined />,
|
||||
files: <FolderOutlined />
|
||||
}
|
||||
|
||||
const pathMap = {
|
||||
assistants: '/',
|
||||
agents: '/agents',
|
||||
paintings: '/paintings',
|
||||
translate: '/translate',
|
||||
minapp: '/apps',
|
||||
knowledge: '/knowledge',
|
||||
files: '/files'
|
||||
}
|
||||
|
||||
return sidebarIcons.visible.map((icon) => {
|
||||
const path = pathMap[icon]
|
||||
const isActive = path === '/' ? isRoute(path) : isRoutes(path)
|
||||
|
||||
return (
|
||||
<Tooltip key={icon} title={t(`${icon}.title`)} mouseEnterDelay={0.8} placement="right">
|
||||
<StyledLink onClick={() => navigate(path)}>
|
||||
<Icon className={isActive}>{iconMap[icon]}</Icon>
|
||||
</StyledLink>
|
||||
</Tooltip>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const PinnedApps: FC = () => {
|
||||
const { pinned, updatePinnedMinapps } = useMinapps()
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<DragableList list={pinned} onUpdate={updatePinnedMinapps}>
|
||||
{(app) => {
|
||||
const menuItems: MenuProps['items'] = [
|
||||
{
|
||||
key: 'togglePin',
|
||||
label: t('minapp.sidebar.remove.title'),
|
||||
onClick: () => {
|
||||
const newPinned = pinned.filter((item) => item.id !== app.id)
|
||||
updatePinnedMinapps(newPinned)
|
||||
}
|
||||
}
|
||||
]
|
||||
return (
|
||||
<Tooltip key={app.id} title={app.name} mouseEnterDelay={0.8} placement="right">
|
||||
<StyledLink>
|
||||
<Dropdown menu={{ items: menuItems }} trigger={['contextMenu']}>
|
||||
<Icon onClick={() => MinApp.start(app)}>
|
||||
<AppIcon
|
||||
src={app.logo}
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
border: app.bodered ? '0.5px solid var(--color-border)' : 'none'
|
||||
}}
|
||||
/>
|
||||
</Icon>
|
||||
</Dropdown>
|
||||
</StyledLink>
|
||||
</Tooltip>
|
||||
)
|
||||
}}
|
||||
</DragableList>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -154,9 +188,10 @@ const AvatarImg = styled(Avatar)`
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
`
|
||||
const MainMenus = styled.div`
|
||||
const MainMenusContainer = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
`
|
||||
|
||||
@ -164,6 +199,7 @@ const Menus = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
`
|
||||
|
||||
const Icon = styled.div`
|
||||
@ -173,7 +209,6 @@ const Icon = styled.div`
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 5px;
|
||||
-webkit-app-region: none;
|
||||
border: 0.5px solid transparent;
|
||||
.iconfont,
|
||||
@ -215,27 +250,23 @@ const AppIcon = styled.img`
|
||||
border-radius: 6px;
|
||||
`
|
||||
|
||||
const ScrollContainer = styled.div`
|
||||
const AppsContainer = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
height: 100%;
|
||||
|
||||
margin-bottom: 10px;
|
||||
&::-webkit-scrollbar {
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
&:hover::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: var(--color-border);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
|
||||
const Divider = styled.div`
|
||||
width: 50%;
|
||||
margin: 8px 0;
|
||||
border-bottom: 0.5px solid var(--color-border);
|
||||
`
|
||||
|
||||
export default Sidebar
|
||||
|
||||
23
src/renderer/src/hooks/useMinapps.ts
Normal file
23
src/renderer/src/hooks/useMinapps.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { RootState, useAppDispatch, useAppSelector } from '@renderer/store'
|
||||
import { setDisabledMinApps, setMinApps, setPinnedMinApps } from '@renderer/store/minapps'
|
||||
import { MinAppType } from '@renderer/types'
|
||||
|
||||
export const useMinapps = () => {
|
||||
const { enabled, disabled, pinned } = useAppSelector((state: RootState) => state.minapps)
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
return {
|
||||
minapps: enabled,
|
||||
disabled,
|
||||
pinned,
|
||||
updateMinapps: (minapps: MinAppType[]) => {
|
||||
dispatch(setMinApps(minapps))
|
||||
},
|
||||
updateDisabledMinapps: (minapps: MinAppType[]) => {
|
||||
dispatch(setDisabledMinApps(minapps))
|
||||
},
|
||||
updatePinnedMinapps: (minapps: MinAppType[]) => {
|
||||
dispatch(setPinnedMinApps(minapps))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,13 +2,14 @@ import store, { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||
import {
|
||||
SendMessageShortcut,
|
||||
setSendMessageShortcut as _setSendMessageShortcut,
|
||||
setSidebarIcons,
|
||||
setTheme,
|
||||
SettingsState,
|
||||
setTopicPosition,
|
||||
setTray,
|
||||
setWindowStyle
|
||||
} from '@renderer/store/settings'
|
||||
import { ThemeMode } from '@renderer/types'
|
||||
import { SidebarIcon, ThemeMode } from '@renderer/types'
|
||||
|
||||
export function useSettings() {
|
||||
const settings = useAppSelector((state) => state.settings)
|
||||
@ -30,6 +31,15 @@ export function useSettings() {
|
||||
},
|
||||
setTopicPosition(topicPosition: 'left' | 'right') {
|
||||
dispatch(setTopicPosition(topicPosition))
|
||||
},
|
||||
updateSidebarIcons(icons: { visible: SidebarIcon[]; disabled: SidebarIcon[] }) {
|
||||
dispatch(setSidebarIcons(icons))
|
||||
},
|
||||
updateSidebarVisibleIcons(icons: SidebarIcon[]) {
|
||||
dispatch(setSidebarIcons({ visible: icons }))
|
||||
},
|
||||
updateSidebarDisabledIcons(icons: SidebarIcon[]) {
|
||||
dispatch(setSidebarIcons({ disabled: icons }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,8 +265,8 @@
|
||||
},
|
||||
"minapp": {
|
||||
"title": "MinApp",
|
||||
"sidebar.add.title": "Add minAPP to sidebar",
|
||||
"sidebar.remove.title": "Remove minAPP from sidebar"
|
||||
"sidebar.add.title": "Add to sidebar",
|
||||
"sidebar.remove.title": "Remove from sidebar"
|
||||
},
|
||||
"ollama": {
|
||||
"keep_alive_time.description": "The time in minutes to keep the connection alive, default is 5 minutes.",
|
||||
@ -403,15 +403,15 @@
|
||||
"display.sidebar.knowledge.icon": "Show Knowledge icon",
|
||||
"display.sidebar.files.icon": "Show Files icon",
|
||||
"display.sidebar.title": "Sidebar Settings",
|
||||
"display.sidebar.visible": "Show my sidebar icons",
|
||||
"display.sidebar.disabled": "Hide my sidebar icons",
|
||||
"display.sidebar.visible": "Show icons",
|
||||
"display.sidebar.disabled": "Hide icons",
|
||||
"display.sidebar.chat.hiddenMessage": "Assistants are basic functions, not supported for hiding",
|
||||
"display.sidebar.empty": "Drag the hidden feature from the left side here",
|
||||
"display.minApp.title": "MinApp Settings",
|
||||
"display.minApp.visible": "Visible MinApp",
|
||||
"display.minApp.disabled": "Hidden MinApp",
|
||||
"display.minApp.empty": "Drag minApp from the left to hide them here",
|
||||
"display.minApp.pinnedError": "MinApp that have been added to the sidebar do not support hiding. If you want to hide them, please remove them from the sidebar first.",
|
||||
"": "MinApp that have been added to the sidebar do not support hiding. If you want to hide them, please remove them from the sidebar first.",
|
||||
"display.topic.title": "Topic Settings",
|
||||
"display.custom.css": "Custom CSS",
|
||||
"display.custom.css.placeholder": "/* Put custom CSS here */",
|
||||
|
||||
@ -263,8 +263,8 @@
|
||||
},
|
||||
"minapp": {
|
||||
"title": "ミニアプリ",
|
||||
"sidebar.add.title": "ミニプログラムをサイドバーに追加",
|
||||
"sidebar.remove.title": "サイドバーからアプレットを削除する"
|
||||
"sidebar.add.title": "サイドバーに追加",
|
||||
"sidebar.remove.title": "サイドバーから削除"
|
||||
},
|
||||
"ollama": {
|
||||
"keep_alive_time.description": "モデルがメモリに保持される時間(デフォルト:5分)",
|
||||
@ -401,8 +401,8 @@
|
||||
"display.sidebar.knowledge.icon": "ナレッジのアイコンを表示",
|
||||
"display.sidebar.files.icon": "ファイルのアイコンを表示",
|
||||
"display.sidebar.title": "サイドバー設定",
|
||||
"display.sidebar.visible": "サイドバーのアイコンを表示する",
|
||||
"display.sidebar.disabled": "サイドバーのアイコンを非表示にする",
|
||||
"display.sidebar.visible": "アイコンを表示",
|
||||
"display.sidebar.disabled": "アイコンを非表示",
|
||||
"display.sidebar.chat.hiddenMessage": "アシスタントは基本的な機能であり、非表示はサポートされていません",
|
||||
"display.sidebar.empty": "非表示にする機能を左側からここにドラッグ",
|
||||
"display.topic.title": "トピック設定",
|
||||
@ -412,7 +412,6 @@
|
||||
"display.minApp.visible": "表示中ミニプログラム",
|
||||
"display.minApp.disabled": "非表示ミニプログラム",
|
||||
"display.minApp.empty": "非表示にしたいアプレットを左からここまでドラッグします",
|
||||
"display.minApp.pinnedError": "サイドバーに追加されたミニ プログラムは非表示をサポートしていません。非表示にしたい場合は、まずサイドバーから削除してください",
|
||||
"input.auto_translate_with_space": "スペースを3回押して翻訳",
|
||||
"messages.divider": "メッセージ間に区切り線を表示",
|
||||
"messages.input.paste_long_text_as_file": "長いテキストをファイルとして貼り付け",
|
||||
|
||||
@ -265,8 +265,8 @@
|
||||
},
|
||||
"minapp": {
|
||||
"title": "Встроенные приложения",
|
||||
"sidebar.add.title": "Добавить мини-программу на боковую панель",
|
||||
"sidebar.remove.title": "Удалить апплет из боковой панели"
|
||||
"sidebar.add.title": "Добавить в боковую панель",
|
||||
"sidebar.remove.title": "Удалить из боковой панели"
|
||||
},
|
||||
"ollama": {
|
||||
"keep_alive_time.description": "Время в минутах, в течение которого модель остается активной, по умолчанию 5 минут.",
|
||||
@ -403,15 +403,14 @@
|
||||
"display.sidebar.knowledge.icon": "Показывать иконку знаний",
|
||||
"display.sidebar.files.icon": "Показывать иконку файлов",
|
||||
"display.sidebar.title": "Настройки боковой панели",
|
||||
"display.sidebar.visible": "Показать мои значки на боковой панели",
|
||||
"display.sidebar.disabled": "Скрыть значок на боковой панели",
|
||||
"display.sidebar.visible": "Показывать иконки",
|
||||
"display.sidebar.disabled": "Скрыть иконки",
|
||||
"display.sidebar.chat.hiddenMessage": "Помощник является базовой функцией и не поддерживает скрытие",
|
||||
"display.sidebar.empty": "Перетащите скрываемую функцию с левой стороны сюда",
|
||||
"display.minApp.title": "Настройки отображения мини программы",
|
||||
"display.minApp.visible": "Отображаемый апплет",
|
||||
"display.minApp.disabled": "скрытый апплет",
|
||||
"display.minApp.empty": "Перетащите апплет, который хотите скрыть, слева сюда",
|
||||
"display.minApp.pinnedError": "Мини-программы, добавленные на боковую панель, не поддерживают скрытие. Если вы хотите скрыть их, сначала удалите их с боковой панели",
|
||||
"display.topic.title": "Настройки топиков",
|
||||
"display.custom.css": "Пользовательский CSS",
|
||||
"display.custom.css.placeholder": "/* Здесь введите пользовательский CSS */",
|
||||
|
||||
@ -266,8 +266,8 @@
|
||||
},
|
||||
"minapp": {
|
||||
"title": "小程序",
|
||||
"sidebar.add.title": "添加小程序到侧边栏",
|
||||
"sidebar.remove.title": "从侧边栏移除小程序"
|
||||
"sidebar.add.title": "添加到侧边栏",
|
||||
"sidebar.remove.title": "从侧边栏移除"
|
||||
},
|
||||
"ollama": {
|
||||
"keep_alive_time.description": "对话后模型在内存中保持的时间(默认:5分钟)",
|
||||
@ -404,15 +404,14 @@
|
||||
"display.sidebar.knowledge.icon": "显示知识图标",
|
||||
"display.sidebar.files.icon": "显示文件图标",
|
||||
"display.sidebar.title": "侧边栏设置",
|
||||
"display.sidebar.visible": "显示我的侧边栏图标",
|
||||
"display.sidebar.disabled": "隐藏我的侧边栏图标",
|
||||
"display.sidebar.visible": "显示的图标",
|
||||
"display.sidebar.disabled": "隐藏的图标",
|
||||
"display.sidebar.chat.hiddenMessage": "助手是基础功能,不支持隐藏",
|
||||
"display.sidebar.empty": "把要隐藏的功能从左侧拖拽到这里",
|
||||
"display.minApp.title": "小程序显示设置",
|
||||
"display.minApp.visible": "显示的小程序",
|
||||
"display.minApp.disabled": "隐藏的小程序",
|
||||
"display.minApp.empty": "把要隐藏的小程序从左侧拖拽到这里",
|
||||
"display.minApp.pinnedError": "已经添加到侧边栏的小程序,不支持隐藏,如需隐藏请先从侧边栏移除",
|
||||
"display.topic.title": "话题设置",
|
||||
"display.custom.css": "自定义 CSS",
|
||||
"display.custom.css.placeholder": "/* 这里写自定义CSS */",
|
||||
|
||||
@ -265,8 +265,8 @@
|
||||
},
|
||||
"minapp": {
|
||||
"title": "小程序",
|
||||
"sidebar.add.title": "新增小程式到側邊欄",
|
||||
"sidebar.remove.title": "從側邊欄移除小程式"
|
||||
"sidebar.add.title": "添加到側邊欄",
|
||||
"sidebar.remove.title": "從側邊欄移除"
|
||||
},
|
||||
"ollama": {
|
||||
"keep_alive_time.description": "對話後模型在記憶體中保持的時間(預設為 5 分鐘)。",
|
||||
@ -406,13 +406,12 @@
|
||||
"display.topic.title": "話題設定",
|
||||
"display.sidebar.chat.hiddenMessage": "助手是基礎功能,不支援隱藏",
|
||||
"display.sidebar.empty": "把要隱藏的功能從左側拖拽到這裡",
|
||||
"display.sidebar.visible": "顯示我的側邊欄圖標",
|
||||
"display.sidebar.disabled": "隱藏我的側邊欄圖標",
|
||||
"display.sidebar.visible": "顯示的圖標",
|
||||
"display.sidebar.disabled": "隱藏的圖標",
|
||||
"display.minApp.title": "小程序顯示設定",
|
||||
"display.minApp.visible": "顯示的小程序",
|
||||
"display.minApp.disabled": "隱藏的小程序",
|
||||
"display.minApp.empty": "把要隱藏的小程序從左側拖拽到這裡",
|
||||
"display.minApp.pinnedError": "已新增至側邊欄的小程序,不支援隱藏,如需隱藏請先從側邊欄移除",
|
||||
"display.custom.css": "自定義 CSS",
|
||||
"display.custom.css.placeholder": "/* 這裡寫自定義 CSS */",
|
||||
"input.auto_translate_with_space": "快速敲擊3次空格翻譯",
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import MinApp from '@renderer/components/MinApp'
|
||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||
import { setMiniAppIcons } from '@renderer/store/settings'
|
||||
import { useMinapps } from '@renderer/hooks/useMinapps'
|
||||
import { MinAppType } from '@renderer/types'
|
||||
import type { MenuProps } from 'antd'
|
||||
import { Dropdown } from 'antd'
|
||||
@ -16,10 +15,9 @@ interface Props {
|
||||
|
||||
const App: FC<Props> = ({ app, onClick, size = 60 }) => {
|
||||
const { t } = useTranslation()
|
||||
const dispatch = useAppDispatch()
|
||||
const { miniAppIcons } = useAppSelector((state) => state.settings)
|
||||
const isPinned = miniAppIcons?.pinned.includes(app.id)
|
||||
const isVisible = miniAppIcons?.visible.includes(app.id)
|
||||
const { minapps, pinned, updatePinnedMinapps } = useMinapps()
|
||||
const isPinned = pinned.some((p) => p.id === app.id)
|
||||
const isVisible = minapps.some((m) => m.id === app.id)
|
||||
|
||||
const handleClick = () => {
|
||||
MinApp.start(app)
|
||||
@ -31,17 +29,8 @@ const App: FC<Props> = ({ app, onClick, size = 60 }) => {
|
||||
key: 'togglePin',
|
||||
label: isPinned ? t('minapp.sidebar.remove.title') : t('minapp.sidebar.add.title'),
|
||||
onClick: () => {
|
||||
const newPinned = isPinned
|
||||
? miniAppIcons.pinned.filter((id) => id !== app.id)
|
||||
: [...(miniAppIcons.pinned || []), app.id]
|
||||
|
||||
dispatch(
|
||||
setMiniAppIcons({
|
||||
...miniAppIcons,
|
||||
pinned: newPinned,
|
||||
visible: isPinned ? miniAppIcons.visible : [...new Set([...miniAppIcons.visible, app.id])]
|
||||
})
|
||||
)
|
||||
const newPinned = isPinned ? pinned.filter((item) => item.id !== app.id) : [...(pinned || []), app]
|
||||
updatePinnedMinapps(newPinned)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import { SearchOutlined } from '@ant-design/icons'
|
||||
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
|
||||
import { Center } from '@renderer/components/Layout'
|
||||
import { getAllMinApps } from '@renderer/config/minapps'
|
||||
import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import { useMinapps } from '@renderer/hooks/useMinapps'
|
||||
import { Empty, Input } from 'antd'
|
||||
import { isEmpty } from 'lodash'
|
||||
import React, { FC, useMemo, useState } from 'react'
|
||||
import React, { FC, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
@ -14,24 +13,15 @@ import App from './App'
|
||||
const AppsPage: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const [search, setSearch] = useState('')
|
||||
const { miniAppIcons } = useSettings()
|
||||
const allApps = useMemo(() => getAllMinApps(), [])
|
||||
const { minapps } = useMinapps()
|
||||
|
||||
// 只显示可见的小程序,但包括所有固定的小程序
|
||||
const visibleApps = useMemo(() => {
|
||||
if (!miniAppIcons?.visible) return allApps
|
||||
const visibleIds = new Set([
|
||||
...miniAppIcons.visible,
|
||||
...(miniAppIcons.pinned || []) // 确保固定的小程序总是可见
|
||||
])
|
||||
return allApps.filter((app) => visibleIds.has(app.id))
|
||||
}, [allApps, miniAppIcons?.visible, miniAppIcons?.pinned])
|
||||
console.debug('minapps', minapps)
|
||||
|
||||
const filteredApps = search
|
||||
? visibleApps.filter(
|
||||
? minapps.filter(
|
||||
(app) => app.name.toLowerCase().includes(search.toLowerCase()) || app.url.includes(search.toLowerCase())
|
||||
)
|
||||
: visibleApps
|
||||
: minapps
|
||||
|
||||
// Calculate the required number of lines
|
||||
const itemsPerRow = Math.floor(930 / 115) // Maximum width divided by the width of each item (including spacing)
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { isMac } from '@renderer/config/constant'
|
||||
import { getAllMinApps } from '@renderer/config/minapps'
|
||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||
import { useMinapps } from '@renderer/hooks/useMinapps'
|
||||
import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import { useAppDispatch } from '@renderer/store'
|
||||
import {
|
||||
DEFAULT_MINIAPP_ICONS,
|
||||
DEFAULT_SIDEBAR_ICONS,
|
||||
setClickAssistantToShowTopic,
|
||||
setCustomCss,
|
||||
setMiniAppIcons,
|
||||
setShowTopicTime,
|
||||
setSidebarIcons
|
||||
} from '@renderer/store/settings'
|
||||
@ -32,17 +32,17 @@ const DisplaySettings: FC = () => {
|
||||
clickAssistantToShowTopic,
|
||||
showTopicTime,
|
||||
customCss,
|
||||
sidebarIcons,
|
||||
miniAppIcons
|
||||
sidebarIcons
|
||||
} = useSettings()
|
||||
const { minapps, disabled, updateMinapps, updateDisabledMinapps } = useMinapps()
|
||||
const { theme: themeMode } = useTheme()
|
||||
const { t } = useTranslation()
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const [visibleIcons, setVisibleIcons] = useState(sidebarIcons?.visible || DEFAULT_SIDEBAR_ICONS)
|
||||
const [disabledIcons, setDisabledIcons] = useState(sidebarIcons?.disabled || [])
|
||||
const [visibleMiniApps, setVisibleMiniApps] = useState(miniAppIcons?.visible || DEFAULT_MINIAPP_ICONS)
|
||||
const [disabledMiniApps, setDisabledMiniApps] = useState(miniAppIcons?.disabled || [])
|
||||
const [visibleMiniApps, setVisibleMiniApps] = useState(minapps)
|
||||
const [disabledMiniApps, setDisabledMiniApps] = useState(disabled || [])
|
||||
|
||||
// 使用useCallback优化回调函数
|
||||
const handleWindowStyleChange = useCallback(
|
||||
@ -59,16 +59,11 @@ const DisplaySettings: FC = () => {
|
||||
}, [dispatch])
|
||||
|
||||
const handleResetMinApps = useCallback(() => {
|
||||
setVisibleMiniApps(DEFAULT_MINIAPP_ICONS)
|
||||
setVisibleMiniApps(getAllMinApps())
|
||||
setDisabledMiniApps([])
|
||||
dispatch(
|
||||
setMiniAppIcons({
|
||||
visible: DEFAULT_MINIAPP_ICONS,
|
||||
disabled: [],
|
||||
pinned: miniAppIcons?.pinned || []
|
||||
})
|
||||
)
|
||||
}, [dispatch, miniAppIcons?.pinned])
|
||||
updateMinapps(getAllMinApps())
|
||||
updateDisabledMinapps([])
|
||||
}, [updateDisabledMinapps, updateMinapps])
|
||||
|
||||
return (
|
||||
<SettingContainer theme={themeMode}>
|
||||
|
||||
@ -8,39 +8,33 @@ import {
|
||||
DropResult
|
||||
} from '@hello-pangea/dnd'
|
||||
import { getAllMinApps } from '@renderer/config/minapps'
|
||||
import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import { useAppDispatch } from '@renderer/store'
|
||||
import { MinAppIcon, setMiniAppIcons } from '@renderer/store/settings'
|
||||
import { useMinapps } from '@renderer/hooks/useMinapps'
|
||||
import { MinAppType } from '@renderer/types'
|
||||
import { FC, useCallback, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
interface MiniAppManagerProps {
|
||||
visibleMiniApps: MinAppIcon[]
|
||||
disabledMiniApps: MinAppIcon[]
|
||||
setVisibleMiniApps: (programs: MinAppIcon[]) => void
|
||||
setDisabledMiniApps: (programs: MinAppIcon[]) => void
|
||||
visibleMiniApps: MinAppType[]
|
||||
disabledMiniApps: MinAppType[]
|
||||
setVisibleMiniApps: (programs: MinAppType[]) => void
|
||||
setDisabledMiniApps: (programs: MinAppType[]) => void
|
||||
}
|
||||
|
||||
// 将可复用的类型和常量提取出来
|
||||
type ListType = 'visible' | 'disabled'
|
||||
interface AppInfo {
|
||||
name: string
|
||||
logo?: string
|
||||
}
|
||||
|
||||
// 添加 reorderLists 函数的接口定义
|
||||
interface ReorderListsParams {
|
||||
sourceList: MinAppIcon[]
|
||||
destList: MinAppIcon[]
|
||||
sourceList: MinAppType[]
|
||||
destList: MinAppType[]
|
||||
sourceIndex: number
|
||||
destIndex: number
|
||||
isSameList: boolean
|
||||
}
|
||||
|
||||
interface ReorderListsResult {
|
||||
sourceList: MinAppIcon[]
|
||||
destList: MinAppIcon[]
|
||||
sourceList: MinAppType[]
|
||||
destList: MinAppType[]
|
||||
}
|
||||
|
||||
// 添加 reorderLists 函数
|
||||
@ -80,43 +74,18 @@ const MiniAppIconsManager: FC<MiniAppManagerProps> = ({
|
||||
setDisabledMiniApps
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const dispatch = useAppDispatch()
|
||||
const { miniAppIcons } = useSettings()
|
||||
const allApps = useMemo(() => getAllMinApps(), [])
|
||||
|
||||
// 创建 app 信息的 Map 缓存
|
||||
const appInfoMap = useMemo(() => {
|
||||
return allApps.reduce(
|
||||
(acc, app) => {
|
||||
acc[String(app.id)] = { name: app.name, logo: app.logo }
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, AppInfo>
|
||||
)
|
||||
}, [allApps])
|
||||
|
||||
const getAppInfo = useCallback(
|
||||
(id: MinAppIcon) => {
|
||||
return appInfoMap[String(id)] || { name: id, logo: '' }
|
||||
},
|
||||
[appInfoMap]
|
||||
)
|
||||
const { pinned, updateMinapps, updateDisabledMinapps, updatePinnedMinapps } = useMinapps()
|
||||
|
||||
const handleListUpdate = useCallback(
|
||||
(newVisible: MinAppIcon[], newDisabled: MinAppIcon[]) => {
|
||||
(newVisible: MinAppType[], newDisabled: MinAppType[]) => {
|
||||
setVisibleMiniApps(newVisible)
|
||||
setDisabledMiniApps(newDisabled)
|
||||
|
||||
// 保持 pinned 状态不变
|
||||
dispatch(
|
||||
setMiniAppIcons({
|
||||
visible: newVisible,
|
||||
disabled: newDisabled,
|
||||
pinned: miniAppIcons.pinned // 保持原有的 pinned 状态
|
||||
})
|
||||
)
|
||||
updateMinapps(newVisible)
|
||||
updateDisabledMinapps(newDisabled)
|
||||
updatePinnedMinapps(pinned.filter((p) => !newDisabled.some((d) => d.id === p.id)))
|
||||
},
|
||||
[dispatch, setVisibleMiniApps, setDisabledMiniApps, miniAppIcons.pinned]
|
||||
[pinned, setDisabledMiniApps, setVisibleMiniApps, updateDisabledMinapps, updateMinapps, updatePinnedMinapps]
|
||||
)
|
||||
|
||||
const onDragEnd = useCallback(
|
||||
@ -127,15 +96,7 @@ const MiniAppIconsManager: FC<MiniAppManagerProps> = ({
|
||||
const sourceList = source.droppableId as ListType
|
||||
const destList = destination.droppableId as ListType
|
||||
|
||||
// 如果是 pinned 的小程序,不允许拖到 disabled
|
||||
if (destList === 'disabled') {
|
||||
const draggedApp = sourceList === 'visible' ? visibleMiniApps[source.index] : disabledMiniApps[source.index]
|
||||
|
||||
if (miniAppIcons.pinned.includes(draggedApp)) {
|
||||
window.message.error(t('settings.display.minApp.pinnedError'))
|
||||
return
|
||||
}
|
||||
}
|
||||
if (source.droppableId === destination.droppableId) return
|
||||
|
||||
const newLists = reorderLists({
|
||||
sourceList: sourceList === 'visible' ? visibleMiniApps : disabledMiniApps,
|
||||
@ -150,32 +111,26 @@ const MiniAppIconsManager: FC<MiniAppManagerProps> = ({
|
||||
sourceList === 'visible' ? newLists.destList : newLists.sourceList
|
||||
)
|
||||
},
|
||||
[visibleMiniApps, disabledMiniApps, handleListUpdate, miniAppIcons.pinned, t]
|
||||
[disabledMiniApps, handleListUpdate, visibleMiniApps]
|
||||
)
|
||||
|
||||
const onMoveMiniApp = useCallback(
|
||||
(program: MinAppIcon, fromList: ListType) => {
|
||||
// 如果是从可见列表移动到隐藏列表,且程序是 pinned 状态,则阻止移动
|
||||
if (fromList === 'visible' && miniAppIcons.pinned.includes(program)) {
|
||||
window.message.error(t('settings.display.minApp.pinnedError'))
|
||||
return
|
||||
}
|
||||
|
||||
(program: MinAppType, fromList: ListType) => {
|
||||
const isMovingToVisible = fromList === 'disabled'
|
||||
const newVisible = isMovingToVisible
|
||||
? [...visibleMiniApps, program]
|
||||
: visibleMiniApps.filter((p) => p !== program)
|
||||
: visibleMiniApps.filter((p) => p.id !== program.id)
|
||||
const newDisabled = isMovingToVisible
|
||||
? disabledMiniApps.filter((p) => p !== program)
|
||||
? disabledMiniApps.filter((p) => p.id !== program.id)
|
||||
: [...disabledMiniApps, program]
|
||||
|
||||
handleListUpdate(newVisible, newDisabled)
|
||||
},
|
||||
[visibleMiniApps, disabledMiniApps, handleListUpdate, miniAppIcons.pinned, t]
|
||||
[visibleMiniApps, disabledMiniApps, handleListUpdate]
|
||||
)
|
||||
|
||||
const renderProgramItem = (program: MinAppIcon, provided: DraggableProvided, listType: ListType) => {
|
||||
const { name, logo } = getAppInfo(program)
|
||||
const renderProgramItem = (program: MinAppType, provided: DraggableProvided, listType: ListType) => {
|
||||
const { name, logo } = allApps.find((app) => app.id === program.id) || { name: program.name, logo: '' }
|
||||
|
||||
return (
|
||||
<ProgramItem ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
|
||||
@ -201,7 +156,7 @@ const MiniAppIconsManager: FC<MiniAppManagerProps> = ({
|
||||
<ProgramList ref={provided.innerRef} {...provided.droppableProps}>
|
||||
<ScrollContainer>
|
||||
{(listType === 'visible' ? visibleMiniApps : disabledMiniApps).map((program, index) => (
|
||||
<Draggable key={program} draggableId={String(program)} index={index}>
|
||||
<Draggable key={program.id} draggableId={String(program.id)} index={index}>
|
||||
{(provided: DraggableProvided) => renderProgramItem(program, provided, listType)}
|
||||
</Draggable>
|
||||
))}
|
||||
@ -230,6 +185,7 @@ const AppLogo = styled.img`
|
||||
const ScrollContainer = styled.div`
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
padding-right: 5px;
|
||||
`
|
||||
|
||||
const ProgramSection = styled.div`
|
||||
@ -253,6 +209,7 @@ const ProgramList = styled.div`
|
||||
height: 365px;
|
||||
min-height: 365px;
|
||||
padding: 10px;
|
||||
padding-right: 5px;
|
||||
background: var(--color-background-soft);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--color-border);
|
||||
|
||||
@ -8,6 +8,7 @@ import assistants from './assistants'
|
||||
import knowledge from './knowledge'
|
||||
import llm from './llm'
|
||||
import migrate from './migrate'
|
||||
import minapps from './minapps'
|
||||
import paintings from './paintings'
|
||||
import runtime from './runtime'
|
||||
import settings from './settings'
|
||||
@ -21,7 +22,8 @@ const rootReducer = combineReducers({
|
||||
settings,
|
||||
runtime,
|
||||
shortcuts,
|
||||
knowledge
|
||||
knowledge,
|
||||
minapps
|
||||
})
|
||||
|
||||
const persistedReducer = persistReducer(
|
||||
|
||||
@ -9,7 +9,6 @@ import { isEmpty } from 'lodash'
|
||||
import { createMigrate } from 'redux-persist'
|
||||
|
||||
import { RootState } from '.'
|
||||
import { DEFAULT_MINIAPP_ICONS, DEFAULT_SIDEBAR_ICONS } from './settings'
|
||||
|
||||
const migrateConfig = {
|
||||
'2': (state: RootState) => {
|
||||
@ -785,15 +784,6 @@ const migrateConfig = {
|
||||
system: false
|
||||
})
|
||||
}
|
||||
state.settings.sidebarIcons = {
|
||||
visible: DEFAULT_SIDEBAR_ICONS,
|
||||
disabled: []
|
||||
}
|
||||
state.settings.miniAppIcons = {
|
||||
visible: DEFAULT_MINIAPP_ICONS,
|
||||
disabled: [],
|
||||
pinned: []
|
||||
}
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
48
src/renderer/src/store/minapps.ts
Normal file
48
src/renderer/src/store/minapps.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||
import { getAllMinApps } from '@renderer/config/minapps'
|
||||
import { MinAppType, SidebarIcon } from '@renderer/types'
|
||||
|
||||
export const DEFAULT_SIDEBAR_ICONS: SidebarIcon[] = [
|
||||
'assistants',
|
||||
'agents',
|
||||
'paintings',
|
||||
'translate',
|
||||
'minapp',
|
||||
'knowledge',
|
||||
'files'
|
||||
]
|
||||
|
||||
export interface MinAppsState {
|
||||
enabled: MinAppType[]
|
||||
disabled: MinAppType[]
|
||||
pinned: MinAppType[]
|
||||
}
|
||||
|
||||
const initialState: MinAppsState = {
|
||||
enabled: getAllMinApps(),
|
||||
disabled: [],
|
||||
pinned: []
|
||||
}
|
||||
|
||||
const minAppsSlice = createSlice({
|
||||
name: 'minApps',
|
||||
initialState,
|
||||
reducers: {
|
||||
setMinApps: (state, action: PayloadAction<MinAppType[]>) => {
|
||||
state.enabled = action.payload
|
||||
},
|
||||
addMinApp: (state, action: PayloadAction<MinAppType>) => {
|
||||
state.enabled.push(action.payload)
|
||||
},
|
||||
setDisabledMinApps: (state, action: PayloadAction<MinAppType[]>) => {
|
||||
state.disabled = action.payload
|
||||
},
|
||||
setPinnedMinApps: (state, action: PayloadAction<MinAppType[]>) => {
|
||||
state.pinned = action.payload
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const { setMinApps, addMinApp, setDisabledMinApps, setPinnedMinApps } = minAppsSlice.actions
|
||||
|
||||
export default minAppsSlice.reducer
|
||||
@ -1,5 +1,4 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||
import { getAllMinApps } from '@renderer/config/minapps'
|
||||
import { TRANSLATE_PROMPT } from '@renderer/config/prompts'
|
||||
import { CodeStyleVarious, LanguageVarious, ThemeMode } from '@renderer/types'
|
||||
|
||||
@ -16,11 +15,6 @@ export const DEFAULT_SIDEBAR_ICONS: SidebarIcon[] = [
|
||||
'knowledge',
|
||||
'files'
|
||||
]
|
||||
const [minApps] = await Promise.all([getAllMinApps()])
|
||||
|
||||
export type MinAppIcon = (typeof minApps)[number]['id'] // 假设每个小程序对象有 type 字段
|
||||
|
||||
export const DEFAULT_MINIAPP_ICONS: MinAppIcon[] = minApps.map((app) => app.id)
|
||||
|
||||
export interface SettingsState {
|
||||
showAssistants: boolean
|
||||
@ -67,11 +61,6 @@ export interface SettingsState {
|
||||
disabled: SidebarIcon[]
|
||||
}
|
||||
narrowMode: boolean
|
||||
miniAppIcons: {
|
||||
visible: MinAppIcon[]
|
||||
disabled: MinAppIcon[]
|
||||
pinned: MinAppIcon[]
|
||||
}
|
||||
}
|
||||
|
||||
const initialState: SettingsState = {
|
||||
@ -116,12 +105,7 @@ const initialState: SettingsState = {
|
||||
visible: DEFAULT_SIDEBAR_ICONS,
|
||||
disabled: []
|
||||
},
|
||||
narrowMode: false,
|
||||
miniAppIcons: {
|
||||
visible: DEFAULT_MINIAPP_ICONS,
|
||||
disabled: [],
|
||||
pinned: []
|
||||
}
|
||||
narrowMode: false
|
||||
}
|
||||
|
||||
const settingsSlice = createSlice({
|
||||
@ -246,17 +230,16 @@ const settingsSlice = createSlice({
|
||||
setTopicNamingPrompt: (state, action: PayloadAction<string>) => {
|
||||
state.topicNamingPrompt = action.payload
|
||||
},
|
||||
setSidebarIcons: (state, action: PayloadAction<{ visible: SidebarIcon[]; disabled: SidebarIcon[] }>) => {
|
||||
state.sidebarIcons = action.payload
|
||||
setSidebarIcons: (state, action: PayloadAction<{ visible?: SidebarIcon[]; disabled?: SidebarIcon[] }>) => {
|
||||
if (action.payload.visible) {
|
||||
state.sidebarIcons.visible = action.payload.visible
|
||||
}
|
||||
if (action.payload.disabled) {
|
||||
state.sidebarIcons.disabled = action.payload.disabled
|
||||
}
|
||||
},
|
||||
setNarrowMode: (state, action: PayloadAction<boolean>) => {
|
||||
state.narrowMode = action.payload
|
||||
},
|
||||
setMiniAppIcons: (
|
||||
state,
|
||||
action: PayloadAction<{ visible: MinAppIcon[]; disabled: MinAppIcon[]; pinned: MinAppIcon[] }>
|
||||
) => {
|
||||
state.miniAppIcons = action.payload
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -302,8 +285,7 @@ export const {
|
||||
setCustomCss,
|
||||
setTopicNamingPrompt,
|
||||
setSidebarIcons,
|
||||
setNarrowMode,
|
||||
setMiniAppIcons
|
||||
setNarrowMode
|
||||
} = settingsSlice.actions
|
||||
|
||||
export default settingsSlice.reducer
|
||||
|
||||
@ -163,7 +163,9 @@ export enum ThemeMode {
|
||||
dark = 'dark',
|
||||
auto = 'auto'
|
||||
}
|
||||
|
||||
export type LanguageVarious = 'zh-CN' | 'zh-TW' | 'en-US' | 'ru-RU' | 'ja-JP'
|
||||
|
||||
export type CodeStyleVarious = BuiltinTheme | 'auto'
|
||||
|
||||
export type WebDavConfig = {
|
||||
@ -241,3 +243,5 @@ export type GenerateImageParams = {
|
||||
signal?: AbortSignal
|
||||
promptEnhancement?: boolean
|
||||
}
|
||||
|
||||
export type SidebarIcon = 'assistants' | 'agents' | 'paintings' | 'translate' | 'minapp' | 'knowledge' | 'files'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user