diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index e138a812ab..c3d6fd9d7e 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -7,15 +7,13 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { Provider } from 'react-redux' import { PersistGate } from 'redux-persist/integration/react' -// TODO: 新路由系统入口,迁移完成后启用 -// import { AppShell } from './components/layout/AppShell' +import { AppShell } from './components/layout/AppShell' import TopViewContainer from './components/TopView' import AntdProvider from './context/AntdProvider' import { CodeStyleProvider } from './context/CodeStyleProvider' import { NotificationProvider } from './context/NotificationProvider' import StyleSheetManager from './context/StyleSheetManager' import { ThemeProvider } from './context/ThemeProvider' -import Router from './Router' const logger = loggerService.withContext('App.tsx') @@ -44,8 +42,7 @@ function App(): React.ReactElement { - {/* TODO: 迁移完成后切换到 */} - + diff --git a/src/renderer/src/Router.tsx b/src/renderer/src/Router.tsx deleted file mode 100644 index fb555d8bc3..0000000000 --- a/src/renderer/src/Router.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import '@renderer/databases' - -import type { FC } from 'react' -import { useMemo } from 'react' -import { HashRouter, Route, Routes } from 'react-router-dom' - -import Sidebar from './components/app/Sidebar' -import { ErrorBoundary } from './components/ErrorBoundary' -import TabsContainer from './components/Tab/TabContainer' -import NavigationHandler from './handler/NavigationHandler' -import { useNavbarPosition } from './hooks/useNavbar' -import CodeToolsPage from './pages/code/CodeToolsPage' -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 MinAppPage from './pages/minapps/MinAppPage' -import MinAppsPage from './pages/minapps/MinAppsPage' -import NotesPage from './pages/notes/NotesPage' -import PaintingsRoutePage from './pages/paintings/PaintingsRoutePage' -import SettingsPage from './pages/settings/SettingsPage' -import AssistantPresetsPage from './pages/store/assistants/presets/AssistantPresetsPage' -import TranslatePage from './pages/translate/TranslatePage' - -const Router: FC = () => { - const { navbarPosition } = useNavbarPosition() - - const routes = useMemo(() => { - return ( - - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - ) - }, []) - - if (navbarPosition === 'left') { - return ( - - - {routes} - - - ) - } - - return ( - - - {routes} - - ) -} - -export default Router diff --git a/src/renderer/src/components/FreeTrialModelTag.tsx b/src/renderer/src/components/FreeTrialModelTag.tsx index ad142ae0cf..649452f3de 100644 --- a/src/renderer/src/components/FreeTrialModelTag.tsx +++ b/src/renderer/src/components/FreeTrialModelTag.tsx @@ -30,13 +30,13 @@ export const FreeTrialModelTag: FC = ({ model, showLabel = true }) => { } const onSelectProvider = () => { - NavigationService.navigate!(`/settings/provider?id=${providerId}`) + NavigationService.navigate!({ to: `/settings/provider`, search: { id: providerId } }) } const onNavigateProvider = (e: MouseEvent) => { e.stopPropagation() SelectModelPopup.hide() - NavigationService.navigate!(`/settings/provider?id=${providerId}`) + NavigationService.navigate?.({ to: '/settings/provider', search: { id: providerId } }) } if (!showLabel) { diff --git a/src/renderer/src/components/MinApp/MinApp.tsx b/src/renderer/src/components/MinApp/MinApp.tsx index 801db2b082..3fb0e89715 100644 --- a/src/renderer/src/components/MinApp/MinApp.tsx +++ b/src/renderer/src/components/MinApp/MinApp.tsx @@ -6,11 +6,11 @@ import { useMinappPopup } from '@renderer/hooks/useMinappPopup' import { useMinapps } from '@renderer/hooks/useMinapps' import { useNavbarPosition } from '@renderer/hooks/useNavbar' import type { MinAppType } from '@renderer/types' +import { useNavigate } from '@tanstack/react-router' import type { MenuProps } from 'antd' import { Dropdown } from 'antd' import type { FC } from 'react' import { useTranslation } from 'react-i18next' -import { useNavigate } from 'react-router-dom' import styled from 'styled-components' interface Props { @@ -47,7 +47,7 @@ const MinApp: FC = ({ app, onClick, size = 60, isLast }) => { const handleClick = () => { if (isTopNavbar) { // 顶部导航栏:导航到小程序页面 - navigate(`/apps/${app.id}`) + navigate({ to: `/apps/${app.id}` }) } else { // 侧边导航栏:保持原有弹窗行为 openMinappKeepAlive(app) diff --git a/src/renderer/src/components/MinApp/MinAppTabsPool.tsx b/src/renderer/src/components/MinApp/MinAppTabsPool.tsx index 1bef9a532a..6bf54d4c7f 100644 --- a/src/renderer/src/components/MinApp/MinAppTabsPool.tsx +++ b/src/renderer/src/components/MinApp/MinAppTabsPool.tsx @@ -3,9 +3,9 @@ import WebviewContainer from '@renderer/components/MinApp/WebviewContainer' import { useMinapps } from '@renderer/hooks/useMinapps' import { useNavbarPosition } from '@renderer/hooks/useNavbar' import { getWebviewLoaded, setWebviewLoaded } from '@renderer/utils/webviewStateManager' +import { useLocation } from '@tanstack/react-router' import type { WebviewTag } from 'electron' import React, { useEffect, useRef } from 'react' -import { useLocation } from 'react-router-dom' import styled from 'styled-components' /** diff --git a/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx b/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx index 1aa7859a99..908f471819 100644 --- a/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx +++ b/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx @@ -193,7 +193,7 @@ const PopupContainer: React.FC = ({ model, filter: baseFilter, showTagFil e.stopPropagation() setOpen(false) resolve(undefined) - window.navigate(`/settings/provider?id=${p.id}`) + window.navigate({ to: '/settings/provider', search: { id: p.id } }) }} /> diff --git a/src/renderer/src/components/Tab/TabContainer.tsx b/src/renderer/src/components/Tab/TabContainer.tsx index 08f16bb9cc..986c6ec782 100644 --- a/src/renderer/src/components/Tab/TabContainer.tsx +++ b/src/renderer/src/components/Tab/TabContainer.tsx @@ -17,6 +17,7 @@ import { addTab, removeTab, setActiveTab, setTabs } from '@renderer/store/tabs' import type { MinAppType } from '@renderer/types' import { classNames } from '@renderer/utils' import { ThemeMode } from '@shared/data/preference/preferenceTypes' +import { useLocation, useNavigate } from '@tanstack/react-router' import type { LRUCache } from 'lru-cache' import { FileSearch, @@ -37,7 +38,6 @@ import { } from 'lucide-react' import { useCallback, useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' import MinAppIcon from '../Icons/MinAppIcon' @@ -203,17 +203,17 @@ const TabsContainer: React.FC = ({ children }) => { const handleAddTab = () => { hideMinappPopup() - navigate('/launchpad') + navigate({ to: '/launchpad' }) } const handleSettingsClick = () => { hideMinappPopup() - navigate(lastSettingsPath) + navigate({ to: lastSettingsPath }) } const handleTabClick = (tab: Tab) => { hideMinappPopup() - navigate(tab.path) + navigate({ to: tab.path }) } const visibleTabs = useMemo(() => tabs.filter((tab) => !specialTabs.includes(tab.id)), [tabs]) diff --git a/src/renderer/src/components/app/Sidebar.tsx b/src/renderer/src/components/app/Sidebar.tsx index de30b1467b..d332a4a78f 100644 --- a/src/renderer/src/components/app/Sidebar.tsx +++ b/src/renderer/src/components/app/Sidebar.tsx @@ -30,9 +30,9 @@ import { } from 'lucide-react' import type { FC } from 'react' import { useTranslation } from 'react-i18next' -import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' +import { useTabs } from '../../hooks/useTabs' import UserPopup from '../Popups/UserPopup' import { SidebarOpenedMinappTabs, SidebarPinnedApps } from './PinnedMinapps' @@ -40,9 +40,11 @@ const Sidebar: FC = () => { const { hideMinappPopup } = useMinappPopup() const { pinned, minappShow } = useMinapps() const [visibleSidebarIcons] = usePreference('ui.sidebar.icons.visible') + const { tabs, activeTabId, updateTab } = useTabs() - const { pathname } = useLocation() - const navigate = useNavigate() + // 获取当前 Tab 的 URL 作为 pathname + const activeTab = tabs.find((t) => t.id === activeTabId) + const pathname = activeTab?.url || '/' const { theme, settedTheme, toggleTheme } = useTheme() const avatar = useAvatar() @@ -54,9 +56,12 @@ const Sidebar: FC = () => { const showPinnedApps = pinned.length > 0 && visibleSidebarIcons.includes('minapp') + // 在当前 Tab 内跳转 const to = async (path: string) => { await modelGenerating() - navigate(path) + if (activeTabId) { + updateTab(activeTabId, { url: path }) + } } const isFullscreen = useFullscreen() @@ -121,14 +126,16 @@ const Sidebar: FC = () => { const MainMenus: FC = () => { const { hideMinappPopup } = useMinappPopup() const { minappShow } = useMinapps() + const { tabs, activeTabId, updateTab } = useTabs() + + // 获取当前 Tab 的 URL 作为 pathname + const activeTab = tabs.find((t) => t.id === activeTabId) + const pathname = activeTab?.url || '/' - const { pathname } = useLocation() const [visibleSidebarIcons] = usePreference('ui.sidebar.icons.visible') const { defaultPaintingProvider } = useSettings() - const navigate = useNavigate() const { theme } = useTheme() - const isRoute = (path: string): string => (pathname === path && !minappShow ? 'active' : '') const isRoutes = (path: string): string => (pathname.startsWith(path) && !minappShow ? 'active' : '') const iconMap = { @@ -144,7 +151,7 @@ const MainMenus: FC = () => { } const pathMap = { - assistants: '/', + assistants: '/chat', store: '/store', paintings: `/paintings/${defaultPaintingProvider}`, translate: '/translate', @@ -155,17 +162,24 @@ const MainMenus: FC = () => { notes: '/notes' } + // 在当前 Tab 内跳转 + const to = async (path: string) => { + await modelGenerating() + if (activeTabId) { + updateTab(activeTabId, { url: path }) + } + } + return visibleSidebarIcons.map((icon) => { const path = pathMap[icon] - const isActive = path === '/' ? isRoute(path) : isRoutes(path) + const isActive = isRoutes(path) return ( { hideMinappPopup() - await modelGenerating() - navigate(path) + await to(path) }}> {iconMap[icon]} diff --git a/src/renderer/src/components/layout/AppShell.tsx b/src/renderer/src/components/layout/AppShell.tsx index aece19abd7..75e9e5a824 100644 --- a/src/renderer/src/components/layout/AppShell.tsx +++ b/src/renderer/src/components/layout/AppShell.tsx @@ -1,3 +1,5 @@ +import '@renderer/databases' + import { cn, Tabs, TabsList, TabsTrigger } from '@cherrystudio/ui' import { Plus, X } from 'lucide-react' import { Activity } from 'react' @@ -31,7 +33,7 @@ export const AppShell = () => { id: uuid(), type: 'route', url: '/', - title: 'New Tab', + title: 'New Tab' }) } diff --git a/src/renderer/src/env.d.ts b/src/renderer/src/env.d.ts index f4452a5c31..8ff317bf1b 100644 --- a/src/renderer/src/env.d.ts +++ b/src/renderer/src/env.d.ts @@ -2,8 +2,8 @@ import type { PermissionUpdate } from '@anthropic-ai/claude-agent-sdk' import type { ToastUtilities } from '@cherrystudio/ui' +import type { UseNavigateResult } from '@tanstack/react-router' import type { HookAPI } from 'antd/es/modal/useModal' -import type { NavigateFunction } from 'react-router-dom' interface ImportMetaEnv { VITE_RENDERER_INTEGRATED_MODEL: string @@ -18,7 +18,7 @@ declare global { root: HTMLElement modal: HookAPI store: any - navigate: NavigateFunction + navigate: UseNavigateResult toast: ToastUtilities agentTools: { respondToPermission: (payload: { diff --git a/src/renderer/src/handler/NavigationHandler.tsx b/src/renderer/src/handler/NavigationHandler.tsx index 5e1ef56113..578f0daab7 100644 --- a/src/renderer/src/handler/NavigationHandler.tsx +++ b/src/renderer/src/handler/NavigationHandler.tsx @@ -1,8 +1,8 @@ import { useAppSelector } from '@renderer/store' import { IpcChannel } from '@shared/IpcChannel' +import { useLocation, useNavigate } from '@tanstack/react-router' import { useEffect } from 'react' import { useHotkeys } from 'react-hotkeys-hook' -import { useLocation, useNavigate } from 'react-router-dom' const NavigationHandler: React.FC = () => { const location = useLocation() @@ -17,7 +17,7 @@ const NavigationHandler: React.FC = () => { if (location.pathname.startsWith('/settings')) { return } - navigate('/settings/provider') + navigate({ to: '/settings/provider' }) }, { splitKey: '!', @@ -30,7 +30,7 @@ const NavigationHandler: React.FC = () => { // Listen for navigate to About page event from macOS menu useEffect(() => { const handleNavigateToAbout = () => { - navigate('/settings/about') + navigate({ to: '/settings/about' }) } const removeListener = window.electron.ipcRenderer.on(IpcChannel.Windows_NavigateToAbout, handleNavigateToAbout) diff --git a/src/renderer/src/hooks/useAppInit.ts b/src/renderer/src/hooks/useAppInit.ts index 9c747033a9..e21b1ac95d 100644 --- a/src/renderer/src/hooks/useAppInit.ts +++ b/src/renderer/src/hooks/useAppInit.ts @@ -64,7 +64,7 @@ export function useAppInit() { useEffect(() => { window.api.getDataPathFromArgs().then((dataPath) => { if (dataPath) { - window.navigate('/settings/data', { replace: true }) + window.navigate({ to: '/settings/data', replace: true }) } }) }, []) diff --git a/src/renderer/src/hooks/useMCPServers.ts b/src/renderer/src/hooks/useMCPServers.ts index 26731e14fd..d9f59ca1a1 100644 --- a/src/renderer/src/hooks/useMCPServers.ts +++ b/src/renderer/src/hooks/useMCPServers.ts @@ -13,8 +13,8 @@ window.electron.ipcRenderer.on(IpcChannel.Mcp_ServersChanged, (_event, servers) window.electron.ipcRenderer.on(IpcChannel.Mcp_AddServer, (_event, server: MCPServer) => { store.dispatch(addMCPServer(server)) - NavigationService.navigate?.('/settings/mcp') - NavigationService.navigate?.(`/settings/mcp/settings/${encodeURIComponent(server.id)}`) + NavigationService.navigate?.({ to: '/settings/mcp' }) + NavigationService.navigate?.({ to: `/settings/mcp/settings/${encodeURIComponent(server.id)}` }) }) const selectMcpServers = (state: RootState) => state.mcp.servers diff --git a/src/renderer/src/hooks/useMinappPopup.ts b/src/renderer/src/hooks/useMinappPopup.ts index f08924bd0a..ccf06baba5 100644 --- a/src/renderer/src/hooks/useMinappPopup.ts +++ b/src/renderer/src/hooks/useMinappPopup.ts @@ -186,7 +186,7 @@ export const useMinappPopup = () => { // Then navigate to the app tab using NavigationService if (NavigationService.navigate) { - NavigationService.navigate(`/apps/${config.id}`) + NavigationService.navigate({ to: `/apps/${config.id}` }) } } else { // For side navbar, use the traditional popup system diff --git a/src/renderer/src/pages/code/CodeToolsPage.tsx b/src/renderer/src/pages/code/CodeToolsPage.tsx index 1e90e8cf57..4944367874 100644 --- a/src/renderer/src/pages/code/CodeToolsPage.tsx +++ b/src/renderer/src/pages/code/CodeToolsPage.tsx @@ -19,12 +19,12 @@ import { getClaudeSupportedProviders } from '@renderer/utils/provider' import type { TerminalConfig } from '@shared/config/constant' import { codeTools, terminalApps } from '@shared/config/constant' import { isSiliconAnthropicCompatibleModel } from '@shared/config/providers' +import { Link } from '@tanstack/react-router' import { Alert, Checkbox, Input, Popover, Select, Space } from 'antd' import { ArrowUpRight, Download, FolderOpen, HelpCircle, Terminal, X } from 'lucide-react' import type { FC } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' -import { Link } from 'react-router-dom' import styled from 'styled-components' import { diff --git a/src/renderer/src/pages/history/components/TopicMessages.tsx b/src/renderer/src/pages/history/components/TopicMessages.tsx index 3d1400e7d4..48ab4ad6bd 100644 --- a/src/renderer/src/pages/history/components/TopicMessages.tsx +++ b/src/renderer/src/pages/history/components/TopicMessages.tsx @@ -11,9 +11,9 @@ import { getTopicById } from '@renderer/hooks/useTopic' import { getAssistantById } from '@renderer/services/AssistantService' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { locateToMessage } from '@renderer/services/MessagesService' -import NavigationService from '@renderer/services/NavigationService' import type { Topic } from '@renderer/types' import { classNames, runAsyncFunction } from '@renderer/utils' +import { useNavigate } from '@tanstack/react-router' import { Divider, Empty } from 'antd' import { t } from 'i18next' import { Forward } from 'lucide-react' @@ -27,7 +27,8 @@ interface Props extends React.HTMLAttributes { } const TopicMessages: FC = ({ topic: _topic, ...props }) => { - const navigate = NavigationService.navigate! + const navigate = useNavigate() + const { handleScroll, containerRef } = useScrollPosition('TopicMessages') const [messageStyle] = usePreference('chat.message.style') const { setTimeoutTimer } = useTimer() @@ -53,7 +54,7 @@ const TopicMessages: FC = ({ topic: _topic, ...props }) => { await modelGenerating() SearchPopup.hide() const assistant = getAssistantById(topic.assistantId) - navigate('/', { state: { assistant, topic } }) + navigate({ to: '/chat', search: { assistantId: assistant?.id, topicId: topic.id } }) setTimeoutTimer('onContinueChat', () => EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR), 100) } diff --git a/src/renderer/src/pages/home/HomePage.tsx b/src/renderer/src/pages/home/HomePage.tsx index 4c216cf168..6c5d31e738 100644 --- a/src/renderer/src/pages/home/HomePage.tsx +++ b/src/renderer/src/pages/home/HomePage.tsx @@ -10,11 +10,11 @@ import { newMessagesActions } from '@renderer/store/newMessage' import { setActiveAgentId, setActiveTopicOrSessionAction } from '@renderer/store/runtime' import type { Assistant, Topic } from '@renderer/types' import { MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH, SECOND_MIN_WINDOW_WIDTH } from '@shared/config/constant' +import { useNavigate, useSearch } from '@tanstack/react-router' import { AnimatePresence, motion } from 'motion/react' import type { FC } from 'react' import { startTransition, useCallback, useEffect, useState } from 'react' import { useDispatch } from 'react-redux' -import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' import Chat from './Chat' @@ -31,13 +31,23 @@ const HomePage: FC = () => { // Initialize agent session hook useAgentSessionInitializer() - const location = useLocation() - const state = location.state + const search = useSearch({ strict: false }) as { assistantId?: string; topicId?: string } + + // 根据 search params 中的 ID 查找对应的 assistant + const assistantFromSearch = search.assistantId + ? assistants.find((a) => a.id === search.assistantId) + : undefined const [activeAssistant, _setActiveAssistant] = useState( - state?.assistant || _activeAssistant || assistants[0] + assistantFromSearch || _activeAssistant || assistants[0] ) - const { activeTopic, setActiveTopic: _setActiveTopic } = useActiveTopic(activeAssistant?.id ?? '', state?.topic) + + // 根据 search params 中的 topicId 查找对应的 topic + const topicFromSearch = search.topicId + ? activeAssistant?.topics?.find((t) => t.id === search.topicId) + : undefined + + const { activeTopic, setActiveTopic: _setActiveTopic } = useActiveTopic(activeAssistant?.id ?? '', topicFromSearch) const [showAssistants] = usePreference('assistant.tab.show') const [showTopics] = usePreference('topic.tab.show') const [topicPosition] = usePreference('topic.position') @@ -80,10 +90,10 @@ const HomePage: FC = () => { }, [navigate]) useEffect(() => { - state?.assistant && setActiveAssistant(state?.assistant) - state?.topic && setActiveTopic(state?.topic) + assistantFromSearch && setActiveAssistant(assistantFromSearch) + topicFromSearch && setActiveTopic(topicFromSearch) // eslint-disable-next-line react-hooks/exhaustive-deps - }, [state]) + }, [search.assistantId, search.topicId]) useEffect(() => { const canMinimize = topicPosition == 'left' ? !showAssistants : !showAssistants && !showTopics diff --git a/src/renderer/src/pages/home/Inputbar/tools/components/KnowledgeBaseButton.tsx b/src/renderer/src/pages/home/Inputbar/tools/components/KnowledgeBaseButton.tsx index 9d43284022..e8d587db5d 100644 --- a/src/renderer/src/pages/home/Inputbar/tools/components/KnowledgeBaseButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/tools/components/KnowledgeBaseButton.tsx @@ -5,11 +5,11 @@ import { QuickPanelReservedSymbol, useQuickPanel } from '@renderer/components/Qu import type { ToolQuickPanelApi } from '@renderer/pages/home/Inputbar/types' import { useAppSelector } from '@renderer/store' import type { KnowledgeBase } from '@renderer/types' +import { useNavigate } from '@tanstack/react-router' import { CircleX, FileSearch, Plus } from 'lucide-react' import type { FC } from 'react' import { memo, useCallback, useEffect, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' -import { useNavigate } from 'react-router' interface Props { quickPanel: ToolQuickPanelApi @@ -54,7 +54,7 @@ const KnowledgeBaseButton: FC = ({ quickPanel, selectedBases, onSelect, d items.push({ label: t('knowledge.add.title') + '...', icon: , - action: () => navigate('/knowledge'), + action: () => navigate({ to: '/knowledge' }), isSelected: false }) diff --git a/src/renderer/src/pages/home/Inputbar/tools/components/MCPToolsButton.tsx b/src/renderer/src/pages/home/Inputbar/tools/components/MCPToolsButton.tsx index 174a96936f..42eec3acd2 100644 --- a/src/renderer/src/pages/home/Inputbar/tools/components/MCPToolsButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/tools/components/MCPToolsButton.tsx @@ -12,12 +12,12 @@ import { EventEmitter } from '@renderer/services/EventService' import type { MCPPrompt, MCPResource, MCPServer } from '@renderer/types' import { isToolUseModeFunction } from '@renderer/utils/assistant' import { isGeminiWebSearchProvider, isSupportUrlContextProvider } from '@renderer/utils/provider' +import { useNavigate } from '@tanstack/react-router' import { Form, Input } from 'antd' import { CircleX, Hammer, Plus } from 'lucide-react' import type { FC } from 'react' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useNavigate } from 'react-router' interface Props { assistantId: string @@ -205,7 +205,7 @@ const MCPToolsButton: FC = ({ quickPanel, setInputValue, resizeTextArea, newList.push({ label: t('settings.mcp.addServer.label') + '...', icon: , - action: () => navigate('/settings/mcp') + action: () => navigate({ to: '/settings/mcp' }) }) newList.unshift({ diff --git a/src/renderer/src/pages/home/Inputbar/tools/components/useMentionModelsPanel.tsx b/src/renderer/src/pages/home/Inputbar/tools/components/useMentionModelsPanel.tsx index cda8816c6b..aa496e5265 100644 --- a/src/renderer/src/pages/home/Inputbar/tools/components/useMentionModelsPanel.tsx +++ b/src/renderer/src/pages/home/Inputbar/tools/components/useMentionModelsPanel.tsx @@ -9,6 +9,7 @@ import { getModelUniqId } from '@renderer/services/ModelService' import type { FileType, Model } from '@renderer/types' import { FileTypes } from '@renderer/types' import { getFancyProviderName } from '@renderer/utils' +import { useNavigate } from '@tanstack/react-router' import { Avatar } from 'antd' import { useLiveQuery } from 'dexie-react-hooks' import { first, sortBy } from 'lodash' @@ -16,7 +17,6 @@ import { AtSign, CircleX, Plus } from 'lucide-react' import type React from 'react' import { useCallback, useEffect, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' -import { useNavigate } from 'react-router' import styled from 'styled-components' export type MentionTriggerInfo = { type: 'input' | 'button'; position?: number; originalText?: string } @@ -194,7 +194,7 @@ export const useMentionModelsPanel = (params: Params, role: 'button' | 'manager' items.push({ label: t('settings.models.add.add_model') + '...', icon: , - action: () => navigate('/settings/provider'), + action: () => navigate({ to: '/settings/provider' }), isSelected: false }) diff --git a/src/renderer/src/pages/home/Messages/Blocks/ErrorBlock.tsx b/src/renderer/src/pages/home/Messages/Blocks/ErrorBlock.tsx index 1cb445f12d..7399dc226a 100644 --- a/src/renderer/src/pages/home/Messages/Blocks/ErrorBlock.tsx +++ b/src/renderer/src/pages/home/Messages/Blocks/ErrorBlock.tsx @@ -3,7 +3,6 @@ import CodeViewer from '@renderer/components/CodeViewer' import { useCodeStyle } from '@renderer/context/CodeStyleProvider' import { useTimer } from '@renderer/hooks/useTimer' import { getHttpMessageLabel, getProviderLabel } from '@renderer/i18n/label' -import { getProviderById } from '@renderer/services/ProviderService' import { useAppDispatch } from '@renderer/store' import { removeBlocksThunk } from '@renderer/store/thunk/messageThunk' import type { SerializedAiSdkError, SerializedAiSdkErrorUnion, SerializedError } from '@renderer/types/error' @@ -33,10 +32,10 @@ import { } from '@renderer/types/error' import type { ErrorMessageBlock, Message } from '@renderer/types/newMessage' import { formatAiSdkError, formatError, safeToString } from '@renderer/utils/error' +import { Link } from '@tanstack/react-router' import { Alert as AntdAlert, Modal } from 'antd' import React, { useEffect, useState } from 'react' import { Trans, useTranslation } from 'react-i18next' -import { Link } from 'react-router-dom' import styled from 'styled-components' const HTTP_ERROR_CODES = [400, 401, 403, 404, 429, 500, 502, 503, 504] @@ -71,8 +70,8 @@ const ErrorMessage: React.FC<{ block: ErrorMessageBlock }> = ({ block }) => { provider: ( ) }} diff --git a/src/renderer/src/pages/home/Navbar.tsx b/src/renderer/src/pages/home/Navbar.tsx index fc3e1a76a1..20479478a6 100644 --- a/src/renderer/src/pages/home/Navbar.tsx +++ b/src/renderer/src/pages/home/Navbar.tsx @@ -16,6 +16,7 @@ import styled from 'styled-components' import AssistantsDrawer from './components/AssistantsDrawer' import UpdateAppButton from './components/UpdateAppButton' + interface Props { activeAssistant: Assistant activeTopic: Topic diff --git a/src/renderer/src/pages/launchpad/LaunchpadPage.tsx b/src/renderer/src/pages/launchpad/LaunchpadPage.tsx index f9c80f6aec..e17597975b 100644 --- a/src/renderer/src/pages/launchpad/LaunchpadPage.tsx +++ b/src/renderer/src/pages/launchpad/LaunchpadPage.tsx @@ -1,11 +1,11 @@ import App from '@renderer/components/MinApp/MinApp' import { useMinapps } from '@renderer/hooks/useMinapps' import { useSettings } from '@renderer/hooks/useSettings' +import { useNavigate } from '@tanstack/react-router' import { Code, FileSearch, Folder, Languages, LayoutGrid, NotepadText, Palette, Sparkle } from 'lucide-react' import type { FC } from 'react' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { useNavigate } from 'react-router-dom' import styled from 'styled-components' const LaunchpadPage: FC = () => { @@ -87,7 +87,7 @@ const LaunchpadPage: FC = () => { {t('launchpad.apps')} {appMenuItems.map((item) => ( - navigate(item.path)}> + navigate({ to: item.path })}> {item.icon} diff --git a/src/renderer/src/pages/minapps/MinAppPage.tsx b/src/renderer/src/pages/minapps/MinAppPage.tsx index 4ee4549303..003d374880 100644 --- a/src/renderer/src/pages/minapps/MinAppPage.tsx +++ b/src/renderer/src/pages/minapps/MinAppPage.tsx @@ -6,10 +6,10 @@ import { useMinapps } from '@renderer/hooks/useMinapps' import { useNavbarPosition } from '@renderer/hooks/useNavbar' import TabsService from '@renderer/services/TabsService' import { getWebviewLoaded, onWebviewStateChange, setWebviewLoaded } from '@renderer/utils/webviewStateManager' +import { useNavigate, useParams } from '@tanstack/react-router' import type { WebviewTag } from 'electron' import type { FC } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { useNavigate, useParams } from 'react-router-dom' import BeatLoader from 'react-spinners/BeatLoader' import styled from 'styled-components' @@ -20,7 +20,7 @@ import WebviewSearch from './components/WebviewSearch' const logger = loggerService.withContext('MinAppPage') const MinAppPage: FC = () => { - const { appId } = useParams<{ appId: string }>() + const { appId } = useParams({ strict: false }) as { appId: string } const { isTopNavbar } = useNavbarPosition() const { openMinappKeepAlive, minAppsCache } = useMinappPopup() const { minapps } = useMinapps() @@ -64,7 +64,7 @@ const MinAppPage: FC = () => { useEffect(() => { // If app not found, redirect to apps list if (!app) { - navigate('/apps') + navigate({ to: '/apps' }) return } @@ -72,7 +72,7 @@ const MinAppPage: FC = () => { // Only check once and only if we haven't already redirected if (!initialIsTopNavbar.current && !hasRedirected.current) { hasRedirected.current = true - navigate('/apps') + navigate({ to: '/apps' }) // Open popup after navigation setTimeout(() => { openMinappKeepAlive(app) diff --git a/src/renderer/src/pages/minapps/components/MinimalToolbar.tsx b/src/renderer/src/pages/minapps/components/MinimalToolbar.tsx index 0d2d555e52..f94f594a2f 100644 --- a/src/renderer/src/pages/minapps/components/MinimalToolbar.tsx +++ b/src/renderer/src/pages/minapps/components/MinimalToolbar.tsx @@ -15,11 +15,11 @@ import { isDev } from '@renderer/config/constant' import { DEFAULT_MIN_APPS } from '@renderer/config/minapps' import { useMinapps } from '@renderer/hooks/useMinapps' import type { MinAppType } from '@renderer/types' +import { useNavigate } from '@tanstack/react-router' import type { WebviewTag } from 'electron' import type { FC } from 'react' import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useNavigate } from 'react-router-dom' import styled from 'styled-components' const logger = loggerService.withContext('MinimalToolbar') @@ -213,7 +213,7 @@ const MinimalToolbar: FC = ({ app, webviewRef, currentUrl, onReload, onOp }, [app.id, webviewRef, scheduleNavigationUpdate]) const handleMinimize = useCallback(() => { - navigate('/apps') + navigate({ to: '/apps' }) }, [navigate]) const handleTogglePin = useCallback(() => { diff --git a/src/renderer/src/pages/paintings/AihubmixPage.tsx b/src/renderer/src/pages/paintings/AihubmixPage.tsx index 294bed47f2..e3b494084d 100644 --- a/src/renderer/src/pages/paintings/AihubmixPage.tsx +++ b/src/renderer/src/pages/paintings/AihubmixPage.tsx @@ -19,12 +19,12 @@ import { translateText } from '@renderer/services/TranslateService' import type { FileMetadata } from '@renderer/types' import type { PaintingAction, PaintingsState } from '@renderer/types' import { getErrorMessage, uuid } from '@renderer/utils' +import { useLocation, useNavigate } from '@tanstack/react-router' import { Input, InputNumber, Radio, Segmented, Select, Slider, Upload } from 'antd' import TextArea from 'antd/es/input/TextArea' import type { FC } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' import SendMessageButton from '../home/Inputbar/SendMessageButton' @@ -667,7 +667,7 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => { const handleProviderChange = (providerId: string) => { const routeName = location.pathname.split('/').pop() if (providerId !== routeName) { - navigate('../' + providerId, { replace: true }) + navigate({ to: '../' + providerId, replace: true }) } } diff --git a/src/renderer/src/pages/paintings/DmxapiPage.tsx b/src/renderer/src/pages/paintings/DmxapiPage.tsx index 7f83183d28..286bbfa210 100644 --- a/src/renderer/src/pages/paintings/DmxapiPage.tsx +++ b/src/renderer/src/pages/paintings/DmxapiPage.tsx @@ -11,13 +11,13 @@ import { useAllProviders } from '@renderer/hooks/useProvider' import FileManager from '@renderer/services/FileManager' import type { FileMetadata } from '@renderer/types' import { convertToBase64, uuid } from '@renderer/utils' +import { useLocation, useNavigate } from '@tanstack/react-router' import type { DmxapiPainting } from '@types' import { Input, InputNumber, Segmented, Select } from 'antd' import TextArea from 'antd/es/input/TextArea' import type { FC } from 'react' import React, { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' import { generationModeType } from '../../types' @@ -640,7 +640,7 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => { const handleProviderChange = (providerId: string) => { const routeName = location.pathname.split('/').pop() if (providerId !== routeName) { - navigate('../' + providerId, { replace: true }) + navigate({ to: '../' + providerId, replace: true }) } } diff --git a/src/renderer/src/pages/paintings/NewApiPage.tsx b/src/renderer/src/pages/paintings/NewApiPage.tsx index ae2e423535..6275cba420 100644 --- a/src/renderer/src/pages/paintings/NewApiPage.tsx +++ b/src/renderer/src/pages/paintings/NewApiPage.tsx @@ -29,13 +29,13 @@ import type { PaintingAction, PaintingsState } from '@renderer/types' import type { FileMetadata } from '@renderer/types' import { getErrorMessage, uuid } from '@renderer/utils' import { isNewApiProvider } from '@renderer/utils/provider' +import { useLocation, useNavigate } from '@tanstack/react-router' import { Empty, InputNumber, Segmented, Select, Upload } from 'antd' import TextArea from 'antd/es/input/TextArea' import type { FC } from 'react' import React from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' import SendMessageButton from '../home/Inputbar/SendMessageButton' @@ -436,7 +436,7 @@ const NewApiPage: FC<{ Options: string[] }> = ({ Options }) => { const handleProviderChange = (providerId: string) => { const routeName = location.pathname.split('/').pop() if (providerId !== routeName) { - navigate('../' + providerId, { replace: true }) + navigate({ to: '../' + providerId, replace: true }) } } @@ -463,7 +463,7 @@ const NewApiPage: FC<{ Options: string[] }> = ({ Options }) => { // 当 modelOptions 为空时,引导用户跳转到 Provider 设置页面,新增 image-generation 端点模型 const handleShowAddModelPopup = () => { - navigate(`/settings/provider?id=${newApiProvider.id}`) + navigate({ to: `/settings/provider?id=${newApiProvider.id}` }) } useEffect(() => { diff --git a/src/renderer/src/pages/paintings/OvmsPage.tsx b/src/renderer/src/pages/paintings/OvmsPage.tsx index 5e23c89984..38e207841c 100644 --- a/src/renderer/src/pages/paintings/OvmsPage.tsx +++ b/src/renderer/src/pages/paintings/OvmsPage.tsx @@ -15,13 +15,13 @@ import FileManager from '@renderer/services/FileManager' import { translateText } from '@renderer/services/TranslateService' import type { FileMetadata, OvmsPainting } from '@renderer/types' import { getErrorMessage, uuid } from '@renderer/utils' +import { useLocation, useNavigate } from '@tanstack/react-router' import { Avatar, Input, InputNumber, Select, Slider } from 'antd' import TextArea from 'antd/es/input/TextArea' import { Info } from 'lucide-react' import type { FC } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' import SendMessageButton from '../home/Inputbar/SendMessageButton' @@ -330,7 +330,7 @@ const OvmsPage: FC<{ Options: string[] }> = ({ Options }) => { const handleProviderChange = (providerId: string) => { const routeName = location.pathname.split('/').pop() if (providerId !== routeName) { - navigate('../' + providerId, { replace: true }) + navigate({ to: '../' + providerId, replace: true }) } } diff --git a/src/renderer/src/pages/paintings/PaintingsRoutePage.tsx b/src/renderer/src/pages/paintings/PaintingsRoutePage.tsx index 6629946879..7f2ce4c781 100644 --- a/src/renderer/src/pages/paintings/PaintingsRoutePage.tsx +++ b/src/renderer/src/pages/paintings/PaintingsRoutePage.tsx @@ -5,9 +5,9 @@ import { setDefaultPaintingProvider } from '@renderer/store/settings' import { updateTab } from '@renderer/store/tabs' import type { PaintingProvider, SystemProviderId } from '@renderer/types' import { isNewApiProvider } from '@renderer/utils/provider' +import { useParams } from '@tanstack/react-router' import type { FC } from 'react' import { useEffect, useMemo, useState } from 'react' -import { Route, Routes, useParams } from 'react-router-dom' import AihubmixPage from './AihubmixPage' import DmxapiPage from './DmxapiPage' @@ -22,8 +22,8 @@ const logger = loggerService.withContext('PaintingsRoutePage') const BASE_OPTIONS: SystemProviderId[] = ['zhipu', 'aihubmix', 'silicon', 'dmxapi', 'tokenflux', 'ovms'] const PaintingsRoutePage: FC = () => { - const params = useParams() - const provider = params['*'] + const params = useParams({ strict: false }) as { _splat?: string } + const provider = params._splat const dispatch = useAppDispatch() const providers = useAllProviders() const [ovmsStatus, setOvmsStatus] = useState<'not-installed' | 'not-running' | 'running'>('not-running') @@ -49,22 +49,34 @@ const PaintingsRoutePage: FC = () => { } }, [provider, dispatch, validOptions]) - return ( - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - {/* new-api family providers are mounted dynamically below */} - {newApiProviders.map((p) => ( - } /> - ))} - - ) + // 根据 provider 渲染对应的页面 + const renderPage = () => { + switch (provider) { + case 'zhipu': + return + case 'aihubmix': + return + case 'silicon': + return + case 'dmxapi': + return + case 'tokenflux': + return + case 'ovms': + return + case 'new-api': + return + default: + // 检查是否是 new-api 家族的 provider + if (provider && newApiProviders.some((p) => p.id === provider)) { + return + } + // 默认页面 + return + } + } + + return renderPage() } export default PaintingsRoutePage diff --git a/src/renderer/src/pages/paintings/SiliconPage.tsx b/src/renderer/src/pages/paintings/SiliconPage.tsx index 16ff35363a..4bd2b85b5d 100644 --- a/src/renderer/src/pages/paintings/SiliconPage.tsx +++ b/src/renderer/src/pages/paintings/SiliconPage.tsx @@ -23,12 +23,12 @@ import FileManager from '@renderer/services/FileManager' import { translateText } from '@renderer/services/TranslateService' import type { FileMetadata, Painting } from '@renderer/types' import { getErrorMessage, uuid } from '@renderer/utils' +import { useLocation, useNavigate } from '@tanstack/react-router' import { Input, InputNumber, Radio, Select, Slider } from 'antd' import TextArea from 'antd/es/input/TextArea' import type { FC } from 'react' import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' import SendMessageButton from '../home/Inputbar/SendMessageButton' @@ -333,7 +333,7 @@ const SiliconPage: FC<{ Options: string[] }> = ({ Options }) => { const handleProviderChange = (providerId: string) => { const routeName = location.pathname.split('/').pop() if (providerId !== routeName) { - navigate('../' + providerId, { replace: true }) + navigate({ to: '../' + providerId, replace: true }) } } diff --git a/src/renderer/src/pages/paintings/TokenFluxPage.tsx b/src/renderer/src/pages/paintings/TokenFluxPage.tsx index 5ecba7a9b5..9c3f3c4b5b 100644 --- a/src/renderer/src/pages/paintings/TokenFluxPage.tsx +++ b/src/renderer/src/pages/paintings/TokenFluxPage.tsx @@ -15,12 +15,12 @@ import FileManager from '@renderer/services/FileManager' import { translateText } from '@renderer/services/TranslateService' import type { TokenFluxPainting } from '@renderer/types' import { getErrorMessage, uuid } from '@renderer/utils' +import { useLocation, useNavigate } from '@tanstack/react-router' import { Select } from 'antd' import TextArea from 'antd/es/input/TextArea' import type { FC } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' import SendMessageButton from '../home/Inputbar/SendMessageButton' @@ -268,7 +268,7 @@ const TokenFluxPage: FC<{ Options: string[] }> = ({ Options }) => { const handleProviderChange = (providerId: string) => { const routeName = location.pathname.split('/').pop() if (providerId !== routeName) { - navigate('../' + providerId, { replace: true }) + navigate({ to: '../' + providerId, replace: true }) } } diff --git a/src/renderer/src/pages/paintings/ZhipuPage.tsx b/src/renderer/src/pages/paintings/ZhipuPage.tsx index 846e0e4e39..7c8e9c5e4c 100644 --- a/src/renderer/src/pages/paintings/ZhipuPage.tsx +++ b/src/renderer/src/pages/paintings/ZhipuPage.tsx @@ -12,12 +12,12 @@ import { usePaintings } from '@renderer/hooks/usePaintings' import { useAllProviders } from '@renderer/hooks/useProvider' import FileManager from '@renderer/services/FileManager' import { getErrorMessage, uuid } from '@renderer/utils' +import { useLocation, useNavigate } from '@tanstack/react-router' import { InputNumber, Radio, Select } from 'antd' import TextArea from 'antd/es/input/TextArea' import type { FC } from 'react' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' import SendMessageButton from '../home/Inputbar/SendMessageButton' @@ -260,7 +260,7 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => { const handleProviderChange = (providerId: string) => { const routeName = location.pathname.split('/').pop() if (providerId !== routeName) { - navigate('../' + providerId, { replace: true }) + navigate({ to: '../' + providerId, replace: true }) } } diff --git a/src/renderer/src/pages/paintings/utils/index.ts b/src/renderer/src/pages/paintings/utils/index.ts index 8df6108c1e..d7f7359def 100644 --- a/src/renderer/src/pages/paintings/utils/index.ts +++ b/src/renderer/src/pages/paintings/utils/index.ts @@ -15,7 +15,7 @@ export function checkProviderEnabled(provider: Provider, t: TFunction): Promise< closable: true, okText: t('common.go_to_settings'), onOk: () => { - window.navigate?.(`/settings/provider?id=${provider.id}`) + window.navigate?.({ to: `/settings/provider`, search: { id: provider.id } }) reject('Provider disabled') }, onCancel: () => reject('Provider disabled') diff --git a/src/renderer/src/pages/settings/AboutSettings.tsx b/src/renderer/src/pages/settings/AboutSettings.tsx index 571914ac4e..4a6b3cbe60 100644 --- a/src/renderer/src/pages/settings/AboutSettings.tsx +++ b/src/renderer/src/pages/settings/AboutSettings.tsx @@ -13,6 +13,7 @@ import i18n from '@renderer/i18n' // import { setUpdateState as setAppUpdateState } from '@renderer/store/runtime' import { runAsyncFunction } from '@renderer/utils' import { ThemeMode, UpgradeChannel } from '@shared/data/preference/preferenceTypes' +import { Link } from '@tanstack/react-router' import { Avatar, Progress, Radio, Row, Tag } from 'antd' import { debounce } from 'lodash' import { Bug, Building2, Github, Globe, Mail, Rss } from 'lucide-react' @@ -21,7 +22,6 @@ import type { FC } from 'react' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Markdown from 'react-markdown' -import { Link } from 'react-router-dom' import styled from 'styled-components' import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingTitle } from '.' diff --git a/src/renderer/src/pages/settings/MCPSettings/InstallNpxUv.tsx b/src/renderer/src/pages/settings/MCPSettings/InstallNpxUv.tsx index 20d6864582..6bd9acd5d7 100644 --- a/src/renderer/src/pages/settings/MCPSettings/InstallNpxUv.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/InstallNpxUv.tsx @@ -3,11 +3,11 @@ import { Center, ColFlex } from '@cherrystudio/ui' import { Button } from '@cherrystudio/ui' import { useAppDispatch, useAppSelector } from '@renderer/store' import { setIsBunInstalled, setIsUvInstalled } from '@renderer/store/mcp' +import { useNavigate } from '@tanstack/react-router' import { Alert } from 'antd' import type { FC } from 'react' import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useNavigate } from 'react-router' import styled from 'styled-components' import { SettingDescription, SettingRow, SettingSubtitle } from '..' @@ -87,7 +87,7 @@ const InstallNpxUv: FC = ({ mini = false }) => { diff --git a/src/renderer/src/pages/settings/MCPSettings/McpServersList.tsx b/src/renderer/src/pages/settings/MCPSettings/McpServersList.tsx index 436f051faa..2fe6bd3185 100644 --- a/src/renderer/src/pages/settings/MCPSettings/McpServersList.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/McpServersList.tsx @@ -9,12 +9,12 @@ import { useMCPServerTrust } from '@renderer/hooks/useMCPServerTrust' import type { MCPServer } from '@renderer/types' import { formatMcpError } from '@renderer/utils/error' import { matchKeywordsInString } from '@renderer/utils/match' +import { useNavigate } from '@tanstack/react-router' import { Button, Dropdown, Empty } from 'antd' import { Plus } from 'lucide-react' import type { FC } from 'react' import { startTransition, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useNavigate } from 'react-router' import styled from 'styled-components' import { SettingTitle } from '..' @@ -115,7 +115,7 @@ const McpServersList: FC = () => { isActive: false } addMCPServer(newServer) - navigate(`/settings/mcp/settings/${encodeURIComponent(newServer.id)}`) + navigate({ to: `/settings/mcp/settings/${encodeURIComponent(newServer.id)}` }) window.toast.success(t('settings.mcp.addSuccess')) }, [addMCPServer, navigate, t]) @@ -260,7 +260,7 @@ const McpServersList: FC = () => { isLoading={loadingServerIds.has(server.id)} onToggle={async (active) => await handleToggleActive(server, active)} onDelete={() => onDeleteMcpServer(server)} - onEdit={() => navigate(`/settings/mcp/settings/${encodeURIComponent(server.id)}`)} + onEdit={() => navigate({ to: `/settings/mcp/settings/${encodeURIComponent(server.id)}` })} onOpenUrl={(url) => window.open(url, '_blank')} /> )} diff --git a/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx b/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx index 2ff233ef7c..a9f7ad21e5 100644 --- a/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx @@ -11,13 +11,13 @@ import MCPDescription from '@renderer/pages/settings/MCPSettings/McpDescription' import type { MCPPrompt, MCPResource, MCPServer, MCPTool } from '@renderer/types' import { parseKeyValueString } from '@renderer/utils/env' import { formatMcpError } from '@renderer/utils/error' +import { useNavigate, useParams } from '@tanstack/react-router' import type { TabsProps } from 'antd' import { Badge, Form, Input, Radio, Select, Tabs } from 'antd' import TextArea from 'antd/es/input/TextArea' import { ChevronDown, SaveIcon } from 'lucide-react' import React, { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useNavigate, useParams } from 'react-router' import styled from 'styled-components' import { SettingContainer, SettingDivider, SettingGroup, SettingTitle } from '..' @@ -68,7 +68,8 @@ type TabKey = 'settings' | 'description' | 'tools' | 'prompts' | 'resources' const McpSettings: React.FC = () => { const { t } = useTranslation() - const { serverId } = useParams<{ serverId: string }>() + const params = useParams({ strict: false }) as { serverId?: string } + const serverId = params.serverId const decodedServerId = serverId ? decodeURIComponent(serverId) : '' const server = useMCPServer(decodedServerId).server as MCPServer const { deleteMCPServer, updateMCPServer } = useMCPServers() @@ -376,7 +377,7 @@ const McpSettings: React.FC = () => { await window.api.mcp.removeServer(server) deleteMCPServer(server.id) window.toast.success(t('settings.mcp.deleteSuccess')) - navigate('/settings/mcp') + navigate({ to: '/settings/mcp' }) } }) } catch (error: any) { diff --git a/src/renderer/src/pages/settings/MCPSettings/index.tsx b/src/renderer/src/pages/settings/MCPSettings/index.tsx index 4637cf2e89..4dd878112b 100644 --- a/src/renderer/src/pages/settings/MCPSettings/index.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/index.tsx @@ -8,30 +8,17 @@ import TokenFluxProviderLogo from '@renderer/assets/images/providers/tokenflux.p import DividerWithText from '@renderer/components/DividerWithText' import ListItem from '@renderer/components/ListItem' import Scrollbar from '@renderer/components/Scrollbar' -import { useTheme } from '@renderer/context/ThemeProvider' -import { useMCPServers } from '@renderer/hooks/useMCPServers' +import { Link, Outlet, useLocation, useNavigate } from '@tanstack/react-router' import { Button, Flex } from 'antd' import { FolderCog, Package, ShoppingBag } from 'lucide-react' import type { FC } from 'react' import { useTranslation } from 'react-i18next' -import { Navigate, Route, Routes, useLocation, useNavigate } from 'react-router' -import { Link } from 'react-router-dom' import styled from 'styled-components' -import { SettingContainer } from '..' -import BuiltinMCPServerList from './BuiltinMCPServerList' -import InstallNpxUv from './InstallNpxUv' -import McpMarketList from './McpMarketList' -import ProviderDetail from './McpProviderSettings' -import McpServersList from './McpServersList' -import McpSettings from './McpSettings' -import NpxSearch from './NpxSearch' import { providers } from './providers/config' const MCPSettings: FC = () => { - const { theme } = useTheme() const { t } = useTranslation() - const { mcpServers } = useMCPServers() const navigate = useNavigate() const location = useLocation() @@ -84,7 +71,7 @@ const MCPSettings: FC = () => { navigate('/settings/mcp/servers')} + onClick={() => navigate({ to: '/settings/mcp/servers' })} icon={} titleStyle={{ fontWeight: 500 }} /> @@ -92,14 +79,14 @@ const MCPSettings: FC = () => { navigate('/settings/mcp/builtin')} + onClick={() => navigate({ to: '/settings/mcp/builtin' })} icon={} titleStyle={{ fontWeight: 500 }} /> navigate('/settings/mcp/marketplaces')} + onClick={() => navigate({ to: '/settings/mcp/marketplaces' })} icon={} titleStyle={{ fontWeight: 500 }} /> @@ -109,7 +96,7 @@ const MCPSettings: FC = () => { key={provider.key} title={provider.name} active={activeView === provider.key} - onClick={() => navigate(`/settings/mcp/${provider.key}`)} + onClick={() => navigate({ to: `/settings/mcp/${provider.key}` })} icon={providerIcons[provider.key] || } titleStyle={{ fontWeight: 500 }} /> @@ -125,50 +112,7 @@ const MCPSettings: FC = () => { )} - - } /> - } /> - } /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - {providers.map((provider) => ( - } - /> - ))} - + @@ -212,12 +156,6 @@ const ProviderIcon = styled.img` background-color: var(--color-background-soft); ` -const ContentWrapper = styled.div` - padding: 20px; - overflow-y: auto; - height: 100%; -` - const BackButtonContainer = styled.div` display: flex; align-items: center; diff --git a/src/renderer/src/pages/settings/ProviderSettings/ProviderList.tsx b/src/renderer/src/pages/settings/ProviderSettings/ProviderList.tsx index 2d13804171..6bb0a9f93d 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ProviderList.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ProviderList.tsx @@ -14,13 +14,13 @@ import ImageStorage from '@renderer/services/ImageStorage' import type { Provider, ProviderType } from '@renderer/types' import { isSystemProvider } from '@renderer/types' import { getFancyProviderName, matchKeywordsInModel, matchKeywordsInProvider, uuid } from '@renderer/utils' +import { useLocation, useNavigate, useSearch } from '@tanstack/react-router' import type { MenuProps } from 'antd' import { Dropdown, Input, Tag } from 'antd' import { GripVertical, PlusIcon, Search, UserPen } from 'lucide-react' import type { FC } from 'react' import { startTransition, useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useSearchParams } from 'react-router-dom' import styled from 'styled-components' import AddProviderPopup from './AddProviderPopup' @@ -35,7 +35,9 @@ const systemType = await window.api.system.getDeviceType() const cpuName = await window.api.system.getCpuName() const ProviderList: FC = () => { - const [searchParams, setSearchParams] = useSearchParams() + const search = useSearch({ strict: false }) as Record + const navigate = useNavigate() + const location = useLocation() const providers = useAllProviders() const { updateProviders, addProvider, removeProvider, updateProvider } = useProviders() const { setTimeoutTimer } = useTimer() @@ -72,8 +74,8 @@ const ProviderList: FC = () => { }, [providers]) useEffect(() => { - if (searchParams.get('id')) { - const providerId = searchParams.get('id') + if (search.id) { + const providerId = search.id const provider = providers.find((p) => p.id === providerId) if (provider) { setSelectedProvider(provider) @@ -89,10 +91,17 @@ const ProviderList: FC = () => { } else { setSelectedProvider(providers[0]) } - searchParams.delete('id') - setSearchParams(searchParams) + // 清除 id 参数 + navigate({ + to: location.pathname, + search: (prev) => { + const { id: _, ...rest } = prev as Record + return rest + }, + replace: true + }) } - }, [providers, searchParams, setSearchParams, setSelectedProvider, setTimeoutTimer]) + }, [providers, search.id, navigate, location.pathname, setSelectedProvider, setTimeoutTimer]) // Handle provider add key from URL schema useEffect(() => { @@ -106,7 +115,7 @@ const ProviderList: FC = () => { const { id } = data const { updatedProvider, isNew, displayName } = await UrlSchemaInfoPopup.show(data) - window.navigate(`/settings/provider?id=${id}`) + navigate({ to: '/settings/provider', search: { id } }) if (!updatedProvider) { return @@ -123,7 +132,7 @@ const ProviderList: FC = () => { } // 检查 URL 参数 - const addProviderData = searchParams.get('addProviderData') + const addProviderData = search.addProviderData if (!addProviderData) { return } @@ -132,17 +141,17 @@ const ProviderList: FC = () => { const { id, apiKey: newApiKey, baseUrl, type, name } = JSON.parse(addProviderData) if (!id || !newApiKey || !baseUrl) { window.toast.error(t('settings.models.provider_key_add_failed_by_invalid_data')) - window.navigate('/settings/provider') + navigate({ to: '/settings/provider' }) return } handleProviderAddKey({ id, apiKey: newApiKey, baseUrl, type, name }) } catch (error) { window.toast.error(t('settings.models.provider_key_add_failed_by_invalid_data')) - window.navigate('/settings/provider') + navigate({ to: '/settings/provider' }) } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [searchParams]) + }, [search.addProviderData]) const onAddProvider = async () => { const { name: providerName, type, logo } = await AddProviderPopup.show() diff --git a/src/renderer/src/pages/settings/SelectionAssistantSettings/SelectionAssistantSettings.tsx b/src/renderer/src/pages/settings/SelectionAssistantSettings/SelectionAssistantSettings.tsx index 55bdb2ed0e..57aa40b97e 100644 --- a/src/renderer/src/pages/settings/SelectionAssistantSettings/SelectionAssistantSettings.tsx +++ b/src/renderer/src/pages/settings/SelectionAssistantSettings/SelectionAssistantSettings.tsx @@ -6,12 +6,12 @@ import { useTheme } from '@renderer/context/ThemeProvider' import { getSelectionDescriptionLabel } from '@renderer/i18n/label' import SelectionToolbar from '@renderer/windows/selection/toolbar/SelectionToolbar' import type { SelectionFilterMode, SelectionTriggerMode } from '@shared/data/preference/preferenceTypes' +import { Link } from '@tanstack/react-router' import { Row, Slider } from 'antd' import { CircleHelp, Edit2 } from 'lucide-react' import type { FC } from 'react' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { Link } from 'react-router-dom' import styled from 'styled-components' import { diff --git a/src/renderer/src/pages/settings/SettingsPage.tsx b/src/renderer/src/pages/settings/SettingsPage.tsx index a14e10973d..57bbcd6d2e 100644 --- a/src/renderer/src/pages/settings/SettingsPage.tsx +++ b/src/renderer/src/pages/settings/SettingsPage.tsx @@ -1,7 +1,7 @@ import { GlobalOutlined } from '@ant-design/icons' import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar' import Scrollbar from '@renderer/components/Scrollbar' -import ModelSettings from '@renderer/pages/settings/ModelSettings/ModelSettings' +import { Link, Outlet, useLocation } from '@tanstack/react-router' import { Divider as AntDivider } from 'antd' import { Brain, @@ -22,27 +22,11 @@ import { } from 'lucide-react' import type { FC } from 'react' import { useTranslation } from 'react-i18next' -import { Link, Route, Routes, useLocation } from 'react-router-dom' import styled from 'styled-components' -import AboutSettings from './AboutSettings' -import DataSettings from './DataSettings/DataSettings' -import DisplaySettings from './DisplaySettings/DisplaySettings' -import DocProcessSettings from './DocProcessSettings' -import GeneralSettings from './GeneralSettings' -import MCPSettings from './MCPSettings' -import MemorySettings from './MemorySettings' -import NotesSettings from './NotesSettings' -import { ProviderList } from './ProviderSettings' -import QuickAssistantSettings from './QuickAssistantSettings' -import QuickPhraseSettings from './QuickPhraseSettings' -import SelectionAssistantSettings from './SelectionAssistantSettings/SelectionAssistantSettings' -import ShortcutSettings from './ShortcutSettings' -import { ApiServerSettings } from './ToolSettings/ApiServerSettings' -import WebSearchSettings from './WebSearchSettings' - const SettingsPage: FC = () => { - const { pathname } = useLocation() + const location = useLocation() + const { pathname } = location const { t } = useTranslation() const isRoute = (path: string): string => (pathname.startsWith(path) ? 'active' : '') @@ -156,24 +140,7 @@ const SettingsPage: FC = () => { - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - + diff --git a/src/renderer/src/routeTree.gen.ts b/src/renderer/src/routeTree.gen.ts index f9781b3f31..b5357bda95 100644 --- a/src/renderer/src/routeTree.gen.ts +++ b/src/renderer/src/routeTree.gen.ts @@ -9,48 +9,510 @@ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' +import { Route as TranslateRouteImport } from './routes/translate' +import { Route as StoreRouteImport } from './routes/store' import { Route as SettingsRouteImport } from './routes/settings' +import { Route as NotesRouteImport } from './routes/notes' +import { Route as KnowledgeRouteImport } from './routes/knowledge' +import { Route as FilesRouteImport } from './routes/files' +import { Route as CodeRouteImport } from './routes/code' +import { Route as ChatRouteImport } from './routes/chat' import { Route as IndexRouteImport } from './routes/index' +import { Route as SettingsIndexRouteImport } from './routes/settings/index' +import { Route as PaintingsIndexRouteImport } from './routes/paintings/index' +import { Route as AppsIndexRouteImport } from './routes/apps/index' +import { Route as SettingsWebsearchRouteImport } from './routes/settings/websearch' +import { Route as SettingsShortcutRouteImport } from './routes/settings/shortcut' +import { Route as SettingsSelectionAssistantRouteImport } from './routes/settings/selectionAssistant' +import { Route as SettingsQuickphraseRouteImport } from './routes/settings/quickphrase' +import { Route as SettingsQuickAssistantRouteImport } from './routes/settings/quickAssistant' +import { Route as SettingsProviderRouteImport } from './routes/settings/provider' +import { Route as SettingsNotesRouteImport } from './routes/settings/notes' +import { Route as SettingsModelRouteImport } from './routes/settings/model' +import { Route as SettingsMemoryRouteImport } from './routes/settings/memory' +import { Route as SettingsMcpRouteImport } from './routes/settings/mcp' +import { Route as SettingsGeneralRouteImport } from './routes/settings/general' +import { Route as SettingsDocprocessRouteImport } from './routes/settings/docprocess' +import { Route as SettingsDisplayRouteImport } from './routes/settings/display' +import { Route as SettingsDataRouteImport } from './routes/settings/data' +import { Route as SettingsApiServerRouteImport } from './routes/settings/api-server' +import { Route as SettingsAboutRouteImport } from './routes/settings/about' +import { Route as PaintingsSplatRouteImport } from './routes/paintings/$' +import { Route as AppsAppIdRouteImport } from './routes/apps/$appId' +import { Route as SettingsMcpIndexRouteImport } from './routes/settings/mcp/index' +import { Route as SettingsMcpServersRouteImport } from './routes/settings/mcp/servers' +import { Route as SettingsMcpNpxSearchRouteImport } from './routes/settings/mcp/npx-search' +import { Route as SettingsMcpMcpInstallRouteImport } from './routes/settings/mcp/mcp-install' +import { Route as SettingsMcpMarketplacesRouteImport } from './routes/settings/mcp/marketplaces' +import { Route as SettingsMcpBuiltinRouteImport } from './routes/settings/mcp/builtin' +import { Route as SettingsMcpSplatRouteImport } from './routes/settings/mcp/$' +import { Route as SettingsMcpSettingsServerIdRouteImport } from './routes/settings/mcp/settings.$serverId' +const TranslateRoute = TranslateRouteImport.update({ + id: '/translate', + path: '/translate', + getParentRoute: () => rootRouteImport, +} as any) +const StoreRoute = StoreRouteImport.update({ + id: '/store', + path: '/store', + getParentRoute: () => rootRouteImport, +} as any) const SettingsRoute = SettingsRouteImport.update({ id: '/settings', path: '/settings', getParentRoute: () => rootRouteImport, } as any) +const NotesRoute = NotesRouteImport.update({ + id: '/notes', + path: '/notes', + getParentRoute: () => rootRouteImport, +} as any) +const KnowledgeRoute = KnowledgeRouteImport.update({ + id: '/knowledge', + path: '/knowledge', + getParentRoute: () => rootRouteImport, +} as any) +const FilesRoute = FilesRouteImport.update({ + id: '/files', + path: '/files', + getParentRoute: () => rootRouteImport, +} as any) +const CodeRoute = CodeRouteImport.update({ + id: '/code', + path: '/code', + getParentRoute: () => rootRouteImport, +} as any) +const ChatRoute = ChatRouteImport.update({ + id: '/chat', + path: '/chat', + getParentRoute: () => rootRouteImport, +} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const SettingsIndexRoute = SettingsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => SettingsRoute, +} as any) +const PaintingsIndexRoute = PaintingsIndexRouteImport.update({ + id: '/paintings/', + path: '/paintings/', + getParentRoute: () => rootRouteImport, +} as any) +const AppsIndexRoute = AppsIndexRouteImport.update({ + id: '/apps/', + path: '/apps/', + getParentRoute: () => rootRouteImport, +} as any) +const SettingsWebsearchRoute = SettingsWebsearchRouteImport.update({ + id: '/websearch', + path: '/websearch', + getParentRoute: () => SettingsRoute, +} as any) +const SettingsShortcutRoute = SettingsShortcutRouteImport.update({ + id: '/shortcut', + path: '/shortcut', + getParentRoute: () => SettingsRoute, +} as any) +const SettingsSelectionAssistantRoute = + SettingsSelectionAssistantRouteImport.update({ + id: '/selectionAssistant', + path: '/selectionAssistant', + getParentRoute: () => SettingsRoute, + } as any) +const SettingsQuickphraseRoute = SettingsQuickphraseRouteImport.update({ + id: '/quickphrase', + path: '/quickphrase', + getParentRoute: () => SettingsRoute, +} as any) +const SettingsQuickAssistantRoute = SettingsQuickAssistantRouteImport.update({ + id: '/quickAssistant', + path: '/quickAssistant', + getParentRoute: () => SettingsRoute, +} as any) +const SettingsProviderRoute = SettingsProviderRouteImport.update({ + id: '/provider', + path: '/provider', + getParentRoute: () => SettingsRoute, +} as any) +const SettingsNotesRoute = SettingsNotesRouteImport.update({ + id: '/notes', + path: '/notes', + getParentRoute: () => SettingsRoute, +} as any) +const SettingsModelRoute = SettingsModelRouteImport.update({ + id: '/model', + path: '/model', + getParentRoute: () => SettingsRoute, +} as any) +const SettingsMemoryRoute = SettingsMemoryRouteImport.update({ + id: '/memory', + path: '/memory', + getParentRoute: () => SettingsRoute, +} as any) +const SettingsMcpRoute = SettingsMcpRouteImport.update({ + id: '/mcp', + path: '/mcp', + getParentRoute: () => SettingsRoute, +} as any) +const SettingsGeneralRoute = SettingsGeneralRouteImport.update({ + id: '/general', + path: '/general', + getParentRoute: () => SettingsRoute, +} as any) +const SettingsDocprocessRoute = SettingsDocprocessRouteImport.update({ + id: '/docprocess', + path: '/docprocess', + getParentRoute: () => SettingsRoute, +} as any) +const SettingsDisplayRoute = SettingsDisplayRouteImport.update({ + id: '/display', + path: '/display', + getParentRoute: () => SettingsRoute, +} as any) +const SettingsDataRoute = SettingsDataRouteImport.update({ + id: '/data', + path: '/data', + getParentRoute: () => SettingsRoute, +} as any) +const SettingsApiServerRoute = SettingsApiServerRouteImport.update({ + id: '/api-server', + path: '/api-server', + getParentRoute: () => SettingsRoute, +} as any) +const SettingsAboutRoute = SettingsAboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => SettingsRoute, +} as any) +const PaintingsSplatRoute = PaintingsSplatRouteImport.update({ + id: '/paintings/$', + path: '/paintings/$', + getParentRoute: () => rootRouteImport, +} as any) +const AppsAppIdRoute = AppsAppIdRouteImport.update({ + id: '/apps/$appId', + path: '/apps/$appId', + getParentRoute: () => rootRouteImport, +} as any) +const SettingsMcpIndexRoute = SettingsMcpIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => SettingsMcpRoute, +} as any) +const SettingsMcpServersRoute = SettingsMcpServersRouteImport.update({ + id: '/servers', + path: '/servers', + getParentRoute: () => SettingsMcpRoute, +} as any) +const SettingsMcpNpxSearchRoute = SettingsMcpNpxSearchRouteImport.update({ + id: '/npx-search', + path: '/npx-search', + getParentRoute: () => SettingsMcpRoute, +} as any) +const SettingsMcpMcpInstallRoute = SettingsMcpMcpInstallRouteImport.update({ + id: '/mcp-install', + path: '/mcp-install', + getParentRoute: () => SettingsMcpRoute, +} as any) +const SettingsMcpMarketplacesRoute = SettingsMcpMarketplacesRouteImport.update({ + id: '/marketplaces', + path: '/marketplaces', + getParentRoute: () => SettingsMcpRoute, +} as any) +const SettingsMcpBuiltinRoute = SettingsMcpBuiltinRouteImport.update({ + id: '/builtin', + path: '/builtin', + getParentRoute: () => SettingsMcpRoute, +} as any) +const SettingsMcpSplatRoute = SettingsMcpSplatRouteImport.update({ + id: '/$', + path: '/$', + getParentRoute: () => SettingsMcpRoute, +} as any) +const SettingsMcpSettingsServerIdRoute = + SettingsMcpSettingsServerIdRouteImport.update({ + id: '/settings/$serverId', + path: '/settings/$serverId', + getParentRoute: () => SettingsMcpRoute, + } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute - '/settings': typeof SettingsRoute + '/chat': typeof ChatRoute + '/code': typeof CodeRoute + '/files': typeof FilesRoute + '/knowledge': typeof KnowledgeRoute + '/notes': typeof NotesRoute + '/settings': typeof SettingsRouteWithChildren + '/store': typeof StoreRoute + '/translate': typeof TranslateRoute + '/apps/$appId': typeof AppsAppIdRoute + '/paintings/$': typeof PaintingsSplatRoute + '/settings/about': typeof SettingsAboutRoute + '/settings/api-server': typeof SettingsApiServerRoute + '/settings/data': typeof SettingsDataRoute + '/settings/display': typeof SettingsDisplayRoute + '/settings/docprocess': typeof SettingsDocprocessRoute + '/settings/general': typeof SettingsGeneralRoute + '/settings/mcp': typeof SettingsMcpRouteWithChildren + '/settings/memory': typeof SettingsMemoryRoute + '/settings/model': typeof SettingsModelRoute + '/settings/notes': typeof SettingsNotesRoute + '/settings/provider': typeof SettingsProviderRoute + '/settings/quickAssistant': typeof SettingsQuickAssistantRoute + '/settings/quickphrase': typeof SettingsQuickphraseRoute + '/settings/selectionAssistant': typeof SettingsSelectionAssistantRoute + '/settings/shortcut': typeof SettingsShortcutRoute + '/settings/websearch': typeof SettingsWebsearchRoute + '/apps': typeof AppsIndexRoute + '/paintings': typeof PaintingsIndexRoute + '/settings/': typeof SettingsIndexRoute + '/settings/mcp/$': typeof SettingsMcpSplatRoute + '/settings/mcp/builtin': typeof SettingsMcpBuiltinRoute + '/settings/mcp/marketplaces': typeof SettingsMcpMarketplacesRoute + '/settings/mcp/mcp-install': typeof SettingsMcpMcpInstallRoute + '/settings/mcp/npx-search': typeof SettingsMcpNpxSearchRoute + '/settings/mcp/servers': typeof SettingsMcpServersRoute + '/settings/mcp/': typeof SettingsMcpIndexRoute + '/settings/mcp/settings/$serverId': typeof SettingsMcpSettingsServerIdRoute } export interface FileRoutesByTo { '/': typeof IndexRoute - '/settings': typeof SettingsRoute + '/chat': typeof ChatRoute + '/code': typeof CodeRoute + '/files': typeof FilesRoute + '/knowledge': typeof KnowledgeRoute + '/notes': typeof NotesRoute + '/store': typeof StoreRoute + '/translate': typeof TranslateRoute + '/apps/$appId': typeof AppsAppIdRoute + '/paintings/$': typeof PaintingsSplatRoute + '/settings/about': typeof SettingsAboutRoute + '/settings/api-server': typeof SettingsApiServerRoute + '/settings/data': typeof SettingsDataRoute + '/settings/display': typeof SettingsDisplayRoute + '/settings/docprocess': typeof SettingsDocprocessRoute + '/settings/general': typeof SettingsGeneralRoute + '/settings/memory': typeof SettingsMemoryRoute + '/settings/model': typeof SettingsModelRoute + '/settings/notes': typeof SettingsNotesRoute + '/settings/provider': typeof SettingsProviderRoute + '/settings/quickAssistant': typeof SettingsQuickAssistantRoute + '/settings/quickphrase': typeof SettingsQuickphraseRoute + '/settings/selectionAssistant': typeof SettingsSelectionAssistantRoute + '/settings/shortcut': typeof SettingsShortcutRoute + '/settings/websearch': typeof SettingsWebsearchRoute + '/apps': typeof AppsIndexRoute + '/paintings': typeof PaintingsIndexRoute + '/settings': typeof SettingsIndexRoute + '/settings/mcp/$': typeof SettingsMcpSplatRoute + '/settings/mcp/builtin': typeof SettingsMcpBuiltinRoute + '/settings/mcp/marketplaces': typeof SettingsMcpMarketplacesRoute + '/settings/mcp/mcp-install': typeof SettingsMcpMcpInstallRoute + '/settings/mcp/npx-search': typeof SettingsMcpNpxSearchRoute + '/settings/mcp/servers': typeof SettingsMcpServersRoute + '/settings/mcp': typeof SettingsMcpIndexRoute + '/settings/mcp/settings/$serverId': typeof SettingsMcpSettingsServerIdRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute - '/settings': typeof SettingsRoute + '/chat': typeof ChatRoute + '/code': typeof CodeRoute + '/files': typeof FilesRoute + '/knowledge': typeof KnowledgeRoute + '/notes': typeof NotesRoute + '/settings': typeof SettingsRouteWithChildren + '/store': typeof StoreRoute + '/translate': typeof TranslateRoute + '/apps/$appId': typeof AppsAppIdRoute + '/paintings/$': typeof PaintingsSplatRoute + '/settings/about': typeof SettingsAboutRoute + '/settings/api-server': typeof SettingsApiServerRoute + '/settings/data': typeof SettingsDataRoute + '/settings/display': typeof SettingsDisplayRoute + '/settings/docprocess': typeof SettingsDocprocessRoute + '/settings/general': typeof SettingsGeneralRoute + '/settings/mcp': typeof SettingsMcpRouteWithChildren + '/settings/memory': typeof SettingsMemoryRoute + '/settings/model': typeof SettingsModelRoute + '/settings/notes': typeof SettingsNotesRoute + '/settings/provider': typeof SettingsProviderRoute + '/settings/quickAssistant': typeof SettingsQuickAssistantRoute + '/settings/quickphrase': typeof SettingsQuickphraseRoute + '/settings/selectionAssistant': typeof SettingsSelectionAssistantRoute + '/settings/shortcut': typeof SettingsShortcutRoute + '/settings/websearch': typeof SettingsWebsearchRoute + '/apps/': typeof AppsIndexRoute + '/paintings/': typeof PaintingsIndexRoute + '/settings/': typeof SettingsIndexRoute + '/settings/mcp/$': typeof SettingsMcpSplatRoute + '/settings/mcp/builtin': typeof SettingsMcpBuiltinRoute + '/settings/mcp/marketplaces': typeof SettingsMcpMarketplacesRoute + '/settings/mcp/mcp-install': typeof SettingsMcpMcpInstallRoute + '/settings/mcp/npx-search': typeof SettingsMcpNpxSearchRoute + '/settings/mcp/servers': typeof SettingsMcpServersRoute + '/settings/mcp/': typeof SettingsMcpIndexRoute + '/settings/mcp/settings/$serverId': typeof SettingsMcpSettingsServerIdRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/settings' + fullPaths: + | '/' + | '/chat' + | '/code' + | '/files' + | '/knowledge' + | '/notes' + | '/settings' + | '/store' + | '/translate' + | '/apps/$appId' + | '/paintings/$' + | '/settings/about' + | '/settings/api-server' + | '/settings/data' + | '/settings/display' + | '/settings/docprocess' + | '/settings/general' + | '/settings/mcp' + | '/settings/memory' + | '/settings/model' + | '/settings/notes' + | '/settings/provider' + | '/settings/quickAssistant' + | '/settings/quickphrase' + | '/settings/selectionAssistant' + | '/settings/shortcut' + | '/settings/websearch' + | '/apps' + | '/paintings' + | '/settings/' + | '/settings/mcp/$' + | '/settings/mcp/builtin' + | '/settings/mcp/marketplaces' + | '/settings/mcp/mcp-install' + | '/settings/mcp/npx-search' + | '/settings/mcp/servers' + | '/settings/mcp/' + | '/settings/mcp/settings/$serverId' fileRoutesByTo: FileRoutesByTo - to: '/' | '/settings' - id: '__root__' | '/' | '/settings' + to: + | '/' + | '/chat' + | '/code' + | '/files' + | '/knowledge' + | '/notes' + | '/store' + | '/translate' + | '/apps/$appId' + | '/paintings/$' + | '/settings/about' + | '/settings/api-server' + | '/settings/data' + | '/settings/display' + | '/settings/docprocess' + | '/settings/general' + | '/settings/memory' + | '/settings/model' + | '/settings/notes' + | '/settings/provider' + | '/settings/quickAssistant' + | '/settings/quickphrase' + | '/settings/selectionAssistant' + | '/settings/shortcut' + | '/settings/websearch' + | '/apps' + | '/paintings' + | '/settings' + | '/settings/mcp/$' + | '/settings/mcp/builtin' + | '/settings/mcp/marketplaces' + | '/settings/mcp/mcp-install' + | '/settings/mcp/npx-search' + | '/settings/mcp/servers' + | '/settings/mcp' + | '/settings/mcp/settings/$serverId' + id: + | '__root__' + | '/' + | '/chat' + | '/code' + | '/files' + | '/knowledge' + | '/notes' + | '/settings' + | '/store' + | '/translate' + | '/apps/$appId' + | '/paintings/$' + | '/settings/about' + | '/settings/api-server' + | '/settings/data' + | '/settings/display' + | '/settings/docprocess' + | '/settings/general' + | '/settings/mcp' + | '/settings/memory' + | '/settings/model' + | '/settings/notes' + | '/settings/provider' + | '/settings/quickAssistant' + | '/settings/quickphrase' + | '/settings/selectionAssistant' + | '/settings/shortcut' + | '/settings/websearch' + | '/apps/' + | '/paintings/' + | '/settings/' + | '/settings/mcp/$' + | '/settings/mcp/builtin' + | '/settings/mcp/marketplaces' + | '/settings/mcp/mcp-install' + | '/settings/mcp/npx-search' + | '/settings/mcp/servers' + | '/settings/mcp/' + | '/settings/mcp/settings/$serverId' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute - SettingsRoute: typeof SettingsRoute + ChatRoute: typeof ChatRoute + CodeRoute: typeof CodeRoute + FilesRoute: typeof FilesRoute + KnowledgeRoute: typeof KnowledgeRoute + NotesRoute: typeof NotesRoute + SettingsRoute: typeof SettingsRouteWithChildren + StoreRoute: typeof StoreRoute + TranslateRoute: typeof TranslateRoute + AppsAppIdRoute: typeof AppsAppIdRoute + PaintingsSplatRoute: typeof PaintingsSplatRoute + AppsIndexRoute: typeof AppsIndexRoute + PaintingsIndexRoute: typeof PaintingsIndexRoute } declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/translate': { + id: '/translate' + path: '/translate' + fullPath: '/translate' + preLoaderRoute: typeof TranslateRouteImport + parentRoute: typeof rootRouteImport + } + '/store': { + id: '/store' + path: '/store' + fullPath: '/store' + preLoaderRoute: typeof StoreRouteImport + parentRoute: typeof rootRouteImport + } '/settings': { id: '/settings' path: '/settings' @@ -58,6 +520,41 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof SettingsRouteImport parentRoute: typeof rootRouteImport } + '/notes': { + id: '/notes' + path: '/notes' + fullPath: '/notes' + preLoaderRoute: typeof NotesRouteImport + parentRoute: typeof rootRouteImport + } + '/knowledge': { + id: '/knowledge' + path: '/knowledge' + fullPath: '/knowledge' + preLoaderRoute: typeof KnowledgeRouteImport + parentRoute: typeof rootRouteImport + } + '/files': { + id: '/files' + path: '/files' + fullPath: '/files' + preLoaderRoute: typeof FilesRouteImport + parentRoute: typeof rootRouteImport + } + '/code': { + id: '/code' + path: '/code' + fullPath: '/code' + preLoaderRoute: typeof CodeRouteImport + parentRoute: typeof rootRouteImport + } + '/chat': { + id: '/chat' + path: '/chat' + fullPath: '/chat' + preLoaderRoute: typeof ChatRouteImport + parentRoute: typeof rootRouteImport + } '/': { id: '/' path: '/' @@ -65,12 +562,296 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/settings/': { + id: '/settings/' + path: '/' + fullPath: '/settings/' + preLoaderRoute: typeof SettingsIndexRouteImport + parentRoute: typeof SettingsRoute + } + '/paintings/': { + id: '/paintings/' + path: '/paintings' + fullPath: '/paintings' + preLoaderRoute: typeof PaintingsIndexRouteImport + parentRoute: typeof rootRouteImport + } + '/apps/': { + id: '/apps/' + path: '/apps' + fullPath: '/apps' + preLoaderRoute: typeof AppsIndexRouteImport + parentRoute: typeof rootRouteImport + } + '/settings/websearch': { + id: '/settings/websearch' + path: '/websearch' + fullPath: '/settings/websearch' + preLoaderRoute: typeof SettingsWebsearchRouteImport + parentRoute: typeof SettingsRoute + } + '/settings/shortcut': { + id: '/settings/shortcut' + path: '/shortcut' + fullPath: '/settings/shortcut' + preLoaderRoute: typeof SettingsShortcutRouteImport + parentRoute: typeof SettingsRoute + } + '/settings/selectionAssistant': { + id: '/settings/selectionAssistant' + path: '/selectionAssistant' + fullPath: '/settings/selectionAssistant' + preLoaderRoute: typeof SettingsSelectionAssistantRouteImport + parentRoute: typeof SettingsRoute + } + '/settings/quickphrase': { + id: '/settings/quickphrase' + path: '/quickphrase' + fullPath: '/settings/quickphrase' + preLoaderRoute: typeof SettingsQuickphraseRouteImport + parentRoute: typeof SettingsRoute + } + '/settings/quickAssistant': { + id: '/settings/quickAssistant' + path: '/quickAssistant' + fullPath: '/settings/quickAssistant' + preLoaderRoute: typeof SettingsQuickAssistantRouteImport + parentRoute: typeof SettingsRoute + } + '/settings/provider': { + id: '/settings/provider' + path: '/provider' + fullPath: '/settings/provider' + preLoaderRoute: typeof SettingsProviderRouteImport + parentRoute: typeof SettingsRoute + } + '/settings/notes': { + id: '/settings/notes' + path: '/notes' + fullPath: '/settings/notes' + preLoaderRoute: typeof SettingsNotesRouteImport + parentRoute: typeof SettingsRoute + } + '/settings/model': { + id: '/settings/model' + path: '/model' + fullPath: '/settings/model' + preLoaderRoute: typeof SettingsModelRouteImport + parentRoute: typeof SettingsRoute + } + '/settings/memory': { + id: '/settings/memory' + path: '/memory' + fullPath: '/settings/memory' + preLoaderRoute: typeof SettingsMemoryRouteImport + parentRoute: typeof SettingsRoute + } + '/settings/mcp': { + id: '/settings/mcp' + path: '/mcp' + fullPath: '/settings/mcp' + preLoaderRoute: typeof SettingsMcpRouteImport + parentRoute: typeof SettingsRoute + } + '/settings/general': { + id: '/settings/general' + path: '/general' + fullPath: '/settings/general' + preLoaderRoute: typeof SettingsGeneralRouteImport + parentRoute: typeof SettingsRoute + } + '/settings/docprocess': { + id: '/settings/docprocess' + path: '/docprocess' + fullPath: '/settings/docprocess' + preLoaderRoute: typeof SettingsDocprocessRouteImport + parentRoute: typeof SettingsRoute + } + '/settings/display': { + id: '/settings/display' + path: '/display' + fullPath: '/settings/display' + preLoaderRoute: typeof SettingsDisplayRouteImport + parentRoute: typeof SettingsRoute + } + '/settings/data': { + id: '/settings/data' + path: '/data' + fullPath: '/settings/data' + preLoaderRoute: typeof SettingsDataRouteImport + parentRoute: typeof SettingsRoute + } + '/settings/api-server': { + id: '/settings/api-server' + path: '/api-server' + fullPath: '/settings/api-server' + preLoaderRoute: typeof SettingsApiServerRouteImport + parentRoute: typeof SettingsRoute + } + '/settings/about': { + id: '/settings/about' + path: '/about' + fullPath: '/settings/about' + preLoaderRoute: typeof SettingsAboutRouteImport + parentRoute: typeof SettingsRoute + } + '/paintings/$': { + id: '/paintings/$' + path: '/paintings/$' + fullPath: '/paintings/$' + preLoaderRoute: typeof PaintingsSplatRouteImport + parentRoute: typeof rootRouteImport + } + '/apps/$appId': { + id: '/apps/$appId' + path: '/apps/$appId' + fullPath: '/apps/$appId' + preLoaderRoute: typeof AppsAppIdRouteImport + parentRoute: typeof rootRouteImport + } + '/settings/mcp/': { + id: '/settings/mcp/' + path: '/' + fullPath: '/settings/mcp/' + preLoaderRoute: typeof SettingsMcpIndexRouteImport + parentRoute: typeof SettingsMcpRoute + } + '/settings/mcp/servers': { + id: '/settings/mcp/servers' + path: '/servers' + fullPath: '/settings/mcp/servers' + preLoaderRoute: typeof SettingsMcpServersRouteImport + parentRoute: typeof SettingsMcpRoute + } + '/settings/mcp/npx-search': { + id: '/settings/mcp/npx-search' + path: '/npx-search' + fullPath: '/settings/mcp/npx-search' + preLoaderRoute: typeof SettingsMcpNpxSearchRouteImport + parentRoute: typeof SettingsMcpRoute + } + '/settings/mcp/mcp-install': { + id: '/settings/mcp/mcp-install' + path: '/mcp-install' + fullPath: '/settings/mcp/mcp-install' + preLoaderRoute: typeof SettingsMcpMcpInstallRouteImport + parentRoute: typeof SettingsMcpRoute + } + '/settings/mcp/marketplaces': { + id: '/settings/mcp/marketplaces' + path: '/marketplaces' + fullPath: '/settings/mcp/marketplaces' + preLoaderRoute: typeof SettingsMcpMarketplacesRouteImport + parentRoute: typeof SettingsMcpRoute + } + '/settings/mcp/builtin': { + id: '/settings/mcp/builtin' + path: '/builtin' + fullPath: '/settings/mcp/builtin' + preLoaderRoute: typeof SettingsMcpBuiltinRouteImport + parentRoute: typeof SettingsMcpRoute + } + '/settings/mcp/$': { + id: '/settings/mcp/$' + path: '/$' + fullPath: '/settings/mcp/$' + preLoaderRoute: typeof SettingsMcpSplatRouteImport + parentRoute: typeof SettingsMcpRoute + } + '/settings/mcp/settings/$serverId': { + id: '/settings/mcp/settings/$serverId' + path: '/settings/$serverId' + fullPath: '/settings/mcp/settings/$serverId' + preLoaderRoute: typeof SettingsMcpSettingsServerIdRouteImport + parentRoute: typeof SettingsMcpRoute + } } } +interface SettingsMcpRouteChildren { + SettingsMcpSplatRoute: typeof SettingsMcpSplatRoute + SettingsMcpBuiltinRoute: typeof SettingsMcpBuiltinRoute + SettingsMcpMarketplacesRoute: typeof SettingsMcpMarketplacesRoute + SettingsMcpMcpInstallRoute: typeof SettingsMcpMcpInstallRoute + SettingsMcpNpxSearchRoute: typeof SettingsMcpNpxSearchRoute + SettingsMcpServersRoute: typeof SettingsMcpServersRoute + SettingsMcpIndexRoute: typeof SettingsMcpIndexRoute + SettingsMcpSettingsServerIdRoute: typeof SettingsMcpSettingsServerIdRoute +} + +const SettingsMcpRouteChildren: SettingsMcpRouteChildren = { + SettingsMcpSplatRoute: SettingsMcpSplatRoute, + SettingsMcpBuiltinRoute: SettingsMcpBuiltinRoute, + SettingsMcpMarketplacesRoute: SettingsMcpMarketplacesRoute, + SettingsMcpMcpInstallRoute: SettingsMcpMcpInstallRoute, + SettingsMcpNpxSearchRoute: SettingsMcpNpxSearchRoute, + SettingsMcpServersRoute: SettingsMcpServersRoute, + SettingsMcpIndexRoute: SettingsMcpIndexRoute, + SettingsMcpSettingsServerIdRoute: SettingsMcpSettingsServerIdRoute, +} + +const SettingsMcpRouteWithChildren = SettingsMcpRoute._addFileChildren( + SettingsMcpRouteChildren, +) + +interface SettingsRouteChildren { + SettingsAboutRoute: typeof SettingsAboutRoute + SettingsApiServerRoute: typeof SettingsApiServerRoute + SettingsDataRoute: typeof SettingsDataRoute + SettingsDisplayRoute: typeof SettingsDisplayRoute + SettingsDocprocessRoute: typeof SettingsDocprocessRoute + SettingsGeneralRoute: typeof SettingsGeneralRoute + SettingsMcpRoute: typeof SettingsMcpRouteWithChildren + SettingsMemoryRoute: typeof SettingsMemoryRoute + SettingsModelRoute: typeof SettingsModelRoute + SettingsNotesRoute: typeof SettingsNotesRoute + SettingsProviderRoute: typeof SettingsProviderRoute + SettingsQuickAssistantRoute: typeof SettingsQuickAssistantRoute + SettingsQuickphraseRoute: typeof SettingsQuickphraseRoute + SettingsSelectionAssistantRoute: typeof SettingsSelectionAssistantRoute + SettingsShortcutRoute: typeof SettingsShortcutRoute + SettingsWebsearchRoute: typeof SettingsWebsearchRoute + SettingsIndexRoute: typeof SettingsIndexRoute +} + +const SettingsRouteChildren: SettingsRouteChildren = { + SettingsAboutRoute: SettingsAboutRoute, + SettingsApiServerRoute: SettingsApiServerRoute, + SettingsDataRoute: SettingsDataRoute, + SettingsDisplayRoute: SettingsDisplayRoute, + SettingsDocprocessRoute: SettingsDocprocessRoute, + SettingsGeneralRoute: SettingsGeneralRoute, + SettingsMcpRoute: SettingsMcpRouteWithChildren, + SettingsMemoryRoute: SettingsMemoryRoute, + SettingsModelRoute: SettingsModelRoute, + SettingsNotesRoute: SettingsNotesRoute, + SettingsProviderRoute: SettingsProviderRoute, + SettingsQuickAssistantRoute: SettingsQuickAssistantRoute, + SettingsQuickphraseRoute: SettingsQuickphraseRoute, + SettingsSelectionAssistantRoute: SettingsSelectionAssistantRoute, + SettingsShortcutRoute: SettingsShortcutRoute, + SettingsWebsearchRoute: SettingsWebsearchRoute, + SettingsIndexRoute: SettingsIndexRoute, +} + +const SettingsRouteWithChildren = SettingsRoute._addFileChildren( + SettingsRouteChildren, +) + const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, - SettingsRoute: SettingsRoute, + ChatRoute: ChatRoute, + CodeRoute: CodeRoute, + FilesRoute: FilesRoute, + KnowledgeRoute: KnowledgeRoute, + NotesRoute: NotesRoute, + SettingsRoute: SettingsRouteWithChildren, + StoreRoute: StoreRoute, + TranslateRoute: TranslateRoute, + AppsAppIdRoute: AppsAppIdRoute, + PaintingsSplatRoute: PaintingsSplatRoute, + AppsIndexRoute: AppsIndexRoute, + PaintingsIndexRoute: PaintingsIndexRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) diff --git a/src/renderer/src/services/MessagesService.ts b/src/renderer/src/services/MessagesService.ts index 58fa6fbea1..2773a56cec 100644 --- a/src/renderer/src/services/MessagesService.ts +++ b/src/renderer/src/services/MessagesService.ts @@ -23,9 +23,9 @@ import { } from '@renderer/utils/messageUtils/create' import { filterContextMessages } from '@renderer/utils/messageUtils/filters' import { getMainTextContent } from '@renderer/utils/messageUtils/find' +import type { UseNavigateResult } from '@tanstack/react-router' import dayjs from 'dayjs' import { t } from 'i18next' -import type { NavigateFunction } from 'react-router' import { getAssistantById, getAssistantProvider, getDefaultModel } from './AssistantService' import { EVENT_NAMES, EventEmitter } from './EventService' @@ -68,14 +68,14 @@ export function deleteMessageFiles(message: Message) { }) } -export async function locateToMessage(navigate: NavigateFunction, message: Message) { +export async function locateToMessage(navigate: UseNavigateResult, message: Message) { await modelGenerating() SearchPopup.hide() const assistant = getAssistantById(message.assistantId) const topic = await getTopicById(message.topicId) - navigate('/', { state: { assistant, topic } }) + navigate({ to: '/chat', search: { assistantId: assistant?.id, topicId: topic?.id } }) setTimeout(() => EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR), 0) setTimeout(() => EventEmitter.emit(EVENT_NAMES.LOCATE_MESSAGE + ':' + message.id), 300) diff --git a/src/renderer/src/services/NavigationService.ts b/src/renderer/src/services/NavigationService.ts index 3c4ae54abe..ac4747b160 100644 --- a/src/renderer/src/services/NavigationService.ts +++ b/src/renderer/src/services/NavigationService.ts @@ -1,14 +1,15 @@ -import type { NavigateFunction } from 'react-router-dom' +import type { UseNavigateResult } from '@tanstack/react-router' +// Tab 导航服务 - 用于在非 React 组件中进行路由导航 interface INavigationService { - navigate: NavigateFunction | null - setNavigate: (navigateFunc: NavigateFunction) => void + navigate: UseNavigateResult | null + setNavigate: (navigateFunc: UseNavigateResult) => void } const NavigationService: INavigationService = { navigate: null, - setNavigate: (navigateFunc: NavigateFunction): void => { + setNavigate: (navigateFunc: UseNavigateResult): void => { NavigationService.navigate = navigateFunc window.navigate = NavigationService.navigate } diff --git a/src/renderer/src/services/TabsService.ts b/src/renderer/src/services/TabsService.ts index a0c22c488b..e94faea192 100644 --- a/src/renderer/src/services/TabsService.ts +++ b/src/renderer/src/services/TabsService.ts @@ -55,12 +55,12 @@ class TabsService { // 使用 NavigationService 导航到新的标签页 if (NavigationService.navigate) { - NavigationService.navigate(lastTab.path) + NavigationService.navigate({ to: lastTab.path }) } else { logger.warn('Navigation service not ready, will navigate on next render') setTimeout(() => { if (NavigationService.navigate) { - NavigationService.navigate(lastTab.path) + NavigationService.navigate({ to: lastTab.path }) } }, 100) } @@ -133,7 +133,7 @@ class TabsService { // 导航到对应页面 if (NavigationService.navigate) { - NavigationService.navigate(tab.path) + NavigationService.navigate({ to: tab.path }) } return true