mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-19 14:41:24 +08:00
refactor(tabs): enhance URL handling and default title logic
- Updated `AppShell` and `TabRouter` components to improve URL synchronization with tab states, ensuring full URLs (including search/hash) are correctly managed. - Enhanced `getDefaultRouteTitle` utility to handle full URLs for better title mapping, improving internationalization support. - Refactored `useTabs` to retain complete URLs in tab management, optimizing tab state tracking. These changes enhance the application's navigation and tab management, providing a more consistent user experience.
This commit is contained in:
parent
4f7a14b044
commit
bcbd5ed717
@ -23,7 +23,7 @@ const WebviewContainer = ({ url, isActive }: { url: string; isActive: boolean })
|
||||
export const AppShell = () => {
|
||||
const { tabs, activeTabId, setActiveTab, closeTab, updateTab, addTab } = useTabs()
|
||||
|
||||
// Sync internal navigation back to tab state with default title
|
||||
// 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) })
|
||||
}
|
||||
|
||||
@ -24,20 +24,20 @@ export const TabRouter = ({ tab, isActive, onUrlChange }: TabRouterProps) => {
|
||||
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 }) => {
|
||||
if (toLocation.pathname !== tab.url) {
|
||||
onUrlChange(toLocation.pathname)
|
||||
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 currentPath = router.state.location.pathname
|
||||
if (tab.url !== currentPath) {
|
||||
const currentHref = router.state.location.href
|
||||
if (tab.url !== currentHref) {
|
||||
router.navigate({ to: tab.url })
|
||||
}
|
||||
}, [router, tab.url])
|
||||
|
||||
@ -179,7 +179,7 @@ export function useTabs() {
|
||||
return
|
||||
}
|
||||
|
||||
// 添加 LRU 字段
|
||||
// 添加 LRU 字段,保留完整 URL(含 search/hash)
|
||||
const newTab: Tab = {
|
||||
...tab,
|
||||
lastAccessTime: Date.now(),
|
||||
@ -255,7 +255,7 @@ export function useTabs() {
|
||||
const newTab: Tab = {
|
||||
id: id || uuid(),
|
||||
type,
|
||||
url,
|
||||
url, // full URL including search/hash
|
||||
title: title || getDefaultRouteTitle(url),
|
||||
lastAccessTime: Date.now(),
|
||||
isDormant: false
|
||||
|
||||
@ -94,8 +94,8 @@ const ProviderList: FC = () => {
|
||||
// 清除 id 参数
|
||||
navigate({
|
||||
to: location.pathname,
|
||||
search: (prev) => {
|
||||
const { id: _, ...rest } = prev as Record<string, unknown>
|
||||
search: ({ id, ...rest }) => {
|
||||
void id
|
||||
return rest
|
||||
},
|
||||
replace: true
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
import NavigationHandler from '@renderer/handler/NavigationHandler'
|
||||
import { createRootRoute, Outlet } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createRootRoute({
|
||||
component: () => <Outlet />
|
||||
component: () => (
|
||||
<>
|
||||
<NavigationHandler />
|
||||
<Outlet />
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import i18n from '@renderer/i18n'
|
||||
|
||||
/** Base URL for parsing relative route paths */
|
||||
const BASE_URL = 'https://www.cherry-ai.com/'
|
||||
|
||||
/**
|
||||
* Route to i18n key mapping for default tab titles
|
||||
*/
|
||||
@ -29,30 +32,35 @@ const routeTitleKeys: Record<string, string> = {
|
||||
* getDefaultRouteTitle('/unknown') // 'unknown'
|
||||
*/
|
||||
export function getDefaultRouteTitle(url: string): string {
|
||||
const sanitizedUrl = new URL(url, BASE_URL).pathname
|
||||
|
||||
// Try exact match first
|
||||
const exactKey = routeTitleKeys[url]
|
||||
const exactKey = routeTitleKeys[sanitizedUrl]
|
||||
if (exactKey) {
|
||||
return i18n.t(exactKey)
|
||||
}
|
||||
|
||||
// Try matching base path (e.g., '/chat/123' -> '/chat')
|
||||
const basePath = '/' + url.split('/').filter(Boolean)[0]
|
||||
const basePath = '/' + sanitizedUrl.split('/').filter(Boolean)[0]
|
||||
const baseKey = routeTitleKeys[basePath]
|
||||
if (baseKey) {
|
||||
return i18n.t(baseKey)
|
||||
}
|
||||
|
||||
// Fallback to URL path
|
||||
return url.split('/').pop() || url
|
||||
// Fallback to last segment of pathname
|
||||
const segments = sanitizedUrl.split('/').filter(Boolean)
|
||||
return segments.pop() || sanitizedUrl
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the i18n key for a route (without translating)
|
||||
*/
|
||||
export function getRouteTitleKey(url: string): string | undefined {
|
||||
const exactKey = routeTitleKeys[url]
|
||||
const sanitizedUrl = new URL(url, BASE_URL).pathname
|
||||
|
||||
const exactKey = routeTitleKeys[sanitizedUrl]
|
||||
if (exactKey) return exactKey
|
||||
|
||||
const basePath = '/' + url.split('/').filter(Boolean)[0]
|
||||
const basePath = '/' + sanitizedUrl.split('/').filter(Boolean)[0]
|
||||
return routeTitleKeys[basePath]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user