From 377ce36c28a1ae3919bb1ca00d7ea4ff210280d7 Mon Sep 17 00:00:00 2001 From: Junyan Qin Date: Fri, 3 Apr 2026 14:31:06 +0800 Subject: [PATCH] fix(web): clean up remaining Next.js artifacts in Vite migration - Add vite-env.d.ts for import.meta.env and asset type declarations - Remove dead layout.tsx (providers already in main.tsx) - Fix useSearchParams destructuring to [searchParams] tuple (11 locations) - Replace process.env.NEXT_PUBLIC_* with import.meta.env.VITE_* - Fix langbotIcon.src to langbotIcon (Vite returns URL string) - Fix Link href to Link to for react-router-dom - Fix navigate({ scroll: false }) to { preventScrollReset: true } - Fix [router] dependency arrays to [navigate] - Remove Next.js plugin from tsconfig, set rsc: false in components.json - Replace next lint with eslint in lint-staged --- web/.env.example | 2 +- web/components.json | 2 +- web/package.json | 2 +- web/src/app/auth/space/callback/page.tsx | 10 +++--- web/src/app/home/bots/page.tsx | 2 +- .../ApiIntegrationDialog.tsx | 10 +++--- .../components/home-sidebar/HomeSidebar.tsx | 20 +++++++----- .../models-dialog/components/ProviderCard.tsx | 2 +- .../knowledge/components/kb-form/KBForm.tsx | 2 +- web/src/app/home/knowledge/page.tsx | 2 +- web/src/app/home/layout.tsx | 4 +-- web/src/app/home/mcp/page.tsx | 2 +- .../monitoring/hooks/useMonitoringFilters.ts | 2 +- web/src/app/home/pipelines/page.tsx | 2 +- .../plugin-market/PluginMarketComponent.tsx | 2 +- web/src/app/home/plugins/page.tsx | 4 +-- web/src/app/infra/http/index.ts | 4 +-- .../app/infra/websocket/WebSocketClient.ts | 6 ++-- web/src/app/layout.tsx | 31 ------------------- web/src/app/login/page.tsx | 6 ++-- web/src/app/register/page.tsx | 2 +- web/src/app/reset-password/page.tsx | 2 +- web/src/app/wizard/page.tsx | 6 ++-- web/src/vite-env.d.ts | 1 + web/tsconfig.json | 9 +----- 25 files changed, 53 insertions(+), 84 deletions(-) delete mode 100644 web/src/app/layout.tsx create mode 100644 web/src/vite-env.d.ts diff --git a/web/.env.example b/web/.env.example index 963ad1c9..2c5cdb15 100644 --- a/web/.env.example +++ b/web/.env.example @@ -1 +1 @@ -NEXT_PUBLIC_API_BASE_URL=http://localhost:5300 +VITE_API_BASE_URL=http://localhost:5300 diff --git a/web/components.json b/web/components.json index b37ee514..fdf90ce4 100644 --- a/web/components.json +++ b/web/components.json @@ -1,7 +1,7 @@ { "$schema": "https://ui.shadcn.com/schema.json", "style": "new-york", - "rsc": true, + "rsc": false, "tsx": true, "tailwind": { "config": "", diff --git a/web/package.json b/web/package.json index da1a1cba..2410cd83 100644 --- a/web/package.json +++ b/web/package.json @@ -11,7 +11,7 @@ }, "lint-staged": { "*.{js,jsx,ts,tsx}": [ - "next lint --fix", + "eslint --fix", "prettier --write" ] }, diff --git a/web/src/app/auth/space/callback/page.tsx b/web/src/app/auth/space/callback/page.tsx index 0aa83006..2131a17c 100644 --- a/web/src/app/auth/space/callback/page.tsx +++ b/web/src/app/auth/space/callback/page.tsx @@ -1,5 +1,5 @@ import { useEffect, useState, useCallback, Suspense } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useSearchParams } from 'react-router-dom'; import { httpClient } from '@/app/infra/http/HttpClient'; import { toast } from 'sonner'; import { useTranslation } from 'react-i18next'; @@ -22,7 +22,7 @@ import langbotIcon from '@/app/assets/langbot-logo.webp'; function SpaceOAuthCallbackContent() { const navigate = useNavigate(); - const searchParams = useSearchParams(); + const [searchParams] = useSearchParams(); const { t } = useTranslation(); const [status, setStatus] = useState< @@ -62,7 +62,7 @@ function SpaceOAuthCallbackContent() { } } }, - [router, t], + [navigate, t], ); const [bindState, setBindState] = useState(null); @@ -94,7 +94,7 @@ function SpaceOAuthCallbackContent() { setIsProcessing(false); } }, - [router, t], + [navigate, t], ); useEffect(() => { @@ -152,7 +152,7 @@ function SpaceOAuthCallbackContent() { LangBot diff --git a/web/src/app/home/bots/page.tsx b/web/src/app/home/bots/page.tsx index 574eb0d4..aa7194b7 100644 --- a/web/src/app/home/bots/page.tsx +++ b/web/src/app/home/bots/page.tsx @@ -4,7 +4,7 @@ import BotDetailContent from './BotDetailContent'; export default function BotConfigPage() { const { t } = useTranslation(); - const searchParams = useSearchParams(); + const [searchParams] = useSearchParams(); const detailId = searchParams.get('id'); if (detailId) { diff --git a/web/src/app/home/components/api-integration-dialog/ApiIntegrationDialog.tsx b/web/src/app/home/components/api-integration-dialog/ApiIntegrationDialog.tsx index 6967dc6e..bc9bf67a 100644 --- a/web/src/app/home/components/api-integration-dialog/ApiIntegrationDialog.tsx +++ b/web/src/app/home/components/api-integration-dialog/ApiIntegrationDialog.tsx @@ -3,7 +3,7 @@ import { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { toast } from 'sonner'; import { Copy, Check, Trash2, Plus } from 'lucide-react'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useLocation, useSearchParams } from 'react-router-dom'; import { Dialog, DialogContent, @@ -68,7 +68,7 @@ export default function ApiIntegrationDialog({ const navigate = useNavigate(); const location = useLocation(); const pathname = location.pathname; - const searchParams = useSearchParams(); + const [searchParams] = useSearchParams(); const [activeTab, setActiveTab] = useState('apikeys'); const [apiKeys, setApiKeys] = useState([]); const [webhooks, setWebhooks] = useState([]); @@ -93,7 +93,9 @@ export default function ApiIntegrationDialog({ if (open) { const params = new URLSearchParams(searchParams.toString()); params.set('action', 'showApiIntegrationSettings'); - navigate(`${pathname}?${params.toString()}`, { scroll: false }); + navigate(`${pathname}?${params.toString()}`, { + preventScrollReset: true, + }); } }, [open]); @@ -107,7 +109,7 @@ export default function ApiIntegrationDialog({ const newUrl = params.toString() ? `${pathname}?${params.toString()}` : pathname; - navigate(newUrl, { scroll: false }); + navigate(newUrl, { preventScrollReset: true }); } onOpenChange(newOpen); }; diff --git a/web/src/app/home/components/home-sidebar/HomeSidebar.tsx b/web/src/app/home/components/home-sidebar/HomeSidebar.tsx index 25120d1b..7313a746 100644 --- a/web/src/app/home/components/home-sidebar/HomeSidebar.tsx +++ b/web/src/app/home/components/home-sidebar/HomeSidebar.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; import { SidebarChildVO } from '@/app/home/components/home-sidebar/HomeSidebarChild'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useLocation, useSearchParams } from 'react-router-dom'; import { sidebarConfigList } from '@/app/home/components/home-sidebar/sidbarConfigList'; import langbotIcon from '@/app/assets/langbot-logo.webp'; import { systemInfo, httpClient } from '@/app/infra/http/HttpClient'; @@ -245,7 +245,7 @@ function NavItems({ const navigate = useNavigate(); const location = useLocation(); const pathname = location.pathname; - const searchParams = useSearchParams(); + const [searchParams] = useSearchParams(); const sidebarData = useSidebarData(); const { setPendingPluginInstallAction } = sidebarData; const { state: sidebarState, isMobile } = useSidebar(); @@ -1031,7 +1031,7 @@ export default function HomeSidebar({ const navigate = useNavigate(); const location = useLocation(); const pathname = location.pathname; - const searchParams = useSearchParams(); + const [searchParams] = useSearchParams(); const { isMobile } = useSidebar(); useEffect(() => { @@ -1071,14 +1071,16 @@ export default function HomeSidebar({ if (open) { const params = new URLSearchParams(searchParams.toString()); params.set('action', 'showModelSettings'); - navigate(`${pathname}?${params.toString()}`, { scroll: false }); + navigate(`${pathname}?${params.toString()}`, { + preventScrollReset: true, + }); } else { const params = new URLSearchParams(searchParams.toString()); params.delete('action'); const newUrl = params.toString() ? `${pathname}?${params.toString()}` : pathname; - navigate(newUrl, { scroll: false }); + navigate(newUrl, { preventScrollReset: true }); } } @@ -1087,14 +1089,16 @@ export default function HomeSidebar({ if (open) { const params = new URLSearchParams(searchParams.toString()); params.set('action', 'showAccountSettings'); - navigate(`${pathname}?${params.toString()}`, { scroll: false }); + navigate(`${pathname}?${params.toString()}`, { + preventScrollReset: true, + }); } else { const params = new URLSearchParams(searchParams.toString()); params.delete('action'); const newUrl = params.toString() ? `${pathname}?${params.toString()}` : pathname; - navigate(newUrl, { scroll: false }); + navigate(newUrl, { preventScrollReset: true }); } } @@ -1226,7 +1230,7 @@ export default function HomeSidebar({ tooltip="LangBot" > LangBot diff --git a/web/src/app/home/components/models-dialog/components/ProviderCard.tsx b/web/src/app/home/components/models-dialog/components/ProviderCard.tsx index ee7507ce..70adebe6 100644 --- a/web/src/app/home/components/models-dialog/components/ProviderCard.tsx +++ b/web/src/app/home/components/models-dialog/components/ProviderCard.tsx @@ -133,7 +133,7 @@ export default function ProviderCard({ {isLangBotModels ? (
LangBot diff --git a/web/src/app/home/knowledge/components/kb-form/KBForm.tsx b/web/src/app/home/knowledge/components/kb-form/KBForm.tsx index e0dd73ae..af9250fb 100644 --- a/web/src/app/home/knowledge/components/kb-form/KBForm.tsx +++ b/web/src/app/home/knowledge/components/kb-form/KBForm.tsx @@ -304,7 +304,7 @@ export default function KBForm({ {t('knowledge.noEnginesAvailable')}

{t('knowledge.installEngineHint')} diff --git a/web/src/app/home/knowledge/page.tsx b/web/src/app/home/knowledge/page.tsx index f051814c..66d21784 100644 --- a/web/src/app/home/knowledge/page.tsx +++ b/web/src/app/home/knowledge/page.tsx @@ -8,7 +8,7 @@ import KBDetailContent from './KBDetailContent'; export default function KnowledgePage() { const { t } = useTranslation(); - const searchParams = useSearchParams(); + const [searchParams] = useSearchParams(); const detailId = searchParams.get('id'); const { refreshKnowledgeBases } = useSidebarData(); diff --git a/web/src/app/home/layout.tsx b/web/src/app/home/layout.tsx index 2d741407..55a66de7 100644 --- a/web/src/app/home/layout.tsx +++ b/web/src/app/home/layout.tsx @@ -19,7 +19,7 @@ import { initializeUserInfo, initializeSystemInfo, } from '@/app/infra/http'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useLocation } from 'react-router-dom'; import { Link } from 'react-router-dom'; import { extractI18nObject } from '@/i18n/I18nProvider'; import { CircleHelp } from 'lucide-react'; @@ -80,7 +80,7 @@ export default function HomeLayout({ } }; checkWizard(); - }, [router]); + }, [navigate]); return ( diff --git a/web/src/app/home/mcp/page.tsx b/web/src/app/home/mcp/page.tsx index 49cf61a3..391679c3 100644 --- a/web/src/app/home/mcp/page.tsx +++ b/web/src/app/home/mcp/page.tsx @@ -4,7 +4,7 @@ import MCPDetailContent from './MCPDetailContent'; export default function MCPPage() { const { t } = useTranslation(); - const searchParams = useSearchParams(); + const [searchParams] = useSearchParams(); const detailId = searchParams.get('id'); if (detailId) { diff --git a/web/src/app/home/monitoring/hooks/useMonitoringFilters.ts b/web/src/app/home/monitoring/hooks/useMonitoringFilters.ts index 63488a49..53545bab 100644 --- a/web/src/app/home/monitoring/hooks/useMonitoringFilters.ts +++ b/web/src/app/home/monitoring/hooks/useMonitoringFilters.ts @@ -7,7 +7,7 @@ import { getPresetDateRange } from '../utils/dateUtils'; * Custom hook for managing monitoring filters */ export function useMonitoringFilters() { - const searchParams = useSearchParams(); + const [searchParams] = useSearchParams(); // Initialize filters from URL params const [selectedBots, setSelectedBots] = useState(() => { diff --git a/web/src/app/home/pipelines/page.tsx b/web/src/app/home/pipelines/page.tsx index cb5bdaf8..be65346a 100644 --- a/web/src/app/home/pipelines/page.tsx +++ b/web/src/app/home/pipelines/page.tsx @@ -4,7 +4,7 @@ import PipelineDetailContent from './PipelineDetailContent'; export default function PipelineConfigPage() { const { t } = useTranslation(); - const searchParams = useSearchParams(); + const [searchParams] = useSearchParams(); const detailId = searchParams.get('id'); if (detailId) { diff --git a/web/src/app/home/plugins/components/plugin-market/PluginMarketComponent.tsx b/web/src/app/home/plugins/components/plugin-market/PluginMarketComponent.tsx index be1f5bf6..2b898cca 100644 --- a/web/src/app/home/plugins/components/plugin-market/PluginMarketComponent.tsx +++ b/web/src/app/home/plugins/components/plugin-market/PluginMarketComponent.tsx @@ -45,7 +45,7 @@ function MarketPageContent({ installPlugin: (plugin: PluginV4) => void; }) { const { t } = useTranslation(); - const searchParams = useSearchParams(); + const [searchParams] = useSearchParams(); const validCategories = [ 'Tool', diff --git a/web/src/app/home/plugins/page.tsx b/web/src/app/home/plugins/page.tsx index d12a5f25..0e4b4fb8 100644 --- a/web/src/app/home/plugins/page.tsx +++ b/web/src/app/home/plugins/page.tsx @@ -44,7 +44,7 @@ import { } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import React, { useState, useRef, useCallback, useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useSearchParams } from 'react-router-dom'; import { httpClient } from '@/app/infra/http/HttpClient'; import { toast } from 'sonner'; import { useTranslation } from 'react-i18next'; @@ -83,7 +83,7 @@ interface GithubAsset { } export default function PluginConfigPage() { - const searchParams = useSearchParams(); + const [searchParams] = useSearchParams(); const detailId = searchParams.get('id'); // Show plugin detail view when ?id= query param is present diff --git a/web/src/app/infra/http/index.ts b/web/src/app/infra/http/index.ts index 4a9002a5..b992545d 100644 --- a/web/src/app/infra/http/index.ts +++ b/web/src/app/infra/http/index.ts @@ -31,8 +31,8 @@ export let userInfo: { * 获取基础 URL */ const getBaseURL = (): string => { - if (typeof window !== 'undefined' && process.env.NEXT_PUBLIC_API_BASE_URL) { - return process.env.NEXT_PUBLIC_API_BASE_URL; + if (typeof window !== 'undefined' && import.meta.env.VITE_API_BASE_URL) { + return import.meta.env.VITE_API_BASE_URL; } return '/'; }; diff --git a/web/src/app/infra/websocket/WebSocketClient.ts b/web/src/app/infra/websocket/WebSocketClient.ts index 922b90a3..b1098c1e 100644 --- a/web/src/app/infra/websocket/WebSocketClient.ts +++ b/web/src/app/infra/websocket/WebSocketClient.ts @@ -78,10 +78,10 @@ export class WebSocketClient { // 构建WebSocket URL const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - // extract host from process.env.NEXT_PUBLIC_API_BASE_URL - // 如果环境变量未定义,使用当前页面的 host (适配生产环境) + // extract host from import.meta.env.VITE_API_BASE_URL + // If env var is undefined, use current page host (for production) const host = - process.env.NEXT_PUBLIC_API_BASE_URL?.split('://')[1] || + import.meta.env.VITE_API_BASE_URL?.split('://')[1] || window.location.host; const url = `${protocol}//${host}/api/v1/pipelines/${this.pipelineId}/ws/connect?session_type=${this.sessionType}`; diff --git a/web/src/app/layout.tsx b/web/src/app/layout.tsx deleted file mode 100644 index 592f2330..00000000 --- a/web/src/app/layout.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import './global.css'; -import 'react-photo-view/dist/react-photo-view.css'; -import type { Metadata } from 'next'; -import { Toaster } from '@/components/ui/sonner'; -import I18nProvider from '@/i18n/I18nProvider'; -import { ThemeProvider } from '@/components/providers/theme-provider'; - -export const metadata: Metadata = { - title: 'LangBot', - description: - 'Production-grade platform for building agentic IM bots, integrated with Telegram, Slack, Discord, WeChat, QQ, etc.', -}; - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - - - {children} - - - - - - ); -} diff --git a/web/src/app/login/page.tsx b/web/src/app/login/page.tsx index 2f06be4f..51bd0fb8 100644 --- a/web/src/app/login/page.tsx +++ b/web/src/app/login/page.tsx @@ -153,7 +153,7 @@ export default function Login() {
LangBot @@ -204,7 +204,7 @@ export default function Login() { LangBot @@ -312,7 +312,7 @@ export default function Login() {
{t('common.password')} {t('common.forgotPassword')} diff --git a/web/src/app/register/page.tsx b/web/src/app/register/page.tsx index 682f61c7..13042082 100644 --- a/web/src/app/register/page.tsx +++ b/web/src/app/register/page.tsx @@ -113,7 +113,7 @@ export default function Register() {
LangBot diff --git a/web/src/app/reset-password/page.tsx b/web/src/app/reset-password/page.tsx index 31b2dcfe..321127ba 100644 --- a/web/src/app/reset-password/page.tsx +++ b/web/src/app/reset-password/page.tsx @@ -87,7 +87,7 @@ export default function ResetPassword() {
diff --git a/web/src/app/wizard/page.tsx b/web/src/app/wizard/page.tsx index 949987f1..bc085c12 100644 --- a/web/src/app/wizard/page.tsx +++ b/web/src/app/wizard/page.tsx @@ -518,7 +518,7 @@ export default function WizardPage() { setIsSkipping(false); setShowSkipConfirm(false); navigate('/home'); - }, [router, t]); + }, [navigate, t]); // ---- Render ---- @@ -1212,7 +1212,7 @@ function StepDone() { } setIsCompleting(false); navigate('/home/bots'); - }, [router, t]); + }, [navigate, t]); return (
@@ -1242,7 +1242,7 @@ function StepDone() { {t('wizard.done.backToWorkbench')} -