UNPKG

@lobehub/chat

Version:

Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.

185 lines (155 loc) 6.64 kB
import { findMatchingRoute } from '~common/routes'; import { invoke } from './invoke'; const interceptRoute = async ( path: string, source: 'link-click' | 'push-state' | 'replace-state', url: string, ) => { console.log(`[preload] Intercepted ${source} and prevented default behavior:`, path); // 使用electron-client-ipc的dispatch方法 try { await invoke('interceptRoute', { path, source, url }); } catch (e) { console.error(`[preload] Route interception (${source}) call failed`, e); } }; /** * 路由拦截器 - 负责捕获和拦截客户端路由导航 */ export const setupRouteInterceptors = function () { console.log('[preload] Setting up route interceptors'); // 存储被阻止的路径,避免pushState重复触发 const preventedPaths = new Set<string>(); // 拦截所有a标签的点击事件 - 针对Next.js的Link组件 document.addEventListener( 'click', async (e) => { const link = (e.target as HTMLElement).closest('a'); if (link && link.href) { try { const url = new URL(link.href); // 检查是否为外部链接 if (url.origin !== window.location.origin) { console.log(`[preload] Intercepted external link click:`, url.href); // 阻止默认的链接跳转行为 e.preventDefault(); e.stopPropagation(); // 调用主进程处理外部链接 await invoke('openExternalLink', url.href); return false; // 明确阻止后续处理 } // 如果不是外部链接,则继续处理内部路由拦截逻辑 // 使用共享配置检查是否需要拦截 const matchedRoute = findMatchingRoute(url.pathname); // 如果是需要拦截的路径 if (matchedRoute) { const currentPath = window.location.pathname; const isAlreadyInTargetPage = currentPath.startsWith(matchedRoute.pathPrefix); // 如果已经在目标页面下,则不拦截,让默认导航继续 if (isAlreadyInTargetPage) return; // 立即阻止默认行为,避免Next.js接管路由 e.preventDefault(); e.stopPropagation(); await interceptRoute(url.pathname, 'link-click', link.href); return false; } } catch (err) { // 处理可能的 URL 解析错误或其他问题 // 例如 mailto:, tel: 等协议会导致 new URL() 抛出错误 if (err instanceof TypeError && err.message.includes('Invalid URL')) { console.log( '[preload] Non-HTTP link clicked, allowing default browser behavior:', link.href, ); // 对于非 HTTP/HTTPS 链接,允许浏览器默认处理 // 不需要 e.preventDefault() 或 invoke } else { console.error('[preload] Link interception error:', err); } } } }, true, ); // 拦截 history API (用于捕获Next.js的useRouter().push/replace等) const originalPushState = history.pushState; const originalReplaceState = history.replaceState; // 重写pushState history.pushState = function () { const url = arguments[2]; if (typeof url === 'string') { try { // 只处理相对路径或当前域的URL const parsedUrl = new URL(url, window.location.origin); // 使用共享配置检查是否需要拦截 const matchedRoute = findMatchingRoute(parsedUrl.pathname); // 检查是否需要拦截这个导航 if (matchedRoute) { // 检查当前页面是否已经在目标路径下,如果是则不拦截 const currentPath = window.location.pathname; const isAlreadyInTargetPage = currentPath.startsWith(matchedRoute.pathPrefix); // 如果已经在目标页面下,则不拦截,让默认导航继续 if (isAlreadyInTargetPage) { console.log( `[preload] Skip pushState interception for ${parsedUrl.pathname} because already in target page ${matchedRoute.pathPrefix}`, ); return Reflect.apply(originalPushState, this, arguments); } // 将此路径添加到已阻止集合中 preventedPaths.add(parsedUrl.pathname); interceptRoute(parsedUrl.pathname, 'push-state', parsedUrl.href); // 不执行原始的pushState操作,阻止导航发生 // 但返回undefined以避免错误 return; } } catch (err) { console.error('[preload] pushState interception error:', err); } } return Reflect.apply(originalPushState, this, arguments); }; // 重写replaceState history.replaceState = function () { const url = arguments[2]; if (typeof url === 'string') { try { const parsedUrl = new URL(url, window.location.origin); // 使用共享配置检查是否需要拦截 const matchedRoute = findMatchingRoute(parsedUrl.pathname); // 检查是否需要拦截这个导航 if (matchedRoute) { // 检查当前页面是否已经在目标路径下,如果是则不拦截 const currentPath = window.location.pathname; const isAlreadyInTargetPage = currentPath.startsWith(matchedRoute.pathPrefix); // 如果已经在目标页面下,则不拦截,让默认导航继续 if (isAlreadyInTargetPage) { console.log( `[preload] Skip replaceState interception for ${parsedUrl.pathname} because already in target page ${matchedRoute.pathPrefix}`, ); return Reflect.apply(originalReplaceState, this, arguments); } // 添加到已阻止集合 preventedPaths.add(parsedUrl.pathname); interceptRoute(parsedUrl.pathname, 'replace-state', parsedUrl.href); // 阻止导航 return; } } catch (err) { console.error('[preload] replaceState interception error:', err); } } return Reflect.apply(originalReplaceState, this, arguments); }; // 监听并拦截路由错误 - 有时Next.js会在路由错误时尝试恢复导航 window.addEventListener( 'error', function (e) { if (e.message && e.message.includes('navigation') && preventedPaths.size > 0) { console.log('[preload] Captured possible routing error, preventing default behavior'); e.preventDefault(); } }, true, ); console.log('[preload] Route interceptors setup completed'); };