feat: minApp supports show/hide, add to the sidebar
This commit is contained in:
parent
c45fc2bbad
commit
fc3d15fae8
@ -202,23 +202,40 @@ const EmptyView = styled.div`
|
|||||||
export default class MinApp {
|
export default class MinApp {
|
||||||
static topviewId = 0
|
static topviewId = 0
|
||||||
static onClose = () => {}
|
static onClose = () => {}
|
||||||
|
static isOpening = false
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static close() {
|
static close() {
|
||||||
|
if (!this.isOpening) return
|
||||||
TopView.hide('MinApp')
|
TopView.hide('MinApp')
|
||||||
store.dispatch(setMinappShow(false))
|
store.dispatch(setMinappShow(false))
|
||||||
}
|
}
|
||||||
static start(app: MinAppType) {
|
|
||||||
store.dispatch(setMinappShow(true))
|
|
||||||
return new Promise<any>((resolve) => {
|
|
||||||
TopView.show(
|
|
||||||
<PopupContainer
|
|
||||||
app={app}
|
|
||||||
resolve={(v) => {
|
|
||||||
resolve(v)
|
|
||||||
this.close()
|
|
||||||
}}
|
|
||||||
/>,
|
|
||||||
'MinApp'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { FileSearchOutlined, FolderOutlined, PictureOutlined, TranslationOutlined } from '@ant-design/icons'
|
import { FileSearchOutlined, FolderOutlined, PictureOutlined, TranslationOutlined } from '@ant-design/icons'
|
||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac } from '@renderer/config/constant'
|
||||||
import { isLocalAi, UserAvatar } from '@renderer/config/env'
|
import { isLocalAi, UserAvatar } from '@renderer/config/env'
|
||||||
|
import { getAllMinApps } from '@renderer/config/minapps'
|
||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
import useAvatar from '@renderer/hooks/useAvatar'
|
import useAvatar from '@renderer/hooks/useAvatar'
|
||||||
import { modelGenerating, useRuntime } from '@renderer/hooks/useRuntime'
|
import { modelGenerating, useRuntime } from '@renderer/hooks/useRuntime'
|
||||||
@ -21,8 +22,9 @@ const Sidebar: FC = () => {
|
|||||||
const { minappShow } = useRuntime()
|
const { minappShow } = useRuntime()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { windowStyle, sidebarIcons } = useSettings()
|
const { windowStyle, sidebarIcons, miniAppIcons } = useSettings()
|
||||||
const { theme, toggleTheme } = useTheme()
|
const { theme, toggleTheme } = useTheme()
|
||||||
|
const allApps = getAllMinApps()
|
||||||
|
|
||||||
const isRoute = (path: string): string => (pathname === path ? 'active' : '')
|
const isRoute = (path: string): string => (pathname === path ? 'active' : '')
|
||||||
const isRoutes = (path: string): string => (pathname.startsWith(path) ? 'active' : '')
|
const isRoutes = (path: string): string => (pathname.startsWith(path) ? 'active' : '')
|
||||||
@ -72,6 +74,27 @@ const Sidebar: FC = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<Container
|
<Container
|
||||||
id="app-sidebar"
|
id="app-sidebar"
|
||||||
@ -81,7 +104,12 @@ const Sidebar: FC = () => {
|
|||||||
}}>
|
}}>
|
||||||
<AvatarImg src={avatar || UserAvatar} draggable={false} className="nodrag" onClick={onEditUser} />
|
<AvatarImg src={avatar || UserAvatar} draggable={false} className="nodrag" onClick={onEditUser} />
|
||||||
<MainMenus>
|
<MainMenus>
|
||||||
<Menus onClick={MinApp.onClose}>{renderMainMenus()}</Menus>
|
<ScrollContainer>
|
||||||
|
<Menus onClick={MinApp.onClose}>
|
||||||
|
{renderMainMenus()}
|
||||||
|
{renderPinnedApps()}
|
||||||
|
</Menus>
|
||||||
|
</ScrollContainer>
|
||||||
</MainMenus>
|
</MainMenus>
|
||||||
<Menus onClick={MinApp.onClose}>
|
<Menus onClick={MinApp.onClose}>
|
||||||
<Tooltip title={t('settings.theme.title')} mouseEnterDelay={0.8} placement="right">
|
<Tooltip title={t('settings.theme.title')} mouseEnterDelay={0.8} placement="right">
|
||||||
@ -129,6 +157,7 @@ const AvatarImg = styled(Avatar)`
|
|||||||
const MainMenus = styled.div`
|
const MainMenus = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
`
|
`
|
||||||
|
|
||||||
const Menus = styled.div`
|
const Menus = styled.div`
|
||||||
@ -182,4 +211,31 @@ const StyledLink = styled.div`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const AppIcon = styled.img`
|
||||||
|
border-radius: 6px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ScrollContainer = styled.div`
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&::-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;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export default Sidebar
|
export default Sidebar
|
||||||
|
|||||||
@ -264,7 +264,9 @@
|
|||||||
"error.get_embedding_dimensions": "Failed to get embedding dimensions"
|
"error.get_embedding_dimensions": "Failed to get embedding dimensions"
|
||||||
},
|
},
|
||||||
"minapp": {
|
"minapp": {
|
||||||
"title": "MinApp"
|
"title": "MinApp",
|
||||||
|
"sidebar.add.title": "Add minAPP to sidebar",
|
||||||
|
"sidebar.remove.title": "Remove minAPP from sidebar"
|
||||||
},
|
},
|
||||||
"ollama": {
|
"ollama": {
|
||||||
"keep_alive_time.description": "The time in minutes to keep the connection alive, default is 5 minutes.",
|
"keep_alive_time.description": "The time in minutes to keep the connection alive, default is 5 minutes.",
|
||||||
@ -405,6 +407,10 @@
|
|||||||
"display.sidebar.disabled": "Hide my sidebar icons",
|
"display.sidebar.disabled": "Hide my sidebar icons",
|
||||||
"display.sidebar.chat.hiddenMessage": "Assistants are basic functions, not supported for hiding",
|
"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.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.topic.title": "Topic Settings",
|
"display.topic.title": "Topic Settings",
|
||||||
"display.custom.css": "Custom CSS",
|
"display.custom.css": "Custom CSS",
|
||||||
"display.custom.css.placeholder": "/* Put custom CSS here */",
|
"display.custom.css.placeholder": "/* Put custom CSS here */",
|
||||||
|
|||||||
@ -262,7 +262,9 @@
|
|||||||
"copy.success": "コピーしました!"
|
"copy.success": "コピーしました!"
|
||||||
},
|
},
|
||||||
"minapp": {
|
"minapp": {
|
||||||
"title": "ミニアプリ"
|
"title": "ミニアプリ",
|
||||||
|
"sidebar.add.title": "ミニプログラムをサイドバーに追加",
|
||||||
|
"sidebar.remove.title": "サイドバーからアプレットを削除する"
|
||||||
},
|
},
|
||||||
"ollama": {
|
"ollama": {
|
||||||
"keep_alive_time.description": "モデルがメモリに保持される時間(デフォルト:5分)",
|
"keep_alive_time.description": "モデルがメモリに保持される時間(デフォルト:5分)",
|
||||||
@ -406,6 +408,10 @@
|
|||||||
"display.topic.title": "トピック設定",
|
"display.topic.title": "トピック設定",
|
||||||
"display.custom.css": "カスタムCSS",
|
"display.custom.css": "カスタムCSS",
|
||||||
"display.custom.css.placeholder": "/* ここにカスタムCSSを入力 */",
|
"display.custom.css.placeholder": "/* ここにカスタムCSSを入力 */",
|
||||||
|
"display.minApp.title": "ミニプログラム表示設定",
|
||||||
|
"display.minApp.visible": "表示中ミニプログラム",
|
||||||
|
"display.minApp.disabled": "非表示ミニプログラム",
|
||||||
|
"display.minApp.empty": "非表示にしたいアプレットを左からここまでドラッグします",
|
||||||
"input.auto_translate_with_space": "スペースを3回押して翻訳",
|
"input.auto_translate_with_space": "スペースを3回押して翻訳",
|
||||||
"messages.divider": "メッセージ間に区切り線を表示",
|
"messages.divider": "メッセージ間に区切り線を表示",
|
||||||
"messages.input.paste_long_text_as_file": "長いテキストをファイルとして貼り付け",
|
"messages.input.paste_long_text_as_file": "長いテキストをファイルとして貼り付け",
|
||||||
|
|||||||
@ -264,7 +264,9 @@
|
|||||||
"error.get_embedding_dimensions": "Не удалось получить размерность встраивания"
|
"error.get_embedding_dimensions": "Не удалось получить размерность встраивания"
|
||||||
},
|
},
|
||||||
"minapp": {
|
"minapp": {
|
||||||
"title": "Встроенные приложения"
|
"title": "Встроенные приложения",
|
||||||
|
"sidebar.add.title": "Добавить мини-программу на боковую панель",
|
||||||
|
"sidebar.remove.title": "Удалить апплет из боковой панели"
|
||||||
},
|
},
|
||||||
"ollama": {
|
"ollama": {
|
||||||
"keep_alive_time.description": "Время в минутах, в течение которого модель остается активной, по умолчанию 5 минут.",
|
"keep_alive_time.description": "Время в минутах, в течение которого модель остается активной, по умолчанию 5 минут.",
|
||||||
@ -405,6 +407,10 @@
|
|||||||
"display.sidebar.disabled": "Скрыть значок на боковой панели",
|
"display.sidebar.disabled": "Скрыть значок на боковой панели",
|
||||||
"display.sidebar.chat.hiddenMessage": "Помощник является базовой функцией и не поддерживает скрытие",
|
"display.sidebar.chat.hiddenMessage": "Помощник является базовой функцией и не поддерживает скрытие",
|
||||||
"display.sidebar.empty": "Перетащите скрываемую функцию с левой стороны сюда",
|
"display.sidebar.empty": "Перетащите скрываемую функцию с левой стороны сюда",
|
||||||
|
"display.minApp.title": "Настройки отображения мини программы",
|
||||||
|
"display.minApp.visible": "Отображаемый апплет",
|
||||||
|
"display.minApp.disabled": "скрытый апплет",
|
||||||
|
"display.minApp.empty": "Перетащите апплет, который хотите скрыть, слева сюда",
|
||||||
"display.topic.title": "Настройки топиков",
|
"display.topic.title": "Настройки топиков",
|
||||||
"display.custom.css": "Пользовательский CSS",
|
"display.custom.css": "Пользовательский CSS",
|
||||||
"display.custom.css.placeholder": "/* Здесь введите пользовательский CSS */",
|
"display.custom.css.placeholder": "/* Здесь введите пользовательский CSS */",
|
||||||
|
|||||||
@ -265,7 +265,9 @@
|
|||||||
"error.get_embedding_dimensions": "获取嵌入维度失败"
|
"error.get_embedding_dimensions": "获取嵌入维度失败"
|
||||||
},
|
},
|
||||||
"minapp": {
|
"minapp": {
|
||||||
"title": "小程序"
|
"title": "小程序",
|
||||||
|
"sidebar.add.title": "添加小程序到侧边栏",
|
||||||
|
"sidebar.remove.title": "从侧边栏移除小程序"
|
||||||
},
|
},
|
||||||
"ollama": {
|
"ollama": {
|
||||||
"keep_alive_time.description": "对话后模型在内存中保持的时间(默认:5分钟)",
|
"keep_alive_time.description": "对话后模型在内存中保持的时间(默认:5分钟)",
|
||||||
@ -406,6 +408,10 @@
|
|||||||
"display.sidebar.disabled": "隐藏我的侧边栏图标",
|
"display.sidebar.disabled": "隐藏我的侧边栏图标",
|
||||||
"display.sidebar.chat.hiddenMessage": "助手是基础功能,不支持隐藏",
|
"display.sidebar.chat.hiddenMessage": "助手是基础功能,不支持隐藏",
|
||||||
"display.sidebar.empty": "把要隐藏的功能从左侧拖拽到这里",
|
"display.sidebar.empty": "把要隐藏的功能从左侧拖拽到这里",
|
||||||
|
"display.minApp.title": "小程序显示设置",
|
||||||
|
"display.minApp.visible": "显示的小程序",
|
||||||
|
"display.minApp.disabled": "隐藏的小程序",
|
||||||
|
"display.minApp.empty": "把要隐藏的小程序从左侧拖拽到这里",
|
||||||
"display.topic.title": "话题设置",
|
"display.topic.title": "话题设置",
|
||||||
"display.custom.css": "自定义 CSS",
|
"display.custom.css": "自定义 CSS",
|
||||||
"display.custom.css.placeholder": "/* 这里写自定义CSS */",
|
"display.custom.css.placeholder": "/* 这里写自定义CSS */",
|
||||||
|
|||||||
@ -264,7 +264,9 @@
|
|||||||
"error.get_embedding_dimensions": "獲取嵌入維度失敗"
|
"error.get_embedding_dimensions": "獲取嵌入維度失敗"
|
||||||
},
|
},
|
||||||
"minapp": {
|
"minapp": {
|
||||||
"title": "小程序"
|
"title": "小程序",
|
||||||
|
"sidebar.add.title": "新增小程式到側邊欄",
|
||||||
|
"sidebar.remove.title": "從側邊欄移除小程式"
|
||||||
},
|
},
|
||||||
"ollama": {
|
"ollama": {
|
||||||
"keep_alive_time.description": "對話後模型在記憶體中保持的時間(預設為 5 分鐘)。",
|
"keep_alive_time.description": "對話後模型在記憶體中保持的時間(預設為 5 分鐘)。",
|
||||||
@ -409,6 +411,14 @@
|
|||||||
"display.custom.css": "自定義 CSS",
|
"display.custom.css": "自定義 CSS",
|
||||||
"display.custom.css.placeholder": "/* 這裡寫自定義 CSS */",
|
"display.custom.css.placeholder": "/* 這裡寫自定義 CSS */",
|
||||||
"input.auto_translate_with_space": "快速敲擊3次空格翻譯",
|
"input.auto_translate_with_space": "快速敲擊3次空格翻譯",
|
||||||
|
"display": {
|
||||||
|
"minApp": {
|
||||||
|
"title": "小程序顯示設定",
|
||||||
|
"visible": "顯示的小程序",
|
||||||
|
"disabled": "隱藏的小程序",
|
||||||
|
"empty": "把要隱藏的小程序從左側拖拽到這裡"
|
||||||
|
}
|
||||||
|
},
|
||||||
"messages.divider": "訊息間顯示分隔線",
|
"messages.divider": "訊息間顯示分隔線",
|
||||||
"messages.input.paste_long_text_as_file": "將長文本貼上為檔案",
|
"messages.input.paste_long_text_as_file": "將長文本貼上為檔案",
|
||||||
"messages.input.send_shortcuts": "發送快捷鍵",
|
"messages.input.send_shortcuts": "發送快捷鍵",
|
||||||
|
|||||||
@ -1,8 +1,14 @@
|
|||||||
import MinApp from '@renderer/components/MinApp'
|
import MinApp from '@renderer/components/MinApp'
|
||||||
|
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||||
|
import { setMiniAppIcons } from '@renderer/store/settings'
|
||||||
import { MinAppType } from '@renderer/types'
|
import { MinAppType } from '@renderer/types'
|
||||||
|
import type { MenuProps } from 'antd'
|
||||||
|
import { Dropdown } from 'antd'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
app: MinAppType
|
app: MinAppType
|
||||||
onClick?: () => void
|
onClick?: () => void
|
||||||
@ -10,23 +16,49 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const App: FC<Props> = ({ app, onClick, size = 60 }) => {
|
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 handleClick = () => {
|
const handleClick = () => {
|
||||||
MinApp.start(app)
|
MinApp.start(app)
|
||||||
onClick?.()
|
onClick?.()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const menuItems: MenuProps['items'] = [
|
||||||
|
{
|
||||||
|
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
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container onClick={handleClick}>
|
<Dropdown menu={{ items: menuItems }} trigger={['contextMenu']}>
|
||||||
<AppIcon
|
<Container onClick={handleClick}>
|
||||||
src={app.logo}
|
<AppIcon
|
||||||
style={{
|
src={app.logo}
|
||||||
border: app.bodered ? '0.5px solid var(--color-border)' : 'none',
|
style={{
|
||||||
width: `${size}px`,
|
border: app.bodered ? '0.5px solid var(--color-border)' : 'none',
|
||||||
height: `${size}px`
|
width: `${size}px`,
|
||||||
}}
|
height: `${size}px`
|
||||||
/>
|
}}
|
||||||
<AppTitle>{app.name}</AppTitle>
|
/>
|
||||||
</Container>
|
<AppTitle>{app.name}</AppTitle>
|
||||||
|
</Container>
|
||||||
|
</Dropdown>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,10 @@ import { SearchOutlined } from '@ant-design/icons'
|
|||||||
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
|
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
|
||||||
import { Center } from '@renderer/components/Layout'
|
import { Center } from '@renderer/components/Layout'
|
||||||
import { getAllMinApps } from '@renderer/config/minapps'
|
import { getAllMinApps } from '@renderer/config/minapps'
|
||||||
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { Empty, Input } from 'antd'
|
import { Empty, Input } from 'antd'
|
||||||
import { isEmpty } from 'lodash'
|
import { isEmpty } from 'lodash'
|
||||||
import { FC, useMemo, useState } from 'react'
|
import React, { FC, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
@ -13,16 +14,34 @@ import App from './App'
|
|||||||
const AppsPage: FC = () => {
|
const AppsPage: FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [search, setSearch] = useState('')
|
const [search, setSearch] = useState('')
|
||||||
const apps = useMemo(() => getAllMinApps(), [])
|
const { miniAppIcons } = useSettings()
|
||||||
|
const allApps = useMemo(() => getAllMinApps(), [])
|
||||||
|
|
||||||
|
// 只显示可见的小程序
|
||||||
|
const visibleApps = useMemo(() => {
|
||||||
|
if (!miniAppIcons?.visible) return allApps
|
||||||
|
return allApps.filter((app) => miniAppIcons.visible.includes(app.id))
|
||||||
|
}, [allApps, miniAppIcons?.visible])
|
||||||
|
|
||||||
const filteredApps = search
|
const filteredApps = search
|
||||||
? apps.filter(
|
? visibleApps.filter(
|
||||||
(app) => app.name.toLowerCase().includes(search.toLowerCase()) || app.url.includes(search.toLowerCase())
|
(app) => app.name.toLowerCase().includes(search.toLowerCase()) || app.url.includes(search.toLowerCase())
|
||||||
)
|
)
|
||||||
: apps
|
: visibleApps
|
||||||
|
|
||||||
|
// Calculate the required number of lines
|
||||||
|
const itemsPerRow = Math.floor(930 / 115) // Maximum width divided by the width of each item (including spacing)
|
||||||
|
const rowCount = Math.ceil(filteredApps.length / itemsPerRow)
|
||||||
|
// Each line height is 85px (60px icon + 5px margin + 12px text + spacing)
|
||||||
|
const containerHeight = rowCount * 85 + (rowCount - 1) * 25 // 25px is the line spacing.
|
||||||
|
|
||||||
|
// Disable right-click menu in blank area
|
||||||
|
const handleContextMenu = (e: React.MouseEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container onContextMenu={handleContextMenu}>
|
||||||
<Navbar>
|
<Navbar>
|
||||||
<NavbarCenter style={{ borderRight: 'none', justifyContent: 'space-between' }}>
|
<NavbarCenter style={{ borderRight: 'none', justifyContent: 'space-between' }}>
|
||||||
{t('minapp.title')}
|
{t('minapp.title')}
|
||||||
@ -40,7 +59,7 @@ const AppsPage: FC = () => {
|
|||||||
</NavbarCenter>
|
</NavbarCenter>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
<ContentContainer id="content-container">
|
<ContentContainer id="content-container">
|
||||||
<AppsContainer>
|
<AppsContainer style={{ height: containerHeight }}>
|
||||||
{filteredApps.map((app) => (
|
{filteredApps.map((app) => (
|
||||||
<App key={app.id} app={app} />
|
<App key={app.id} app={app} />
|
||||||
))}
|
))}
|
||||||
@ -68,7 +87,7 @@ const ContentContainer = styled.div`
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
padding: 50px;
|
padding: 50px;
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -77,9 +96,7 @@ const AppsContainer = styled.div`
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
max-width: 930px;
|
max-width: 930px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: 520px;
|
grid-template-columns: repeat(auto-fill, 90px);
|
||||||
min-height: 520px;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
|
|
||||||
gap: 25px;
|
gap: 25px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
`
|
`
|
||||||
|
|||||||
@ -3,9 +3,11 @@ import { useTheme } from '@renderer/context/ThemeProvider'
|
|||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { useAppDispatch } from '@renderer/store'
|
import { useAppDispatch } from '@renderer/store'
|
||||||
import {
|
import {
|
||||||
|
DEFAULT_MINIAPP_ICONS,
|
||||||
DEFAULT_SIDEBAR_ICONS,
|
DEFAULT_SIDEBAR_ICONS,
|
||||||
setClickAssistantToShowTopic,
|
setClickAssistantToShowTopic,
|
||||||
setCustomCss,
|
setCustomCss,
|
||||||
|
setMiniAppIcons,
|
||||||
setShowTopicTime,
|
setShowTopicTime,
|
||||||
setSidebarIcons
|
setSidebarIcons
|
||||||
} from '@renderer/store/settings'
|
} from '@renderer/store/settings'
|
||||||
@ -16,6 +18,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
||||||
|
import MiniAppIconsManager from './MiniAppIconsManager'
|
||||||
import SidebarIconsManager from './SidebarIconsManager'
|
import SidebarIconsManager from './SidebarIconsManager'
|
||||||
|
|
||||||
const DisplaySettings: FC = () => {
|
const DisplaySettings: FC = () => {
|
||||||
@ -29,7 +32,8 @@ const DisplaySettings: FC = () => {
|
|||||||
clickAssistantToShowTopic,
|
clickAssistantToShowTopic,
|
||||||
showTopicTime,
|
showTopicTime,
|
||||||
customCss,
|
customCss,
|
||||||
sidebarIcons
|
sidebarIcons,
|
||||||
|
miniAppIcons
|
||||||
} = useSettings()
|
} = useSettings()
|
||||||
const { theme: themeMode } = useTheme()
|
const { theme: themeMode } = useTheme()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -37,6 +41,8 @@ const DisplaySettings: FC = () => {
|
|||||||
|
|
||||||
const [visibleIcons, setVisibleIcons] = useState(sidebarIcons?.visible || DEFAULT_SIDEBAR_ICONS)
|
const [visibleIcons, setVisibleIcons] = useState(sidebarIcons?.visible || DEFAULT_SIDEBAR_ICONS)
|
||||||
const [disabledIcons, setDisabledIcons] = useState(sidebarIcons?.disabled || [])
|
const [disabledIcons, setDisabledIcons] = useState(sidebarIcons?.disabled || [])
|
||||||
|
const [visibleMiniApps, setVisibleMiniApps] = useState(miniAppIcons?.visible || DEFAULT_MINIAPP_ICONS)
|
||||||
|
const [disabledMiniApps, setDisabledMiniApps] = useState(miniAppIcons?.disabled || [])
|
||||||
|
|
||||||
// 使用useCallback优化回调函数
|
// 使用useCallback优化回调函数
|
||||||
const handleWindowStyleChange = useCallback(
|
const handleWindowStyleChange = useCallback(
|
||||||
@ -52,6 +58,18 @@ const DisplaySettings: FC = () => {
|
|||||||
dispatch(setSidebarIcons({ visible: DEFAULT_SIDEBAR_ICONS, disabled: [] }))
|
dispatch(setSidebarIcons({ visible: DEFAULT_SIDEBAR_ICONS, disabled: [] }))
|
||||||
}, [dispatch])
|
}, [dispatch])
|
||||||
|
|
||||||
|
const handleResetMinApps = useCallback(() => {
|
||||||
|
setVisibleMiniApps(DEFAULT_MINIAPP_ICONS)
|
||||||
|
setDisabledMiniApps([])
|
||||||
|
dispatch(
|
||||||
|
setMiniAppIcons({
|
||||||
|
visible: DEFAULT_MINIAPP_ICONS,
|
||||||
|
disabled: [],
|
||||||
|
pinned: miniAppIcons?.pinned || []
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}, [dispatch, miniAppIcons?.pinned])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingContainer theme={themeMode}>
|
<SettingContainer theme={themeMode}>
|
||||||
<SettingGroup theme={theme}>
|
<SettingGroup theme={theme}>
|
||||||
@ -142,6 +160,22 @@ const DisplaySettings: FC = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
|
<SettingGroup theme={theme}>
|
||||||
|
<SettingTitle
|
||||||
|
style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<span>{t('settings.display.minApp.title')}</span>
|
||||||
|
<ResetButtonWrapper>
|
||||||
|
<Button onClick={handleResetMinApps}>{t('common.reset')}</Button>
|
||||||
|
</ResetButtonWrapper>
|
||||||
|
</SettingTitle>
|
||||||
|
<SettingDivider />
|
||||||
|
<MiniAppIconsManager
|
||||||
|
visibleMiniApps={visibleMiniApps}
|
||||||
|
disabledMiniApps={disabledMiniApps}
|
||||||
|
setVisibleMiniApps={setVisibleMiniApps}
|
||||||
|
setDisabledMiniApps={setDisabledMiniApps}
|
||||||
|
/>
|
||||||
|
</SettingGroup>
|
||||||
</SettingContainer>
|
</SettingContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,256 @@
|
|||||||
|
import { CloseOutlined } from '@ant-design/icons'
|
||||||
|
import {
|
||||||
|
DragDropContext,
|
||||||
|
Draggable,
|
||||||
|
DraggableProvided,
|
||||||
|
Droppable,
|
||||||
|
DroppableProvided,
|
||||||
|
DropResult
|
||||||
|
} from '@hello-pangea/dnd'
|
||||||
|
import { getAllMinApps } from '@renderer/config/minapps'
|
||||||
|
import { useAppDispatch } from '@renderer/store'
|
||||||
|
import { FC, useCallback, useMemo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
import { MinAppIcon, setMiniAppIcons } from '../../../store/settings'
|
||||||
|
|
||||||
|
interface MiniAppManagerProps {
|
||||||
|
visibleMiniApps: MinAppIcon[]
|
||||||
|
disabledMiniApps: MinAppIcon[]
|
||||||
|
setVisibleMiniApps: (programs: MinAppIcon[]) => void
|
||||||
|
setDisabledMiniApps: (programs: MinAppIcon[]) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将可复用的类型和常量提取出来
|
||||||
|
type ListType = 'visible' | 'disabled'
|
||||||
|
interface AppInfo {
|
||||||
|
name: string
|
||||||
|
logo?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const MiniAppIconsManager: FC<MiniAppManagerProps> = ({
|
||||||
|
visibleMiniApps,
|
||||||
|
disabledMiniApps,
|
||||||
|
setVisibleMiniApps,
|
||||||
|
setDisabledMiniApps
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
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 handleListUpdate = useCallback(
|
||||||
|
(visible: MinAppIcon[], disabled: MinAppIcon[]) => {
|
||||||
|
setVisibleMiniApps(visible)
|
||||||
|
setDisabledMiniApps(disabled)
|
||||||
|
dispatch(setMiniAppIcons({ visible, disabled, pinned: [] }))
|
||||||
|
},
|
||||||
|
[dispatch, setVisibleMiniApps, setDisabledMiniApps]
|
||||||
|
)
|
||||||
|
|
||||||
|
const onDragEnd = useCallback(
|
||||||
|
(result: DropResult) => {
|
||||||
|
const { source, destination } = result
|
||||||
|
if (!destination) return
|
||||||
|
|
||||||
|
const sourceList = source.droppableId === 'visible' ? visibleMiniApps : disabledMiniApps
|
||||||
|
const destList = destination.droppableId === 'visible' ? visibleMiniApps : disabledMiniApps
|
||||||
|
|
||||||
|
if (source.droppableId === destination.droppableId) {
|
||||||
|
const newList = [...sourceList]
|
||||||
|
const [removed] = newList.splice(source.index, 1)
|
||||||
|
newList.splice(destination.index, 0, removed)
|
||||||
|
|
||||||
|
handleListUpdate(
|
||||||
|
source.droppableId === 'visible' ? newList : visibleMiniApps,
|
||||||
|
source.droppableId === 'disabled' ? newList : disabledMiniApps
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const sourceNewList = [...sourceList]
|
||||||
|
const [removed] = sourceNewList.splice(source.index, 1)
|
||||||
|
const destNewList = [...destList]
|
||||||
|
destNewList.splice(destination.index, 0, removed)
|
||||||
|
|
||||||
|
handleListUpdate(
|
||||||
|
destination.droppableId === 'visible' ? destNewList : sourceNewList,
|
||||||
|
destination.droppableId === 'disabled' ? destNewList : sourceNewList
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[visibleMiniApps, disabledMiniApps, handleListUpdate]
|
||||||
|
)
|
||||||
|
|
||||||
|
const onMoveMiniApp = useCallback(
|
||||||
|
(program: MinAppIcon, fromList: ListType) => {
|
||||||
|
const isMovingToVisible = fromList === 'disabled'
|
||||||
|
const newVisible = isMovingToVisible
|
||||||
|
? [...visibleMiniApps, program]
|
||||||
|
: visibleMiniApps.filter((p) => p !== program)
|
||||||
|
const newDisabled = isMovingToVisible
|
||||||
|
? disabledMiniApps.filter((p) => p !== program)
|
||||||
|
: [...disabledMiniApps, program]
|
||||||
|
|
||||||
|
handleListUpdate(newVisible, newDisabled)
|
||||||
|
},
|
||||||
|
[visibleMiniApps, disabledMiniApps, handleListUpdate]
|
||||||
|
)
|
||||||
|
|
||||||
|
const renderProgramItem = (program: MinAppIcon, provided: DraggableProvided, listType: ListType) => {
|
||||||
|
const { name, logo } = getAppInfo(program)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProgramItem ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
|
||||||
|
<ProgramContent>
|
||||||
|
<AppLogo src={logo} alt={name} />
|
||||||
|
<span>{name}</span>
|
||||||
|
</ProgramContent>
|
||||||
|
<CloseButton onClick={() => onMoveMiniApp(program, listType)}>
|
||||||
|
<CloseOutlined />
|
||||||
|
</CloseButton>
|
||||||
|
</ProgramItem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
|
<ProgramSection>
|
||||||
|
{(['visible', 'disabled'] as const).map((listType) => (
|
||||||
|
<ProgramColumn key={listType}>
|
||||||
|
<h4>{t(`settings.display.minApp.${listType}`)}</h4>
|
||||||
|
<Droppable droppableId={listType}>
|
||||||
|
{(provided: DroppableProvided) => (
|
||||||
|
<ProgramList ref={provided.innerRef} {...provided.droppableProps}>
|
||||||
|
<ScrollContainer>
|
||||||
|
{(listType === 'visible' ? visibleMiniApps : disabledMiniApps).map((program, index) => (
|
||||||
|
<Draggable key={program} draggableId={String(program)} index={index}>
|
||||||
|
{(provided: DraggableProvided) => renderProgramItem(program, provided, listType)}
|
||||||
|
</Draggable>
|
||||||
|
))}
|
||||||
|
{disabledMiniApps.length === 0 && listType === 'disabled' && (
|
||||||
|
<EmptyPlaceholder>{t('settings.display.minApp.empty')}</EmptyPlaceholder>
|
||||||
|
)}
|
||||||
|
{provided.placeholder}
|
||||||
|
</ScrollContainer>
|
||||||
|
</ProgramList>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</ProgramColumn>
|
||||||
|
))}
|
||||||
|
</ProgramSection>
|
||||||
|
</DragDropContext>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppLogo = styled.img`
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
object-fit: contain;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ScrollContainer = styled.div`
|
||||||
|
overflow-y: auto;
|
||||||
|
height: 100%;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ProgramSection = styled.div`
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
padding: 10px;
|
||||||
|
background: var(--color-background);
|
||||||
|
`
|
||||||
|
|
||||||
|
const ProgramColumn = styled.div`
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: var(--color-text);
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const ProgramList = styled.div`
|
||||||
|
height: 365px;
|
||||||
|
min-height: 365px;
|
||||||
|
padding: 10px;
|
||||||
|
background: var(--color-background-soft);
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow-y: hidden;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ProgramItem = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
background: var(--color-background);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: move;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ProgramContent = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.iconfont {
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const CloseButton = styled.div`
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--color-text-2);
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
${ProgramItem}:hover & {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const EmptyPlaceholder = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--color-text-2);
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
`
|
||||||
|
|
||||||
|
export default MiniAppIconsManager
|
||||||
@ -9,7 +9,7 @@ import { isEmpty } from 'lodash'
|
|||||||
import { createMigrate } from 'redux-persist'
|
import { createMigrate } from 'redux-persist'
|
||||||
|
|
||||||
import { RootState } from '.'
|
import { RootState } from '.'
|
||||||
import { DEFAULT_SIDEBAR_ICONS } from './settings'
|
import { DEFAULT_MINIAPP_ICONS, DEFAULT_SIDEBAR_ICONS } from './settings'
|
||||||
|
|
||||||
const migrateConfig = {
|
const migrateConfig = {
|
||||||
'2': (state: RootState) => {
|
'2': (state: RootState) => {
|
||||||
@ -789,6 +789,11 @@ const migrateConfig = {
|
|||||||
visible: DEFAULT_SIDEBAR_ICONS,
|
visible: DEFAULT_SIDEBAR_ICONS,
|
||||||
disabled: []
|
disabled: []
|
||||||
}
|
}
|
||||||
|
state.settings.miniAppIcons = {
|
||||||
|
visible: DEFAULT_MINIAPP_ICONS,
|
||||||
|
disabled: [],
|
||||||
|
pinned: []
|
||||||
|
}
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||||
|
import { getAllMinApps } from '@renderer/config/minapps'
|
||||||
import { TRANSLATE_PROMPT } from '@renderer/config/prompts'
|
import { TRANSLATE_PROMPT } from '@renderer/config/prompts'
|
||||||
import { CodeStyleVarious, LanguageVarious, ThemeMode } from '@renderer/types'
|
import { CodeStyleVarious, LanguageVarious, ThemeMode } from '@renderer/types'
|
||||||
|
|
||||||
@ -15,6 +16,11 @@ export const DEFAULT_SIDEBAR_ICONS: SidebarIcon[] = [
|
|||||||
'knowledge',
|
'knowledge',
|
||||||
'files'
|
'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 {
|
export interface SettingsState {
|
||||||
showAssistants: boolean
|
showAssistants: boolean
|
||||||
@ -61,6 +67,11 @@ export interface SettingsState {
|
|||||||
disabled: SidebarIcon[]
|
disabled: SidebarIcon[]
|
||||||
}
|
}
|
||||||
narrowMode: boolean
|
narrowMode: boolean
|
||||||
|
miniAppIcons: {
|
||||||
|
visible: MinAppIcon[]
|
||||||
|
disabled: MinAppIcon[]
|
||||||
|
pinned: MinAppIcon[]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: SettingsState = {
|
const initialState: SettingsState = {
|
||||||
@ -105,7 +116,12 @@ const initialState: SettingsState = {
|
|||||||
visible: DEFAULT_SIDEBAR_ICONS,
|
visible: DEFAULT_SIDEBAR_ICONS,
|
||||||
disabled: []
|
disabled: []
|
||||||
},
|
},
|
||||||
narrowMode: false
|
narrowMode: false,
|
||||||
|
miniAppIcons: {
|
||||||
|
visible: DEFAULT_MINIAPP_ICONS,
|
||||||
|
disabled: [],
|
||||||
|
pinned: []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingsSlice = createSlice({
|
const settingsSlice = createSlice({
|
||||||
@ -235,6 +251,12 @@ const settingsSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setNarrowMode: (state, action: PayloadAction<boolean>) => {
|
setNarrowMode: (state, action: PayloadAction<boolean>) => {
|
||||||
state.narrowMode = action.payload
|
state.narrowMode = action.payload
|
||||||
|
},
|
||||||
|
setMiniAppIcons: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ visible: MinAppIcon[]; disabled: MinAppIcon[]; pinned: MinAppIcon[] }>
|
||||||
|
) => {
|
||||||
|
state.miniAppIcons = action.payload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -280,7 +302,8 @@ export const {
|
|||||||
setCustomCss,
|
setCustomCss,
|
||||||
setTopicNamingPrompt,
|
setTopicNamingPrompt,
|
||||||
setSidebarIcons,
|
setSidebarIcons,
|
||||||
setNarrowMode
|
setNarrowMode,
|
||||||
|
setMiniAppIcons
|
||||||
} = settingsSlice.actions
|
} = settingsSlice.actions
|
||||||
|
|
||||||
export default settingsSlice.reducer
|
export default settingsSlice.reducer
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user