mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-08 06:19:05 +08:00
feat: implement core routing architecture with hybrid strategy (#11460)
This commit is contained in:
parent
d60ed5d9e4
commit
d9f413b195
@ -52,6 +52,7 @@
|
||||
"!src/main/integration/**",
|
||||
"!**/tailwind.css",
|
||||
"!**/package.json",
|
||||
"!src/renderer/src/routeTree.gen.ts",
|
||||
"!.zed/**"
|
||||
],
|
||||
"indentStyle": "space",
|
||||
@ -82,7 +83,7 @@
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"includes": ["!**/tailwind.css", "src/renderer/**/*.{tsx,ts}"],
|
||||
"includes": ["!**/tailwind.css", "!src/renderer/src/routeTree.gen.ts", "src/renderer/**/*.{tsx,ts}"],
|
||||
// only enable sorted tailwind css rule. used as formatter instead of linter
|
||||
"rules": {
|
||||
"nursery": {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { tanstackRouter } from '@tanstack/router-plugin/vite'
|
||||
import react from '@vitejs/plugin-react-swc'
|
||||
import { CodeInspectorPlugin } from 'code-inspector-plugin'
|
||||
import { defineConfig } from 'electron-vite'
|
||||
@ -80,6 +81,12 @@ export default defineConfig({
|
||||
},
|
||||
renderer: {
|
||||
plugins: [
|
||||
tanstackRouter({
|
||||
target: 'react',
|
||||
autoCodeSplitting: true,
|
||||
routesDirectory: resolve('src/renderer/src/routes'),
|
||||
generatedRouteTree: resolve('src/renderer/src/routeTree.gen.ts')
|
||||
}),
|
||||
(async () => (await import('@tailwindcss/vite')).default())(),
|
||||
react({
|
||||
tsDecorators: true
|
||||
|
||||
@ -67,6 +67,7 @@ export default defineConfig([
|
||||
'src/main/integration/cherryai/index.js',
|
||||
'src/main/integration/nutstore/sso/lib/**',
|
||||
'src/renderer/src/ui/**',
|
||||
'src/renderer/src/routeTree.gen.ts',
|
||||
'packages/**/dist'
|
||||
]
|
||||
},
|
||||
|
||||
@ -178,7 +178,9 @@
|
||||
"@swc/plugin-styled-components": "^8.0.4",
|
||||
"@tailwindcss/vite": "^4.1.13",
|
||||
"@tanstack/react-query": "^5.85.5",
|
||||
"@tanstack/react-router": "^1.139.3",
|
||||
"@tanstack/react-virtual": "^3.13.12",
|
||||
"@tanstack/router-plugin": "^1.139.3",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
@ -364,8 +366,6 @@
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-player": "^3.3.1",
|
||||
"react-redux": "^9.1.2",
|
||||
"react-router": "6",
|
||||
"react-router-dom": "6",
|
||||
"react-spinners": "^0.14.1",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"redux": "^5.0.1",
|
||||
|
||||
4
packages/shared/data/cache/cacheSchemas.ts
vendored
4
packages/shared/data/cache/cacheSchemas.ts
vendored
@ -225,11 +225,11 @@ export const DefaultSharedCache: SharedCacheSchema = {
|
||||
* This ensures type safety and prevents key conflicts
|
||||
*/
|
||||
export type RendererPersistCacheSchema = {
|
||||
'example_scope.example_key': string
|
||||
'ui.tab.state': CacheValueTypes.TabsState
|
||||
}
|
||||
|
||||
export const DefaultRendererPersistCache: RendererPersistCacheSchema = {
|
||||
'example_scope.example_key': 'example default value'
|
||||
'ui.tab.state': { tabs: [], activeTabId: '' }
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
35
packages/shared/data/cache/cacheValueTypes.ts
vendored
35
packages/shared/data/cache/cacheValueTypes.ts
vendored
@ -17,3 +17,38 @@ export type CacheActiveSearches = Record<string, WebSearchStatus>
|
||||
// The actual type checking will be done at runtime by the cache system
|
||||
export type CacheMinAppType = MinAppType
|
||||
export type CacheTopic = Topic
|
||||
|
||||
/**
|
||||
* Tab type for browser-like tabs
|
||||
*
|
||||
* - 'route': Internal app routes rendered via MemoryRouter
|
||||
* - 'webview': External web content rendered via Electron webview
|
||||
*/
|
||||
export type TabType = 'route' | 'webview'
|
||||
|
||||
/**
|
||||
* Tab saved state for hibernation recovery
|
||||
*/
|
||||
export interface TabSavedState {
|
||||
scrollPosition?: number
|
||||
// 其他必要草稿字段可在此扩展
|
||||
}
|
||||
|
||||
export interface Tab {
|
||||
id: string
|
||||
type: TabType
|
||||
url: string
|
||||
title: string
|
||||
icon?: string
|
||||
metadata?: Record<string, unknown>
|
||||
// LRU 字段
|
||||
lastAccessTime?: number // open/switch 时更新
|
||||
isDormant?: boolean // 是否已休眠
|
||||
isPinned?: boolean // 是否置顶(豁免 LRU)
|
||||
savedState?: TabSavedState // 休眠前保存的状态
|
||||
}
|
||||
|
||||
export interface TabsState {
|
||||
tabs: Tab[]
|
||||
activeTabId: string
|
||||
}
|
||||
|
||||
394
pnpm-lock.yaml
394
pnpm-lock.yaml
@ -412,9 +412,15 @@ importers:
|
||||
'@tanstack/react-query':
|
||||
specifier: ^5.85.5
|
||||
version: 5.90.16(react@19.2.3)
|
||||
'@tanstack/react-router':
|
||||
specifier: ^1.139.3
|
||||
version: 1.145.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
'@tanstack/react-virtual':
|
||||
specifier: ^3.13.12
|
||||
version: 3.13.14(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
'@tanstack/router-plugin':
|
||||
specifier: ^1.139.3
|
||||
version: 1.145.7(@tanstack/react-router@1.145.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(rolldown-vite@7.3.0(@types/node@22.17.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))
|
||||
'@testing-library/dom':
|
||||
specifier: ^10.4.0
|
||||
version: 10.4.1
|
||||
@ -970,12 +976,6 @@ importers:
|
||||
react-redux:
|
||||
specifier: ^9.1.2
|
||||
version: 9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1)
|
||||
react-router:
|
||||
specifier: '6'
|
||||
version: 6.30.2(react@19.2.3)
|
||||
react-router-dom:
|
||||
specifier: '6'
|
||||
version: 6.30.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
react-spinners:
|
||||
specifier: ^0.14.1
|
||||
version: 0.14.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
@ -1311,16 +1311,16 @@ importers:
|
||||
version: 2.8.7(@types/react@19.2.7)(framer-motion@12.23.26(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)
|
||||
'@storybook/addon-docs':
|
||||
specifier: ^10.0.5
|
||||
version: 10.1.11(@types/react@19.2.7)(esbuild@0.25.12)(rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
|
||||
version: 10.1.11(@types/react@19.2.7)(esbuild@0.25.12)(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))
|
||||
'@storybook/addon-themes':
|
||||
specifier: ^10.0.5
|
||||
version: 10.1.11(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
|
||||
version: 10.1.11(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
|
||||
'@storybook/react':
|
||||
specifier: ^10.1.11
|
||||
version: 10.1.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.8.3)
|
||||
version: 10.1.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.8.3)
|
||||
'@storybook/react-vite':
|
||||
specifier: ^10.0.5
|
||||
version: 10.1.11(esbuild@0.25.12)(msw@2.12.7(@types/node@24.10.4)(typescript@5.8.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.8.3)
|
||||
version: 10.1.11(esbuild@0.25.12)(msw@2.12.7(@types/node@24.10.4)(typescript@5.8.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.8.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))
|
||||
'@svgr/core':
|
||||
specifier: ^8.1.0
|
||||
version: 8.1.0(typescript@5.8.3)
|
||||
@ -1356,7 +1356,7 @@ importers:
|
||||
version: 5.27.0(patch_hash=cdc383bd0d9b9fe0df2ce7b1f1d4ead200012b7f9517d9257b4ea0a5b324e243)(moment@2.30.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
eslint-plugin-storybook:
|
||||
specifier: 10.0.5
|
||||
version: 10.0.5(eslint@9.39.2(jiti@2.6.1))(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.8.3)
|
||||
version: 10.0.5(eslint@9.39.2(jiti@2.6.1))(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.8.3)
|
||||
framer-motion:
|
||||
specifier: ^12.23.12
|
||||
version: 12.23.26(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
@ -1371,7 +1371,7 @@ importers:
|
||||
version: 19.2.3(react@19.2.3)
|
||||
storybook:
|
||||
specifier: ^10.0.5
|
||||
version: 10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
version: 10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
styled-components:
|
||||
specifier: ^6.1.15
|
||||
version: 6.1.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
@ -1907,6 +1907,18 @@ packages:
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@babel/plugin-syntax-jsx@7.27.1':
|
||||
resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
|
||||
'@babel/plugin-syntax-typescript@7.27.1':
|
||||
resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
|
||||
'@babel/plugin-transform-arrow-functions@7.27.1':
|
||||
resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@ -5275,10 +5287,6 @@ packages:
|
||||
'@remirror/core-constants@3.0.0':
|
||||
resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==}
|
||||
|
||||
'@remix-run/router@1.23.1':
|
||||
resolution: {integrity: sha512-vDbaOzF7yT2Qs4vO6XV1MHcJv+3dgR1sT+l3B8xxOVhUC336prMvqrvsLL/9Dnw2xr6Qhz4J0dmS0llNAbnUmQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
'@replit/codemirror-lang-nix@6.0.1':
|
||||
resolution: {integrity: sha512-lvzjoYn9nfJzBD5qdm3Ut6G3+Or2wEacYIDJ49h9+19WSChVnxv4ojf+rNmQ78ncuxIt/bfbMvDLMeMP0xze6g==}
|
||||
peerDependencies:
|
||||
@ -6298,6 +6306,10 @@ packages:
|
||||
peerDependencies:
|
||||
vite: ^5.2.0 || ^6 || ^7
|
||||
|
||||
'@tanstack/history@1.145.7':
|
||||
resolution: {integrity: sha512-gMo/ReTUp0a3IOcZoI3hH6PLDC2R/5ELQ7P2yu9F6aEkA0wSQh+Q4qzMrtcKvF2ut0oE+16xWCGDo/TdYd6cEQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@tanstack/query-core@5.90.16':
|
||||
resolution: {integrity: sha512-MvtWckSVufs/ja463/K4PyJeqT+HMlJWtw6PrCpywznd2NSgO3m4KwO9RqbFqGg6iDE8vVMFWMeQI4Io3eEYww==}
|
||||
|
||||
@ -6306,6 +6318,19 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^18 || ^19
|
||||
|
||||
'@tanstack/react-router@1.145.7':
|
||||
resolution: {integrity: sha512-0O+a4TjJSPXd2BsvDPwDPBKRQKYqNIBg5TAg9NzCteqJ0NXRxwohyqCksHqCEEtJe/uItwqmHoqkK4q5MDhEsA==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
react: '>=18.0.0 || >=19.0.0'
|
||||
react-dom: '>=18.0.0 || >=19.0.0'
|
||||
|
||||
'@tanstack/react-store@0.8.0':
|
||||
resolution: {integrity: sha512-1vG9beLIuB7q69skxK9r5xiLN3ztzIPfSQSs0GfeqWGO2tGIyInZx0x1COhpx97RKaONSoAb8C3dxacWksm1ow==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@tanstack/react-virtual@3.11.3':
|
||||
resolution: {integrity: sha512-vCU+OTylXN3hdC8RKg68tPlBPjjxtzon7Ys46MgrSLE+JhSjSTPvoQifV6DQJeJmA8Q3KT6CphJbejupx85vFw==}
|
||||
peerDependencies:
|
||||
@ -6318,12 +6343,52 @@ packages:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@tanstack/router-core@1.145.7':
|
||||
resolution: {integrity: sha512-v6jx6JqVUBM0/FcBq1tX22xiPq8Ufc0PDEP582/4deYoq2/RYd+bZstANp3mGSsqdxE/luhoLYuuSQiwi/j1wA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@tanstack/router-generator@1.145.7':
|
||||
resolution: {integrity: sha512-xg71c1WTku0ro0rgpJWh3Dt+ognV9qWe2KJHAPzrqfOYdUYu9sGq7Ri4jo8Rk0luXWZrWsrFdBP+9Jx6JH6zWA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@tanstack/router-plugin@1.145.7':
|
||||
resolution: {integrity: sha512-Rimo0NragYKHwjoYX9JBLS8VkZD4D/LqzzLIlX9yz93lmWFRu/DbuS7fDZNqX1Ea8naNvo18DlySszYLzC8XDg==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
'@rsbuild/core': '>=1.0.2'
|
||||
'@tanstack/react-router': ^1.145.7
|
||||
vite: '>=5.0.0 || >=6.0.0 || >=7.0.0'
|
||||
vite-plugin-solid: ^2.11.10
|
||||
webpack: '>=5.92.0'
|
||||
peerDependenciesMeta:
|
||||
'@rsbuild/core':
|
||||
optional: true
|
||||
'@tanstack/react-router':
|
||||
optional: true
|
||||
vite:
|
||||
optional: true
|
||||
vite-plugin-solid:
|
||||
optional: true
|
||||
webpack:
|
||||
optional: true
|
||||
|
||||
'@tanstack/router-utils@1.143.11':
|
||||
resolution: {integrity: sha512-N24G4LpfyK8dOlnP8BvNdkuxg1xQljkyl6PcrdiPSA301pOjatRT1y8wuCCJZKVVD8gkd0MpCZ0VEjRMGILOtA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@tanstack/store@0.8.0':
|
||||
resolution: {integrity: sha512-Om+BO0YfMZe//X2z0uLF2j+75nQga6TpTJgLJQBiq85aOyZNIhkCgleNcud2KQg4k4v9Y9l+Uhru3qWMPGTOzQ==}
|
||||
|
||||
'@tanstack/virtual-core@3.11.3':
|
||||
resolution: {integrity: sha512-v2mrNSnMwnPJtcVqNvV0c5roGCBqeogN8jDtgtuHCphdwBasOZ17x8UV8qpHUh+u0MLfX43c0uUHKje0s+Zb0w==}
|
||||
|
||||
'@tanstack/virtual-core@3.13.14':
|
||||
resolution: {integrity: sha512-b5Uvd8J2dc7ICeX9SRb/wkCxWk7pUwN214eEPAQsqrsktSKTCmyLxOQWSMgogBByXclZeAdgZ3k4o0fIYUIBqQ==}
|
||||
|
||||
'@tanstack/virtual-file-routes@1.145.4':
|
||||
resolution: {integrity: sha512-CI75JrfqSluhdGwLssgVeQBaCphgfkMQpi8MCY3UJX1hoGzXa8kHYJcUuIFMOLs1q7zqHy++EVVtMK03osR5wQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@testing-library/dom@10.4.1':
|
||||
resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
|
||||
engines: {node: '>=18'}
|
||||
@ -7571,6 +7636,9 @@ packages:
|
||||
react-native-b4a:
|
||||
optional: true
|
||||
|
||||
babel-dead-code-elimination@1.0.11:
|
||||
resolution: {integrity: sha512-mwq3W3e/pKSI6TG8lXMiDWvEi1VXYlSBlJlB3l+I0bAb5u1RNUl88udos85eOPNK3m5EXK9uO7d2g08pesTySQ==}
|
||||
|
||||
bail@1.0.5:
|
||||
resolution: {integrity: sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==}
|
||||
|
||||
@ -8081,6 +8149,9 @@ packages:
|
||||
convert-source-map@2.0.0:
|
||||
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||
|
||||
cookie-es@2.0.0:
|
||||
resolution: {integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==}
|
||||
|
||||
cookie-signature@1.2.2:
|
||||
resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==}
|
||||
engines: {node: '>=6.6.0'}
|
||||
@ -10039,6 +10110,10 @@ packages:
|
||||
resolution: {integrity: sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==}
|
||||
engines: {node: '>= 18.0.0'}
|
||||
|
||||
isbot@5.1.32:
|
||||
resolution: {integrity: sha512-VNfjM73zz2IBZmdShMfAUg10prm6t7HFUQmNAEOAVS4YH92ZrZcvkMcGX6cIgBJAzWDzPent/EeAtYEHNPNPBQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
isexe@2.0.0:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
|
||||
@ -11742,6 +11817,11 @@ packages:
|
||||
engines: {node: '>=10.13.0'}
|
||||
hasBin: true
|
||||
|
||||
prettier@3.7.4:
|
||||
resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
|
||||
pretty-format@27.5.1:
|
||||
resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
|
||||
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
|
||||
@ -12250,19 +12330,6 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
react-router-dom@6.30.2:
|
||||
resolution: {integrity: sha512-l2OwHn3UUnEVUqc6/1VMmR1cvZryZ3j3NzapC2eUXO1dB0sYp5mvwdjiXhpUbRb21eFow3qSxpP8Yv6oAU824Q==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
react: '>=16.8'
|
||||
react-dom: '>=16.8'
|
||||
|
||||
react-router@6.30.2:
|
||||
resolution: {integrity: sha512-H2Bm38Zu1bm8KUE5NVWRMzuIyAV8p/JrOaBJAwVmp37AXG72+CZJlEBw6pdn9i5TBgLMhNDgijS4ZlblpHyWTA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
react: '>=16.8'
|
||||
|
||||
react-spinners@0.14.1:
|
||||
resolution: {integrity: sha512-2Izq+qgQ08HTofCVEdcAQCXFEYfqTDdfeDQJeo/HHQiQJD4imOicNLhkfN2eh1NYEWVOX4D9ok2lhuDB0z3Aag==}
|
||||
peerDependencies:
|
||||
@ -12760,6 +12827,16 @@ packages:
|
||||
resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
seroval-plugins@1.4.2:
|
||||
resolution: {integrity: sha512-X7p4MEDTi+60o2sXZ4bnDBhgsUYDSkQEvzYZuJyFqWg9jcoPsHts5nrg5O956py2wyt28lUrBxk0M0/wU8URpA==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
seroval: ^1.0
|
||||
|
||||
seroval@1.4.2:
|
||||
resolution: {integrity: sha512-N3HEHRCZYn3cQbsC4B5ldj9j+tHdf4JZoYPlcI4rRYu0Xy4qN8MQf1Z08EibzB0WpgRG5BGK08FTrmM66eSzKQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
serve-static@2.2.1:
|
||||
resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==}
|
||||
engines: {node: '>= 18'}
|
||||
@ -13248,6 +13325,9 @@ packages:
|
||||
tiny-typed-emitter@2.1.0:
|
||||
resolution: {integrity: sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==}
|
||||
|
||||
tiny-warning@1.0.3:
|
||||
resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
|
||||
|
||||
tinybench@2.9.0:
|
||||
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||
|
||||
@ -13779,6 +13859,46 @@ packages:
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
hasBin: true
|
||||
|
||||
vite@7.3.0:
|
||||
resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@types/node': ^20.19.0 || >=22.12.0
|
||||
jiti: '>=1.21.0'
|
||||
less: ^4.0.0
|
||||
lightningcss: ^1.21.0
|
||||
sass: ^1.70.0
|
||||
sass-embedded: ^1.70.0
|
||||
stylus: '>=0.54.8'
|
||||
sugarss: ^5.0.0
|
||||
terser: ^5.16.0
|
||||
tsx: ^4.8.1
|
||||
yaml: ^2.4.2
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
jiti:
|
||||
optional: true
|
||||
less:
|
||||
optional: true
|
||||
lightningcss:
|
||||
optional: true
|
||||
sass:
|
||||
optional: true
|
||||
sass-embedded:
|
||||
optional: true
|
||||
stylus:
|
||||
optional: true
|
||||
sugarss:
|
||||
optional: true
|
||||
terser:
|
||||
optional: true
|
||||
tsx:
|
||||
optional: true
|
||||
yaml:
|
||||
optional: true
|
||||
|
||||
vitest@3.2.4:
|
||||
resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==}
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
@ -15270,6 +15390,16 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/types': 7.28.5
|
||||
|
||||
'@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.5
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
|
||||
'@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.5
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
|
||||
'@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.5)':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.5
|
||||
@ -17664,11 +17794,11 @@ snapshots:
|
||||
|
||||
'@istanbuljs/schema@0.1.3': {}
|
||||
|
||||
'@joshwooding/vite-plugin-react-docgen-typescript@0.6.3(rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(typescript@5.8.3)':
|
||||
'@joshwooding/vite-plugin-react-docgen-typescript@0.6.3(typescript@5.8.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))':
|
||||
dependencies:
|
||||
glob: 11.1.0
|
||||
react-docgen-typescript: 2.4.0(typescript@5.8.3)
|
||||
vite: rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)
|
||||
vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
|
||||
optionalDependencies:
|
||||
typescript: 5.8.3
|
||||
|
||||
@ -19646,8 +19776,6 @@ snapshots:
|
||||
|
||||
'@remirror/core-constants@3.0.0': {}
|
||||
|
||||
'@remix-run/router@1.23.1': {}
|
||||
|
||||
'@replit/codemirror-lang-nix@6.0.1(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/state@6.5.3)(@codemirror/view@6.38.1)(@lezer/common@1.5.0)(@lezer/highlight@1.2.3)(@lezer/lr@1.4.5)':
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.20.0
|
||||
@ -20293,15 +20421,15 @@ snapshots:
|
||||
|
||||
'@standard-schema/utils@0.3.0': {}
|
||||
|
||||
'@storybook/addon-docs@10.1.11(@types/react@19.2.7)(esbuild@0.25.12)(rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))':
|
||||
'@storybook/addon-docs@10.1.11(@types/react@19.2.7)(esbuild@0.25.12)(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))':
|
||||
dependencies:
|
||||
'@mdx-js/react': 3.1.1(@types/react@19.2.7)(react@19.2.3)
|
||||
'@storybook/csf-plugin': 10.1.11(esbuild@0.25.12)(rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
|
||||
'@storybook/csf-plugin': 10.1.11(esbuild@0.25.12)(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))
|
||||
'@storybook/icons': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
'@storybook/react-dom-shim': 10.1.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
|
||||
'@storybook/react-dom-shim': 10.1.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
|
||||
react: 19.2.3
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
ts-dedent: 2.2.0
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
@ -20310,32 +20438,32 @@ snapshots:
|
||||
- vite
|
||||
- webpack
|
||||
|
||||
'@storybook/addon-themes@10.1.11(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))':
|
||||
'@storybook/addon-themes@10.1.11(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))':
|
||||
dependencies:
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
ts-dedent: 2.2.0
|
||||
|
||||
'@storybook/builder-vite@10.1.11(esbuild@0.25.12)(msw@2.12.7(@types/node@24.10.4)(typescript@5.8.3))(rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))':
|
||||
'@storybook/builder-vite@10.1.11(esbuild@0.25.12)(msw@2.12.7(@types/node@24.10.4)(typescript@5.8.3))(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))':
|
||||
dependencies:
|
||||
'@storybook/csf-plugin': 10.1.11(esbuild@0.25.12)(rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
|
||||
'@vitest/mocker': 3.2.4(msw@2.12.7(@types/node@24.10.4)(typescript@5.8.3))(rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
'@storybook/csf-plugin': 10.1.11(esbuild@0.25.12)(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))
|
||||
'@vitest/mocker': 3.2.4(msw@2.12.7(@types/node@24.10.4)(typescript@5.8.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
ts-dedent: 2.2.0
|
||||
vite: rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)
|
||||
vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
|
||||
transitivePeerDependencies:
|
||||
- esbuild
|
||||
- msw
|
||||
- rollup
|
||||
- webpack
|
||||
|
||||
'@storybook/csf-plugin@10.1.11(esbuild@0.25.12)(rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))':
|
||||
'@storybook/csf-plugin@10.1.11(esbuild@0.25.12)(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))':
|
||||
dependencies:
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
unplugin: 2.3.11
|
||||
optionalDependencies:
|
||||
esbuild: 0.25.12
|
||||
rollup: 4.55.1
|
||||
vite: rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)
|
||||
vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
|
||||
|
||||
'@storybook/global@5.0.0': {}
|
||||
|
||||
@ -20344,27 +20472,27 @@ snapshots:
|
||||
react: 19.2.3
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
|
||||
'@storybook/react-dom-shim@10.1.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))':
|
||||
'@storybook/react-dom-shim@10.1.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))':
|
||||
dependencies:
|
||||
react: 19.2.3
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
|
||||
'@storybook/react-vite@10.1.11(esbuild@0.25.12)(msw@2.12.7(@types/node@24.10.4)(typescript@5.8.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.8.3)':
|
||||
'@storybook/react-vite@10.1.11(esbuild@0.25.12)(msw@2.12.7(@types/node@24.10.4)(typescript@5.8.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.8.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))':
|
||||
dependencies:
|
||||
'@joshwooding/vite-plugin-react-docgen-typescript': 0.6.3(rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(typescript@5.8.3)
|
||||
'@joshwooding/vite-plugin-react-docgen-typescript': 0.6.3(typescript@5.8.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))
|
||||
'@rollup/pluginutils': 5.3.0(rollup@4.55.1)
|
||||
'@storybook/builder-vite': 10.1.11(esbuild@0.25.12)(msw@2.12.7(@types/node@24.10.4)(typescript@5.8.3))(rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
|
||||
'@storybook/react': 10.1.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.8.3)
|
||||
'@storybook/builder-vite': 10.1.11(esbuild@0.25.12)(msw@2.12.7(@types/node@24.10.4)(typescript@5.8.3))(rollup@4.55.1)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))
|
||||
'@storybook/react': 10.1.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.8.3)
|
||||
empathic: 2.0.0
|
||||
magic-string: 0.30.21
|
||||
react: 19.2.3
|
||||
react-docgen: 8.0.2
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
resolve: 1.22.11
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
tsconfig-paths: 4.2.0
|
||||
vite: rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)
|
||||
vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
|
||||
transitivePeerDependencies:
|
||||
- esbuild
|
||||
- msw
|
||||
@ -20373,14 +20501,14 @@ snapshots:
|
||||
- typescript
|
||||
- webpack
|
||||
|
||||
'@storybook/react@10.1.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.8.3)':
|
||||
'@storybook/react@10.1.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@storybook/global': 5.0.0
|
||||
'@storybook/react-dom-shim': 10.1.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
|
||||
'@storybook/react-dom-shim': 10.1.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
|
||||
react: 19.2.3
|
||||
react-docgen: 8.0.2
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
optionalDependencies:
|
||||
typescript: 5.8.3
|
||||
transitivePeerDependencies:
|
||||
@ -20610,6 +20738,8 @@ snapshots:
|
||||
tailwindcss: 4.1.18
|
||||
vite: rolldown-vite@7.3.0(@types/node@22.17.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)
|
||||
|
||||
'@tanstack/history@1.145.7': {}
|
||||
|
||||
'@tanstack/query-core@5.90.16': {}
|
||||
|
||||
'@tanstack/react-query@5.90.16(react@19.2.3)':
|
||||
@ -20617,6 +20747,24 @@ snapshots:
|
||||
'@tanstack/query-core': 5.90.16
|
||||
react: 19.2.3
|
||||
|
||||
'@tanstack/react-router@1.145.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
|
||||
dependencies:
|
||||
'@tanstack/history': 1.145.7
|
||||
'@tanstack/react-store': 0.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
'@tanstack/router-core': 1.145.7
|
||||
isbot: 5.1.32
|
||||
react: 19.2.3
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
tiny-invariant: 1.3.3
|
||||
tiny-warning: 1.0.3
|
||||
|
||||
'@tanstack/react-store@0.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
|
||||
dependencies:
|
||||
'@tanstack/store': 0.8.0
|
||||
react: 19.2.3
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
use-sync-external-store: 1.6.0(react@19.2.3)
|
||||
|
||||
'@tanstack/react-virtual@3.11.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
|
||||
dependencies:
|
||||
'@tanstack/virtual-core': 3.11.3
|
||||
@ -20629,10 +20777,71 @@ snapshots:
|
||||
react: 19.2.3
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
|
||||
'@tanstack/router-core@1.145.7':
|
||||
dependencies:
|
||||
'@tanstack/history': 1.145.7
|
||||
'@tanstack/store': 0.8.0
|
||||
cookie-es: 2.0.0
|
||||
seroval: 1.4.2
|
||||
seroval-plugins: 1.4.2(seroval@1.4.2)
|
||||
tiny-invariant: 1.3.3
|
||||
tiny-warning: 1.0.3
|
||||
|
||||
'@tanstack/router-generator@1.145.7':
|
||||
dependencies:
|
||||
'@tanstack/router-core': 1.145.7
|
||||
'@tanstack/router-utils': 1.143.11
|
||||
'@tanstack/virtual-file-routes': 1.145.4
|
||||
prettier: 3.7.4
|
||||
recast: 0.23.11
|
||||
source-map: 0.7.6
|
||||
tsx: 4.21.0
|
||||
zod: 3.25.76
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@tanstack/router-plugin@1.145.7(@tanstack/react-router@1.145.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(rolldown-vite@7.3.0(@types/node@22.17.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.5
|
||||
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5)
|
||||
'@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5)
|
||||
'@babel/template': 7.27.2
|
||||
'@babel/traverse': 7.28.5
|
||||
'@babel/types': 7.28.5
|
||||
'@tanstack/router-core': 1.145.7
|
||||
'@tanstack/router-generator': 1.145.7
|
||||
'@tanstack/router-utils': 1.143.11
|
||||
'@tanstack/virtual-file-routes': 1.145.4
|
||||
babel-dead-code-elimination: 1.0.11
|
||||
chokidar: 3.6.0
|
||||
unplugin: 2.3.11
|
||||
zod: 3.25.76
|
||||
optionalDependencies:
|
||||
'@tanstack/react-router': 1.145.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
vite: rolldown-vite@7.3.0(@types/node@22.17.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@tanstack/router-utils@1.143.11':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.5
|
||||
'@babel/generator': 7.28.5
|
||||
'@babel/parser': 7.28.5
|
||||
ansis: 4.2.0
|
||||
diff: 8.0.2
|
||||
pathe: 2.0.3
|
||||
tinyglobby: 0.2.15
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@tanstack/store@0.8.0': {}
|
||||
|
||||
'@tanstack/virtual-core@3.11.3': {}
|
||||
|
||||
'@tanstack/virtual-core@3.13.14': {}
|
||||
|
||||
'@tanstack/virtual-file-routes@1.145.4': {}
|
||||
|
||||
'@testing-library/dom@10.4.1':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.27.1
|
||||
@ -22062,6 +22271,15 @@ snapshots:
|
||||
msw: 2.12.7(@types/node@24.10.4)(typescript@5.8.3)
|
||||
vite: rolldown-vite@7.3.0(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)
|
||||
|
||||
'@vitest/mocker@3.2.4(msw@2.12.7(@types/node@24.10.4)(typescript@5.8.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.2.4
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.21
|
||||
optionalDependencies:
|
||||
msw: 2.12.7(@types/node@24.10.4)(typescript@5.8.3)
|
||||
vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
|
||||
|
||||
'@vitest/pretty-format@3.2.4':
|
||||
dependencies:
|
||||
tinyrainbow: 2.0.0
|
||||
@ -22461,6 +22679,15 @@ snapshots:
|
||||
|
||||
b4a@1.7.3: {}
|
||||
|
||||
babel-dead-code-elimination@1.0.11:
|
||||
dependencies:
|
||||
'@babel/core': 7.28.5
|
||||
'@babel/parser': 7.28.5
|
||||
'@babel/traverse': 7.28.5
|
||||
'@babel/types': 7.28.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
bail@1.0.5: {}
|
||||
|
||||
bail@2.0.2: {}
|
||||
@ -23031,6 +23258,8 @@ snapshots:
|
||||
|
||||
convert-source-map@2.0.0: {}
|
||||
|
||||
cookie-es@2.0.0: {}
|
||||
|
||||
cookie-signature@1.2.2: {}
|
||||
|
||||
cookie@0.7.2: {}
|
||||
@ -24050,11 +24279,11 @@ snapshots:
|
||||
dependencies:
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
|
||||
eslint-plugin-storybook@10.0.5(eslint@9.39.2(jiti@2.6.1))(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.8.3):
|
||||
eslint-plugin-storybook@10.0.5(eslint@9.39.2(jiti@2.6.1))(storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.8.3):
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 8.51.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.8.3)
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
storybook: 10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
@ -25264,6 +25493,8 @@ snapshots:
|
||||
|
||||
isbinaryfile@5.0.4: {}
|
||||
|
||||
isbot@5.1.32: {}
|
||||
|
||||
isexe@2.0.0: {}
|
||||
|
||||
isexe@3.1.1: {}
|
||||
@ -27320,6 +27551,8 @@ snapshots:
|
||||
|
||||
prettier@2.8.8: {}
|
||||
|
||||
prettier@3.7.4: {}
|
||||
|
||||
pretty-format@27.5.1:
|
||||
dependencies:
|
||||
ansi-regex: 5.0.1
|
||||
@ -28016,18 +28249,6 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.7
|
||||
|
||||
react-router-dom@6.30.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
||||
dependencies:
|
||||
'@remix-run/router': 1.23.1
|
||||
react: 19.2.3
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
react-router: 6.30.2(react@19.2.3)
|
||||
|
||||
react-router@6.30.2(react@19.2.3):
|
||||
dependencies:
|
||||
'@remix-run/router': 1.23.1
|
||||
react: 19.2.3
|
||||
|
||||
react-spinners@0.14.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
||||
dependencies:
|
||||
react: 19.2.3
|
||||
@ -28557,7 +28778,6 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-gnu': 4.55.1
|
||||
'@rollup/rollup-win32-x64-msvc': 4.55.1
|
||||
fsevents: 2.3.3
|
||||
optional: true
|
||||
|
||||
rope-sequence@1.3.4: {}
|
||||
|
||||
@ -28674,6 +28894,12 @@ snapshots:
|
||||
type-fest: 0.13.1
|
||||
optional: true
|
||||
|
||||
seroval-plugins@1.4.2(seroval@1.4.2):
|
||||
dependencies:
|
||||
seroval: 1.4.2
|
||||
|
||||
seroval@1.4.2: {}
|
||||
|
||||
serve-static@2.2.1:
|
||||
dependencies:
|
||||
encodeurl: 2.0.0
|
||||
@ -28896,7 +29122,7 @@ snapshots:
|
||||
|
||||
std-env@3.10.0: {}
|
||||
|
||||
storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
||||
storybook@10.1.11(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
||||
dependencies:
|
||||
'@storybook/global': 5.0.0
|
||||
'@storybook/icons': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
@ -28911,7 +29137,7 @@ snapshots:
|
||||
use-sync-external-store: 1.6.0(react@19.2.3)
|
||||
ws: 8.18.3
|
||||
optionalDependencies:
|
||||
prettier: 2.8.8
|
||||
prettier: 3.7.4
|
||||
transitivePeerDependencies:
|
||||
- '@testing-library/dom'
|
||||
- bufferutil
|
||||
@ -29279,6 +29505,8 @@ snapshots:
|
||||
|
||||
tiny-typed-emitter@2.1.0: {}
|
||||
|
||||
tiny-warning@1.0.3: {}
|
||||
|
||||
tinybench@2.9.0: {}
|
||||
|
||||
tinyexec@0.3.2: {}
|
||||
@ -29869,6 +30097,22 @@ snapshots:
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2):
|
||||
dependencies:
|
||||
esbuild: 0.25.12
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
picomatch: 4.0.3
|
||||
postcss: 8.5.6
|
||||
rollup: 4.55.1
|
||||
tinyglobby: 0.2.15
|
||||
optionalDependencies:
|
||||
'@types/node': 24.10.4
|
||||
fsevents: 2.3.3
|
||||
jiti: 2.6.1
|
||||
lightningcss: 1.30.2
|
||||
tsx: 4.21.0
|
||||
yaml: 2.8.2
|
||||
|
||||
vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(esbuild@0.25.12)(jiti@2.6.1)(jsdom@26.1.0)(msw@2.12.7(@types/node@22.17.2)(typescript@5.8.3))(tsx@4.21.0)(yaml@2.8.2):
|
||||
dependencies:
|
||||
'@types/chai': 5.2.3
|
||||
|
||||
@ -7,13 +7,13 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { Provider } from 'react-redux'
|
||||
import { PersistGate } from 'redux-persist/integration/react'
|
||||
|
||||
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')
|
||||
|
||||
@ -42,7 +42,7 @@ function App(): React.ReactElement {
|
||||
<CodeStyleProvider>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<TopViewContainer>
|
||||
<Router />
|
||||
<AppShell />
|
||||
</TopViewContainer>
|
||||
</PersistGate>
|
||||
</CodeStyleProvider>
|
||||
|
||||
@ -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 (
|
||||
<ErrorBoundary>
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="/store" element={<AssistantPresetsPage />} />
|
||||
<Route path="/paintings/*" element={<PaintingsRoutePage />} />
|
||||
<Route path="/translate" element={<TranslatePage />} />
|
||||
<Route path="/files" element={<FilesPage />} />
|
||||
<Route path="/notes" element={<NotesPage />} />
|
||||
<Route path="/knowledge" element={<KnowledgePage />} />
|
||||
<Route path="/apps/:appId" element={<MinAppPage />} />
|
||||
<Route path="/apps" element={<MinAppsPage />} />
|
||||
<Route path="/code" element={<CodeToolsPage />} />
|
||||
<Route path="/settings/*" element={<SettingsPage />} />
|
||||
<Route path="/launchpad" element={<LaunchpadPage />} />
|
||||
</Routes>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}, [])
|
||||
|
||||
if (navbarPosition === 'left') {
|
||||
return (
|
||||
<HashRouter>
|
||||
<Sidebar />
|
||||
{routes}
|
||||
<NavigationHandler />
|
||||
</HashRouter>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<HashRouter>
|
||||
<NavigationHandler />
|
||||
<TabsContainer>{routes}</TabsContainer>
|
||||
</HashRouter>
|
||||
)
|
||||
}
|
||||
|
||||
export default Router
|
||||
@ -30,13 +30,13 @@ export const FreeTrialModelTag: FC<Props> = ({ 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) {
|
||||
|
||||
@ -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<Props> = ({ app, onClick, size = 60, isLast }) => {
|
||||
const handleClick = () => {
|
||||
if (isTopNavbar) {
|
||||
// 顶部导航栏:导航到小程序页面
|
||||
navigate(`/apps/${app.id}`)
|
||||
navigate({ to: '/app/minapp/$appId', params: { appId: app.id } })
|
||||
} else {
|
||||
// 侧边导航栏:保持原有弹窗行为
|
||||
openMinappKeepAlive(app)
|
||||
|
||||
@ -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'
|
||||
|
||||
/**
|
||||
@ -31,10 +31,10 @@ const MinAppTabsPool: React.FC = () => {
|
||||
// 使用集中工具进行更稳健的路由判断
|
||||
const isAppDetail = (() => {
|
||||
const pathname = location.pathname
|
||||
if (pathname === '/apps') return false
|
||||
if (!pathname.startsWith('/apps/')) return false
|
||||
const parts = pathname.split('/').filter(Boolean) // ['apps', '<id>', ...]
|
||||
return parts.length >= 2
|
||||
if (pathname === '/app/minapp') return false
|
||||
if (!pathname.startsWith('/app/minapp/')) return false
|
||||
const parts = pathname.split('/').filter(Boolean) // ['app', 'minapp', '<id>', ...]
|
||||
return parts.length >= 3
|
||||
})()
|
||||
const shouldShow = isTopNavbar && isAppDetail
|
||||
|
||||
|
||||
@ -193,7 +193,7 @@ const PopupContainer: React.FC<Props> = ({ 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 } })
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
@ -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,
|
||||
@ -36,7 +37,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'
|
||||
@ -200,17 +200,17 @@ const TabsContainer: React.FC<TabsContainerProps> = ({ 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])
|
||||
|
||||
@ -12,6 +12,7 @@ import useNavBackgroundColor from '@renderer/hooks/useNavBackgroundColor'
|
||||
import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import { getSidebarIconLabel, getThemeModeLabel } from '@renderer/i18n/label'
|
||||
import { isEmoji } from '@renderer/utils'
|
||||
import { getDefaultRouteTitle } from '@renderer/utils/routeTitle'
|
||||
import { ThemeMode } from '@shared/data/preference/preferenceTypes'
|
||||
import {
|
||||
Code,
|
||||
@ -30,9 +31,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 +41,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 +57,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, title: getDefaultRouteTitle(path) })
|
||||
}
|
||||
}
|
||||
|
||||
const isFullscreen = useFullscreen()
|
||||
@ -118,14 +124,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 = {
|
||||
@ -141,28 +149,35 @@ const MainMenus: FC = () => {
|
||||
}
|
||||
|
||||
const pathMap = {
|
||||
assistants: '/',
|
||||
store: '/store',
|
||||
paintings: `/paintings/${defaultPaintingProvider}`,
|
||||
translate: '/translate',
|
||||
minapp: '/apps',
|
||||
knowledge: '/knowledge',
|
||||
files: '/files',
|
||||
code_tools: '/code',
|
||||
notes: '/notes'
|
||||
assistants: '/app/chat',
|
||||
store: '/app/assistant',
|
||||
paintings: `/app/paintings/${defaultPaintingProvider}`,
|
||||
translate: '/app/translate',
|
||||
minapp: '/app/minapp',
|
||||
knowledge: '/app/knowledge',
|
||||
files: '/app/files',
|
||||
code_tools: '/app/code',
|
||||
notes: '/app/notes'
|
||||
}
|
||||
|
||||
// 在当前 Tab 内跳转
|
||||
const to = async (path: string) => {
|
||||
await modelGenerating()
|
||||
if (activeTabId) {
|
||||
updateTab(activeTabId, { url: path, title: getDefaultRouteTitle(path) })
|
||||
}
|
||||
}
|
||||
|
||||
return visibleSidebarIcons.map((icon) => {
|
||||
const path = pathMap[icon]
|
||||
const isActive = path === '/' ? isRoute(path) : isRoutes(path)
|
||||
const isActive = isRoutes(path)
|
||||
|
||||
return (
|
||||
<Tooltip key={icon} placement="right" content={getSidebarIconLabel(icon)} delay={800}>
|
||||
<StyledLink
|
||||
onClick={async () => {
|
||||
hideMinappPopup()
|
||||
await modelGenerating()
|
||||
navigate(path)
|
||||
await to(path)
|
||||
}}>
|
||||
<Icon theme={theme} className={isActive}>
|
||||
{iconMap[icon]}
|
||||
|
||||
111
src/renderer/src/components/layout/AppShell.tsx
Normal file
111
src/renderer/src/components/layout/AppShell.tsx
Normal file
@ -0,0 +1,111 @@
|
||||
import '@renderer/databases'
|
||||
|
||||
import { Tabs, TabsList, TabsTrigger } from '@cherrystudio/ui'
|
||||
import { cn } from '@renderer/utils'
|
||||
import { getDefaultRouteTitle } from '@renderer/utils/routeTitle'
|
||||
import { Plus, X } from 'lucide-react'
|
||||
import { Activity } from 'react'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
import { useTabs } from '../../hooks/useTabs'
|
||||
import Sidebar from '../app/Sidebar'
|
||||
import { TabRouter } from './TabRouter'
|
||||
|
||||
// Mock Webview component (TODO: Replace with actual MinApp/Webview)
|
||||
const WebviewContainer = ({ url, isActive }: { url: string; isActive: boolean }) => (
|
||||
<Activity mode={isActive ? 'visible' : 'hidden'}>
|
||||
<div className="flex h-full w-full flex-col items-center justify-center bg-background">
|
||||
<div className="mb-2 font-bold text-lg">Webview App</div>
|
||||
<code className="rounded bg-muted p-2">{url}</code>
|
||||
</div>
|
||||
</Activity>
|
||||
)
|
||||
|
||||
export const AppShell = () => {
|
||||
const { tabs, activeTabId, setActiveTab, closeTab, updateTab, addTab } = useTabs()
|
||||
|
||||
// Sync internal navigation back to tab state with default title (url may include search/hash)
|
||||
const handleUrlChange = (tabId: string, url: string) => {
|
||||
updateTab(tabId, { url, title: getDefaultRouteTitle(url) })
|
||||
}
|
||||
|
||||
// 新增 Tab(默认打开首页)
|
||||
const handleAddTab = () => {
|
||||
addTab({
|
||||
id: uuid(),
|
||||
type: 'route',
|
||||
url: '/',
|
||||
title: getDefaultRouteTitle('/')
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-screen flex-row overflow-hidden bg-background text-foreground">
|
||||
{/* Zone 1: Sidebar */}
|
||||
<Sidebar />
|
||||
|
||||
<div className="flex h-full w-full flex-1 flex-col overflow-hidden">
|
||||
{/* Zone 2: Tab Bar */}
|
||||
<Tabs value={activeTabId} onValueChange={setActiveTab} variant="line" className="w-full">
|
||||
<header className="flex h-10 w-full items-center border-b bg-muted/5">
|
||||
<TabsList className="flex h-full min-w-0 flex-1 justify-start gap-0 overflow-hidden">
|
||||
{tabs.map((tab) => (
|
||||
<TabsTrigger
|
||||
key={tab.id}
|
||||
value={tab.id}
|
||||
className={cn(
|
||||
'group relative flex h-full min-w-0 max-w-[200px] flex-1 items-center justify-between gap-2 rounded-none border-r px-3 text-sm',
|
||||
tab.id === activeTabId ? 'bg-background' : 'bg-transparent'
|
||||
)}>
|
||||
{/* TODO: pin功能,形式还未确定 */}
|
||||
<span className={cn('truncate text-xs', tab.isDormant && 'opacity-60')}>{tab.title}</span>
|
||||
{tabs.length > 1 && (
|
||||
<div
|
||||
role="button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
closeTab(tab.id)
|
||||
}}
|
||||
className="ml-1 cursor-pointer rounded-sm p-0.5 opacity-0 hover:bg-muted-foreground/20 hover:opacity-100 group-hover:opacity-50">
|
||||
<X className="size-3" />
|
||||
</div>
|
||||
)}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
{/* 新增 Tab 按钮 - 跟随最后一个 Tab */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleAddTab}
|
||||
className="flex h-full shrink-0 items-center justify-center px-3 hover:bg-muted/50"
|
||||
title="New Tab">
|
||||
<Plus className="size-4" />
|
||||
</button>
|
||||
</TabsList>
|
||||
</header>
|
||||
</Tabs>
|
||||
|
||||
{/* Zone 3: Content Area - Multi MemoryRouter Architecture */}
|
||||
<main className="relative flex-1 overflow-hidden bg-background">
|
||||
{/* Route Tabs: Only render non-dormant tabs */}
|
||||
{tabs
|
||||
.filter((t) => t.type === 'route' && !t.isDormant)
|
||||
.map((tab) => (
|
||||
<TabRouter
|
||||
key={tab.id}
|
||||
tab={tab}
|
||||
isActive={tab.id === activeTabId}
|
||||
onUrlChange={(url) => handleUrlChange(tab.id, url)}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Webview Tabs: Only render non-dormant tabs */}
|
||||
{tabs
|
||||
.filter((t) => t.type === 'webview' && !t.isDormant)
|
||||
.map((tab) => (
|
||||
<WebviewContainer key={tab.id} url={tab.url} isActive={tab.id === activeTabId} />
|
||||
))}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
52
src/renderer/src/components/layout/TabRouter.tsx
Normal file
52
src/renderer/src/components/layout/TabRouter.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import type { Tab } from '@shared/data/cache/cacheValueTypes'
|
||||
import { createMemoryHistory, createRouter, RouterProvider } from '@tanstack/react-router'
|
||||
import { Activity } from 'react'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
|
||||
import { routeTree } from '../../routeTree.gen'
|
||||
|
||||
interface TabRouterProps {
|
||||
tab: Tab
|
||||
isActive: boolean
|
||||
onUrlChange: (url: string) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* TabRouter - Independent MemoryRouter for each Tab
|
||||
*
|
||||
* Each tab maintains its own router instance with isolated history,
|
||||
* enabling true KeepAlive behavior via React 19's Activity component.
|
||||
*/
|
||||
export const TabRouter = ({ tab, isActive, onUrlChange }: TabRouterProps) => {
|
||||
// Create independent router instance per tab (only once)
|
||||
const router = useMemo(() => {
|
||||
const history = createMemoryHistory({ initialEntries: [tab.url] })
|
||||
return createRouter({ routeTree, history })
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [tab.id])
|
||||
// Sync internal navigation back to tab state
|
||||
useEffect(() => {
|
||||
return router.subscribe('onResolved', ({ toLocation }) => {
|
||||
const nextHref = toLocation.href
|
||||
if (nextHref !== tab.url) {
|
||||
onUrlChange(nextHref)
|
||||
}
|
||||
})
|
||||
}, [router, tab.url, onUrlChange])
|
||||
|
||||
// Navigate when tab.url changes externally (e.g., from Sidebar)
|
||||
useEffect(() => {
|
||||
const currentHref = router.state.location.href
|
||||
if (tab.url !== currentHref) {
|
||||
router.navigate({ to: tab.url })
|
||||
}
|
||||
}, [router, tab.url])
|
||||
|
||||
return (
|
||||
<Activity mode={isActive ? 'visible' : 'hidden'}>
|
||||
<div className="h-full w-full">
|
||||
<RouterProvider router={router} />
|
||||
</div>
|
||||
</Activity>
|
||||
)
|
||||
}
|
||||
4
src/renderer/src/env.d.ts
vendored
4
src/renderer/src/env.d.ts
vendored
@ -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<string>
|
||||
toast: ToastUtilities
|
||||
agentTools: {
|
||||
respondToPermission: (payload: {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
326
src/renderer/src/hooks/useTabs.ts
Normal file
326
src/renderer/src/hooks/useTabs.ts
Normal file
@ -0,0 +1,326 @@
|
||||
import { loggerService } from '@logger'
|
||||
import { TabLRUManager } from '@renderer/services/TabLRUManager'
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react'
|
||||
|
||||
import { usePersistCache } from '../data/hooks/useCache'
|
||||
import { uuid } from '../utils'
|
||||
import { getDefaultRouteTitle } from '../utils/routeTitle'
|
||||
|
||||
// Re-export types from shared schema
|
||||
export type { Tab, TabsState, TabType } from '@shared/data/cache/cacheValueTypes'
|
||||
import type { Tab, TabSavedState, TabType } from '@shared/data/cache/cacheValueTypes'
|
||||
|
||||
const logger = loggerService.withContext('useTabs')
|
||||
|
||||
const DEFAULT_TAB: Tab = {
|
||||
id: 'home',
|
||||
type: 'route',
|
||||
url: '/home',
|
||||
title: getDefaultRouteTitle('/home'),
|
||||
lastAccessTime: Date.now(),
|
||||
isDormant: false
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for opening a tab
|
||||
*/
|
||||
export interface OpenTabOptions {
|
||||
/** Force open a new tab even if one with the same URL exists */
|
||||
forceNew?: boolean
|
||||
/** Tab title (defaults to URL path) */
|
||||
title?: string
|
||||
/** Tab type (defaults to 'route') */
|
||||
type?: TabType
|
||||
/** Custom tab ID (auto-generated if not provided) */
|
||||
id?: string
|
||||
}
|
||||
|
||||
export function useTabs() {
|
||||
const [tabsState, setTabsState] = usePersistCache('ui.tab.state')
|
||||
|
||||
// LRU 管理器(单例)
|
||||
const lruManagerRef = useRef<TabLRUManager | null>(null)
|
||||
if (!lruManagerRef.current) {
|
||||
lruManagerRef.current = new TabLRUManager()
|
||||
}
|
||||
const lruManager = lruManagerRef.current
|
||||
|
||||
// Ensure at least one default tab exists
|
||||
useEffect(() => {
|
||||
if (tabsState.tabs.length === 0) {
|
||||
setTabsState({ tabs: [DEFAULT_TAB], activeTabId: DEFAULT_TAB.id })
|
||||
}
|
||||
}, [tabsState.tabs.length, setTabsState])
|
||||
|
||||
const tabs = useMemo(() => (tabsState.tabs.length > 0 ? tabsState.tabs : [DEFAULT_TAB]), [tabsState.tabs])
|
||||
const activeTabId = tabsState.activeTabId || DEFAULT_TAB.id
|
||||
|
||||
/**
|
||||
* 内部方法:执行休眠检查并休眠超额标签
|
||||
*/
|
||||
const performHibernationCheck = useCallback(
|
||||
(currentTabs: Tab[], newActiveTabId: string) => {
|
||||
const toHibernate = lruManager.checkAndGetDormantCandidates(currentTabs, newActiveTabId)
|
||||
|
||||
if (toHibernate.length === 0) {
|
||||
return currentTabs
|
||||
}
|
||||
|
||||
// 批量休眠
|
||||
return currentTabs.map((tab) => {
|
||||
if (toHibernate.includes(tab.id)) {
|
||||
logger.info('Tab hibernated', { tabId: tab.id, route: tab.url })
|
||||
// TODO: 保存滚动位置等状态
|
||||
const savedState: TabSavedState = { scrollPosition: 0 }
|
||||
return { ...tab, isDormant: true, savedState }
|
||||
}
|
||||
return tab
|
||||
})
|
||||
},
|
||||
[lruManager]
|
||||
)
|
||||
|
||||
/**
|
||||
* 休眠标签(手动)
|
||||
*
|
||||
* TODO: 目前 savedState 仅为占位符,后续需实现:
|
||||
* - 捕获真实滚动位置
|
||||
* - 保存必要的草稿/表单状态
|
||||
*/
|
||||
const hibernateTab = useCallback(
|
||||
(tabId: string) => {
|
||||
const tab = tabsState.tabs.find((t) => t.id === tabId)
|
||||
if (!tab || tab.isDormant) return
|
||||
|
||||
// TODO: 实现真实的状态捕获
|
||||
const savedState: TabSavedState = { scrollPosition: 0 }
|
||||
|
||||
logger.info('Tab hibernated (manual)', { tabId, route: tab.url })
|
||||
|
||||
setTabsState({
|
||||
...tabsState,
|
||||
tabs: tabsState.tabs.map((t) => (t.id === tabId ? { ...t, isDormant: true, savedState } : t))
|
||||
})
|
||||
},
|
||||
[tabsState, setTabsState]
|
||||
)
|
||||
|
||||
/**
|
||||
* 唤醒标签
|
||||
*
|
||||
* TODO: 目前仅清除 isDormant 标记,后续需实现:
|
||||
* - 从 savedState 恢复滚动位置
|
||||
* - 恢复草稿/表单状态
|
||||
*/
|
||||
const wakeTab = useCallback(
|
||||
(tabId: string) => {
|
||||
const tab = tabsState.tabs.find((t) => t.id === tabId)
|
||||
if (!tab || !tab.isDormant) return
|
||||
|
||||
logger.info('Tab awakened', { tabId, route: tab.url })
|
||||
|
||||
// TODO: 实现真实的状态恢复(从 tab.savedState)
|
||||
setTabsState({
|
||||
...tabsState,
|
||||
tabs: tabsState.tabs.map((t) => (t.id === tabId ? { ...t, isDormant: false, lastAccessTime: Date.now() } : t))
|
||||
})
|
||||
},
|
||||
[tabsState, setTabsState]
|
||||
)
|
||||
|
||||
const updateTab = useCallback(
|
||||
(id: string, updates: Partial<Tab>) => {
|
||||
setTabsState({
|
||||
...tabsState,
|
||||
tabs: tabsState.tabs.map((t) => (t.id === id ? { ...t, ...updates } : t))
|
||||
})
|
||||
},
|
||||
[tabsState, setTabsState]
|
||||
)
|
||||
|
||||
const setActiveTab = useCallback(
|
||||
(id: string) => {
|
||||
if (id === activeTabId) return
|
||||
|
||||
const targetTab = tabs.find((t) => t.id === id)
|
||||
if (!targetTab) return
|
||||
|
||||
// 1. 准备更新后的标签列表
|
||||
let updatedTabs = tabsState.tabs.map((t) =>
|
||||
t.id === id
|
||||
? {
|
||||
...t,
|
||||
lastAccessTime: Date.now(),
|
||||
// 如果目标是休眠状态,唤醒它
|
||||
isDormant: false
|
||||
}
|
||||
: t
|
||||
)
|
||||
|
||||
// 2. 如果唤醒了休眠标签,记录日志
|
||||
if (targetTab.isDormant) {
|
||||
logger.info('Tab awakened', { tabId: id, route: targetTab.url })
|
||||
}
|
||||
|
||||
// 3. 执行休眠检查(可能需要休眠其他标签)
|
||||
updatedTabs = performHibernationCheck(updatedTabs, id)
|
||||
|
||||
// 4. 更新状态
|
||||
setTabsState({ tabs: updatedTabs, activeTabId: id })
|
||||
},
|
||||
[activeTabId, tabs, tabsState, setTabsState, performHibernationCheck]
|
||||
)
|
||||
|
||||
const addTab = useCallback(
|
||||
(tab: Tab) => {
|
||||
const exists = tabs.find((t) => t.id === tab.id)
|
||||
if (exists) {
|
||||
setActiveTab(tab.id)
|
||||
return
|
||||
}
|
||||
|
||||
// 添加 LRU 字段,保留完整 URL(含 search/hash)
|
||||
const newTab: Tab = {
|
||||
...tab,
|
||||
lastAccessTime: Date.now(),
|
||||
isDormant: false
|
||||
}
|
||||
|
||||
// 执行休眠检查
|
||||
let newTabs = [...tabs, newTab]
|
||||
newTabs = performHibernationCheck(newTabs, tab.id)
|
||||
|
||||
setTabsState({ tabs: newTabs, activeTabId: tab.id })
|
||||
},
|
||||
[tabs, setTabsState, setActiveTab, performHibernationCheck]
|
||||
)
|
||||
|
||||
const closeTab = useCallback(
|
||||
(id: string) => {
|
||||
let newTabs = tabs.filter((t) => t.id !== id)
|
||||
let newActiveId = activeTabId
|
||||
|
||||
if (activeTabId === id) {
|
||||
const index = tabs.findIndex((t) => t.id === id)
|
||||
const nextTab = newTabs[index - 1] || newTabs[index]
|
||||
newActiveId = nextTab ? nextTab.id : ''
|
||||
|
||||
if (nextTab?.isDormant) {
|
||||
newTabs = newTabs.map((t) =>
|
||||
t.id === nextTab.id ? { ...t, isDormant: false, lastAccessTime: Date.now() } : t
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
setTabsState({ tabs: newTabs, activeTabId: newActiveId })
|
||||
},
|
||||
[tabs, activeTabId, setTabsState]
|
||||
)
|
||||
|
||||
const setTabs = useCallback(
|
||||
(newTabs: Tab[] | ((prev: Tab[]) => Tab[])) => {
|
||||
const resolvedTabs = typeof newTabs === 'function' ? newTabs(tabs) : newTabs
|
||||
setTabsState({ ...tabsState, tabs: resolvedTabs })
|
||||
},
|
||||
[tabs, tabsState, setTabsState]
|
||||
)
|
||||
|
||||
/**
|
||||
* Open a Tab - reuses existing tab or creates new one
|
||||
*
|
||||
* @example
|
||||
* // Basic usage - reuses existing tab if URL matches
|
||||
* openTab('/settings')
|
||||
*
|
||||
* @example
|
||||
* // With custom title
|
||||
* openTab('/chat/123', { title: 'Chat with Alice' })
|
||||
*
|
||||
* @example
|
||||
* // Force open new tab (e.g., Cmd+Click)
|
||||
* openTab('/settings', { forceNew: true })
|
||||
*
|
||||
* @example
|
||||
* // Open webview tab
|
||||
* openTab('https://example.com', { type: 'webview', title: 'Example' })
|
||||
*/
|
||||
const openTab = useCallback(
|
||||
(url: string, options: OpenTabOptions = {}) => {
|
||||
const { forceNew = false, title, type = 'route', id } = options
|
||||
|
||||
// Try to find existing tab with same URL (unless forceNew)
|
||||
if (!forceNew) {
|
||||
const existingTab = tabs.find((t) => t.type === type && t.url === url)
|
||||
if (existingTab) {
|
||||
setActiveTab(existingTab.id)
|
||||
return existingTab.id
|
||||
}
|
||||
}
|
||||
|
||||
// Create new tab with default route title and LRU fields
|
||||
const newTab: Tab = {
|
||||
id: id || uuid(),
|
||||
type,
|
||||
url, // full URL including search/hash
|
||||
title: title || getDefaultRouteTitle(url),
|
||||
lastAccessTime: Date.now(),
|
||||
isDormant: false
|
||||
}
|
||||
|
||||
addTab(newTab)
|
||||
return newTab.id
|
||||
},
|
||||
[tabs, setActiveTab, addTab]
|
||||
)
|
||||
|
||||
/**
|
||||
* Pin a tab (exempt from LRU hibernation)
|
||||
*/
|
||||
const pinTab = useCallback(
|
||||
(id: string) => {
|
||||
updateTab(id, { isPinned: true })
|
||||
logger.info('Tab pinned', { tabId: id })
|
||||
},
|
||||
[updateTab]
|
||||
)
|
||||
|
||||
/**
|
||||
* Unpin a tab
|
||||
*/
|
||||
const unpinTab = useCallback(
|
||||
(id: string) => {
|
||||
updateTab(id, { isPinned: false })
|
||||
logger.info('Tab unpinned', { tabId: id })
|
||||
},
|
||||
[updateTab]
|
||||
)
|
||||
|
||||
/**
|
||||
* Get the currently active tab
|
||||
*/
|
||||
const activeTab = useMemo(() => tabs.find((t) => t.id === activeTabId), [tabs, activeTabId])
|
||||
|
||||
return {
|
||||
// State
|
||||
tabs,
|
||||
activeTabId,
|
||||
activeTab,
|
||||
isLoading: false,
|
||||
|
||||
// Basic operations
|
||||
addTab,
|
||||
closeTab,
|
||||
setActiveTab,
|
||||
updateTab,
|
||||
setTabs,
|
||||
|
||||
// High-level Tab operations
|
||||
openTab,
|
||||
|
||||
// LRU operations
|
||||
hibernateTab,
|
||||
wakeTab,
|
||||
pinTab,
|
||||
unpinTab
|
||||
}
|
||||
}
|
||||
@ -4922,6 +4922,9 @@
|
||||
"title": "Page Zoom"
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"new": "New Tab"
|
||||
},
|
||||
"title": {
|
||||
"apps": "Apps",
|
||||
"code": "Code",
|
||||
|
||||
@ -4922,6 +4922,9 @@
|
||||
"title": "缩放"
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"new": "新标签页"
|
||||
},
|
||||
"title": {
|
||||
"apps": "小程序",
|
||||
"code": "Code",
|
||||
|
||||
@ -4922,6 +4922,9 @@
|
||||
"title": "縮放"
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"new": "新標籤頁"
|
||||
},
|
||||
"title": {
|
||||
"apps": "小程式",
|
||||
"code": "程式碼",
|
||||
|
||||
@ -4919,6 +4919,9 @@
|
||||
"title": "Zoom"
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"new": "[to be translated]:New Tab"
|
||||
},
|
||||
"title": {
|
||||
"apps": "Mini-Apps",
|
||||
"code": "Code",
|
||||
|
||||
@ -4919,6 +4919,9 @@
|
||||
"title": "Κλίμακα"
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"new": "[to be translated]:New Tab"
|
||||
},
|
||||
"title": {
|
||||
"apps": "Εφαρμογές",
|
||||
"code": "Κώδικας",
|
||||
|
||||
@ -4919,6 +4919,9 @@
|
||||
"title": "Escala"
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"new": "[to be translated]:New Tab"
|
||||
},
|
||||
"title": {
|
||||
"apps": "Aplicaciones",
|
||||
"code": "Código",
|
||||
|
||||
@ -4919,6 +4919,9 @@
|
||||
"title": "Zoom"
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"new": "[to be translated]:New Tab"
|
||||
},
|
||||
"title": {
|
||||
"apps": "Mini-programmes",
|
||||
"code": "Code",
|
||||
|
||||
@ -4919,6 +4919,9 @@
|
||||
"title": "ページズーム"
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"new": "[to be translated]:New Tab"
|
||||
},
|
||||
"title": {
|
||||
"apps": "アプリ",
|
||||
"code": "Code",
|
||||
|
||||
@ -4919,6 +4919,9 @@
|
||||
"title": "Escala"
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"new": "[to be translated]:New Tab"
|
||||
},
|
||||
"title": {
|
||||
"apps": "Miniaplicativos",
|
||||
"code": "Código",
|
||||
|
||||
@ -4919,6 +4919,9 @@
|
||||
"title": "Масштаб страницы"
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"new": "[to be translated]:New Tab"
|
||||
},
|
||||
"title": {
|
||||
"apps": "Приложения",
|
||||
"code": "Code",
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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<HTMLDivElement> {
|
||||
}
|
||||
|
||||
const TopicMessages: FC<Props> = ({ 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<Props> = ({ topic: _topic, ...props }) => {
|
||||
await modelGenerating()
|
||||
SearchPopup.hide()
|
||||
const assistant = getAssistantById(topic.assistantId)
|
||||
navigate('/', { state: { assistant, topic } })
|
||||
navigate({ to: '/app/chat', search: { assistantId: assistant?.id, topicId: topic.id } })
|
||||
setTimeoutTimer('onContinueChat', () => EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR), 100)
|
||||
}
|
||||
|
||||
|
||||
@ -10,11 +10,11 @@ import NavigationService from '@renderer/services/NavigationService'
|
||||
import { newMessagesActions } from '@renderer/store/newMessage'
|
||||
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,19 @@ 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<Assistant>(
|
||||
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')
|
||||
@ -79,10 +85,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
|
||||
|
||||
@ -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<Props> = ({ quickPanel, selectedBases, onSelect, d
|
||||
items.push({
|
||||
label: t('knowledge.add.title') + '...',
|
||||
icon: <Plus />,
|
||||
action: () => navigate('/knowledge'),
|
||||
action: () => navigate({ to: '/app/knowledge' }),
|
||||
isSelected: false
|
||||
})
|
||||
|
||||
|
||||
@ -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<Props> = ({ quickPanel, setInputValue, resizeTextArea,
|
||||
newList.push({
|
||||
label: t('settings.mcp.addServer.label') + '...',
|
||||
icon: <Plus />,
|
||||
action: () => navigate('/settings/mcp')
|
||||
action: () => navigate({ to: '/settings/mcp' })
|
||||
})
|
||||
|
||||
newList.unshift({
|
||||
|
||||
@ -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: <Plus />,
|
||||
action: () => navigate('/settings/provider'),
|
||||
action: () => navigate({ to: '/settings/provider' }),
|
||||
isSelected: false
|
||||
})
|
||||
|
||||
|
||||
@ -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'
|
||||
@ -35,10 +34,10 @@ import type { ErrorMessageBlock, Message } from '@renderer/types/newMessage'
|
||||
import { formatAiSdkError, formatError, safeToString } from '@renderer/utils/error'
|
||||
import { formatFileSize } from '@renderer/utils/file'
|
||||
import { KB } from '@shared/config/constant'
|
||||
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]
|
||||
@ -103,11 +102,7 @@ const ErrorMessage: React.FC<{ block: ErrorMessageBlock }> = ({ block }) => {
|
||||
values={{ provider: getProviderLabel(providerId) }}
|
||||
components={{
|
||||
provider: (
|
||||
<Link
|
||||
style={{ color: 'var(--color-link)' }}
|
||||
to={`/settings/provider`}
|
||||
state={{ provider: getProviderById(providerId) }}
|
||||
/>
|
||||
<Link style={{ color: 'var(--color-link)' }} to="/settings/provider" search={{ id: providerId }} />
|
||||
)
|
||||
}}
|
||||
/>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 = () => {
|
||||
@ -18,49 +18,49 @@ const LaunchpadPage: FC = () => {
|
||||
{
|
||||
icon: <LayoutGrid size={32} className="icon" />,
|
||||
text: t('title.apps'),
|
||||
path: '/apps',
|
||||
path: '/app/minapp',
|
||||
bgColor: 'linear-gradient(135deg, #8B5CF6, #A855F7)' // 小程序:紫色,代表多功能和灵活性
|
||||
},
|
||||
{
|
||||
icon: <FileSearch size={32} className="icon" />,
|
||||
text: t('title.knowledge'),
|
||||
path: '/knowledge',
|
||||
path: '/app/knowledge',
|
||||
bgColor: 'linear-gradient(135deg, #10B981, #34D399)' // 知识库:翠绿色,代表生长和知识
|
||||
},
|
||||
{
|
||||
icon: <Palette size={32} className="icon" />,
|
||||
text: t('title.paintings'),
|
||||
path: `/paintings/${defaultPaintingProvider}`,
|
||||
path: `/app/paintings/${defaultPaintingProvider}`,
|
||||
bgColor: 'linear-gradient(135deg, #EC4899, #F472B6)' // 绘画:活力粉色,代表创造力和艺术
|
||||
},
|
||||
{
|
||||
icon: <Sparkle size={32} className="icon" />,
|
||||
text: t('title.store'),
|
||||
path: '/store',
|
||||
path: '/app/assistant',
|
||||
bgColor: 'linear-gradient(135deg, #6366F1, #4F46E5)' // AI助手:靛蓝渐变,代表智能和科技
|
||||
},
|
||||
{
|
||||
icon: <Languages size={32} className="icon" />,
|
||||
text: t('title.translate'),
|
||||
path: '/translate',
|
||||
path: '/app/translate',
|
||||
bgColor: 'linear-gradient(135deg, #06B6D4, #0EA5E9)' // 翻译:明亮的青蓝色,代表沟通和流畅
|
||||
},
|
||||
{
|
||||
icon: <Folder size={32} className="icon" />,
|
||||
text: t('title.files'),
|
||||
path: '/files',
|
||||
path: '/app/files',
|
||||
bgColor: 'linear-gradient(135deg, #F59E0B, #FBBF24)' // 文件:金色,代表资源和重要性
|
||||
},
|
||||
{
|
||||
icon: <Code size={32} className="icon" />,
|
||||
text: t('title.code'),
|
||||
path: '/code',
|
||||
path: '/app/code',
|
||||
bgColor: 'linear-gradient(135deg, #1F2937, #374151)' // Code CLI:高级暗黑色,代表专业和技术
|
||||
},
|
||||
{
|
||||
icon: <NotepadText size={32} className="icon" />,
|
||||
text: t('title.notes'),
|
||||
path: '/notes',
|
||||
path: '/app/notes',
|
||||
bgColor: 'linear-gradient(135deg, #F97316, #FB923C)' // 笔记:橙色,代表活力和清晰思路
|
||||
}
|
||||
]
|
||||
@ -87,7 +87,7 @@ const LaunchpadPage: FC = () => {
|
||||
<SectionTitle>{t('launchpad.apps')}</SectionTitle>
|
||||
<Grid>
|
||||
{appMenuItems.map((item) => (
|
||||
<AppIcon key={item.path} onClick={() => navigate(item.path)}>
|
||||
<AppIcon key={item.path} onClick={() => navigate({ to: item.path })}>
|
||||
<IconContainer>
|
||||
<IconWrapper bgColor={item.bgColor}>{item.icon}</IconWrapper>
|
||||
</IconContainer>
|
||||
|
||||
@ -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: '/app/minapp' })
|
||||
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: '/app/minapp' })
|
||||
// Open popup after navigation
|
||||
setTimeout(() => {
|
||||
openMinappKeepAlive(app)
|
||||
|
||||
@ -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<Props> = ({ app, webviewRef, currentUrl, onReload, onOp
|
||||
}, [app.id, webviewRef, scheduleNavigationUpdate])
|
||||
|
||||
const handleMinimize = useCallback(() => {
|
||||
navigate('/apps')
|
||||
navigate({ to: '/app/minapp' })
|
||||
}, [navigate])
|
||||
|
||||
const handleTogglePin = useCallback(() => {
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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'
|
||||
@ -653,7 +653,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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -29,6 +29,7 @@ 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 { RcFile } from 'antd/es/upload'
|
||||
@ -37,7 +38,6 @@ 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'
|
||||
@ -438,7 +438,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 })
|
||||
}
|
||||
}
|
||||
|
||||
@ -465,7 +465,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(() => {
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 (
|
||||
<Routes>
|
||||
<Route path="*" element={<NewApiPage Options={validOptions} />} />
|
||||
<Route path="/zhipu" element={<ZhipuPage Options={validOptions} />} />
|
||||
<Route path="/aihubmix" element={<AihubmixPage Options={validOptions} />} />
|
||||
<Route path="/silicon" element={<SiliconPage Options={validOptions} />} />
|
||||
<Route path="/dmxapi" element={<DmxapiPage Options={validOptions} />} />
|
||||
<Route path="/tokenflux" element={<TokenFluxPage Options={validOptions} />} />
|
||||
<Route path="/ovms" element={<OvmsPage Options={validOptions} />} />
|
||||
<Route path="/new-api" element={<NewApiPage Options={validOptions} />} />
|
||||
{/* new-api family providers are mounted dynamically below */}
|
||||
{newApiProviders.map((p) => (
|
||||
<Route key={p.id} path={`/${p.id}`} element={<NewApiPage Options={validOptions} />} />
|
||||
))}
|
||||
</Routes>
|
||||
)
|
||||
// 根据 provider 渲染对应的页面
|
||||
const renderPage = () => {
|
||||
switch (provider) {
|
||||
case 'zhipu':
|
||||
return <ZhipuPage Options={validOptions} />
|
||||
case 'aihubmix':
|
||||
return <AihubmixPage Options={validOptions} />
|
||||
case 'silicon':
|
||||
return <SiliconPage Options={validOptions} />
|
||||
case 'dmxapi':
|
||||
return <DmxapiPage Options={validOptions} />
|
||||
case 'tokenflux':
|
||||
return <TokenFluxPage Options={validOptions} />
|
||||
case 'ovms':
|
||||
return <OvmsPage Options={validOptions} />
|
||||
case 'new-api':
|
||||
return <NewApiPage Options={validOptions} />
|
||||
default:
|
||||
// 检查是否是 new-api 家族的 provider
|
||||
if (provider && newApiProviders.some((p) => p.id === provider)) {
|
||||
return <NewApiPage Options={validOptions} />
|
||||
}
|
||||
// 默认页面
|
||||
return <NewApiPage Options={validOptions} />
|
||||
}
|
||||
}
|
||||
|
||||
return renderPage()
|
||||
}
|
||||
|
||||
export default PaintingsRoutePage
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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 '.'
|
||||
|
||||
@ -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<Props> = ({ mini = false }) => {
|
||||
<Button
|
||||
className="nodrag rounded-full"
|
||||
variant={installed ? 'default' : 'destructive'}
|
||||
onClick={() => navigate('/settings/mcp/mcp-install')}
|
||||
onClick={() => navigate({ to: '/settings/mcp/mcp-install' })}
|
||||
size="icon">
|
||||
{installed ? <CheckCircleOutlined /> : <WarningOutlined />}
|
||||
</Button>
|
||||
|
||||
@ -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')}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -12,13 +12,13 @@ import type { MCPPrompt, MCPResource, MCPServer, MCPTool } from '@renderer/types
|
||||
import { parseKeyValueString } from '@renderer/utils/env'
|
||||
import { formatMcpError } from '@renderer/utils/error'
|
||||
import type { MCPServerLogEntry } from '@shared/config/types'
|
||||
import { useNavigate, useParams } from '@tanstack/react-router'
|
||||
import type { TabsProps } from 'antd'
|
||||
import { Badge, Form, Input, Modal, Radio, Select, Tabs, Tag, Typography } 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 '..'
|
||||
@ -69,7 +69,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()
|
||||
@ -411,7 +412,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) {
|
||||
|
||||
@ -9,30 +9,17 @@ import DividerWithText from '@renderer/components/DividerWithText'
|
||||
import { McpLogo } from '@renderer/components/Icons'
|
||||
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()
|
||||
|
||||
@ -85,7 +72,7 @@ const MCPSettings: FC = () => {
|
||||
<ListItem
|
||||
title={t('settings.mcp.servers', 'MCP Servers')}
|
||||
active={activeView === 'servers'}
|
||||
onClick={() => navigate('/settings/mcp/servers')}
|
||||
onClick={() => navigate({ to: '/settings/mcp/servers' })}
|
||||
icon={<McpLogo width={18} height={18} style={{ opacity: 0.8 }} />}
|
||||
titleStyle={{ fontWeight: 500 }}
|
||||
/>
|
||||
@ -93,14 +80,14 @@ const MCPSettings: FC = () => {
|
||||
<ListItem
|
||||
title={t('settings.mcp.builtinServers', 'Built-in Servers')}
|
||||
active={activeView === 'builtin'}
|
||||
onClick={() => navigate('/settings/mcp/builtin')}
|
||||
onClick={() => navigate({ to: '/settings/mcp/builtin' })}
|
||||
icon={<Package size={18} />}
|
||||
titleStyle={{ fontWeight: 500 }}
|
||||
/>
|
||||
<ListItem
|
||||
title={t('settings.mcp.marketplaces', 'Marketplaces')}
|
||||
active={activeView === 'marketplaces'}
|
||||
onClick={() => navigate('/settings/mcp/marketplaces')}
|
||||
onClick={() => navigate({ to: '/settings/mcp/marketplaces' })}
|
||||
icon={<ShoppingBag size={18} />}
|
||||
titleStyle={{ fontWeight: 500 }}
|
||||
/>
|
||||
@ -110,7 +97,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] || <FolderCog size={16} />}
|
||||
titleStyle={{ fontWeight: 500 }}
|
||||
/>
|
||||
@ -126,50 +113,7 @@ const MCPSettings: FC = () => {
|
||||
</Link>
|
||||
</BackButtonContainer>
|
||||
)}
|
||||
<Routes>
|
||||
<Route index element={<Navigate to="servers" replace />} />
|
||||
<Route path="servers" element={<McpServersList />} />
|
||||
<Route path="settings/:serverId" element={<McpSettings />} />
|
||||
<Route
|
||||
path="npx-search"
|
||||
element={
|
||||
<SettingContainer theme={theme}>
|
||||
<NpxSearch />
|
||||
</SettingContainer>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="mcp-install"
|
||||
element={
|
||||
<SettingContainer style={{ backgroundColor: 'inherit' }}>
|
||||
<InstallNpxUv />
|
||||
</SettingContainer>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="builtin"
|
||||
element={
|
||||
<ContentWrapper>
|
||||
<BuiltinMCPServerList />
|
||||
</ContentWrapper>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="marketplaces"
|
||||
element={
|
||||
<ContentWrapper>
|
||||
<McpMarketList />
|
||||
</ContentWrapper>
|
||||
}
|
||||
/>
|
||||
{providers.map((provider) => (
|
||||
<Route
|
||||
key={provider.key}
|
||||
path={provider.key}
|
||||
element={<ProviderDetail provider={provider} existingServers={mcpServers} />}
|
||||
/>
|
||||
))}
|
||||
</Routes>
|
||||
<Outlet />
|
||||
</RightContainer>
|
||||
</MainContainer>
|
||||
</Container>
|
||||
@ -213,12 +157,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;
|
||||
|
||||
@ -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 useSWRImmutable from 'swr/immutable'
|
||||
|
||||
@ -44,7 +44,9 @@ const getIsOvmsSupported = async (): Promise<boolean> => {
|
||||
}
|
||||
|
||||
const ProviderList: FC = () => {
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const search = useSearch({ strict: false }) as Record<string, string | undefined>
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const providers = useAllProviders()
|
||||
const { updateProviders, addProvider, removeProvider, updateProvider } = useProviders()
|
||||
const { setTimeoutTimer } = useTimer()
|
||||
@ -83,8 +85,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)
|
||||
@ -100,10 +102,17 @@ const ProviderList: FC = () => {
|
||||
} else {
|
||||
setSelectedProvider(providers[0])
|
||||
}
|
||||
searchParams.delete('id')
|
||||
setSearchParams(searchParams)
|
||||
// 清除 id 参数
|
||||
navigate({
|
||||
to: location.pathname,
|
||||
search: ({ id, ...rest }) => {
|
||||
void id
|
||||
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(() => {
|
||||
@ -117,7 +126,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
|
||||
@ -134,7 +143,7 @@ const ProviderList: FC = () => {
|
||||
}
|
||||
|
||||
// 检查 URL 参数
|
||||
const addProviderData = searchParams.get('addProviderData')
|
||||
const addProviderData = search.addProviderData
|
||||
if (!addProviderData) {
|
||||
return
|
||||
}
|
||||
@ -143,17 +152,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()
|
||||
|
||||
@ -5,12 +5,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 { Radio, 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 {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
|
||||
import { McpLogo } from '@renderer/components/Icons'
|
||||
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,
|
||||
@ -21,26 +21,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 { 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' : '')
|
||||
@ -148,23 +133,7 @@ const SettingsPage: FC = () => {
|
||||
</MenuItemLink>
|
||||
</SettingMenus>
|
||||
<SettingContent>
|
||||
<Routes>
|
||||
<Route path="provider" element={<ProviderList />} />
|
||||
<Route path="model" element={<ModelSettings />} />
|
||||
<Route path="websearch/*" element={<WebSearchSettings />} />
|
||||
<Route path="api-server" element={<ApiServerSettings />} />
|
||||
<Route path="docprocess" element={<DocProcessSettings />} />
|
||||
<Route path="quickphrase" element={<QuickPhraseSettings />} />
|
||||
<Route path="mcp/*" element={<MCPSettings />} />
|
||||
<Route path="memory" element={<MemorySettings />} />
|
||||
<Route path="general/*" element={<GeneralSettings />} />
|
||||
<Route path="display" element={<DisplaySettings />} />
|
||||
<Route path="shortcut" element={<ShortcutSettings />} />
|
||||
<Route path="quickAssistant" element={<QuickAssistantSettings />} />
|
||||
<Route path="selectionAssistant" element={<SelectionAssistantSettings />} />
|
||||
<Route path="data" element={<DataSettings />} />
|
||||
<Route path="about" element={<AboutSettings />} />
|
||||
</Routes>
|
||||
<Outlet />
|
||||
</SettingContent>
|
||||
</ContentContainer>
|
||||
</Container>
|
||||
|
||||
@ -18,10 +18,10 @@ import { useAppDispatch } from '@renderer/store'
|
||||
import { setMaxResult, setSearchWithTime } from '@renderer/store/websearch'
|
||||
import type { WebSearchProvider, WebSearchProviderId } from '@renderer/types'
|
||||
import { hasObjectKey } from '@renderer/utils'
|
||||
import { useNavigate } from '@tanstack/react-router'
|
||||
import { Slider } from 'antd'
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useNavigate } from 'react-router'
|
||||
|
||||
import { SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
||||
|
||||
@ -76,7 +76,7 @@ const BasicSettings: FC = () => {
|
||||
cancelText: t('common.cancel'),
|
||||
centered: true,
|
||||
onOk: () => {
|
||||
navigate(`/settings/websearch/provider/${provider.id}`)
|
||||
navigate({ to: '/settings/websearch/provider/$providerId', params: { providerId: provider.id } })
|
||||
}
|
||||
})
|
||||
return
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||
import type { WebSearchProviderId } from '@renderer/types'
|
||||
import { useParams } from '@tanstack/react-router'
|
||||
import type { FC } from 'react'
|
||||
import { useParams } from 'react-router'
|
||||
|
||||
import { SettingContainer, SettingGroup } from '..'
|
||||
import WebSearchProviderSetting from './WebSearchProviderSetting'
|
||||
|
||||
const WebSearchProviderSettings: FC = () => {
|
||||
const { providerId } = useParams<{ providerId: string }>()
|
||||
const params = useParams({ strict: false }) as { providerId?: string }
|
||||
const providerId = params.providerId
|
||||
const { theme } = useTheme()
|
||||
|
||||
if (!providerId) {
|
||||
|
||||
@ -12,16 +12,13 @@ import Scrollbar from '@renderer/components/Scrollbar'
|
||||
import { useDefaultWebSearchProvider, useWebSearchProviders } from '@renderer/hooks/useWebSearchProviders'
|
||||
import type { WebSearchProviderId } from '@renderer/types'
|
||||
import { hasObjectKey } from '@renderer/utils'
|
||||
import { Outlet, useLocation, useNavigate } from '@tanstack/react-router'
|
||||
import { Flex, Tag } from 'antd'
|
||||
import { Search } from 'lucide-react'
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Navigate, Route, Routes, useLocation, useNavigate } from 'react-router'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import WebSearchGeneralSettings from './WebSearchGeneralSettings'
|
||||
import WebSearchProviderSettings from './WebSearchProviderSettings'
|
||||
|
||||
const WebSearchSettings: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const { providers } = useWebSearchProviders()
|
||||
@ -85,7 +82,7 @@ const WebSearchSettings: FC = () => {
|
||||
<ListItem
|
||||
title={t('settings.tool.websearch.title')}
|
||||
active={activeView === 'general'}
|
||||
onClick={() => navigate('/settings/websearch/general')}
|
||||
onClick={() => navigate({ to: '/settings/websearch/general' })}
|
||||
icon={<Search size={18} />}
|
||||
titleStyle={{ fontWeight: 500 }}
|
||||
/>
|
||||
@ -98,7 +95,9 @@ const WebSearchSettings: FC = () => {
|
||||
key={provider.id}
|
||||
title={provider.name}
|
||||
active={activeView === provider.id}
|
||||
onClick={() => navigate(`/settings/websearch/provider/${provider.id}`)}
|
||||
onClick={() =>
|
||||
navigate({ to: '/settings/websearch/provider/$providerId', params: { providerId: provider.id } })
|
||||
}
|
||||
icon={
|
||||
logo ? (
|
||||
<img src={logo} alt={provider.name} className="h-5 w-5 rounded object-contain" />
|
||||
@ -128,7 +127,9 @@ const WebSearchSettings: FC = () => {
|
||||
key={provider.id}
|
||||
title={provider.name}
|
||||
active={activeView === provider.id}
|
||||
onClick={() => navigate(`/settings/websearch/provider/${provider.id}`)}
|
||||
onClick={() =>
|
||||
navigate({ to: '/settings/websearch/provider/$providerId', params: { providerId: provider.id } })
|
||||
}
|
||||
icon={
|
||||
logo ? (
|
||||
<img src={logo} alt={provider.name} className="h-5 w-5 rounded object-contain" />
|
||||
@ -151,11 +152,7 @@ const WebSearchSettings: FC = () => {
|
||||
)}
|
||||
</MenuList>
|
||||
<RightContainer>
|
||||
<Routes>
|
||||
<Route index element={<Navigate to="general" replace />} />
|
||||
<Route path="general" element={<WebSearchGeneralSettings />} />
|
||||
<Route path="provider/:providerId" element={<WebSearchProviderSettings />} />
|
||||
</Routes>
|
||||
<Outlet />
|
||||
</RightContainer>
|
||||
</MainContainer>
|
||||
</Container>
|
||||
|
||||
981
src/renderer/src/routeTree.gen.ts
Normal file
981
src/renderer/src/routeTree.gen.ts
Normal file
@ -0,0 +1,981 @@
|
||||
/* eslint-disable */
|
||||
|
||||
// @ts-nocheck
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
|
||||
// This file was automatically generated by TanStack Router.
|
||||
// You should NOT make any changes in this file as it will be overwritten.
|
||||
// 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 SettingsRouteImport } from './routes/settings'
|
||||
import { Route as HomeRouteImport } from './routes/home'
|
||||
import { Route as AppRouteImport } from './routes/app'
|
||||
import { Route as IndexRouteImport } from './routes/index'
|
||||
import { Route as SettingsIndexRouteImport } from './routes/settings/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 AppTranslateRouteImport } from './routes/app/translate'
|
||||
import { Route as AppNotesRouteImport } from './routes/app/notes'
|
||||
import { Route as AppKnowledgeRouteImport } from './routes/app/knowledge'
|
||||
import { Route as AppFilesRouteImport } from './routes/app/files'
|
||||
import { Route as AppCodeRouteImport } from './routes/app/code'
|
||||
import { Route as AppChatRouteImport } from './routes/app/chat'
|
||||
import { Route as AppAssistantRouteImport } from './routes/app/assistant'
|
||||
import { Route as SettingsWebsearchIndexRouteImport } from './routes/settings/websearch/index'
|
||||
import { Route as SettingsMcpIndexRouteImport } from './routes/settings/mcp/index'
|
||||
import { Route as AppPaintingsIndexRouteImport } from './routes/app/paintings/index'
|
||||
import { Route as AppMinappIndexRouteImport } from './routes/app/minapp/index'
|
||||
import { Route as SettingsWebsearchGeneralRouteImport } from './routes/settings/websearch/general'
|
||||
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 AppPaintingsSplatRouteImport } from './routes/app/paintings/$'
|
||||
import { Route as AppMinappAppIdRouteImport } from './routes/app/minapp/$appId'
|
||||
import { Route as SettingsWebsearchProviderProviderIdRouteImport } from './routes/settings/websearch/provider.$providerId'
|
||||
import { Route as SettingsMcpSettingsServerIdRouteImport } from './routes/settings/mcp/settings.$serverId'
|
||||
|
||||
const SettingsRoute = SettingsRouteImport.update({
|
||||
id: '/settings',
|
||||
path: '/settings',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const HomeRoute = HomeRouteImport.update({
|
||||
id: '/home',
|
||||
path: '/home',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const AppRoute = AppRouteImport.update({
|
||||
id: '/app',
|
||||
path: '/app',
|
||||
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 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 AppTranslateRoute = AppTranslateRouteImport.update({
|
||||
id: '/translate',
|
||||
path: '/translate',
|
||||
getParentRoute: () => AppRoute,
|
||||
} as any)
|
||||
const AppNotesRoute = AppNotesRouteImport.update({
|
||||
id: '/notes',
|
||||
path: '/notes',
|
||||
getParentRoute: () => AppRoute,
|
||||
} as any)
|
||||
const AppKnowledgeRoute = AppKnowledgeRouteImport.update({
|
||||
id: '/knowledge',
|
||||
path: '/knowledge',
|
||||
getParentRoute: () => AppRoute,
|
||||
} as any)
|
||||
const AppFilesRoute = AppFilesRouteImport.update({
|
||||
id: '/files',
|
||||
path: '/files',
|
||||
getParentRoute: () => AppRoute,
|
||||
} as any)
|
||||
const AppCodeRoute = AppCodeRouteImport.update({
|
||||
id: '/code',
|
||||
path: '/code',
|
||||
getParentRoute: () => AppRoute,
|
||||
} as any)
|
||||
const AppChatRoute = AppChatRouteImport.update({
|
||||
id: '/chat',
|
||||
path: '/chat',
|
||||
getParentRoute: () => AppRoute,
|
||||
} as any)
|
||||
const AppAssistantRoute = AppAssistantRouteImport.update({
|
||||
id: '/assistant',
|
||||
path: '/assistant',
|
||||
getParentRoute: () => AppRoute,
|
||||
} as any)
|
||||
const SettingsWebsearchIndexRoute = SettingsWebsearchIndexRouteImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
getParentRoute: () => SettingsWebsearchRoute,
|
||||
} as any)
|
||||
const SettingsMcpIndexRoute = SettingsMcpIndexRouteImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
getParentRoute: () => SettingsMcpRoute,
|
||||
} as any)
|
||||
const AppPaintingsIndexRoute = AppPaintingsIndexRouteImport.update({
|
||||
id: '/paintings/',
|
||||
path: '/paintings/',
|
||||
getParentRoute: () => AppRoute,
|
||||
} as any)
|
||||
const AppMinappIndexRoute = AppMinappIndexRouteImport.update({
|
||||
id: '/minapp/',
|
||||
path: '/minapp/',
|
||||
getParentRoute: () => AppRoute,
|
||||
} as any)
|
||||
const SettingsWebsearchGeneralRoute =
|
||||
SettingsWebsearchGeneralRouteImport.update({
|
||||
id: '/general',
|
||||
path: '/general',
|
||||
getParentRoute: () => SettingsWebsearchRoute,
|
||||
} 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 AppPaintingsSplatRoute = AppPaintingsSplatRouteImport.update({
|
||||
id: '/paintings/$',
|
||||
path: '/paintings/$',
|
||||
getParentRoute: () => AppRoute,
|
||||
} as any)
|
||||
const AppMinappAppIdRoute = AppMinappAppIdRouteImport.update({
|
||||
id: '/minapp/$appId',
|
||||
path: '/minapp/$appId',
|
||||
getParentRoute: () => AppRoute,
|
||||
} as any)
|
||||
const SettingsWebsearchProviderProviderIdRoute =
|
||||
SettingsWebsearchProviderProviderIdRouteImport.update({
|
||||
id: '/provider/$providerId',
|
||||
path: '/provider/$providerId',
|
||||
getParentRoute: () => SettingsWebsearchRoute,
|
||||
} as any)
|
||||
const SettingsMcpSettingsServerIdRoute =
|
||||
SettingsMcpSettingsServerIdRouteImport.update({
|
||||
id: '/settings/$serverId',
|
||||
path: '/settings/$serverId',
|
||||
getParentRoute: () => SettingsMcpRoute,
|
||||
} as any)
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'/app': typeof AppRouteWithChildren
|
||||
'/home': typeof HomeRoute
|
||||
'/settings': typeof SettingsRouteWithChildren
|
||||
'/app/assistant': typeof AppAssistantRoute
|
||||
'/app/chat': typeof AppChatRoute
|
||||
'/app/code': typeof AppCodeRoute
|
||||
'/app/files': typeof AppFilesRoute
|
||||
'/app/knowledge': typeof AppKnowledgeRoute
|
||||
'/app/notes': typeof AppNotesRoute
|
||||
'/app/translate': typeof AppTranslateRoute
|
||||
'/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 SettingsWebsearchRouteWithChildren
|
||||
'/settings/': typeof SettingsIndexRoute
|
||||
'/app/minapp/$appId': typeof AppMinappAppIdRoute
|
||||
'/app/paintings/$': typeof AppPaintingsSplatRoute
|
||||
'/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/websearch/general': typeof SettingsWebsearchGeneralRoute
|
||||
'/app/minapp': typeof AppMinappIndexRoute
|
||||
'/app/paintings': typeof AppPaintingsIndexRoute
|
||||
'/settings/mcp/': typeof SettingsMcpIndexRoute
|
||||
'/settings/websearch/': typeof SettingsWebsearchIndexRoute
|
||||
'/settings/mcp/settings/$serverId': typeof SettingsMcpSettingsServerIdRoute
|
||||
'/settings/websearch/provider/$providerId': typeof SettingsWebsearchProviderProviderIdRoute
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/app': typeof AppRouteWithChildren
|
||||
'/home': typeof HomeRoute
|
||||
'/app/assistant': typeof AppAssistantRoute
|
||||
'/app/chat': typeof AppChatRoute
|
||||
'/app/code': typeof AppCodeRoute
|
||||
'/app/files': typeof AppFilesRoute
|
||||
'/app/knowledge': typeof AppKnowledgeRoute
|
||||
'/app/notes': typeof AppNotesRoute
|
||||
'/app/translate': typeof AppTranslateRoute
|
||||
'/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': typeof SettingsIndexRoute
|
||||
'/app/minapp/$appId': typeof AppMinappAppIdRoute
|
||||
'/app/paintings/$': typeof AppPaintingsSplatRoute
|
||||
'/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/websearch/general': typeof SettingsWebsearchGeneralRoute
|
||||
'/app/minapp': typeof AppMinappIndexRoute
|
||||
'/app/paintings': typeof AppPaintingsIndexRoute
|
||||
'/settings/mcp': typeof SettingsMcpIndexRoute
|
||||
'/settings/websearch': typeof SettingsWebsearchIndexRoute
|
||||
'/settings/mcp/settings/$serverId': typeof SettingsMcpSettingsServerIdRoute
|
||||
'/settings/websearch/provider/$providerId': typeof SettingsWebsearchProviderProviderIdRoute
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
'/': typeof IndexRoute
|
||||
'/app': typeof AppRouteWithChildren
|
||||
'/home': typeof HomeRoute
|
||||
'/settings': typeof SettingsRouteWithChildren
|
||||
'/app/assistant': typeof AppAssistantRoute
|
||||
'/app/chat': typeof AppChatRoute
|
||||
'/app/code': typeof AppCodeRoute
|
||||
'/app/files': typeof AppFilesRoute
|
||||
'/app/knowledge': typeof AppKnowledgeRoute
|
||||
'/app/notes': typeof AppNotesRoute
|
||||
'/app/translate': typeof AppTranslateRoute
|
||||
'/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 SettingsWebsearchRouteWithChildren
|
||||
'/settings/': typeof SettingsIndexRoute
|
||||
'/app/minapp/$appId': typeof AppMinappAppIdRoute
|
||||
'/app/paintings/$': typeof AppPaintingsSplatRoute
|
||||
'/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/websearch/general': typeof SettingsWebsearchGeneralRoute
|
||||
'/app/minapp/': typeof AppMinappIndexRoute
|
||||
'/app/paintings/': typeof AppPaintingsIndexRoute
|
||||
'/settings/mcp/': typeof SettingsMcpIndexRoute
|
||||
'/settings/websearch/': typeof SettingsWebsearchIndexRoute
|
||||
'/settings/mcp/settings/$serverId': typeof SettingsMcpSettingsServerIdRoute
|
||||
'/settings/websearch/provider/$providerId': typeof SettingsWebsearchProviderProviderIdRoute
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fullPaths:
|
||||
| '/'
|
||||
| '/app'
|
||||
| '/home'
|
||||
| '/settings'
|
||||
| '/app/assistant'
|
||||
| '/app/chat'
|
||||
| '/app/code'
|
||||
| '/app/files'
|
||||
| '/app/knowledge'
|
||||
| '/app/notes'
|
||||
| '/app/translate'
|
||||
| '/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'
|
||||
| '/settings/'
|
||||
| '/app/minapp/$appId'
|
||||
| '/app/paintings/$'
|
||||
| '/settings/mcp/$'
|
||||
| '/settings/mcp/builtin'
|
||||
| '/settings/mcp/marketplaces'
|
||||
| '/settings/mcp/mcp-install'
|
||||
| '/settings/mcp/npx-search'
|
||||
| '/settings/mcp/servers'
|
||||
| '/settings/websearch/general'
|
||||
| '/app/minapp'
|
||||
| '/app/paintings'
|
||||
| '/settings/mcp/'
|
||||
| '/settings/websearch/'
|
||||
| '/settings/mcp/settings/$serverId'
|
||||
| '/settings/websearch/provider/$providerId'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to:
|
||||
| '/'
|
||||
| '/app'
|
||||
| '/home'
|
||||
| '/app/assistant'
|
||||
| '/app/chat'
|
||||
| '/app/code'
|
||||
| '/app/files'
|
||||
| '/app/knowledge'
|
||||
| '/app/notes'
|
||||
| '/app/translate'
|
||||
| '/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'
|
||||
| '/app/minapp/$appId'
|
||||
| '/app/paintings/$'
|
||||
| '/settings/mcp/$'
|
||||
| '/settings/mcp/builtin'
|
||||
| '/settings/mcp/marketplaces'
|
||||
| '/settings/mcp/mcp-install'
|
||||
| '/settings/mcp/npx-search'
|
||||
| '/settings/mcp/servers'
|
||||
| '/settings/websearch/general'
|
||||
| '/app/minapp'
|
||||
| '/app/paintings'
|
||||
| '/settings/mcp'
|
||||
| '/settings/websearch'
|
||||
| '/settings/mcp/settings/$serverId'
|
||||
| '/settings/websearch/provider/$providerId'
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
| '/app'
|
||||
| '/home'
|
||||
| '/settings'
|
||||
| '/app/assistant'
|
||||
| '/app/chat'
|
||||
| '/app/code'
|
||||
| '/app/files'
|
||||
| '/app/knowledge'
|
||||
| '/app/notes'
|
||||
| '/app/translate'
|
||||
| '/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'
|
||||
| '/settings/'
|
||||
| '/app/minapp/$appId'
|
||||
| '/app/paintings/$'
|
||||
| '/settings/mcp/$'
|
||||
| '/settings/mcp/builtin'
|
||||
| '/settings/mcp/marketplaces'
|
||||
| '/settings/mcp/mcp-install'
|
||||
| '/settings/mcp/npx-search'
|
||||
| '/settings/mcp/servers'
|
||||
| '/settings/websearch/general'
|
||||
| '/app/minapp/'
|
||||
| '/app/paintings/'
|
||||
| '/settings/mcp/'
|
||||
| '/settings/websearch/'
|
||||
| '/settings/mcp/settings/$serverId'
|
||||
| '/settings/websearch/provider/$providerId'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
AppRoute: typeof AppRouteWithChildren
|
||||
HomeRoute: typeof HomeRoute
|
||||
SettingsRoute: typeof SettingsRouteWithChildren
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface FileRoutesByPath {
|
||||
'/settings': {
|
||||
id: '/settings'
|
||||
path: '/settings'
|
||||
fullPath: '/settings'
|
||||
preLoaderRoute: typeof SettingsRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/home': {
|
||||
id: '/home'
|
||||
path: '/home'
|
||||
fullPath: '/home'
|
||||
preLoaderRoute: typeof HomeRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/app': {
|
||||
id: '/app'
|
||||
path: '/app'
|
||||
fullPath: '/app'
|
||||
preLoaderRoute: typeof AppRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/': {
|
||||
id: '/'
|
||||
path: '/'
|
||||
fullPath: '/'
|
||||
preLoaderRoute: typeof IndexRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/settings/': {
|
||||
id: '/settings/'
|
||||
path: '/'
|
||||
fullPath: '/settings/'
|
||||
preLoaderRoute: typeof SettingsIndexRouteImport
|
||||
parentRoute: typeof SettingsRoute
|
||||
}
|
||||
'/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
|
||||
}
|
||||
'/app/translate': {
|
||||
id: '/app/translate'
|
||||
path: '/translate'
|
||||
fullPath: '/app/translate'
|
||||
preLoaderRoute: typeof AppTranslateRouteImport
|
||||
parentRoute: typeof AppRoute
|
||||
}
|
||||
'/app/notes': {
|
||||
id: '/app/notes'
|
||||
path: '/notes'
|
||||
fullPath: '/app/notes'
|
||||
preLoaderRoute: typeof AppNotesRouteImport
|
||||
parentRoute: typeof AppRoute
|
||||
}
|
||||
'/app/knowledge': {
|
||||
id: '/app/knowledge'
|
||||
path: '/knowledge'
|
||||
fullPath: '/app/knowledge'
|
||||
preLoaderRoute: typeof AppKnowledgeRouteImport
|
||||
parentRoute: typeof AppRoute
|
||||
}
|
||||
'/app/files': {
|
||||
id: '/app/files'
|
||||
path: '/files'
|
||||
fullPath: '/app/files'
|
||||
preLoaderRoute: typeof AppFilesRouteImport
|
||||
parentRoute: typeof AppRoute
|
||||
}
|
||||
'/app/code': {
|
||||
id: '/app/code'
|
||||
path: '/code'
|
||||
fullPath: '/app/code'
|
||||
preLoaderRoute: typeof AppCodeRouteImport
|
||||
parentRoute: typeof AppRoute
|
||||
}
|
||||
'/app/chat': {
|
||||
id: '/app/chat'
|
||||
path: '/chat'
|
||||
fullPath: '/app/chat'
|
||||
preLoaderRoute: typeof AppChatRouteImport
|
||||
parentRoute: typeof AppRoute
|
||||
}
|
||||
'/app/assistant': {
|
||||
id: '/app/assistant'
|
||||
path: '/assistant'
|
||||
fullPath: '/app/assistant'
|
||||
preLoaderRoute: typeof AppAssistantRouteImport
|
||||
parentRoute: typeof AppRoute
|
||||
}
|
||||
'/settings/websearch/': {
|
||||
id: '/settings/websearch/'
|
||||
path: '/'
|
||||
fullPath: '/settings/websearch/'
|
||||
preLoaderRoute: typeof SettingsWebsearchIndexRouteImport
|
||||
parentRoute: typeof SettingsWebsearchRoute
|
||||
}
|
||||
'/settings/mcp/': {
|
||||
id: '/settings/mcp/'
|
||||
path: '/'
|
||||
fullPath: '/settings/mcp/'
|
||||
preLoaderRoute: typeof SettingsMcpIndexRouteImport
|
||||
parentRoute: typeof SettingsMcpRoute
|
||||
}
|
||||
'/app/paintings/': {
|
||||
id: '/app/paintings/'
|
||||
path: '/paintings'
|
||||
fullPath: '/app/paintings'
|
||||
preLoaderRoute: typeof AppPaintingsIndexRouteImport
|
||||
parentRoute: typeof AppRoute
|
||||
}
|
||||
'/app/minapp/': {
|
||||
id: '/app/minapp/'
|
||||
path: '/minapp'
|
||||
fullPath: '/app/minapp'
|
||||
preLoaderRoute: typeof AppMinappIndexRouteImport
|
||||
parentRoute: typeof AppRoute
|
||||
}
|
||||
'/settings/websearch/general': {
|
||||
id: '/settings/websearch/general'
|
||||
path: '/general'
|
||||
fullPath: '/settings/websearch/general'
|
||||
preLoaderRoute: typeof SettingsWebsearchGeneralRouteImport
|
||||
parentRoute: typeof SettingsWebsearchRoute
|
||||
}
|
||||
'/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
|
||||
}
|
||||
'/app/paintings/$': {
|
||||
id: '/app/paintings/$'
|
||||
path: '/paintings/$'
|
||||
fullPath: '/app/paintings/$'
|
||||
preLoaderRoute: typeof AppPaintingsSplatRouteImport
|
||||
parentRoute: typeof AppRoute
|
||||
}
|
||||
'/app/minapp/$appId': {
|
||||
id: '/app/minapp/$appId'
|
||||
path: '/minapp/$appId'
|
||||
fullPath: '/app/minapp/$appId'
|
||||
preLoaderRoute: typeof AppMinappAppIdRouteImport
|
||||
parentRoute: typeof AppRoute
|
||||
}
|
||||
'/settings/websearch/provider/$providerId': {
|
||||
id: '/settings/websearch/provider/$providerId'
|
||||
path: '/provider/$providerId'
|
||||
fullPath: '/settings/websearch/provider/$providerId'
|
||||
preLoaderRoute: typeof SettingsWebsearchProviderProviderIdRouteImport
|
||||
parentRoute: typeof SettingsWebsearchRoute
|
||||
}
|
||||
'/settings/mcp/settings/$serverId': {
|
||||
id: '/settings/mcp/settings/$serverId'
|
||||
path: '/settings/$serverId'
|
||||
fullPath: '/settings/mcp/settings/$serverId'
|
||||
preLoaderRoute: typeof SettingsMcpSettingsServerIdRouteImport
|
||||
parentRoute: typeof SettingsMcpRoute
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface AppRouteChildren {
|
||||
AppAssistantRoute: typeof AppAssistantRoute
|
||||
AppChatRoute: typeof AppChatRoute
|
||||
AppCodeRoute: typeof AppCodeRoute
|
||||
AppFilesRoute: typeof AppFilesRoute
|
||||
AppKnowledgeRoute: typeof AppKnowledgeRoute
|
||||
AppNotesRoute: typeof AppNotesRoute
|
||||
AppTranslateRoute: typeof AppTranslateRoute
|
||||
AppMinappAppIdRoute: typeof AppMinappAppIdRoute
|
||||
AppPaintingsSplatRoute: typeof AppPaintingsSplatRoute
|
||||
AppMinappIndexRoute: typeof AppMinappIndexRoute
|
||||
AppPaintingsIndexRoute: typeof AppPaintingsIndexRoute
|
||||
}
|
||||
|
||||
const AppRouteChildren: AppRouteChildren = {
|
||||
AppAssistantRoute: AppAssistantRoute,
|
||||
AppChatRoute: AppChatRoute,
|
||||
AppCodeRoute: AppCodeRoute,
|
||||
AppFilesRoute: AppFilesRoute,
|
||||
AppKnowledgeRoute: AppKnowledgeRoute,
|
||||
AppNotesRoute: AppNotesRoute,
|
||||
AppTranslateRoute: AppTranslateRoute,
|
||||
AppMinappAppIdRoute: AppMinappAppIdRoute,
|
||||
AppPaintingsSplatRoute: AppPaintingsSplatRoute,
|
||||
AppMinappIndexRoute: AppMinappIndexRoute,
|
||||
AppPaintingsIndexRoute: AppPaintingsIndexRoute,
|
||||
}
|
||||
|
||||
const AppRouteWithChildren = AppRoute._addFileChildren(AppRouteChildren)
|
||||
|
||||
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 SettingsWebsearchRouteChildren {
|
||||
SettingsWebsearchGeneralRoute: typeof SettingsWebsearchGeneralRoute
|
||||
SettingsWebsearchIndexRoute: typeof SettingsWebsearchIndexRoute
|
||||
SettingsWebsearchProviderProviderIdRoute: typeof SettingsWebsearchProviderProviderIdRoute
|
||||
}
|
||||
|
||||
const SettingsWebsearchRouteChildren: SettingsWebsearchRouteChildren = {
|
||||
SettingsWebsearchGeneralRoute: SettingsWebsearchGeneralRoute,
|
||||
SettingsWebsearchIndexRoute: SettingsWebsearchIndexRoute,
|
||||
SettingsWebsearchProviderProviderIdRoute:
|
||||
SettingsWebsearchProviderProviderIdRoute,
|
||||
}
|
||||
|
||||
const SettingsWebsearchRouteWithChildren =
|
||||
SettingsWebsearchRoute._addFileChildren(SettingsWebsearchRouteChildren)
|
||||
|
||||
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 SettingsWebsearchRouteWithChildren
|
||||
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: SettingsWebsearchRouteWithChildren,
|
||||
SettingsIndexRoute: SettingsIndexRoute,
|
||||
}
|
||||
|
||||
const SettingsRouteWithChildren = SettingsRoute._addFileChildren(
|
||||
SettingsRouteChildren,
|
||||
)
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
AppRoute: AppRouteWithChildren,
|
||||
HomeRoute: HomeRoute,
|
||||
SettingsRoute: SettingsRouteWithChildren,
|
||||
}
|
||||
export const routeTree = rootRouteImport
|
||||
._addFileChildren(rootRouteChildren)
|
||||
._addFileTypes<FileRouteTypes>()
|
||||
194
src/renderer/src/routes/README.md
Normal file
194
src/renderer/src/routes/README.md
Normal file
@ -0,0 +1,194 @@
|
||||
# Routing System Developer Guide
|
||||
|
||||
This project uses **TanStack Router + Multi MemoryRouter** architecture, where each Tab has its own independent router instance, enabling native KeepAlive behavior.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Adding a New Page
|
||||
|
||||
Create a file in the `src/renderer/src/routes/` directory:
|
||||
|
||||
```typescript
|
||||
// routes/knowledge.tsx
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/knowledge')({
|
||||
component: KnowledgePage
|
||||
})
|
||||
|
||||
function KnowledgePage() {
|
||||
return <div>Knowledge Page</div>
|
||||
}
|
||||
```
|
||||
|
||||
After running `yarn dev`, TanStack Router will automatically update `routeTree.gen.ts`.
|
||||
|
||||
### 2. Routes with Parameters
|
||||
|
||||
```typescript
|
||||
// routes/chat/$topicId.tsx
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/chat/$topicId')({
|
||||
component: ChatPage
|
||||
})
|
||||
|
||||
function ChatPage() {
|
||||
const { topicId } = Route.useParams()
|
||||
return <div>Chat: {topicId}</div>
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Nested Routes
|
||||
|
||||
```text
|
||||
routes/
|
||||
├── settings.tsx # /settings (layout)
|
||||
├── settings/
|
||||
│ ├── general.tsx # /settings/general
|
||||
│ └── provider.tsx # /settings/provider
|
||||
```
|
||||
|
||||
```typescript
|
||||
// routes/settings.tsx
|
||||
import { createFileRoute, Outlet } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/settings')({
|
||||
component: SettingsLayout
|
||||
})
|
||||
|
||||
function SettingsLayout() {
|
||||
return (
|
||||
<div className="flex">
|
||||
<aside>Settings Menu</aside>
|
||||
<main><Outlet /></main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Navigation API
|
||||
|
||||
This project provides two navigation methods:
|
||||
|
||||
### 1. Tab-Level Navigation - `openTab`
|
||||
|
||||
Open a new Tab or switch to an existing Tab using the `useTabs` hook:
|
||||
|
||||
```typescript
|
||||
import { useTabs } from '@renderer/hooks/useTabs'
|
||||
|
||||
function MyComponent() {
|
||||
const { openTab, closeTab } = useTabs()
|
||||
|
||||
// Basic usage - reuse existing Tab or create new one
|
||||
openTab('/settings')
|
||||
|
||||
// With title
|
||||
openTab('/chat/123', { title: 'Chat with Alice' })
|
||||
|
||||
// Force new Tab (even if same URL exists)
|
||||
openTab('/settings', { forceNew: true })
|
||||
|
||||
// Open Webview Tab
|
||||
openTab('https://example.com', {
|
||||
type: 'webview',
|
||||
title: 'Example Site'
|
||||
})
|
||||
|
||||
// Close Tab
|
||||
closeTab(tabId)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. In-Tab Navigation - `useNavigate`
|
||||
|
||||
Navigate within the same Tab (won't create a new Tab) using TanStack Router's `useNavigate`:
|
||||
|
||||
```typescript
|
||||
import { useNavigate } from '@tanstack/react-router'
|
||||
|
||||
function SettingsPage() {
|
||||
const navigate = useNavigate()
|
||||
|
||||
// Navigate to sub-page within current Tab
|
||||
navigate({ to: '/settings/provider' })
|
||||
|
||||
// Navigate with parameters
|
||||
navigate({ to: '/chat/$topicId', params: { topicId: '123' } })
|
||||
}
|
||||
```
|
||||
|
||||
### Comparison
|
||||
|
||||
| Scenario | Method | Result |
|
||||
|----------|--------|--------|
|
||||
| Open new feature module | `openTab('/knowledge')` | Creates new Tab |
|
||||
| Switch sub-page in settings | `navigate({ to: '/settings/provider' })` | Navigates within current Tab |
|
||||
| Open detail from list | `openTab('/chat/123', { title: '...' })` | Creates new Tab |
|
||||
| Go back to previous page | `navigate({ to: '..' })` | Goes back within current Tab |
|
||||
|
||||
### API Reference
|
||||
|
||||
#### `useTabs()` Return Value
|
||||
|
||||
| Property/Method | Type | Description |
|
||||
|-----------------|------|-------------|
|
||||
| `tabs` | `Tab[]` | List of all Tabs |
|
||||
| `activeTabId` | `string` | Currently active Tab ID |
|
||||
| `activeTab` | `Tab \| undefined` | Currently active Tab object |
|
||||
| `openTab(url, options?)` | `(url: string, options?: OpenTabOptions) => string` | Open Tab, returns Tab ID |
|
||||
| `closeTab(id)` | `(id: string) => void` | Close specified Tab |
|
||||
| `setActiveTab(id)` | `(id: string) => void` | Switch to specified Tab |
|
||||
| `updateTab(id, updates)` | `(id: string, updates: Partial<Tab>) => void` | Update Tab properties |
|
||||
|
||||
#### `OpenTabOptions`
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `forceNew` | `boolean` | `false` | Force create new Tab |
|
||||
| `title` | `string` | URL path | Tab title |
|
||||
| `type` | `'route' \| 'webview'` | `'route'` | Tab type |
|
||||
| `id` | `string` | Auto-generated | Custom Tab ID |
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```text
|
||||
AppShell
|
||||
├── Sidebar
|
||||
├── TabBar
|
||||
└── Content Area
|
||||
├── TabRouter #1 (Home)
|
||||
│ └── Activity(visible) → MemoryRouter → RouterProvider
|
||||
├── TabRouter #2 (Settings)
|
||||
│ └── Activity(hidden) → MemoryRouter → RouterProvider
|
||||
└── WebviewContainer (for webview tabs)
|
||||
```
|
||||
|
||||
- Each Tab has its own independent `MemoryRouter` instance
|
||||
- Uses React 19 `<Activity>` component to control visibility
|
||||
- Components are not unmounted on Tab switch, state is fully preserved (KeepAlive)
|
||||
|
||||
## File Structure
|
||||
|
||||
```text
|
||||
src/renderer/src/
|
||||
├── routes/ # Route pages (TanStack Router file-based routing)
|
||||
│ ├── __root.tsx # Root route (renders Outlet)
|
||||
│ ├── index.tsx # / Home page
|
||||
│ ├── settings.tsx # /settings
|
||||
│ └── README.md # This document
|
||||
├── components/layout/
|
||||
│ ├── AppShell.tsx # Main layout (Sidebar + TabBar + Content)
|
||||
│ └── TabRouter.tsx # Tab router container (MemoryRouter + Activity)
|
||||
├── hooks/
|
||||
│ └── useTabs.ts # Tab state management hook
|
||||
└── routeTree.gen.ts # Auto-generated route tree (do not edit manually)
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. **Do not manually edit `routeTree.gen.ts`** - It is automatically generated by TanStack Router
|
||||
2. **File name determines route path** - `routes/settings.tsx` → `/settings`
|
||||
3. **Dynamic parameters use `$`** - `routes/chat/$topicId.tsx` → `/chat/:topicId`
|
||||
4. **Page state is automatically preserved** - Tab switching won't lose `useState`, scroll position, etc.
|
||||
194
src/renderer/src/routes/README.zh-CN.md
Normal file
194
src/renderer/src/routes/README.zh-CN.md
Normal file
@ -0,0 +1,194 @@
|
||||
# 路由系统开发指南
|
||||
|
||||
本项目使用 **TanStack Router + Multi MemoryRouter** 架构,每个 Tab 拥有独立的路由实例,实现原生 KeepAlive。
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 添加新页面
|
||||
|
||||
在 `src/renderer/src/routes/` 目录下创建文件:
|
||||
|
||||
```typescript
|
||||
// routes/knowledge.tsx
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/knowledge')({
|
||||
component: KnowledgePage
|
||||
})
|
||||
|
||||
function KnowledgePage() {
|
||||
return <div>Knowledge Page</div>
|
||||
}
|
||||
```
|
||||
|
||||
运行 `yarn dev` 后,TanStack Router 会自动更新 `routeTree.gen.ts`。
|
||||
|
||||
### 2. 带参数的路由
|
||||
|
||||
```typescript
|
||||
// routes/chat/$topicId.tsx
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/chat/$topicId')({
|
||||
component: ChatPage
|
||||
})
|
||||
|
||||
function ChatPage() {
|
||||
const { topicId } = Route.useParams()
|
||||
return <div>Chat: {topicId}</div>
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 嵌套路由
|
||||
|
||||
```text
|
||||
routes/
|
||||
├── settings.tsx # /settings (布局)
|
||||
├── settings/
|
||||
│ ├── general.tsx # /settings/general
|
||||
│ └── provider.tsx # /settings/provider
|
||||
```
|
||||
|
||||
```typescript
|
||||
// routes/settings.tsx
|
||||
import { createFileRoute, Outlet } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/settings')({
|
||||
component: SettingsLayout
|
||||
})
|
||||
|
||||
function SettingsLayout() {
|
||||
return (
|
||||
<div className="flex">
|
||||
<aside>Settings Menu</aside>
|
||||
<main><Outlet /></main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## 导航 API
|
||||
|
||||
本项目有两种导航方式:
|
||||
|
||||
### 1. Tab 级别导航 - `openTab`
|
||||
|
||||
打开新 Tab 或切换到已有 Tab,使用 `useTabs` hook:
|
||||
|
||||
```typescript
|
||||
import { useTabs } from '@renderer/hooks/useTabs'
|
||||
|
||||
function MyComponent() {
|
||||
const { openTab, closeTab } = useTabs()
|
||||
|
||||
// 基础用法 - 复用已有 Tab 或新建
|
||||
openTab('/settings')
|
||||
|
||||
// 带标题
|
||||
openTab('/chat/123', { title: 'Chat with Alice' })
|
||||
|
||||
// 强制新开 Tab(即使已有相同 URL)
|
||||
openTab('/settings', { forceNew: true })
|
||||
|
||||
// 打开 Webview Tab
|
||||
openTab('https://example.com', {
|
||||
type: 'webview',
|
||||
title: 'Example Site'
|
||||
})
|
||||
|
||||
// 关闭 Tab
|
||||
closeTab(tabId)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Tab 内部导航 - `useNavigate`
|
||||
|
||||
在同一个 Tab 内跳转路由(不会新开 Tab),使用 TanStack Router 的 `useNavigate`:
|
||||
|
||||
```typescript
|
||||
import { useNavigate } from '@tanstack/react-router'
|
||||
|
||||
function SettingsPage() {
|
||||
const navigate = useNavigate()
|
||||
|
||||
// 在当前 Tab 内跳转到子页面
|
||||
navigate({ to: '/settings/provider' })
|
||||
|
||||
// 带参数跳转
|
||||
navigate({ to: '/chat/$topicId', params: { topicId: '123' } })
|
||||
}
|
||||
```
|
||||
|
||||
### 两者区别
|
||||
|
||||
| 场景 | 使用 | 效果 |
|
||||
|-----|------|------|
|
||||
| 打开新功能模块 | `openTab('/knowledge')` | 新建 Tab |
|
||||
| 设置页内切换子页 | `navigate({ to: '/settings/provider' })` | 当前 Tab 内跳转 |
|
||||
| 从列表打开详情 | `openTab('/chat/123', { title: '...' })` | 新建 Tab |
|
||||
| 返回上一页 | `navigate({ to: '..' })` | 当前 Tab 内返回 |
|
||||
|
||||
### API 参考
|
||||
|
||||
#### `useTabs()` 返回值
|
||||
|
||||
| 属性/方法 | 类型 | 说明 |
|
||||
|----------|------|------|
|
||||
| `tabs` | `Tab[]` | 所有 Tab 列表 |
|
||||
| `activeTabId` | `string` | 当前激活的 Tab ID |
|
||||
| `activeTab` | `Tab \| undefined` | 当前激活的 Tab 对象 |
|
||||
| `openTab(url, options?)` | `(url: string, options?: OpenTabOptions) => string` | 打开 Tab,返回 Tab ID |
|
||||
| `closeTab(id)` | `(id: string) => void` | 关闭指定 Tab |
|
||||
| `setActiveTab(id)` | `(id: string) => void` | 切换到指定 Tab |
|
||||
| `updateTab(id, updates)` | `(id: string, updates: Partial<Tab>) => void` | 更新 Tab 属性 |
|
||||
|
||||
#### `OpenTabOptions`
|
||||
|
||||
| 选项 | 类型 | 默认值 | 说明 |
|
||||
|-----|------|-------|------|
|
||||
| `forceNew` | `boolean` | `false` | 强制新开 Tab |
|
||||
| `title` | `string` | URL 路径 | Tab 标题 |
|
||||
| `type` | `'route' \| 'webview'` | `'route'` | Tab 类型 |
|
||||
| `id` | `string` | 自动生成 | 自定义 Tab ID |
|
||||
|
||||
## 架构说明
|
||||
|
||||
```text
|
||||
AppShell
|
||||
├── Sidebar
|
||||
├── TabBar
|
||||
└── Content Area
|
||||
├── TabRouter #1 (Home)
|
||||
│ └── Activity(visible) → MemoryRouter → RouterProvider
|
||||
├── TabRouter #2 (Settings)
|
||||
│ └── Activity(hidden) → MemoryRouter → RouterProvider
|
||||
└── WebviewContainer (for webview tabs)
|
||||
```
|
||||
|
||||
- 每个 Tab 拥有独立的 `MemoryRouter` 实例
|
||||
- 使用 React 19 `<Activity>` 组件控制可见性
|
||||
- Tab 切换时组件不卸载,状态完全保持(KeepAlive)
|
||||
|
||||
## 文件结构
|
||||
|
||||
```text
|
||||
src/renderer/src/
|
||||
├── routes/ # 路由页面(TanStack Router 文件路由)
|
||||
│ ├── __root.tsx # 根路由(渲染 Outlet)
|
||||
│ ├── index.tsx # / 首页
|
||||
│ ├── settings.tsx # /settings
|
||||
│ └── README.md # 本文档
|
||||
├── components/layout/
|
||||
│ ├── AppShell.tsx # 主布局(Sidebar + TabBar + Content)
|
||||
│ └── TabRouter.tsx # Tab 路由容器(MemoryRouter + Activity)
|
||||
├── hooks/
|
||||
│ └── useTabs.ts # Tab 状态管理 Hook
|
||||
└── routeTree.gen.ts # 自动生成的路由树(勿手动编辑)
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **不要手动编辑 `routeTree.gen.ts`** - 它由 TanStack Router 自动生成
|
||||
2. **路由文件命名即路径** - `routes/settings.tsx` → `/settings`
|
||||
3. **动态参数使用 `$`** - `routes/chat/$topicId.tsx` → `/chat/:topicId`
|
||||
4. **页面状态自动保持** - Tab 切换不会丢失 `useState`、滚动位置等
|
||||
11
src/renderer/src/routes/__root.tsx
Normal file
11
src/renderer/src/routes/__root.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import NavigationHandler from '@renderer/handler/NavigationHandler'
|
||||
import { createRootRoute, Outlet } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createRootRoute({
|
||||
component: () => (
|
||||
<>
|
||||
<NavigationHandler />
|
||||
<Outlet />
|
||||
</>
|
||||
)
|
||||
})
|
||||
5
src/renderer/src/routes/app.tsx
Normal file
5
src/renderer/src/routes/app.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { createFileRoute, Outlet } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/app')({
|
||||
component: () => <Outlet />
|
||||
})
|
||||
6
src/renderer/src/routes/app/assistant.tsx
Normal file
6
src/renderer/src/routes/app/assistant.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import AssistantPresetsPage from '@renderer/pages/store/assistants/presets/AssistantPresetsPage'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/app/assistant')({
|
||||
component: AssistantPresetsPage
|
||||
})
|
||||
6
src/renderer/src/routes/app/chat.tsx
Normal file
6
src/renderer/src/routes/app/chat.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import HomePage from '@renderer/pages/home/HomePage'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/app/chat')({
|
||||
component: HomePage
|
||||
})
|
||||
6
src/renderer/src/routes/app/code.tsx
Normal file
6
src/renderer/src/routes/app/code.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import CodeToolsPage from '@renderer/pages/code/CodeToolsPage'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/app/code')({
|
||||
component: CodeToolsPage
|
||||
})
|
||||
6
src/renderer/src/routes/app/files.tsx
Normal file
6
src/renderer/src/routes/app/files.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import FilesPage from '@renderer/pages/files/FilesPage'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/app/files')({
|
||||
component: FilesPage
|
||||
})
|
||||
6
src/renderer/src/routes/app/knowledge.tsx
Normal file
6
src/renderer/src/routes/app/knowledge.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import KnowledgePage from '@renderer/pages/knowledge/KnowledgePage'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/app/knowledge')({
|
||||
component: KnowledgePage
|
||||
})
|
||||
6
src/renderer/src/routes/app/minapp/$appId.tsx
Normal file
6
src/renderer/src/routes/app/minapp/$appId.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import MinAppPage from '@renderer/pages/minapps/MinAppPage'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/app/minapp/$appId')({
|
||||
component: MinAppPage
|
||||
})
|
||||
6
src/renderer/src/routes/app/minapp/index.tsx
Normal file
6
src/renderer/src/routes/app/minapp/index.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import MinAppsPage from '@renderer/pages/minapps/MinAppsPage'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/app/minapp/')({
|
||||
component: MinAppsPage
|
||||
})
|
||||
6
src/renderer/src/routes/app/notes.tsx
Normal file
6
src/renderer/src/routes/app/notes.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import NotesPage from '@renderer/pages/notes/NotesPage'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/app/notes')({
|
||||
component: NotesPage
|
||||
})
|
||||
7
src/renderer/src/routes/app/paintings/$.tsx
Normal file
7
src/renderer/src/routes/app/paintings/$.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import PaintingsRoutePage from '@renderer/pages/paintings/PaintingsRoutePage'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
// 通配符路由:捕获 /app/paintings/* 所有子路径
|
||||
export const Route = createFileRoute('/app/paintings/$')({
|
||||
component: PaintingsRoutePage
|
||||
})
|
||||
6
src/renderer/src/routes/app/paintings/index.tsx
Normal file
6
src/renderer/src/routes/app/paintings/index.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import PaintingsRoutePage from '@renderer/pages/paintings/PaintingsRoutePage'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/app/paintings/')({
|
||||
component: PaintingsRoutePage
|
||||
})
|
||||
6
src/renderer/src/routes/app/translate.tsx
Normal file
6
src/renderer/src/routes/app/translate.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import TranslatePage from '@renderer/pages/translate/TranslatePage'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/app/translate')({
|
||||
component: TranslatePage
|
||||
})
|
||||
6
src/renderer/src/routes/home.tsx
Normal file
6
src/renderer/src/routes/home.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import LaunchpadPage from '@renderer/pages/launchpad/LaunchpadPage'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/home')({
|
||||
component: LaunchpadPage
|
||||
})
|
||||
7
src/renderer/src/routes/index.tsx
Normal file
7
src/renderer/src/routes/index.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import { createFileRoute, redirect } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/')({
|
||||
beforeLoad: () => {
|
||||
throw redirect({ to: '/home' })
|
||||
}
|
||||
})
|
||||
7
src/renderer/src/routes/settings.tsx
Normal file
7
src/renderer/src/routes/settings.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import SettingsPage from '@renderer/pages/settings/SettingsPage'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
// 布局路由:SettingsPage 作为布局组件,使用 Outlet 渲染子路由
|
||||
export const Route = createFileRoute('/settings')({
|
||||
component: SettingsPage
|
||||
})
|
||||
6
src/renderer/src/routes/settings/about.tsx
Normal file
6
src/renderer/src/routes/settings/about.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import AboutSettings from '@renderer/pages/settings/AboutSettings'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/settings/about')({
|
||||
component: AboutSettings
|
||||
})
|
||||
6
src/renderer/src/routes/settings/api-server.tsx
Normal file
6
src/renderer/src/routes/settings/api-server.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import { ApiServerSettings } from '@renderer/pages/settings/ToolSettings/ApiServerSettings'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/settings/api-server')({
|
||||
component: ApiServerSettings
|
||||
})
|
||||
6
src/renderer/src/routes/settings/data.tsx
Normal file
6
src/renderer/src/routes/settings/data.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import DataSettings from '@renderer/pages/settings/DataSettings/DataSettings'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/settings/data')({
|
||||
component: DataSettings
|
||||
})
|
||||
6
src/renderer/src/routes/settings/display.tsx
Normal file
6
src/renderer/src/routes/settings/display.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import DisplaySettings from '@renderer/pages/settings/DisplaySettings/DisplaySettings'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/settings/display')({
|
||||
component: DisplaySettings
|
||||
})
|
||||
6
src/renderer/src/routes/settings/docprocess.tsx
Normal file
6
src/renderer/src/routes/settings/docprocess.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import DocProcessSettings from '@renderer/pages/settings/DocProcessSettings'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/settings/docprocess')({
|
||||
component: DocProcessSettings
|
||||
})
|
||||
6
src/renderer/src/routes/settings/general.tsx
Normal file
6
src/renderer/src/routes/settings/general.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import GeneralSettings from '@renderer/pages/settings/GeneralSettings'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/settings/general')({
|
||||
component: GeneralSettings
|
||||
})
|
||||
8
src/renderer/src/routes/settings/index.tsx
Normal file
8
src/renderer/src/routes/settings/index.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
import { createFileRoute, redirect } from '@tanstack/react-router'
|
||||
|
||||
// /settings/ 重定向到 /settings/provider
|
||||
export const Route = createFileRoute('/settings/')({
|
||||
beforeLoad: () => {
|
||||
throw redirect({ to: '/settings/provider' })
|
||||
}
|
||||
})
|
||||
7
src/renderer/src/routes/settings/mcp.tsx
Normal file
7
src/renderer/src/routes/settings/mcp.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import MCPSettings from '@renderer/pages/settings/MCPSettings'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
// MCP 布局路由:MCPSettings 作为布局组件,使用 Outlet 渲染子路由
|
||||
export const Route = createFileRoute('/settings/mcp')({
|
||||
component: MCPSettings
|
||||
})
|
||||
24
src/renderer/src/routes/settings/mcp/$.tsx
Normal file
24
src/renderer/src/routes/settings/mcp/$.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { useMCPServers } from '@renderer/hooks/useMCPServers'
|
||||
import ProviderDetail from '@renderer/pages/settings/MCPSettings/McpProviderSettings'
|
||||
import { providers } from '@renderer/pages/settings/MCPSettings/providers/config'
|
||||
import { useParams } from '@tanstack/react-router'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
// 通配符路由:捕获 provider 页面 /settings/mcp/:providerKey
|
||||
const ProviderPage = () => {
|
||||
const params = useParams({ strict: false }) as { _splat?: string }
|
||||
const providerKey = params._splat
|
||||
const { mcpServers } = useMCPServers()
|
||||
|
||||
const provider = providers.find((p) => p.key === providerKey)
|
||||
|
||||
if (!provider) {
|
||||
return <div>Provider not found</div>
|
||||
}
|
||||
|
||||
return <ProviderDetail provider={provider} existingServers={mcpServers} />
|
||||
}
|
||||
|
||||
export const Route = createFileRoute('/settings/mcp/$')({
|
||||
component: ProviderPage
|
||||
})
|
||||
12
src/renderer/src/routes/settings/mcp/builtin.tsx
Normal file
12
src/renderer/src/routes/settings/mcp/builtin.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import BuiltinMCPServerList from '@renderer/pages/settings/MCPSettings/BuiltinMCPServerList'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
const BuiltinWrapper = () => (
|
||||
<div className="h-full overflow-y-auto p-5">
|
||||
<BuiltinMCPServerList />
|
||||
</div>
|
||||
)
|
||||
|
||||
export const Route = createFileRoute('/settings/mcp/builtin')({
|
||||
component: BuiltinWrapper
|
||||
})
|
||||
8
src/renderer/src/routes/settings/mcp/index.tsx
Normal file
8
src/renderer/src/routes/settings/mcp/index.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
import { createFileRoute, redirect } from '@tanstack/react-router'
|
||||
|
||||
// /settings/mcp/ 重定向到 /settings/mcp/servers
|
||||
export const Route = createFileRoute('/settings/mcp/')({
|
||||
beforeLoad: () => {
|
||||
throw redirect({ to: '/settings/mcp/servers' })
|
||||
}
|
||||
})
|
||||
12
src/renderer/src/routes/settings/mcp/marketplaces.tsx
Normal file
12
src/renderer/src/routes/settings/mcp/marketplaces.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import McpMarketList from '@renderer/pages/settings/MCPSettings/McpMarketList'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
const MarketplacesWrapper = () => (
|
||||
<div className="h-full overflow-y-auto p-5">
|
||||
<McpMarketList />
|
||||
</div>
|
||||
)
|
||||
|
||||
export const Route = createFileRoute('/settings/mcp/marketplaces')({
|
||||
component: MarketplacesWrapper
|
||||
})
|
||||
13
src/renderer/src/routes/settings/mcp/mcp-install.tsx
Normal file
13
src/renderer/src/routes/settings/mcp/mcp-install.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { SettingContainer } from '@renderer/pages/settings'
|
||||
import InstallNpxUv from '@renderer/pages/settings/MCPSettings/InstallNpxUv'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
const McpInstallWrapper = () => (
|
||||
<SettingContainer style={{ backgroundColor: 'inherit' }}>
|
||||
<InstallNpxUv />
|
||||
</SettingContainer>
|
||||
)
|
||||
|
||||
export const Route = createFileRoute('/settings/mcp/mcp-install')({
|
||||
component: McpInstallWrapper
|
||||
})
|
||||
17
src/renderer/src/routes/settings/mcp/npx-search.tsx
Normal file
17
src/renderer/src/routes/settings/mcp/npx-search.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||
import { SettingContainer } from '@renderer/pages/settings'
|
||||
import NpxSearch from '@renderer/pages/settings/MCPSettings/NpxSearch'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
const NpxSearchWrapper = () => {
|
||||
const { theme } = useTheme()
|
||||
return (
|
||||
<SettingContainer theme={theme}>
|
||||
<NpxSearch />
|
||||
</SettingContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export const Route = createFileRoute('/settings/mcp/npx-search')({
|
||||
component: NpxSearchWrapper
|
||||
})
|
||||
6
src/renderer/src/routes/settings/mcp/servers.tsx
Normal file
6
src/renderer/src/routes/settings/mcp/servers.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import McpServersList from '@renderer/pages/settings/MCPSettings/McpServersList'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/settings/mcp/servers')({
|
||||
component: McpServersList
|
||||
})
|
||||
@ -0,0 +1,6 @@
|
||||
import McpSettings from '@renderer/pages/settings/MCPSettings/McpSettings'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/settings/mcp/settings/$serverId')({
|
||||
component: McpSettings
|
||||
})
|
||||
6
src/renderer/src/routes/settings/memory.tsx
Normal file
6
src/renderer/src/routes/settings/memory.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import MemorySettings from '@renderer/pages/settings/MemorySettings'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/settings/memory')({
|
||||
component: MemorySettings
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user