diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx
index 85171e4edb..312f8f60bd 100644
--- a/src/renderer/src/App.tsx
+++ b/src/renderer/src/App.tsx
@@ -5,7 +5,7 @@ import { Provider } from 'react-redux'
import { HashRouter } from 'react-router-dom'
import { PersistGate } from 'redux-persist/integration/react'
-import TabsContainer from './components/Tabs/TabsContainer'
+import AppLayout from './components/Layout/AppLayout'
import TopViewContainer from './components/TopView'
import AntdProvider from './context/AntdProvider'
import { CodeStyleProvider } from './context/CodeStyleProvider'
@@ -13,6 +13,7 @@ import { NotificationProvider } from './context/NotificationProvider'
import StyleSheetManager from './context/StyleSheetManager'
import { ThemeProvider } from './context/ThemeProvider'
import NavigationHandler from './handler/NavigationHandler'
+import { ChatProvider } from './hooks/useChat'
import Routes from './Routes'
function App(): React.ReactElement {
@@ -24,14 +25,16 @@ function App(): React.ReactElement {
-
-
+
+
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/src/renderer/src/Routes.tsx b/src/renderer/src/Routes.tsx
index 489ac8c98f..02fe6aba3a 100644
--- a/src/renderer/src/Routes.tsx
+++ b/src/renderer/src/Routes.tsx
@@ -5,10 +5,8 @@ import AppsPage from './pages/apps/AppsPage'
import FilesPage from './pages/files/FilesPage'
import HomePage from './pages/home/HomePage'
import KnowledgePage from './pages/knowledge/KnowledgePage'
-import LaunchpadPage from './pages/launchpad/LaunchpadPage'
import McpServersPage from './pages/mcp-servers'
import PaintingsRoutePage from './pages/paintings/PaintingsRoutePage'
-import SettingsPage from './pages/settings/SettingsPage'
import TranslatePage from './pages/translate/TranslatePage'
const RouteContainer = () => {
@@ -22,8 +20,8 @@ const RouteContainer = () => {
} />
} />
} />
- } />
- } />
+ {/* } />
+ } /> */}
)
}
diff --git a/src/renderer/src/assets/styles/variables.scss b/src/renderer/src/assets/styles/variables.scss
index 371590ff8a..659758f570 100644
--- a/src/renderer/src/assets/styles/variables.scss
+++ b/src/renderer/src/assets/styles/variables.scss
@@ -12,7 +12,7 @@
--list-item-border-radius: 15px;
--border-width: 0.5px;
- --main-height: calc(100vh - var(--navbar-height) - 6px);
+ --main-height: 100vh;
--border-width: 0.5px;
}
diff --git a/src/renderer/src/components/Layout/AppLayout.tsx b/src/renderer/src/components/Layout/AppLayout.tsx
new file mode 100644
index 0000000000..1afb002114
--- /dev/null
+++ b/src/renderer/src/components/Layout/AppLayout.tsx
@@ -0,0 +1,27 @@
+import { HStack } from '@renderer/components/Layout'
+import MainSidebar from '@renderer/pages/home/MainSidebar/MainSidebar'
+import { FC } from 'react'
+import styled from 'styled-components'
+
+interface AppLayoutProps {
+ children: React.ReactNode
+}
+
+const AppLayout: FC = ({ children }) => {
+ return (
+
+
+ {children}
+
+ )
+}
+
+const ContentArea = styled.div`
+ min-width: 0;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ height: 100vh;
+`
+
+export default AppLayout
diff --git a/src/renderer/src/components/Popups/SettingsPopup.tsx b/src/renderer/src/components/Popups/SettingsPopup.tsx
new file mode 100644
index 0000000000..a4bd356999
--- /dev/null
+++ b/src/renderer/src/components/Popups/SettingsPopup.tsx
@@ -0,0 +1,230 @@
+import AboutSettings from '@renderer/pages/settings/AboutSettings'
+import DataSettings from '@renderer/pages/settings/DataSettings/DataSettings'
+import DisplaySettings from '@renderer/pages/settings/DisplaySettings/DisplaySettings'
+import GeneralSettings from '@renderer/pages/settings/GeneralSettings'
+import ModelSettings from '@renderer/pages/settings/ModelSettings/ModelSettings'
+import ProvidersList from '@renderer/pages/settings/ProviderSettings'
+import QuickAssistantSettings from '@renderer/pages/settings/QuickAssistantSettings'
+import QuickPhraseSettings from '@renderer/pages/settings/QuickPhraseSettings'
+import SelectionAssistantSettings from '@renderer/pages/settings/SelectionAssistantSettings/SelectionAssistantSettings'
+import ShortcutSettings from '@renderer/pages/settings/ShortcutSettings'
+import WebSearchSettings from '@renderer/pages/settings/WebSearchSettings'
+import { Modal, Spin } from 'antd'
+import {
+ Cloud,
+ Command,
+ Globe,
+ HardDrive,
+ Info,
+ MonitorCog,
+ Package,
+ Rocket,
+ Settings2,
+ TextCursorInput,
+ Zap
+} from 'lucide-react'
+import React, { Suspense, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import styled from 'styled-components'
+
+import { TopView } from '../TopView'
+
+type SettingsTab =
+ | 'provider'
+ | 'model'
+ | 'web-search'
+ | 'general'
+ | 'display'
+ | 'shortcut'
+ | 'quickAssistant'
+ | 'selectionAssistant'
+ | 'data'
+ | 'about'
+ | 'quickPhrase'
+
+interface SettingsPopupShowParams {
+ defaultTab?: SettingsTab
+}
+
+interface Props extends SettingsPopupShowParams {
+ resolve?: (value: any) => void
+}
+
+const SettingsPopupContainer: React.FC = ({ defaultTab = 'provider', resolve }) => {
+ const { t } = useTranslation()
+ const [activeTab, setActiveTab] = useState(defaultTab)
+ const [open, setOpen] = useState(true)
+
+ const menuItems = [
+ { key: 'provider', icon: , label: t('settings.provider.title') },
+ { key: 'model', icon: , label: t('settings.model') },
+ { key: 'web-search', icon: , label: t('settings.websearch.title') },
+ { key: 'general', icon: , label: t('settings.general') },
+ { key: 'display', icon: , label: t('settings.display.title') },
+ { key: 'shortcut', icon: , label: t('settings.shortcuts.title') },
+ { key: 'quickAssistant', icon: , label: t('settings.quickAssistant.title') },
+ { key: 'selectionAssistant', icon: , label: t('selection.name') },
+ { key: 'quickPhrase', icon: , label: t('settings.quickPhrase.title') },
+ { key: 'data', icon: , label: t('settings.data.title') },
+ { key: 'about', icon: , label: t('settings.about') }
+ ] as const
+
+ const renderContent = () => {
+ switch (activeTab) {
+ case 'provider':
+ return (
+ }>
+
+
+ )
+ case 'model':
+ return
+ case 'web-search':
+ return
+ case 'general':
+ return
+ case 'display':
+ return
+ case 'shortcut':
+ return
+ case 'quickAssistant':
+ return
+ case 'selectionAssistant':
+ return
+ case 'data':
+ return
+ case 'about':
+ return
+ case 'quickPhrase':
+ return
+ default:
+ return
+ }
+ }
+
+ const onCancel = () => {
+ setOpen(false)
+ }
+
+ const onAfterClose = () => {
+ resolve && resolve(null)
+ TopView.hide(TopViewKey)
+ }
+
+ // 设置全局隐藏方法
+ SettingsPopup.hide = onCancel
+
+ return (
+
+
+
+ {menuItems.map((item) => (
+
+ ))}
+
+ {renderContent()}
+
+
+ )
+}
+
+const TopViewKey = 'SettingsPopup'
+
+export default class SettingsPopup {
+ static hide() {
+ TopView.hide(TopViewKey)
+ }
+
+ static show(props: SettingsPopupShowParams = {}) {
+ return new Promise((resolve) => {
+ TopView.show(, TopViewKey)
+ })
+ }
+}
+
+const StyledModal = styled(Modal)`
+ .ant-modal-content {
+ height: 80vh;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .ant-modal-body {
+ flex: 1;
+ padding: 0;
+ overflow: hidden;
+ }
+`
+
+const ContentContainer = styled.div`
+ display: flex;
+ flex: 1;
+ height: 100%;
+`
+
+const SettingMenus = styled.div`
+ display: flex;
+ flex-direction: column;
+ min-width: var(--settings-width);
+ border-right: 0.5px solid var(--color-border);
+ padding: 10px;
+ user-select: none;
+ background: var(--color-background);
+`
+
+const MenuItem = styled.div`
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 8px;
+ padding: 6px 10px;
+ width: 100%;
+ cursor: pointer;
+ border-radius: var(--list-item-border-radius);
+ font-weight: 500;
+ transition: all 0.2s ease-in-out;
+ border: 0.5px solid transparent;
+ margin-bottom: 5px;
+
+ .anticon {
+ font-size: 16px;
+ opacity: 0.8;
+ }
+
+ .iconfont {
+ font-size: 18px;
+ line-height: 18px;
+ opacity: 0.7;
+ margin-left: -1px;
+ }
+
+ &:hover {
+ background: var(--color-background-soft);
+ }
+
+ &.active {
+ background: var(--color-background-soft);
+ border: 0.5px solid var(--color-border);
+ }
+`
+
+const SettingContent = styled.div`
+ display: flex;
+ height: 100%;
+ flex: 1;
+ overflow: auto;
+`
diff --git a/src/renderer/src/handler/NavigationHandler.tsx b/src/renderer/src/handler/NavigationHandler.tsx
index 14e4e18304..01dc8b2ea5 100644
--- a/src/renderer/src/handler/NavigationHandler.tsx
+++ b/src/renderer/src/handler/NavigationHandler.tsx
@@ -1,15 +1,12 @@
+import SettingsPopup from '@renderer/components/Popups/SettingsPopup'
import NavigationService from '@renderer/services/NavigationService'
-import { useAppDispatch, useAppSelector } from '@renderer/store'
-import { addTab, Tab } from '@renderer/store/tabs'
+import { useAppSelector } from '@renderer/store'
import { useEffect } from 'react'
import { useHotkeys } from 'react-hotkeys-hook'
-import { useLocation, useNavigate } from 'react-router-dom'
+import { useNavigate } from 'react-router-dom'
const NavigationHandler: React.FC = () => {
- const dispatch = useAppDispatch()
- const location = useLocation()
const navigate = useNavigate()
- const tabs = useAppSelector((state) => state.tabs.tabs)
const showSettingsShortcutEnabled = useAppSelector(
(state) => state.shortcuts.shortcuts.find((s) => s.key === 'show_settings')?.enabled
@@ -22,10 +19,7 @@ const NavigationHandler: React.FC = () => {
useHotkeys(
'meta+, ! ctrl+,',
function () {
- if (location.pathname.startsWith('/settings')) {
- return
- }
- navigate('/settings/provider')
+ SettingsPopup.show({ defaultTab: 'provider' })
},
{
splitKey: '!',
@@ -35,20 +29,6 @@ const NavigationHandler: React.FC = () => {
}
)
- // 初始化 home tab
- useEffect(() => {
- if (tabs.length === 0) {
- const homeTab: Tab = {
- id: 'home',
- titleKey: 'title.home',
- title: '',
- path: '/',
- iconType: 'home'
- }
- dispatch(addTab(homeTab))
- }
- }, [dispatch, tabs.length])
-
return null
}
diff --git a/src/renderer/src/hooks/useChat.tsx b/src/renderer/src/hooks/useChat.tsx
index 0a2c80404b..dd322288a7 100644
--- a/src/renderer/src/hooks/useChat.tsx
+++ b/src/renderer/src/hooks/useChat.tsx
@@ -1,10 +1,9 @@
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { useAppDispatch, useAppSelector } from '@renderer/store'
import { loadTopicMessagesThunk } from '@renderer/store/thunk/messageThunk'
-import { Assistant } from '@renderer/types'
-import { Topic } from '@renderer/types'
-import { use, useEffect, useMemo, useState } from 'react'
-import { createContext } from 'react'
+import { Assistant, Topic } from '@renderer/types'
+import { createContext, use, useCallback, useEffect, useMemo, useState } from 'react'
+import { useLocation, useNavigate } from 'react-router-dom'
import { useTopicsForAssistant } from './useAssistant'
import { useSettings } from './useSettings'
@@ -20,14 +19,25 @@ const ChatContext = createContext(null)
export const ChatProvider = ({ children }) => {
const assistants = useAppSelector((state) => state.assistants.assistants)
- const [activeAssistant, setActiveAssistant] = useState(assistants[0])
+ const [activeAssistant, setActiveAssistantBase] = useState(assistants[0])
const topics = useTopicsForAssistant(activeAssistant.id)
const [activeTopic, setActiveTopic] = useState(topics[0])
const { clickAssistantToShowTopic } = useSettings()
const dispatch = useAppDispatch()
+ const navigate = useNavigate()
+ const location = useLocation()
- console.log('activeAssistant', activeAssistant)
- console.log('activeTopic', activeTopic)
+ // 包装setActiveAssistant以添加导航逻辑
+ const setActiveAssistant = useCallback(
+ (assistant: Assistant) => {
+ setActiveAssistantBase(assistant)
+ // 如果当前不在聊天页面,导航到聊天页面
+ if (location.pathname !== '/') {
+ navigate('/')
+ }
+ },
+ [setActiveAssistantBase, location.pathname, navigate]
+ )
// 当 topics 变化时,如果当前 activeTopic 不在 topics 中,设置第一个 topic
useEffect(() => {
@@ -58,7 +68,7 @@ export const ChatProvider = ({ children }) => {
EventEmitter.on(EVENT_NAMES.SET_TOPIC, setActiveTopic)
]
return () => subscriptions.forEach((subscription) => subscription())
- }, [])
+ }, [setActiveAssistant])
const value = useMemo(
() => ({
@@ -67,7 +77,7 @@ export const ChatProvider = ({ children }) => {
setActiveAssistant,
setActiveTopic
}),
- [activeAssistant, activeTopic]
+ [activeAssistant, activeTopic, setActiveAssistant]
)
return {children}
diff --git a/src/renderer/src/pages/files/FilesPage.tsx b/src/renderer/src/pages/files/FilesPage.tsx
index e1c0f0b0cd..0fd04e50f9 100644
--- a/src/renderer/src/pages/files/FilesPage.tsx
+++ b/src/renderer/src/pages/files/FilesPage.tsx
@@ -134,17 +134,6 @@ const FilesPage: FC = () => {
{t('files.title')}
-
- {menuItems.map((item) => (
- setFileType(item.key as FileTypes)}
- />
- ))}
-
@@ -207,6 +196,17 @@ const FilesPage: FC = () => {
)}
+
+ {menuItems.map((item) => (
+ setFileType(item.key as FileTypes)}
+ />
+ ))}
+
)
diff --git a/src/renderer/src/pages/home/Chat.tsx b/src/renderer/src/pages/home/Chat.tsx
index 906c99ca6f..9e1d820ff6 100644
--- a/src/renderer/src/pages/home/Chat.tsx
+++ b/src/renderer/src/pages/home/Chat.tsx
@@ -122,7 +122,7 @@ const Chat: FC = () => {
}
const Main = styled(Flex)`
- height: calc(100vh - var(--navbar-height) - 50px);
+ height: calc(100vh - var(--navbar-height));
transform: translateZ(0);
position: relative;
`
diff --git a/src/renderer/src/pages/home/HomePage.tsx b/src/renderer/src/pages/home/HomePage.tsx
index 527717c7a3..64252fd1b4 100644
--- a/src/renderer/src/pages/home/HomePage.tsx
+++ b/src/renderer/src/pages/home/HomePage.tsx
@@ -1,12 +1,9 @@
-import { HStack } from '@renderer/components/Layout'
-import { ChatProvider } from '@renderer/hooks/useChat'
import { useSettings } from '@renderer/hooks/useSettings'
import { FC, useEffect } from 'react'
import styled from 'styled-components'
import Chat from './Chat'
import ChatNavbar from './ChatNavbar'
-import MainSidebar from './MainSidebar/MainSidebar'
const HomePage: FC<{ style?: React.CSSProperties }> = ({ style }) => {
const { showAssistants, showTopics, topicPosition } = useSettings()
@@ -20,17 +17,12 @@ const HomePage: FC<{ style?: React.CSSProperties }> = ({ style }) => {
}, [showAssistants, showTopics, topicPosition])
return (
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
)
}
diff --git a/src/renderer/src/pages/home/MainSidebar/MainSidebarStyles.tsx b/src/renderer/src/pages/home/MainSidebar/MainSidebarStyles.tsx
index 1f0cdcd32f..6b42f4120f 100644
--- a/src/renderer/src/pages/home/MainSidebar/MainSidebarStyles.tsx
+++ b/src/renderer/src/pages/home/MainSidebar/MainSidebarStyles.tsx
@@ -56,6 +56,7 @@ export const Container = styled.div<{ transparent?: boolean }>`
min-height: var(--main-height);
background: var(--color-background);
padding-top: 10px;
+ margin-top: 50px;
`
export const MainMenu = styled.div`
diff --git a/src/renderer/src/pages/knowledge/KnowledgePage.tsx b/src/renderer/src/pages/knowledge/KnowledgePage.tsx
index 3e609e7cab..aa24e698ff 100644
--- a/src/renderer/src/pages/knowledge/KnowledgePage.tsx
+++ b/src/renderer/src/pages/knowledge/KnowledgePage.tsx
@@ -96,6 +96,13 @@ const KnowledgePage: FC = () => {
{t('knowledge.title')}
+ {bases.length === 0 ? (
+
+
+
+ ) : selectedBase ? (
+
+ ) : null}
{
- {bases.length === 0 ? (
-
-
-
- ) : selectedBase ? (
-
- ) : null}
)