From b9a56fcec13e9f0f4c05a4105e90cd34d709f00c Mon Sep 17 00:00:00 2001 From: MyPrototypeWhat Date: Tue, 9 Dec 2025 17:30:04 +0800 Subject: [PATCH] feat(navigation): migrate to TanStack Router and enhance navigation structure - Replaced react-router with TanStack Router for improved routing capabilities. - Updated navigation logic across various components to utilize the new navigation API. - Refactored App.tsx to integrate AppShell as the main routing component, removing the deprecated Router. - Enhanced navigation methods to support search parameters, improving URL handling for settings and provider navigation. These changes streamline the routing architecture, enhancing the overall user experience and maintainability of the application. --- src/renderer/src/App.tsx | 7 +- src/renderer/src/Router.tsx | 67 -- .../src/components/FreeTrialModelTag.tsx | 4 +- src/renderer/src/components/MinApp/MinApp.tsx | 4 +- .../src/components/MinApp/MinAppTabsPool.tsx | 2 +- .../Popups/SelectModelPopup/popup.tsx | 2 +- .../src/components/Tab/TabContainer.tsx | 8 +- src/renderer/src/components/app/Sidebar.tsx | 36 +- .../src/components/layout/AppShell.tsx | 4 +- src/renderer/src/env.d.ts | 4 +- .../src/handler/NavigationHandler.tsx | 6 +- src/renderer/src/hooks/useAppInit.ts | 2 +- src/renderer/src/hooks/useMCPServers.ts | 4 +- src/renderer/src/hooks/useMinappPopup.ts | 2 +- src/renderer/src/pages/code/CodeToolsPage.tsx | 2 +- .../history/components/TopicMessages.tsx | 7 +- src/renderer/src/pages/home/HomePage.tsx | 26 +- .../tools/components/KnowledgeBaseButton.tsx | 4 +- .../tools/components/MCPToolsButton.tsx | 4 +- .../components/useMentionModelsPanel.tsx | 4 +- .../pages/home/Messages/Blocks/ErrorBlock.tsx | 7 +- src/renderer/src/pages/home/Navbar.tsx | 1 + .../src/pages/launchpad/LaunchpadPage.tsx | 4 +- src/renderer/src/pages/minapps/MinAppPage.tsx | 8 +- .../minapps/components/MinimalToolbar.tsx | 4 +- .../src/pages/paintings/AihubmixPage.tsx | 4 +- .../src/pages/paintings/DmxapiPage.tsx | 4 +- .../src/pages/paintings/NewApiPage.tsx | 6 +- src/renderer/src/pages/paintings/OvmsPage.tsx | 4 +- .../pages/paintings/PaintingsRoutePage.tsx | 50 +- .../src/pages/paintings/SiliconPage.tsx | 4 +- .../src/pages/paintings/TokenFluxPage.tsx | 4 +- .../src/pages/paintings/ZhipuPage.tsx | 4 +- .../src/pages/paintings/utils/index.ts | 2 +- .../src/pages/settings/AboutSettings.tsx | 2 +- .../settings/MCPSettings/InstallNpxUv.tsx | 4 +- .../settings/MCPSettings/McpServersList.tsx | 6 +- .../settings/MCPSettings/McpSettings.tsx | 7 +- .../src/pages/settings/MCPSettings/index.tsx | 74 +- .../ProviderSettings/ProviderList.tsx | 33 +- .../SelectionAssistantSettings.tsx | 2 +- .../src/pages/settings/SettingsPage.tsx | 41 +- src/renderer/src/routeTree.gen.ts | 797 +++++++++++++++++- src/renderer/src/services/MessagesService.ts | 6 +- .../src/services/NavigationService.ts | 9 +- src/renderer/src/services/TabsService.ts | 6 +- 46 files changed, 979 insertions(+), 313 deletions(-) delete mode 100644 src/renderer/src/Router.tsx 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