xiaozhi-client 1.6.4-beta.8 → 1.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +2 -3
- package/package.json +2 -3
- package/web/dist/assets/index-DXZqFLxf.js +31 -0
- package/web/dist/assets/index-DXZqFLxf.js.map +1 -0
- package/web/dist/assets/index-FjYZvnDD.css +1 -0
- package/web/dist/assets/{react-vendor-Bm2cC0fo.js → react-vendor-krhZvXtS.js} +41 -36
- package/web/dist/assets/react-vendor-krhZvXtS.js.map +1 -0
- package/web/dist/assets/{utils-DE0xezXx.js → utils-BWOcwU4X.js} +2 -2
- package/web/dist/assets/{utils-DE0xezXx.js.map → utils-BWOcwU4X.js.map} +1 -1
- package/web/dist/index.html +4 -4
- package/web/dist/assets/index-CCieCKu1.js +0 -31
- package/web/dist/assets/index-CCieCKu1.js.map +0 -1
- package/web/dist/assets/index-eZDZS6qd.css +0 -1
- package/web/dist/assets/react-vendor-Bm2cC0fo.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-DXZqFLxf.js","sources":["../../src/services/api.ts","../../src/services/websocket.ts","../../src/stores/status.ts","../../src/hooks/useRestartNotifications.ts","../../src/lib/utils.ts","../../src/components/ui/button.tsx","../../src/components/ui/input.tsx","../../src/components/ui/separator.tsx","../../src/components/ui/sheet.tsx","../../src/components/ui/skeleton.tsx","../../src/components/ui/tooltip.tsx","../../src/hooks/use-mobile.tsx","../../src/components/ui/sidebar.tsx","../../src/components/AppSidebarNav.tsx","../../src/components/AppSidebar.tsx","../../src/components/ui/alert-dialog.tsx","../../src/components/ui/dialog.tsx","../../src/stores/config.ts","../../src/stores/websocket.ts","../../src/utils/portUtils.ts","../../src/hooks/useWebSocket.ts","../../src/components/McpEndpointSettingButton.tsx","../../src/components/ui/label.tsx","../../src/components/ui/form.tsx","../../src/components/WebUrlSettingButton.tsx","../../src/components/ui/card.tsx","../../src/components/DashboardStatusCard.tsx","../../src/components/ui/badge.tsx","../../src/utils/mcpServerUtils.ts","../../src/components/ui/textarea.tsx","../../src/components/AddMcpServerButton.tsx","../../src/components/McpServerSettingButton.tsx","../../src/components/RemoveMcpServerButton.tsx","../../src/components/RestartButton.tsx","../../src/components/McpServerList.tsx","../../src/components/SiteHeder.tsx","../../src/services/index.ts","../../src/hooks/useNetworkService.ts","../../src/stores/index.ts","../../src/providers/WebSocketProvider.tsx","../../src/pages/DashboardPage.tsx","../../src/pages/SettingsPage.tsx","../../src/App.tsx","../../src/main.tsx"],"sourcesContent":["/**\n * 统一的 HTTP API 客户端\n * 负责所有的配置管理、状态查询和服务控制操作\n */\n\nimport type { AppConfig, ClientStatus } from \"../types\";\n\n/**\n * API 响应格式\n */\ninterface ApiResponse<T = any> {\n success: boolean;\n data?: T;\n message?: string;\n}\n\ninterface ApiErrorResponse {\n error: {\n code: string;\n message: string;\n details?: any;\n };\n}\n\n/**\n * 服务状态接口\n */\ninterface ServiceStatus {\n running: boolean;\n mode?: string;\n pid?: number;\n}\n\n/**\n * 服务健康状态接口\n */\ninterface ServiceHealth {\n status: string;\n timestamp: number;\n uptime: number;\n memory: {\n rss: number;\n heapTotal: number;\n heapUsed: number;\n external: number;\n arrayBuffers: number;\n };\n version: string;\n}\n\n/**\n * 重启状态接口\n */\ninterface RestartStatus {\n status: \"restarting\" | \"completed\" | \"failed\";\n error?: string;\n timestamp: number;\n}\n\n/**\n * 完整状态接口\n */\ninterface FullStatus {\n client: ClientStatus;\n restart?: RestartStatus;\n timestamp: number;\n}\n\n/**\n * HTTP API 客户端类\n */\nexport class ApiClient {\n private baseUrl: string;\n\n constructor(baseUrl?: string) {\n // 从当前页面 URL 推断 API 基础 URL\n if (baseUrl) {\n this.baseUrl = baseUrl;\n } else {\n const protocol = window.location.protocol;\n const hostname = window.location.hostname;\n const port = window.location.port;\n this.baseUrl = `${protocol}//${hostname}${port ? `:${port}` : \"\"}`;\n }\n }\n\n /**\n * 通用请求方法\n */\n private async request<T>(\n endpoint: string,\n options: RequestInit = {}\n ): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`;\n\n const defaultOptions: RequestInit = {\n headers: {\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n };\n\n const response = await fetch(url, { ...defaultOptions, ...options });\n\n if (!response.ok) {\n let errorMessage = `HTTP ${response.status}: ${response.statusText}`;\n\n try {\n const errorData: ApiErrorResponse = await response.json();\n errorMessage = errorData.error?.message || errorMessage;\n } catch {\n // 如果无法解析错误响应,使用默认错误消息\n }\n\n throw new Error(errorMessage);\n }\n\n return response.json();\n }\n\n // ==================== 配置管理 API ====================\n\n /**\n * 获取完整配置\n */\n async getConfig(): Promise<AppConfig> {\n const response: ApiResponse<AppConfig> = await this.request(\"/api/config\");\n if (!response.success || !response.data) {\n throw new Error(\"获取配置失败\");\n }\n return response.data;\n }\n\n /**\n * 更新配置\n */\n async updateConfig(config: AppConfig): Promise<void> {\n const response: ApiResponse = await this.request(\"/api/config\", {\n method: \"PUT\",\n body: JSON.stringify(config),\n });\n\n if (!response.success) {\n throw new Error(response.message || \"配置更新失败\");\n }\n }\n\n /**\n * 获取 MCP 端点\n */\n async getMcpEndpoint(): Promise<string> {\n const response: ApiResponse<{ endpoint: string }> = await this.request(\n \"/api/config/mcp-endpoint\"\n );\n if (!response.success || !response.data) {\n throw new Error(\"获取 MCP 端点失败\");\n }\n return response.data.endpoint;\n }\n\n /**\n * 获取 MCP 端点列表\n */\n async getMcpEndpoints(): Promise<string[]> {\n const response: ApiResponse<{ endpoints: string[] }> = await this.request(\n \"/api/config/mcp-endpoints\"\n );\n if (!response.success || !response.data) {\n throw new Error(\"获取 MCP 端点列表失败\");\n }\n return response.data.endpoints;\n }\n\n /**\n * 获取 MCP 服务配置\n */\n async getMcpServers(): Promise<Record<string, any>> {\n const response: ApiResponse<{ servers: Record<string, any> }> =\n await this.request(\"/api/config/mcp-servers\");\n if (!response.success || !response.data) {\n throw new Error(\"获取 MCP 服务配置失败\");\n }\n return response.data.servers;\n }\n\n /**\n * 获取连接配置\n */\n async getConnectionConfig(): Promise<any> {\n const response: ApiResponse<{ connection: any }> = await this.request(\n \"/api/config/connection\"\n );\n if (!response.success || !response.data) {\n throw new Error(\"获取连接配置失败\");\n }\n return response.data.connection;\n }\n\n /**\n * 重新加载配置\n */\n async reloadConfig(): Promise<AppConfig> {\n const response: ApiResponse<AppConfig> = await this.request(\n \"/api/config/reload\",\n { method: \"POST\" }\n );\n if (!response.success || !response.data) {\n throw new Error(\"重新加载配置失败\");\n }\n return response.data;\n }\n\n /**\n * 获取配置文件路径\n */\n async getConfigPath(): Promise<string> {\n const response: ApiResponse<{ path: string }> =\n await this.request(\"/api/config/path\");\n if (!response.success || !response.data) {\n throw new Error(\"获取配置文件路径失败\");\n }\n return response.data.path;\n }\n\n /**\n * 检查配置是否存在\n */\n async checkConfigExists(): Promise<boolean> {\n const response: ApiResponse<{ exists: boolean }> =\n await this.request(\"/api/config/exists\");\n if (!response.success || response.data?.exists === undefined) {\n throw new Error(\"检查配置是否存在失败\");\n }\n return response.data.exists;\n }\n\n // ==================== 状态管理 API ====================\n\n /**\n * 获取完整状态\n */\n async getStatus(): Promise<FullStatus> {\n const response: ApiResponse<FullStatus> = await this.request(\"/api/status\");\n if (!response.success || !response.data) {\n throw new Error(\"获取状态失败\");\n }\n return response.data;\n }\n\n /**\n * 获取客户端状态\n */\n async getClientStatus(): Promise<ClientStatus> {\n const response: ApiResponse<ClientStatus> =\n await this.request(\"/api/status/client\");\n if (!response.success || !response.data) {\n throw new Error(\"获取客户端状态失败\");\n }\n return response.data;\n }\n\n /**\n * 获取重启状态\n */\n async getRestartStatus(): Promise<RestartStatus | null> {\n const response: ApiResponse<RestartStatus> = await this.request(\n \"/api/status/restart\"\n );\n if (!response.success) {\n throw new Error(\"获取重启状态失败\");\n }\n return response.data || null;\n }\n\n /**\n * 检查客户端是否连接\n */\n async checkClientConnected(): Promise<boolean> {\n const response: ApiResponse<{ connected: boolean }> = await this.request(\n \"/api/status/connected\"\n );\n if (!response.success || response.data?.connected === undefined) {\n throw new Error(\"检查客户端连接失败\");\n }\n return response.data.connected;\n }\n\n /**\n * 获取最后心跳时间\n */\n async getLastHeartbeat(): Promise<number | null> {\n const response: ApiResponse<{ lastHeartbeat?: number }> =\n await this.request(\"/api/status/heartbeat\");\n if (!response.success) {\n throw new Error(\"获取最后心跳时间失败\");\n }\n return response.data?.lastHeartbeat || null;\n }\n\n /**\n * 获取活跃的 MCP 服务器列表\n */\n async getActiveMCPServers(): Promise<string[]> {\n const response: ApiResponse<{ servers: string[] }> = await this.request(\n \"/api/status/mcp-servers\"\n );\n if (!response.success || !response.data) {\n throw new Error(\"获取活跃 MCP 服务器失败\");\n }\n return response.data.servers;\n }\n\n /**\n * 更新客户端状态\n */\n async updateClientStatus(status: Partial<ClientStatus>): Promise<void> {\n const response: ApiResponse = await this.request(\"/api/status/client\", {\n method: \"PUT\",\n body: JSON.stringify(status),\n });\n\n if (!response.success) {\n throw new Error(response.message || \"更新客户端状态失败\");\n }\n }\n\n /**\n * 设置活跃的 MCP 服务器列表\n */\n async setActiveMCPServers(servers: string[]): Promise<void> {\n const response: ApiResponse = await this.request(\n \"/api/status/mcp-servers\",\n {\n method: \"PUT\",\n body: JSON.stringify({ servers }),\n }\n );\n\n if (!response.success) {\n throw new Error(response.message || \"设置活跃 MCP 服务器失败\");\n }\n }\n\n /**\n * 重置状态\n */\n async resetStatus(): Promise<void> {\n const response: ApiResponse = await this.request(\"/api/status/reset\", {\n method: \"POST\",\n });\n\n if (!response.success) {\n throw new Error(response.message || \"重置状态失败\");\n }\n }\n\n // ==================== 服务控制 API ====================\n\n /**\n * 重启服务\n */\n async restartService(): Promise<void> {\n const response: ApiResponse = await this.request(\"/api/services/restart\", {\n method: \"POST\",\n });\n\n if (!response.success) {\n throw new Error(response.message || \"重启服务失败\");\n }\n }\n\n /**\n * 停止服务\n */\n async stopService(): Promise<void> {\n const response: ApiResponse = await this.request(\"/api/services/stop\", {\n method: \"POST\",\n });\n\n if (!response.success) {\n throw new Error(response.message || \"停止服务失败\");\n }\n }\n\n /**\n * 启动服务\n */\n async startService(): Promise<void> {\n const response: ApiResponse = await this.request(\"/api/services/start\", {\n method: \"POST\",\n });\n\n if (!response.success) {\n throw new Error(response.message || \"启动服务失败\");\n }\n }\n\n /**\n * 获取服务状态\n */\n async getServiceStatus(): Promise<ServiceStatus> {\n const response: ApiResponse<ServiceStatus> = await this.request(\n \"/api/services/status\"\n );\n if (!response.success || !response.data) {\n throw new Error(\"获取服务状态失败\");\n }\n return response.data;\n }\n\n /**\n * 获取服务健康状态\n */\n async getServiceHealth(): Promise<ServiceHealth> {\n const response: ApiResponse<ServiceHealth> = await this.request(\n \"/api/services/health\"\n );\n if (!response.success || !response.data) {\n throw new Error(\"获取服务健康状态失败\");\n }\n return response.data;\n }\n}\n\n// 创建默认的 API 客户端实例\nexport const apiClient = new ApiClient();\n\n// 导出类型\nexport type {\n ApiResponse,\n ApiErrorResponse,\n ServiceStatus,\n ServiceHealth,\n RestartStatus,\n FullStatus,\n};\n","/**\n * 重构后的 WebSocket 管理器\n * 特性:\n * - 严格单例模式\n * - 全局事件总线机制\n * - 完善的错误处理和重连逻辑\n * - 支持多个 store 订阅 WebSocket 事件\n */\n\nimport type { AppConfig, ClientStatus } from \"../types\";\n\n/**\n * WebSocket 消息类型\n */\ninterface WebSocketMessage {\n type: string;\n data?: any;\n timestamp?: number;\n error?: {\n code: string;\n message: string;\n timestamp?: number;\n };\n}\n\n/**\n * 重启状态接口\n */\ninterface RestartStatus {\n status: \"restarting\" | \"completed\" | \"failed\";\n error?: string;\n timestamp: number;\n}\n\n/**\n * 事件总线事件类型\n */\ninterface EventBusEvents {\n // 连接状态事件\n \"connection:connecting\": undefined;\n \"connection:connected\": undefined;\n \"connection:disconnected\": undefined;\n \"connection:reconnecting\": { attempt: number; maxAttempts: number };\n \"connection:error\": { error: Error; context?: string };\n\n // 数据更新事件\n \"data:configUpdate\": AppConfig;\n \"data:statusUpdate\": ClientStatus;\n \"data:restartStatus\": RestartStatus;\n\n // 系统事件\n \"system:heartbeat\": { timestamp: number };\n \"system:message\": WebSocketMessage;\n \"system:error\": { error: Error; message?: WebSocketMessage };\n}\n\n/**\n * 事件监听器类型\n */\ntype EventListener<T = any> = (data: T) => void;\n\n/**\n * WebSocket 事件监听器类型(向后兼容)\n */\ninterface WebSocketEventListeners {\n connected: () => void;\n disconnected: () => void;\n configUpdate: (config: AppConfig) => void;\n statusUpdate: (status: ClientStatus) => void;\n restartStatus: (status: RestartStatus) => void;\n error: (error: Error) => void;\n}\n\n/**\n * WebSocket 连接状态\n */\nenum ConnectionState {\n DISCONNECTED = \"disconnected\",\n CONNECTING = \"connecting\",\n CONNECTED = \"connected\",\n RECONNECTING = \"reconnecting\",\n}\n\n/**\n * WebSocket 管理器配置\n */\ninterface WebSocketManagerConfig {\n url?: string;\n reconnectInterval?: number;\n maxReconnectAttempts?: number;\n heartbeatInterval?: number;\n heartbeatTimeout?: number;\n}\n\n/**\n * 事件总线类 - 支持多个订阅者\n */\nclass EventBus {\n private listeners: Map<string, Set<EventListener>> = new Map();\n\n /**\n * 订阅事件\n */\n on<K extends keyof EventBusEvents>(\n event: K,\n listener: EventListener<EventBusEvents[K]>\n ): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(listener);\n\n // 返回取消订阅函数\n return () => {\n this.off(event, listener);\n };\n }\n\n /**\n * 取消订阅事件\n */\n off<K extends keyof EventBusEvents>(\n event: K,\n listener: EventListener<EventBusEvents[K]>\n ): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n eventListeners.delete(listener);\n if (eventListeners.size === 0) {\n this.listeners.delete(event);\n }\n }\n }\n\n /**\n * 发布事件\n */\n emit<K extends keyof EventBusEvents>(\n event: K,\n data: EventBusEvents[K]\n ): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n for (const listener of eventListeners) {\n try {\n listener(data);\n } catch (error) {\n console.error(`[EventBus] 事件监听器执行失败 (${event}):`, error);\n }\n }\n }\n }\n\n /**\n * 清除所有监听器\n */\n clear(): void {\n this.listeners.clear();\n }\n\n /**\n * 获取事件监听器数量\n */\n getListenerCount(event?: keyof EventBusEvents): number {\n if (event) {\n return this.listeners.get(event)?.size || 0;\n }\n return Array.from(this.listeners.values()).reduce(\n (total, listeners) => total + listeners.size,\n 0\n );\n }\n}\n\n/**\n * WebSocket 管理器类 - 严格单例模式\n */\nexport class WebSocketManager {\n private static instance: WebSocketManager | null = null;\n private static isCreating = false;\n\n private ws: WebSocket | null = null;\n private url: string;\n private state: ConnectionState = ConnectionState.DISCONNECTED;\n private eventBus: EventBus = new EventBus();\n private legacyListeners: Partial<WebSocketEventListeners> = {};\n private reconnectAttempts = 0;\n private maxReconnectAttempts: number;\n private reconnectInterval: number;\n private reconnectTimer?: NodeJS.Timeout;\n private heartbeatTimer?: NodeJS.Timeout;\n private heartbeatInterval: number;\n private heartbeatTimeout: number;\n private lastHeartbeat = 0;\n\n private constructor(config: WebSocketManagerConfig = {}) {\n this.url = config.url || this.getDefaultWebSocketUrl();\n this.maxReconnectAttempts = config.maxReconnectAttempts || 5;\n this.reconnectInterval = config.reconnectInterval || 3000;\n this.heartbeatInterval = config.heartbeatInterval || 30000; // 30秒\n this.heartbeatTimeout = config.heartbeatTimeout || 35000; // 35秒\n\n // 设置向后兼容的事件桥接\n this.setupLegacyEventBridge();\n }\n\n /**\n * 获取单例实例\n */\n static getInstance(config?: WebSocketManagerConfig): WebSocketManager {\n if (WebSocketManager.instance) {\n return WebSocketManager.instance;\n }\n\n if (WebSocketManager.isCreating) {\n throw new Error(\"[WebSocketManager] 检测到循环创建,请检查代码逻辑\");\n }\n\n WebSocketManager.isCreating = true;\n try {\n WebSocketManager.instance = new WebSocketManager(config);\n console.log(\"[WebSocketManager] 单例实例已创建\");\n return WebSocketManager.instance;\n } finally {\n WebSocketManager.isCreating = false;\n }\n }\n\n /**\n * 重置单例实例(仅用于测试)\n */\n static resetInstance(): void {\n if (WebSocketManager.instance) {\n WebSocketManager.instance.disconnect();\n WebSocketManager.instance.eventBus.clear();\n WebSocketManager.instance = null;\n console.log(\"[WebSocketManager] 单例实例已重置\");\n }\n }\n\n /**\n * 设置向后兼容的事件桥接\n */\n private setupLegacyEventBridge(): void {\n // 连接状态事件桥接\n this.eventBus.on(\"connection:connected\", () => {\n this.legacyListeners.connected?.();\n });\n\n this.eventBus.on(\"connection:disconnected\", () => {\n this.legacyListeners.disconnected?.();\n });\n\n this.eventBus.on(\"connection:error\", ({ error }) => {\n this.legacyListeners.error?.(error);\n });\n\n // 数据更新事件桥接\n this.eventBus.on(\"data:configUpdate\", (config) => {\n this.legacyListeners.configUpdate?.(config);\n });\n\n this.eventBus.on(\"data:statusUpdate\", (status) => {\n this.legacyListeners.statusUpdate?.(status);\n });\n\n this.eventBus.on(\"data:restartStatus\", (status) => {\n this.legacyListeners.restartStatus?.(status);\n });\n }\n\n /**\n * 获取默认的 WebSocket URL\n */\n private getDefaultWebSocketUrl(): string {\n // 从 localStorage 获取自定义 URL\n const savedUrl = localStorage.getItem(\"xiaozhi-ws-url\");\n if (savedUrl) {\n return savedUrl;\n }\n\n // 根据当前页面 URL 构建 WebSocket URL\n const protocol = window.location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const hostname = window.location.hostname;\n const port = window.location.port;\n return `${protocol}//${hostname}${port ? `:${port}` : \"\"}`;\n }\n\n /**\n * 连接 WebSocket\n */\n connect(): void {\n if (\n this.state === ConnectionState.CONNECTED ||\n this.state === ConnectionState.CONNECTING\n ) {\n return;\n }\n\n this.state = ConnectionState.CONNECTING;\n console.log(`[WebSocket] 连接到: ${this.url}`);\n\n // 发布连接中事件\n this.eventBus.emit(\"connection:connecting\", undefined);\n\n try {\n this.ws = new WebSocket(this.url);\n this.setupEventHandlers();\n } catch (error) {\n console.error(\"[WebSocket] 连接失败:\", error);\n this.handleConnectionError(error as Error);\n }\n }\n\n /**\n * 断开 WebSocket 连接\n */\n disconnect(): void {\n console.log(\"[WebSocket] 主动断开连接\");\n\n this.clearTimers();\n this.state = ConnectionState.DISCONNECTED;\n this.reconnectAttempts = 0;\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n }\n\n /**\n * 新的事件订阅方法 - 使用事件总线\n */\n subscribe<K extends keyof EventBusEvents>(\n event: K,\n listener: EventListener<EventBusEvents[K]>\n ): () => void {\n return this.eventBus.on(event, listener);\n }\n\n /**\n * 取消事件订阅\n */\n unsubscribe<K extends keyof EventBusEvents>(\n event: K,\n listener: EventListener<EventBusEvents[K]>\n ): void {\n this.eventBus.off(event, listener);\n }\n\n /**\n * 获取事件总线实例(用于高级用法)\n */\n getEventBus(): EventBus {\n return this.eventBus;\n }\n\n /**\n * 设置事件监听器(向后兼容)\n * @deprecated 使用 subscribe 方法替代\n */\n on<K extends keyof WebSocketEventListeners>(\n event: K,\n listener: WebSocketEventListeners[K]\n ): void {\n console.warn(\"[WebSocketManager] on() 方法已废弃,请使用 subscribe() 方法\");\n this.legacyListeners[event] = listener;\n }\n\n /**\n * 移除事件监听器(向后兼容)\n * @deprecated 使用 unsubscribe 方法替代\n */\n off<K extends keyof WebSocketEventListeners>(event: K): void {\n console.warn(\n \"[WebSocketManager] off() 方法已废弃,请使用 unsubscribe() 方法\"\n );\n delete this.legacyListeners[event];\n }\n\n /**\n * 获取连接状态\n */\n getState(): ConnectionState {\n return this.state;\n }\n\n /**\n * 检查是否已连接\n */\n isConnected(): boolean {\n return (\n this.state === ConnectionState.CONNECTED &&\n this.ws?.readyState === WebSocket.OPEN\n );\n }\n\n /**\n * 更新 WebSocket URL\n */\n setUrl(url: string): void {\n if (this.url !== url) {\n this.url = url;\n localStorage.setItem(\"xiaozhi-ws-url\", url);\n\n // 如果当前已连接,重新连接到新 URL\n if (this.isConnected()) {\n this.disconnect();\n setTimeout(() => this.connect(), 1000);\n }\n }\n }\n\n /**\n * 发送消息\n */\n send(message: any): boolean {\n if (!this.isConnected()) {\n console.warn(\"[WebSocket] 连接未建立,无法发送消息\");\n return false;\n }\n\n try {\n const messageStr =\n typeof message === \"string\" ? message : JSON.stringify(message);\n this.ws!.send(messageStr);\n return true;\n } catch (error) {\n console.error(\"[WebSocket] 发送消息失败:\", error);\n this.eventBus.emit(\"connection:error\", {\n error: error as Error,\n context: \"send_message\",\n });\n return false;\n }\n }\n\n /**\n * 获取当前 URL\n */\n getUrl(): string {\n return this.url;\n }\n\n /**\n * 获取连接统计信息\n */\n getConnectionStats() {\n return {\n state: this.state,\n url: this.url,\n reconnectAttempts: this.reconnectAttempts,\n maxReconnectAttempts: this.maxReconnectAttempts,\n lastHeartbeat: this.lastHeartbeat,\n eventListenerCount: this.eventBus.getListenerCount(),\n };\n }\n\n /**\n * 设置 WebSocket 事件处理器\n */\n private setupEventHandlers(): void {\n if (!this.ws) return;\n\n this.ws.onopen = () => {\n console.log(\"[WebSocket] 连接已建立\");\n this.state = ConnectionState.CONNECTED;\n this.reconnectAttempts = 0;\n this.startHeartbeat();\n\n // 发布连接成功事件\n this.eventBus.emit(\"connection:connected\", undefined);\n };\n\n this.ws.onmessage = (event) => {\n try {\n const message: WebSocketMessage = JSON.parse(event.data);\n this.handleMessage(message);\n } catch (error) {\n console.error(\"[WebSocket] 消息解析失败:\", error);\n }\n };\n\n this.ws.onclose = (event) => {\n console.log(`[WebSocket] 连接已关闭 (code: ${event.code})`);\n this.handleConnectionClose();\n };\n\n this.ws.onerror = (error) => {\n console.error(\"[WebSocket] 连接错误:\", error);\n this.handleConnectionError(new Error(\"WebSocket 连接错误\"));\n };\n }\n\n /**\n * 处理 WebSocket 消息\n */\n private handleMessage(message: WebSocketMessage): void {\n console.log(\"[WebSocket] 收到消息:\", message.type);\n\n // 发布原始消息事件\n this.eventBus.emit(\"system:message\", message);\n\n try {\n switch (message.type) {\n case \"configUpdate\":\n case \"config\":\n if (message.data) {\n this.eventBus.emit(\"data:configUpdate\", message.data);\n }\n break;\n\n case \"statusUpdate\":\n case \"status\":\n if (message.data) {\n this.eventBus.emit(\"data:statusUpdate\", message.data);\n }\n break;\n\n case \"restartStatus\":\n if (message.data) {\n this.eventBus.emit(\"data:restartStatus\", message.data);\n }\n break;\n\n case \"heartbeatResponse\":\n this.lastHeartbeat = Date.now();\n this.eventBus.emit(\"system:heartbeat\", {\n timestamp: this.lastHeartbeat,\n });\n break;\n\n case \"error\": {\n const error = new Error(message.error?.message || \"服务器错误\");\n console.error(\"[WebSocket] 服务器错误:\", message.error);\n this.eventBus.emit(\"system:error\", { error, message });\n this.eventBus.emit(\"connection:error\", {\n error,\n context: \"server_error\",\n });\n break;\n }\n\n default:\n console.log(\"[WebSocket] 未处理的消息类型:\", message.type);\n }\n } catch (error) {\n console.error(\"[WebSocket] 消息处理失败:\", error);\n this.eventBus.emit(\"system:error\", {\n error: error as Error,\n message,\n });\n }\n }\n\n /**\n * 处理连接关闭\n */\n private handleConnectionClose(): void {\n this.state = ConnectionState.DISCONNECTED;\n this.clearTimers();\n\n // 发布连接断开事件\n this.eventBus.emit(\"connection:disconnected\", undefined);\n\n // 如果不是主动断开连接,尝试重连\n if (this.reconnectAttempts < this.maxReconnectAttempts) {\n this.scheduleReconnect();\n } else {\n console.error(\"[WebSocket] 达到最大重连次数,停止重连\");\n this.eventBus.emit(\"connection:error\", {\n error: new Error(\"达到最大重连次数\"),\n context: \"max_reconnect_attempts\",\n });\n }\n }\n\n /**\n * 处理连接错误\n */\n private handleConnectionError(error: Error): void {\n this.state = ConnectionState.DISCONNECTED;\n this.clearTimers();\n\n // 发布连接错误事件\n this.eventBus.emit(\"connection:error\", {\n error,\n context: \"connection_error\",\n });\n\n // 尝试重连\n if (this.reconnectAttempts < this.maxReconnectAttempts) {\n this.scheduleReconnect();\n } else {\n console.error(\"[WebSocket] 达到最大重连次数,停止重连\");\n }\n }\n\n /**\n * 安排重连\n */\n private scheduleReconnect(): void {\n this.reconnectAttempts++;\n this.state = ConnectionState.RECONNECTING;\n\n console.log(\n `[WebSocket] 安排重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts}) 在 ${this.reconnectInterval}ms 后`\n );\n\n // 发布重连事件\n this.eventBus.emit(\"connection:reconnecting\", {\n attempt: this.reconnectAttempts,\n maxAttempts: this.maxReconnectAttempts,\n });\n\n this.reconnectTimer = setTimeout(() => {\n this.connect();\n }, this.reconnectInterval);\n }\n\n /**\n * 开始心跳检测\n */\n private startHeartbeat(): void {\n this.lastHeartbeat = Date.now();\n\n this.heartbeatTimer = setInterval(() => {\n if (this.isConnected()) {\n // 发送心跳消息\n this.sendHeartbeat();\n\n // 检查心跳超时\n const now = Date.now();\n if (now - this.lastHeartbeat > this.heartbeatTimeout) {\n console.warn(\"[WebSocket] 心跳超时,重新连接\");\n this.disconnect();\n this.connect();\n }\n }\n }, this.heartbeatInterval);\n }\n\n /**\n * 发送心跳消息\n */\n private sendHeartbeat(): void {\n if (this.isConnected()) {\n const heartbeatMessage = {\n type: \"clientStatus\",\n data: {\n status: \"connected\" as const,\n timestamp: Date.now(),\n },\n };\n\n this.ws?.send(JSON.stringify(heartbeatMessage));\n }\n }\n\n /**\n * 清理定时器\n */\n private clearTimers(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = undefined;\n }\n\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = undefined;\n }\n }\n}\n\n// 创建默认的 WebSocket 管理器实例(使用单例模式)\nexport const webSocketManager = WebSocketManager.getInstance();\n\n// 导出类型和枚举\nexport { ConnectionState };\nexport type {\n WebSocketMessage,\n RestartStatus,\n WebSocketEventListeners,\n WebSocketManagerConfig,\n};\n","/**\n * 状态数据统一管理 Store\n *\n * 特性:\n * - 支持定时轮询和 WebSocket 实时更新\n * - 提供异步方法:getStatus()、refreshStatus()、restartService()\n * - 管理重启状态和服务状态\n * - 使用 Zustand 进行状态管理\n * - 提供选择器 hooks 优化组件渲染\n * - 集成 WebSocket 事件监听\n */\n\nimport { create } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport { apiClient } from \"../services/api\";\nimport { webSocketManager } from \"../services/websocket\";\nimport type { ClientStatus } from \"../types\";\n\n/**\n * 重启状态接口\n */\ninterface RestartStatus {\n status: \"restarting\" | \"completed\" | \"failed\";\n error?: string;\n timestamp: number;\n}\n\n/**\n * 服务状态接口\n */\ninterface ServiceStatus {\n running: boolean;\n mode?: string;\n pid?: number;\n}\n\n/**\n * 服务健康状态接口\n */\ninterface ServiceHealth {\n status: string;\n timestamp: number;\n uptime: number;\n memory: {\n rss: number;\n heapTotal: number;\n heapUsed: number;\n external: number;\n arrayBuffers: number;\n };\n version: string;\n}\n\n/**\n * 完整状态接口\n */\ninterface FullStatus {\n client: ClientStatus;\n restart?: RestartStatus;\n timestamp: number;\n}\n\n/**\n * 状态加载状态\n */\ninterface StatusLoadingState {\n isLoading: boolean;\n isRefreshing: boolean;\n isRestarting: boolean;\n lastUpdated: number | null;\n lastError: Error | null;\n}\n\n/**\n * 轮询配置\n */\ninterface PollingConfig {\n enabled: boolean;\n interval: number; // 毫秒\n maxRetries: number;\n currentRetries: number;\n}\n\n/**\n * 重启轮询配置\n */\ninterface RestartPollingConfig {\n enabled: boolean;\n interval: number; // 毫秒,重启检查间隔\n maxAttempts: number; // 最大检查次数\n currentAttempts: number; // 当前检查次数\n timeout: number; // 总超时时间(毫秒)\n startTime: number | null; // 开始时间戳\n}\n\n/**\n * 状态 Store 状态\n */\ninterface StatusState {\n // 状态数据\n clientStatus: ClientStatus | null;\n restartStatus: RestartStatus | null;\n serviceStatus: ServiceStatus | null;\n serviceHealth: ServiceHealth | null;\n fullStatus: FullStatus | null;\n\n // 加载状态\n loading: StatusLoadingState;\n\n // 轮询配置\n polling: PollingConfig;\n\n // 重启轮询配置\n restartPolling: RestartPollingConfig;\n\n // 状态来源追踪\n lastSource: \"http\" | \"websocket\" | \"polling\" | \"initial\" | null;\n}\n\n/**\n * 状态 Store 操作方法\n */\ninterface StatusActions {\n // 基础操作\n setClientStatus: (\n status: ClientStatus,\n source?: \"http\" | \"websocket\" | \"polling\" | \"initial\"\n ) => void;\n setRestartStatus: (\n status: RestartStatus | null,\n source?: \"http\" | \"websocket\" | \"polling\" | \"initial\"\n ) => void;\n setServiceStatus: (status: ServiceStatus) => void;\n setServiceHealth: (health: ServiceHealth) => void;\n setFullStatus: (\n status: FullStatus,\n source?: \"http\" | \"websocket\" | \"polling\" | \"initial\"\n ) => void;\n setLoading: (loading: Partial<StatusLoadingState>) => void;\n setError: (error: Error | null) => void;\n\n // 异步操作\n getStatus: () => Promise<FullStatus>;\n refreshStatus: () => Promise<FullStatus>;\n restartService: () => Promise<void>;\n getServiceStatus: () => Promise<ServiceStatus>;\n getServiceHealth: () => Promise<ServiceHealth>;\n\n // 轮询控制\n startPolling: (interval?: number) => void;\n stopPolling: () => void;\n setPollingConfig: (config: Partial<PollingConfig>) => void;\n\n // 重启轮询控制\n startRestartPolling: () => void;\n stopRestartPolling: () => void;\n setRestartPollingConfig: (config: Partial<RestartPollingConfig>) => void;\n\n // 工具方法\n reset: () => void;\n initialize: () => Promise<void>;\n}\n\n/**\n * 完整的状态 Store 接口\n */\nexport interface StatusStore extends StatusState, StatusActions {}\n\n/**\n * 初始状态\n */\nconst initialState: StatusState = {\n clientStatus: null,\n restartStatus: null,\n serviceStatus: null,\n serviceHealth: null,\n fullStatus: null,\n loading: {\n isLoading: false,\n isRefreshing: false,\n isRestarting: false,\n lastUpdated: null,\n lastError: null,\n },\n polling: {\n enabled: false,\n interval: 30000, // 30秒\n maxRetries: 3,\n currentRetries: 0,\n },\n restartPolling: {\n enabled: false,\n interval: 1000, // 1秒检查间隔\n maxAttempts: 60, // 最多检查60次(60秒)\n currentAttempts: 0,\n timeout: 60000, // 60秒总超时\n startTime: null,\n },\n lastSource: null,\n};\n\n/**\n * 轮询定时器引用\n */\nlet pollingTimer: NodeJS.Timeout | null = null;\n\n/**\n * 重启轮询定时器引用\n */\nlet restartPollingTimer: NodeJS.Timeout | null = null;\n\n/**\n * 创建状态 Store\n */\nexport const useStatusStore = create<StatusStore>()(\n devtools(\n (set, get) => ({\n ...initialState,\n\n // ==================== 基础操作 ====================\n\n setClientStatus: (status: ClientStatus, source = \"http\") => {\n console.log(`[StatusStore] 设置客户端状态,来源: ${source}`);\n set(\n (state) => ({\n clientStatus: status,\n lastSource: source,\n loading: {\n ...state.loading,\n lastUpdated: Date.now(),\n lastError: null,\n },\n }),\n false,\n \"setClientStatus\"\n );\n },\n\n setRestartStatus: (status: RestartStatus | null, source = \"http\") => {\n console.log(`[StatusStore] 设置重启状态,来源: ${source}`);\n set(\n (state) => ({\n restartStatus: status,\n lastSource: source,\n loading: {\n ...state.loading,\n lastUpdated: Date.now(),\n lastError: null,\n },\n }),\n false,\n \"setRestartStatus\"\n );\n },\n\n setServiceStatus: (status: ServiceStatus) => {\n console.log(\"[StatusStore] 设置服务状态\");\n set({ serviceStatus: status }, false, \"setServiceStatus\");\n },\n\n setServiceHealth: (health: ServiceHealth) => {\n console.log(\"[StatusStore] 设置服务健康状态\");\n set({ serviceHealth: health }, false, \"setServiceHealth\");\n },\n\n setFullStatus: (status: FullStatus, source = \"http\") => {\n console.log(`[StatusStore] 设置完整状态,来源: ${source}`);\n set(\n (state) => ({\n fullStatus: status,\n clientStatus: status.client,\n restartStatus: status.restart || null,\n lastSource: source,\n loading: {\n ...state.loading,\n lastUpdated: Date.now(),\n lastError: null,\n },\n }),\n false,\n \"setFullStatus\"\n );\n },\n\n setLoading: (loading: Partial<StatusLoadingState>) => {\n set(\n (state) => ({\n loading: { ...state.loading, ...loading },\n }),\n false,\n \"setLoading\"\n );\n },\n\n setError: (error: Error | null) => {\n set(\n (state) => ({\n loading: { ...state.loading, lastError: error },\n }),\n false,\n \"setError\"\n );\n },\n\n // ==================== 异步操作 ====================\n\n getStatus: async (): Promise<FullStatus> => {\n const { fullStatus, loading } = get();\n\n // 如果已有状态且不超过30秒,直接返回\n if (\n fullStatus &&\n loading.lastUpdated &&\n Date.now() - loading.lastUpdated < 30 * 1000\n ) {\n return fullStatus;\n }\n\n // 否则从服务器获取最新状态\n return get().refreshStatus();\n },\n\n refreshStatus: async (): Promise<FullStatus> => {\n const { setLoading, setFullStatus, setError, polling } = get();\n\n try {\n setLoading({ isRefreshing: true, lastError: null });\n console.log(\"[StatusStore] 开始刷新状态\");\n\n // 从服务器获取最新状态\n const status = await apiClient.getStatus();\n\n // 更新本地状态\n setFullStatus(status, \"http\");\n\n // 重置轮询重试计数\n if (polling.enabled) {\n get().setPollingConfig({ currentRetries: 0 });\n }\n\n console.log(\"[StatusStore] 状态刷新成功\");\n return status;\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"状态刷新失败\");\n console.error(\"[StatusStore] 状态刷新失败:\", err);\n setError(err);\n\n // 增加轮询重试计数\n if (polling.enabled) {\n const newRetries = polling.currentRetries + 1;\n get().setPollingConfig({ currentRetries: newRetries });\n\n // 如果达到最大重试次数,停止轮询\n if (newRetries >= polling.maxRetries) {\n console.warn(\"[StatusStore] 达到最大重试次数,停止轮询\");\n get().stopPolling();\n }\n }\n\n throw err;\n } finally {\n setLoading({ isRefreshing: false });\n }\n },\n\n restartService: async (): Promise<void> => {\n const { setLoading, setRestartStatus, setError, startRestartPolling } =\n get();\n\n try {\n setLoading({ isRestarting: true, lastError: null });\n console.log(\"[StatusStore] 开始重启服务\");\n\n // 设置重启状态\n setRestartStatus(\n {\n status: \"restarting\",\n timestamp: Date.now(),\n },\n \"http\"\n );\n\n // 调用重启 API\n await apiClient.restartService();\n\n console.log(\"[StatusStore] 服务重启请求已发送,开始重连检查\");\n\n // 启动重启后的重连检查轮询\n startRestartPolling();\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"服务重启失败\");\n console.error(\"[StatusStore] 服务重启失败:\", err);\n\n // 设置重启失败状态\n setRestartStatus(\n {\n status: \"failed\",\n error: err.message,\n timestamp: Date.now(),\n },\n \"http\"\n );\n\n setError(err);\n setLoading({ isRestarting: false });\n throw err;\n }\n },\n\n getServiceStatus: async (): Promise<ServiceStatus> => {\n try {\n console.log(\"[StatusStore] 获取服务状态\");\n const status = await apiClient.getServiceStatus();\n get().setServiceStatus(status);\n return status;\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"获取服务状态失败\");\n console.error(\"[StatusStore] 获取服务状态失败:\", err);\n get().setError(err);\n throw err;\n }\n },\n\n getServiceHealth: async (): Promise<ServiceHealth> => {\n try {\n console.log(\"[StatusStore] 获取服务健康状态\");\n const health = await apiClient.getServiceHealth();\n get().setServiceHealth(health);\n return health;\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"获取服务健康状态失败\");\n console.error(\"[StatusStore] 获取服务健康状态失败:\", err);\n get().setError(err);\n throw err;\n }\n },\n\n // ==================== 轮询控制 ====================\n\n startPolling: (interval = 30000) => {\n const { polling, refreshStatus } = get();\n\n if (polling.enabled) {\n console.log(\"[StatusStore] 轮询已启用,跳过启动\");\n return;\n }\n\n console.log(`[StatusStore] 启动状态轮询,间隔: ${interval}ms`);\n\n set(\n (state) => ({\n polling: {\n ...state.polling,\n enabled: true,\n interval,\n currentRetries: 0,\n },\n }),\n false,\n \"startPolling\"\n );\n\n // 立即执行一次刷新\n refreshStatus().catch((error) => {\n console.error(\"[StatusStore] 轮询初始刷新失败:\", error);\n });\n\n // 设置定时器\n pollingTimer = setInterval(() => {\n const currentState = get();\n if (!currentState.polling.enabled) {\n return;\n }\n\n refreshStatus().catch((error) => {\n console.error(\"[StatusStore] 轮询刷新失败:\", error);\n });\n }, interval);\n },\n\n stopPolling: () => {\n console.log(\"[StatusStore] 停止状态轮询\");\n\n set(\n (state) => ({\n polling: {\n ...state.polling,\n enabled: false,\n currentRetries: 0,\n },\n }),\n false,\n \"stopPolling\"\n );\n\n if (pollingTimer) {\n clearInterval(pollingTimer);\n pollingTimer = null;\n }\n },\n\n setPollingConfig: (config: Partial<PollingConfig>) => {\n set(\n (state) => ({\n polling: { ...state.polling, ...config },\n }),\n false,\n \"setPollingConfig\"\n );\n },\n\n // ==================== 重启轮询控制 ====================\n\n startRestartPolling: () => {\n const { restartPolling, refreshStatus, setRestartStatus, setLoading } =\n get();\n\n if (restartPolling.enabled) {\n console.log(\"[StatusStore] 重启轮询已启用,跳过启动\");\n return;\n }\n\n console.log(\"[StatusStore] 启动重启后重连检查轮询\");\n\n const startTime = Date.now();\n set(\n (state) => ({\n restartPolling: {\n ...state.restartPolling,\n enabled: true,\n currentAttempts: 0,\n startTime,\n },\n }),\n false,\n \"startRestartPolling\"\n );\n\n // 设置定时器进行重连检查\n restartPollingTimer = setInterval(async () => {\n const currentState = get();\n const { restartPolling: currentRestartPolling } = currentState;\n\n if (!currentRestartPolling.enabled) {\n return;\n }\n\n const elapsed = Date.now() - (currentRestartPolling.startTime || 0);\n const attempts = currentRestartPolling.currentAttempts + 1;\n\n console.log(\n `[StatusStore] 重启重连检查 (第 ${attempts} 次,已用时 ${Math.round(elapsed / 1000)}s)`\n );\n\n try {\n // 尝试获取状态以检查服务是否重连成功\n const status = await refreshStatus();\n\n // 检查是否重连成功\n const isReconnected = status.client?.status === \"connected\";\n\n if (isReconnected) {\n console.log(\"[StatusStore] 服务重连成功,停止重启轮询\");\n\n // 设置重启完成状态\n setRestartStatus(\n {\n status: \"completed\",\n timestamp: Date.now(),\n },\n \"polling\"\n );\n\n // 停止重启轮询和loading状态\n currentState.stopRestartPolling();\n setLoading({ isRestarting: false });\n return;\n }\n\n // 更新尝试次数\n currentState.setRestartPollingConfig({ currentAttempts: attempts });\n\n // 检查是否超时或达到最大尝试次数\n if (\n elapsed >= currentRestartPolling.timeout ||\n attempts >= currentRestartPolling.maxAttempts\n ) {\n console.warn(\"[StatusStore] 重启重连检查超时或达到最大尝试次数\");\n\n // 设置重启失败状态\n setRestartStatus(\n {\n status: \"failed\",\n error: \"重连超时,服务可能未成功重启\",\n timestamp: Date.now(),\n },\n \"polling\"\n );\n\n // 停止重启轮询和loading状态\n currentState.stopRestartPolling();\n setLoading({ isRestarting: false });\n }\n } catch (error) {\n console.log(\n `[StatusStore] 重启重连检查失败 (第 ${attempts} 次):`,\n error\n );\n\n // 更新尝试次数\n currentState.setRestartPollingConfig({ currentAttempts: attempts });\n\n // 检查是否超时或达到最大尝试次数\n if (\n elapsed >= currentRestartPolling.timeout ||\n attempts >= currentRestartPolling.maxAttempts\n ) {\n console.error(\"[StatusStore] 重启重连检查超时或达到最大尝试次数\");\n\n // 设置重启失败状态\n setRestartStatus(\n {\n status: \"failed\",\n error: \"重连超时,服务可能未成功重启\",\n timestamp: Date.now(),\n },\n \"polling\"\n );\n\n // 停止重启轮询和loading状态\n currentState.stopRestartPolling();\n setLoading({ isRestarting: false });\n }\n }\n }, restartPolling.interval);\n },\n\n stopRestartPolling: () => {\n console.log(\"[StatusStore] 停止重启轮询\");\n\n set(\n (state) => ({\n restartPolling: {\n ...state.restartPolling,\n enabled: false,\n currentAttempts: 0,\n startTime: null,\n },\n }),\n false,\n \"stopRestartPolling\"\n );\n\n if (restartPollingTimer) {\n clearInterval(restartPollingTimer);\n restartPollingTimer = null;\n }\n },\n\n setRestartPollingConfig: (config: Partial<RestartPollingConfig>) => {\n set(\n (state) => ({\n restartPolling: { ...state.restartPolling, ...config },\n }),\n false,\n \"setRestartPollingConfig\"\n );\n },\n\n // ==================== 工具方法 ====================\n\n reset: () => {\n console.log(\"[StatusStore] 重置状态\");\n\n // 停止所有轮询\n get().stopPolling();\n get().stopRestartPolling();\n\n // 重置状态\n set(initialState, false, \"reset\");\n },\n\n initialize: async (): Promise<void> => {\n const { setLoading, refreshStatus } = get();\n\n try {\n setLoading({ isLoading: true });\n console.log(\"[StatusStore] 初始化状态 Store\");\n\n // 设置 WebSocket 事件监听\n webSocketManager.subscribe(\"data:statusUpdate\", (status) => {\n console.log(\"[StatusStore] 收到 WebSocket 状态更新\");\n get().setClientStatus(status, \"websocket\");\n });\n\n webSocketManager.subscribe(\"data:restartStatus\", (status) => {\n console.log(\"[StatusStore] 收到 WebSocket 重启状态更新\");\n get().setRestartStatus(status, \"websocket\");\n });\n\n // 获取初始状态\n await refreshStatus();\n\n // 启动轮询(可选)\n // get().startPolling();\n\n console.log(\"[StatusStore] 状态 Store 初始化完成\");\n } catch (error) {\n console.error(\"[StatusStore] 状态 Store 初始化失败:\", error);\n throw error;\n } finally {\n setLoading({ isLoading: false });\n }\n },\n }),\n {\n name: \"status-store\",\n }\n )\n);\n\n// ==================== 选择器 Hooks ====================\n\n/**\n * 获取客户端状态\n */\nexport const useClientStatus = () =>\n useStatusStore((state) => state.clientStatus);\n\n/**\n * 获取重启状态\n */\nexport const useRestartStatus = () =>\n useStatusStore((state) => state.restartStatus);\n\n/**\n * 获取服务状态\n */\nexport const useServiceStatus = () =>\n useStatusStore((state) => state.serviceStatus);\n\n/**\n * 获取服务健康状态\n */\nexport const useServiceHealth = () =>\n useStatusStore((state) => state.serviceHealth);\n\n/**\n * 获取完整状态\n */\nexport const useFullStatus = () => useStatusStore((state) => state.fullStatus);\n\n/**\n * 获取状态加载状态\n */\nexport const useStatusLoading = () => useStatusStore((state) => state.loading);\n\n/**\n * 获取状态是否正在加载\n */\nexport const useStatusIsLoading = () =>\n useStatusStore(\n (state) => state.loading.isLoading || state.loading.isRefreshing\n );\n\n/**\n * 获取状态是否正在重启\n */\nexport const useStatusIsRestarting = () =>\n useStatusStore((state) => state.loading.isRestarting);\n\n/**\n * 获取状态错误\n */\nexport const useStatusError = () =>\n useStatusStore((state) => state.loading.lastError);\n\n/**\n * 获取轮询配置\n */\nexport const usePollingConfig = () => useStatusStore((state) => state.polling);\n\n/**\n * 获取轮询是否启用\n */\nexport const usePollingEnabled = () =>\n useStatusStore((state) => state.polling.enabled);\n\n/**\n * 获取重启轮询状态\n */\nexport const useRestartPollingStatus = () =>\n useStatusStore((state) => state.restartPolling);\n\n/**\n * 获取状态来源\n */\nexport const useStatusSource = () =>\n useStatusStore((state) => state.lastSource);\n\n/**\n * 获取连接状态(从客户端状态中提取)\n */\nexport const useConnectionStatus = () =>\n useStatusStore((state) => state.clientStatus?.status === \"connected\");\n\n/**\n * 获取 MCP 端点(从客户端状态中提取)\n */\nexport const useStatusMcpEndpoint = () =>\n useStatusStore((state) => state.clientStatus?.mcpEndpoint);\n\n/**\n * 获取活跃的 MCP 服务器(从客户端状态中提取)\n */\nexport const useActiveMcpServers = () =>\n useStatusStore((state) => state.clientStatus?.activeMCPServers || []);\n\n/**\n * 获取最后心跳时间(从客户端状态中提取)\n */\nexport const useLastHeartbeat = () =>\n useStatusStore((state) => state.clientStatus?.lastHeartbeat);\n\n// ==================== 复合选择器 ====================\n\n/**\n * 获取状态数据和加载状态\n */\nexport const useStatusWithLoading = () =>\n useStatusStore(\n useShallow((state) => ({\n clientStatus: state.clientStatus,\n restartStatus: state.restartStatus,\n fullStatus: state.fullStatus,\n isLoading: state.loading.isLoading || state.loading.isRefreshing,\n isRestarting: state.loading.isRestarting,\n error: state.loading.lastError,\n }))\n );\n\n/**\n * 获取服务相关状态\n */\nexport const useServiceInfo = () =>\n useStatusStore(\n useShallow((state) => ({\n status: state.serviceStatus,\n health: state.serviceHealth,\n isRestarting: state.loading.isRestarting,\n restartStatus: state.restartStatus,\n }))\n );\n\n/**\n * 获取连接相关信息\n */\nexport const useConnectionInfo = () =>\n useStatusStore(\n useShallow((state) => ({\n connected: state.clientStatus?.status === \"connected\",\n endpoint: state.clientStatus?.mcpEndpoint,\n activeServers: state.clientStatus?.activeMCPServers || [],\n lastHeartbeat: state.clientStatus?.lastHeartbeat,\n }))\n );\n\n// ==================== 操作方法 Hooks ====================\n\n/**\n * 获取状态操作方法\n */\nexport const useStatusActions = () =>\n useStatusStore(\n useShallow((state) => ({\n getStatus: state.getStatus,\n refreshStatus: state.refreshStatus,\n restartService: state.restartService,\n getServiceStatus: state.getServiceStatus,\n getServiceHealth: state.getServiceHealth,\n startPolling: state.startPolling,\n stopPolling: state.stopPolling,\n setPollingConfig: state.setPollingConfig,\n startRestartPolling: state.startRestartPolling,\n stopRestartPolling: state.stopRestartPolling,\n setRestartPollingConfig: state.setRestartPollingConfig,\n reset: state.reset,\n initialize: state.initialize,\n }))\n );\n\n/**\n * 获取轮询控制方法\n */\nexport const usePollingActions = () =>\n useStatusStore(\n useShallow((state) => ({\n startPolling: state.startPolling,\n stopPolling: state.stopPolling,\n setPollingConfig: state.setPollingConfig,\n enabled: state.polling.enabled,\n interval: state.polling.interval,\n }))\n );\n","/**\n * 重启通知管理 Hook\n *\n * 职责:\n * - 监听重启状态变化\n * - 在适当时机显示 toast 通知\n * - 避免重复通知\n * - 提供一致的用户体验\n */\n\nimport { useRestartPollingStatus, useRestartStatus } from \"@/stores/status\";\nimport { useEffect, useRef } from \"react\";\nimport { toast } from \"sonner\";\n\n/**\n * 重启通知管理 Hook\n *\n * 使用方式:\n * 1. 在应用的根组件或布局组件中调用\n * 2. 自动监听重启状态变化并显示相应通知\n * 3. 确保全局只有一个实例在运行\n */\nexport function useRestartNotifications() {\n const restartStatus = useRestartStatus();\n const restartPollingStatus = useRestartPollingStatus();\n\n // 使用 ref 来跟踪已显示的通知,避免重复\n const lastNotifiedStatus = useRef<string | null>(null);\n const lastNotifiedTimestamp = useRef<number | null>(null);\n\n useEffect(() => {\n if (!restartStatus) {\n return;\n }\n\n const { status, timestamp, error } = restartStatus;\n\n // 避免重复通知:检查状态和时间戳是否已经通知过\n if (\n lastNotifiedStatus.current === status &&\n lastNotifiedTimestamp.current === timestamp\n ) {\n return;\n }\n\n // 更新已通知的状态\n lastNotifiedStatus.current = status;\n lastNotifiedTimestamp.current = timestamp;\n\n switch (status) {\n case \"restarting\":\n // 重启开始时的通知(可选,根据需要启用)\n toast.info(\"正在重启服务...\", {\n id: \"restart-status-progress\", // 使用固定ID避免重复\n description: \"请耐心等待\",\n duration: 0,\n });\n break;\n\n case \"completed\": {\n // 重启成功通知\n const successMessage = restartPollingStatus.enabled\n ? `服务重启成功!重连检查完成 (${restartPollingStatus.currentAttempts}次检查)`\n : \"服务重启成功!\";\n\n toast.dismiss(\"restart-status-progress\");\n toast.success(successMessage, {\n id: \"restart-status-success\", // 替换之前的通知\n description: \"服务已恢复正常运行\",\n });\n break;\n }\n\n case \"failed\": {\n // 重启失败通知\n const failureMessage = error || \"服务重启失败\";\n const description = restartPollingStatus.enabled\n ? `重连检查超时 (${restartPollingStatus.currentAttempts}/${restartPollingStatus.maxAttempts}次)`\n : \"请检查服务状态或稍后重试\";\n\n toast.dismiss(\"restart-status-progress\");\n toast.error(failureMessage, {\n id: \"restart-status-failed\", // 替换之前的通知\n description,\n });\n break;\n }\n }\n }, [restartStatus, restartPollingStatus]);\n\n // 清理函数:组件卸载时清理状态\n useEffect(() => {\n return () => {\n lastNotifiedStatus.current = null;\n lastNotifiedTimestamp.current = null;\n };\n }, []);\n}\n\n/**\n * 重启通知提供者组件\n *\n * 使用方式:在应用根组件中使用\n * <RestartNotificationProvider />\n */\nexport function RestartNotificationProvider() {\n useRestartNotifications();\n return null; // 这是一个纯逻辑组件,不渲染任何内容\n}\n","import type { ClassValue } from \"clsx\";\nimport { clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { Slot } from \"@radix-ui/react-slot\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline:\n \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3\",\n lg: \"h-11 rounded-md px-8\",\n icon: \"h-10 w-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n {...props}\n />\n );\n }\n);\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n","import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Input = React.forwardRef<HTMLInputElement, React.ComponentProps<\"input\">>(\n ({ className, type, ...props }, ref) => {\n return (\n <input\n type={type}\n className={cn(\n \"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n className\n )}\n ref={ref}\n {...props}\n />\n );\n }\n);\nInput.displayName = \"Input\";\n\nexport { Input };\n","import * as SeparatorPrimitive from \"@radix-ui/react-separator\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Separator = React.forwardRef<\n React.ElementRef<typeof SeparatorPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>\n>(\n (\n { className, orientation = \"horizontal\", decorative = true, ...props },\n ref\n ) => (\n <SeparatorPrimitive.Root\n ref={ref}\n decorative={decorative}\n orientation={orientation}\n className={cn(\n \"shrink-0 bg-border\",\n orientation === \"horizontal\" ? \"h-[1px] w-full\" : \"h-full w-[1px]\",\n className\n )}\n {...props}\n />\n )\n);\nSeparator.displayName = SeparatorPrimitive.Root.displayName;\n\nexport { Separator };\n","\"use client\";\n\nimport * as SheetPrimitive from \"@radix-ui/react-dialog\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport { X } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Sheet = SheetPrimitive.Root;\n\nconst SheetTrigger = SheetPrimitive.Trigger;\n\nconst SheetClose = SheetPrimitive.Close;\n\nconst SheetPortal = SheetPrimitive.Portal;\n\nconst SheetOverlay = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Overlay\n className={cn(\n \"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n ref={ref}\n />\n));\nSheetOverlay.displayName = SheetPrimitive.Overlay.displayName;\n\nconst sheetVariants = cva(\n \"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500\",\n {\n variants: {\n side: {\n top: \"inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top\",\n bottom:\n \"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\",\n left: \"inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm\",\n right:\n \"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm\",\n },\n },\n defaultVariants: {\n side: \"right\",\n },\n }\n);\n\ninterface SheetContentProps\n extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,\n VariantProps<typeof sheetVariants> {}\n\nconst SheetContent = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Content>,\n SheetContentProps\n>(({ side = \"right\", className, children, ...props }, ref) => (\n <SheetPortal>\n <SheetOverlay />\n <SheetPrimitive.Content\n ref={ref}\n className={cn(sheetVariants({ side }), className)}\n {...props}\n >\n {children}\n <SheetPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary\">\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </SheetPrimitive.Close>\n </SheetPrimitive.Content>\n </SheetPortal>\n));\nSheetContent.displayName = SheetPrimitive.Content.displayName;\n\nconst SheetHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-2 text-center sm:text-left\",\n className\n )}\n {...props}\n />\n);\nSheetHeader.displayName = \"SheetHeader\";\n\nconst SheetFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className\n )}\n {...props}\n />\n);\nSheetFooter.displayName = \"SheetFooter\";\n\nconst SheetTitle = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Title\n ref={ref}\n className={cn(\"text-lg font-semibold text-foreground\", className)}\n {...props}\n />\n));\nSheetTitle.displayName = SheetPrimitive.Title.displayName;\n\nconst SheetDescription = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nSheetDescription.displayName = SheetPrimitive.Description.displayName;\n\nexport {\n Sheet,\n SheetPortal,\n SheetOverlay,\n SheetTrigger,\n SheetClose,\n SheetContent,\n SheetHeader,\n SheetFooter,\n SheetTitle,\n SheetDescription,\n};\n","import { cn } from \"@/lib/utils\";\n\nfunction Skeleton({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n className={cn(\"animate-pulse rounded-md bg-muted\", className)}\n {...props}\n />\n );\n}\n\nexport { Skeleton };\n","import * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst TooltipProvider = TooltipPrimitive.Provider;\n\nconst Tooltip = TooltipPrimitive.Root;\n\nconst TooltipTrigger = TooltipPrimitive.Trigger;\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <TooltipPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]\",\n className\n )}\n {...props}\n />\n));\nTooltipContent.displayName = TooltipPrimitive.Content.displayName;\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n","import * as React from \"react\";\n\nconst MOBILE_BREAKPOINT = 768;\n\nexport function useIsMobile() {\n const [isMobile, setIsMobile] = React.useState<boolean | undefined>(\n undefined\n );\n\n React.useEffect(() => {\n const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);\n const onChange = () => {\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\n };\n mql.addEventListener(\"change\", onChange);\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\n return () => mql.removeEventListener(\"change\", onChange);\n }, []);\n\n return !!isMobile;\n}\n","\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport { PanelLeft } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Separator } from \"@/components/ui/separator\";\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n} from \"@/components/ui/sheet\";\nimport { Skeleton } from \"@/components/ui/skeleton\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport { useIsMobile } from \"@/hooks/use-mobile\";\nimport { cn } from \"@/lib/utils\";\n\nconst SIDEBAR_COOKIE_NAME = \"sidebar_state\";\nconst SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;\nconst SIDEBAR_WIDTH = \"16rem\";\nconst SIDEBAR_WIDTH_MOBILE = \"18rem\";\nconst SIDEBAR_WIDTH_ICON = \"3rem\";\nconst SIDEBAR_KEYBOARD_SHORTCUT = \"b\";\n\ntype SidebarContextProps = {\n state: \"expanded\" | \"collapsed\";\n open: boolean;\n setOpen: (open: boolean) => void;\n openMobile: boolean;\n setOpenMobile: (open: boolean) => void;\n isMobile: boolean;\n toggleSidebar: () => void;\n};\n\nconst SidebarContext = React.createContext<SidebarContextProps | null>(null);\n\nfunction useSidebar() {\n const context = React.useContext(SidebarContext);\n if (!context) {\n throw new Error(\"useSidebar must be used within a SidebarProvider.\");\n }\n\n return context;\n}\n\nconst SidebarProvider = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n defaultOpen?: boolean;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n }\n>(\n (\n {\n defaultOpen = true,\n open: openProp,\n onOpenChange: setOpenProp,\n className,\n style,\n children,\n ...props\n },\n ref\n ) => {\n const isMobile = useIsMobile();\n const [openMobile, setOpenMobile] = React.useState(false);\n\n // This is the internal state of the sidebar.\n // We use openProp and setOpenProp for control from outside the component.\n const [_open, _setOpen] = React.useState(defaultOpen);\n const open = openProp ?? _open;\n const setOpen = React.useCallback(\n (value: boolean | ((value: boolean) => boolean)) => {\n const openState = typeof value === \"function\" ? value(open) : value;\n if (setOpenProp) {\n setOpenProp(openState);\n } else {\n _setOpen(openState);\n }\n\n // This sets the cookie to keep the sidebar state.\n document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;\n },\n [setOpenProp, open]\n );\n\n // Helper to toggle the sidebar.\n const toggleSidebar = React.useCallback(() => {\n return isMobile\n ? setOpenMobile((open) => !open)\n : setOpen((open) => !open);\n }, [isMobile, setOpen]);\n\n // Adds a keyboard shortcut to toggle the sidebar.\n React.useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (\n event.key === SIDEBAR_KEYBOARD_SHORTCUT &&\n (event.metaKey || event.ctrlKey)\n ) {\n event.preventDefault();\n toggleSidebar();\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [toggleSidebar]);\n\n // We add a state so that we can do data-state=\"expanded\" or \"collapsed\".\n // This makes it easier to style the sidebar with Tailwind classes.\n const state = open ? \"expanded\" : \"collapsed\";\n\n const contextValue = React.useMemo<SidebarContextProps>(\n () => ({\n state,\n open,\n setOpen,\n isMobile,\n openMobile,\n setOpenMobile,\n toggleSidebar,\n }),\n [state, open, setOpen, isMobile, openMobile, toggleSidebar]\n );\n\n return (\n <SidebarContext.Provider value={contextValue}>\n <TooltipProvider delayDuration={0}>\n <div\n style={\n {\n \"--sidebar-width\": SIDEBAR_WIDTH,\n \"--sidebar-width-icon\": SIDEBAR_WIDTH_ICON,\n ...style,\n } as React.CSSProperties\n }\n className={cn(\n \"group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar\",\n className\n )}\n ref={ref}\n {...props}\n >\n {children}\n </div>\n </TooltipProvider>\n </SidebarContext.Provider>\n );\n }\n);\nSidebarProvider.displayName = \"SidebarProvider\";\n\nconst Sidebar = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n side?: \"left\" | \"right\";\n variant?: \"sidebar\" | \"floating\" | \"inset\";\n collapsible?: \"offcanvas\" | \"icon\" | \"none\";\n }\n>(\n (\n {\n side = \"left\",\n variant = \"sidebar\",\n collapsible = \"offcanvas\",\n className,\n children,\n ...props\n },\n ref\n ) => {\n const { isMobile, state, openMobile, setOpenMobile } = useSidebar();\n\n if (collapsible === \"none\") {\n return (\n <div\n className={cn(\n \"flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground\",\n className\n )}\n ref={ref}\n {...props}\n >\n {children}\n </div>\n );\n }\n\n if (isMobile) {\n return (\n <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>\n <SheetContent\n data-sidebar=\"sidebar\"\n data-mobile=\"true\"\n className=\"w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden\"\n style={\n {\n \"--sidebar-width\": SIDEBAR_WIDTH_MOBILE,\n } as React.CSSProperties\n }\n side={side}\n >\n <SheetHeader className=\"sr-only\">\n <SheetTitle>Sidebar</SheetTitle>\n <SheetDescription>Displays the mobile sidebar.</SheetDescription>\n </SheetHeader>\n <div className=\"flex h-full w-full flex-col\">{children}</div>\n </SheetContent>\n </Sheet>\n );\n }\n\n return (\n <div\n ref={ref}\n className=\"group peer hidden text-sidebar-foreground md:block\"\n data-state={state}\n data-collapsible={state === \"collapsed\" ? collapsible : \"\"}\n data-variant={variant}\n data-side={side}\n >\n {/* This is what handles the sidebar gap on desktop */}\n <div\n className={cn(\n \"relative w-[--sidebar-width] bg-transparent transition-[width] duration-200 ease-linear\",\n \"group-data-[collapsible=offcanvas]:w-0\",\n \"group-data-[side=right]:rotate-180\",\n variant === \"floating\" || variant === \"inset\"\n ? \"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]\"\n : \"group-data-[collapsible=icon]:w-[--sidebar-width-icon]\"\n )}\n />\n <div\n className={cn(\n \"fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] duration-200 ease-linear md:flex\",\n side === \"left\"\n ? \"left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]\"\n : \"right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]\",\n // Adjust the padding for floating and inset variants.\n variant === \"floating\" || variant === \"inset\"\n ? \"p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]\"\n : \"group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l\",\n className\n )}\n {...props}\n >\n <div\n data-sidebar=\"sidebar\"\n className=\"flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow\"\n >\n {children}\n </div>\n </div>\n </div>\n );\n }\n);\nSidebar.displayName = \"Sidebar\";\n\nconst SidebarTrigger = React.forwardRef<\n React.ElementRef<typeof Button>,\n React.ComponentProps<typeof Button>\n>(({ className, onClick, ...props }, ref) => {\n const { toggleSidebar } = useSidebar();\n\n return (\n <Button\n ref={ref}\n data-sidebar=\"trigger\"\n variant=\"ghost\"\n size=\"icon\"\n className={cn(\"h-7 w-7\", className)}\n onClick={(event) => {\n onClick?.(event);\n toggleSidebar();\n }}\n {...props}\n >\n <PanelLeft />\n <span className=\"sr-only\">Toggle Sidebar</span>\n </Button>\n );\n});\nSidebarTrigger.displayName = \"SidebarTrigger\";\n\nconst SidebarRail = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\">\n>(({ className, ...props }, ref) => {\n const { toggleSidebar } = useSidebar();\n\n return (\n <button\n ref={ref}\n data-sidebar=\"rail\"\n aria-label=\"Toggle Sidebar\"\n tabIndex={-1}\n onClick={toggleSidebar}\n title=\"Toggle Sidebar\"\n className={cn(\n \"absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex\",\n \"[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize\",\n \"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize\",\n \"group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar\",\n \"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2\",\n \"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2\",\n className\n )}\n {...props}\n />\n );\n});\nSidebarRail.displayName = \"SidebarRail\";\n\nconst SidebarInset = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"main\">\n>(({ className, ...props }, ref) => {\n return (\n <main\n ref={ref}\n className={cn(\n \"relative flex w-full flex-1 flex-col bg-background\",\n \"md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow\",\n className\n )}\n {...props}\n />\n );\n});\nSidebarInset.displayName = \"SidebarInset\";\n\nconst SidebarInput = React.forwardRef<\n React.ElementRef<typeof Input>,\n React.ComponentProps<typeof Input>\n>(({ className, ...props }, ref) => {\n return (\n <Input\n ref={ref}\n data-sidebar=\"input\"\n className={cn(\n \"h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring\",\n className\n )}\n {...props}\n />\n );\n});\nSidebarInput.displayName = \"SidebarInput\";\n\nconst SidebarHeader = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"header\"\n className={cn(\"flex flex-col gap-2 p-2\", className)}\n {...props}\n />\n );\n});\nSidebarHeader.displayName = \"SidebarHeader\";\n\nconst SidebarFooter = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"footer\"\n className={cn(\"flex flex-col gap-2 p-2\", className)}\n {...props}\n />\n );\n});\nSidebarFooter.displayName = \"SidebarFooter\";\n\nconst SidebarSeparator = React.forwardRef<\n React.ElementRef<typeof Separator>,\n React.ComponentProps<typeof Separator>\n>(({ className, ...props }, ref) => {\n return (\n <Separator\n ref={ref}\n data-sidebar=\"separator\"\n className={cn(\"mx-2 w-auto bg-sidebar-border\", className)}\n {...props}\n />\n );\n});\nSidebarSeparator.displayName = \"SidebarSeparator\";\n\nconst SidebarContent = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"content\"\n className={cn(\n \"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden\",\n className\n )}\n {...props}\n />\n );\n});\nSidebarContent.displayName = \"SidebarContent\";\n\nconst SidebarGroup = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"group\"\n className={cn(\"relative flex w-full min-w-0 flex-col p-2\", className)}\n {...props}\n />\n );\n});\nSidebarGroup.displayName = \"SidebarGroup\";\n\nconst SidebarGroupLabel = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & { asChild?: boolean }\n>(({ className, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"div\";\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"group-label\"\n className={cn(\n \"flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n \"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0\",\n className\n )}\n {...props}\n />\n );\n});\nSidebarGroupLabel.displayName = \"SidebarGroupLabel\";\n\nconst SidebarGroupAction = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> & { asChild?: boolean }\n>(({ className, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"group-action\"\n className={cn(\n \"absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n // Increases the hit area of the button on mobile.\n \"after:absolute after:-inset-2 after:md:hidden\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n );\n});\nSidebarGroupAction.displayName = \"SidebarGroupAction\";\n\nconst SidebarGroupContent = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-sidebar=\"group-content\"\n className={cn(\"w-full text-sm\", className)}\n {...props}\n />\n));\nSidebarGroupContent.displayName = \"SidebarGroupContent\";\n\nconst SidebarMenu = React.forwardRef<\n HTMLUListElement,\n React.ComponentProps<\"ul\">\n>(({ className, ...props }, ref) => (\n <ul\n ref={ref}\n data-sidebar=\"menu\"\n className={cn(\"flex w-full min-w-0 flex-col gap-1\", className)}\n {...props}\n />\n));\nSidebarMenu.displayName = \"SidebarMenu\";\n\nconst SidebarMenuItem = React.forwardRef<\n HTMLLIElement,\n React.ComponentProps<\"li\">\n>(({ className, ...props }, ref) => (\n <li\n ref={ref}\n data-sidebar=\"menu-item\"\n className={cn(\"group/menu-item relative\", className)}\n {...props}\n />\n));\nSidebarMenuItem.displayName = \"SidebarMenuItem\";\n\nconst sidebarMenuButtonVariants = cva(\n \"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"hover:bg-sidebar-accent hover:text-sidebar-accent-foreground\",\n outline:\n \"bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]\",\n },\n size: {\n default: \"h-8 text-sm\",\n sm: \"h-7 text-xs\",\n lg: \"h-12 text-sm group-data-[collapsible=icon]:!p-0\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\n\nconst SidebarMenuButton = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> & {\n asChild?: boolean;\n isActive?: boolean;\n tooltip?: string | React.ComponentProps<typeof TooltipContent>;\n } & VariantProps<typeof sidebarMenuButtonVariants>\n>(\n (\n {\n asChild = false,\n isActive = false,\n variant = \"default\",\n size = \"default\",\n tooltip,\n className,\n ...props\n },\n ref\n ) => {\n const Comp = asChild ? Slot : \"button\";\n const { isMobile, state } = useSidebar();\n\n const button = (\n <Comp\n ref={ref}\n data-sidebar=\"menu-button\"\n data-size={size}\n data-active={isActive}\n className={cn(sidebarMenuButtonVariants({ variant, size }), className)}\n {...props}\n />\n );\n\n if (!tooltip) {\n return button;\n }\n\n if (typeof tooltip === \"string\") {\n tooltip = {\n children: tooltip,\n };\n }\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>{button}</TooltipTrigger>\n <TooltipContent\n side=\"right\"\n align=\"center\"\n hidden={state !== \"collapsed\" || isMobile}\n {...tooltip}\n />\n </Tooltip>\n );\n }\n);\nSidebarMenuButton.displayName = \"SidebarMenuButton\";\n\nconst SidebarMenuAction = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> & {\n asChild?: boolean;\n showOnHover?: boolean;\n }\n>(({ className, asChild = false, showOnHover = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"menu-action\"\n className={cn(\n \"absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg]:size-4 [&>svg]:shrink-0\",\n // Increases the hit area of the button on mobile.\n \"after:absolute after:-inset-2 after:md:hidden\",\n \"peer-data-[size=sm]/menu-button:top-1\",\n \"peer-data-[size=default]/menu-button:top-1.5\",\n \"peer-data-[size=lg]/menu-button:top-2.5\",\n \"group-data-[collapsible=icon]:hidden\",\n showOnHover &&\n \"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0\",\n className\n )}\n {...props}\n />\n );\n});\nSidebarMenuAction.displayName = \"SidebarMenuAction\";\n\nconst SidebarMenuBadge = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-sidebar=\"menu-badge\"\n className={cn(\n \"pointer-events-none absolute right-1 flex h-5 min-w-5 select-none items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground\",\n \"peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground\",\n \"peer-data-[size=sm]/menu-button:top-1\",\n \"peer-data-[size=default]/menu-button:top-1.5\",\n \"peer-data-[size=lg]/menu-button:top-2.5\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n));\nSidebarMenuBadge.displayName = \"SidebarMenuBadge\";\n\nconst SidebarMenuSkeleton = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n showIcon?: boolean;\n }\n>(({ className, showIcon = false, ...props }, ref) => {\n // Random width between 50 to 90%.\n const width = React.useMemo(() => {\n return `${Math.floor(Math.random() * 40) + 50}%`;\n }, []);\n\n return (\n <div\n ref={ref}\n data-sidebar=\"menu-skeleton\"\n className={cn(\"flex h-8 items-center gap-2 rounded-md px-2\", className)}\n {...props}\n >\n {showIcon && (\n <Skeleton\n className=\"size-4 rounded-md\"\n data-sidebar=\"menu-skeleton-icon\"\n />\n )}\n <Skeleton\n className=\"h-4 max-w-[--skeleton-width] flex-1\"\n data-sidebar=\"menu-skeleton-text\"\n style={\n {\n \"--skeleton-width\": width,\n } as React.CSSProperties\n }\n />\n </div>\n );\n});\nSidebarMenuSkeleton.displayName = \"SidebarMenuSkeleton\";\n\nconst SidebarMenuSub = React.forwardRef<\n HTMLUListElement,\n React.ComponentProps<\"ul\">\n>(({ className, ...props }, ref) => (\n <ul\n ref={ref}\n data-sidebar=\"menu-sub\"\n className={cn(\n \"mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n));\nSidebarMenuSub.displayName = \"SidebarMenuSub\";\n\nconst SidebarMenuSubItem = React.forwardRef<\n HTMLLIElement,\n React.ComponentProps<\"li\">\n>(({ ...props }, ref) => <li ref={ref} {...props} />);\nSidebarMenuSubItem.displayName = \"SidebarMenuSubItem\";\n\nconst SidebarMenuSubButton = React.forwardRef<\n HTMLAnchorElement,\n React.ComponentProps<\"a\"> & {\n asChild?: boolean;\n size?: \"sm\" | \"md\";\n isActive?: boolean;\n }\n>(({ asChild = false, size = \"md\", isActive, className, ...props }, ref) => {\n const Comp = asChild ? Slot : \"a\";\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"menu-sub-button\"\n data-size={size}\n data-active={isActive}\n className={cn(\n \"flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground\",\n \"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground\",\n size === \"sm\" && \"text-xs\",\n size === \"md\" && \"text-sm\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n );\n});\nSidebarMenuSubButton.displayName = \"SidebarMenuSubButton\";\n\nexport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarGroup,\n SidebarGroupAction,\n SidebarGroupContent,\n SidebarGroupLabel,\n SidebarHeader,\n SidebarInput,\n SidebarInset,\n SidebarMenu,\n SidebarMenuAction,\n SidebarMenuBadge,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSkeleton,\n SidebarMenuSub,\n SidebarMenuSubButton,\n SidebarMenuSubItem,\n SidebarProvider,\n SidebarRail,\n SidebarSeparator,\n SidebarTrigger,\n useSidebar,\n};\n","import type { LucideIcon } from \"lucide-react\";\n\nimport {\n SidebarGroup,\n SidebarGroupContent,\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n} from \"@/components/ui/sidebar\";\nimport { cn } from \"@/lib/utils\";\nimport { Link, useLocation } from \"react-router-dom\";\n\nexport function AppSidebarNav({\n items,\n}: {\n items: {\n title: string;\n url: string;\n icon?: LucideIcon;\n }[];\n}) {\n const location = useLocation();\n\n return (\n <SidebarGroup>\n <SidebarGroupContent className=\"flex flex-col gap-2\">\n <SidebarMenu>\n {items.map((item) => {\n // 获取当前路径的 pathname 部分,忽略 query 参数\n const currentPathname = location.pathname;\n const isActive = currentPathname === item.url;\n\n return (\n <SidebarMenuItem key={item.title}>\n <SidebarMenuButton\n className={cn(\n \"min-w-8 duration-200 ease-linear hover:bg-primary/5\",\n isActive\n ? \"bg-primary text-primary-foreground duration-200 ease-linear hover:bg-primary/90 hover:text-primary-foreground active:bg-primary/90 active:text-primary-foreground\"\n : \"min-w-8 duration-200 ease-linear\"\n )}\n tooltip={item.title}\n asChild\n >\n <Link to={item.url}>\n {item.icon && <item.icon />}\n <span>{item.title}</span>\n </Link>\n </SidebarMenuButton>\n </SidebarMenuItem>\n );\n })}\n </SidebarMenu>\n </SidebarGroupContent>\n </SidebarGroup>\n );\n}\n","import { LayoutDashboardIcon, SettingsIcon } from \"lucide-react\";\nimport type React from \"react\";\nimport { Link } from \"react-router-dom\";\n\nimport { AppSidebarNav } from \"@/components/AppSidebarNav\";\nimport {\n Sidebar,\n SidebarContent,\n SidebarHeader,\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n} from \"@/components/ui/sidebar\";\n\nconst data = {\n navMain: [\n {\n title: \"仪表板\",\n url: \"/dashboard\",\n icon: LayoutDashboardIcon,\n },\n // {\n // title: \"小智服务端\",\n // url: \"/mcp-endpoint\",\n // icon: BotIcon,\n // },\n // {\n // title: \"MCP 服务\",\n // url: \"#\",\n // icon: CableIcon,\n // },\n // {\n // title: \"烧录固件\",\n // url: \"#\",\n // icon: ZapIcon,\n // },\n // {\n // title: \"帮助文档\",\n // url: \"#\",\n // icon: BadgeQuestionMarkIcon,\n // },\n {\n title: \"全局配置\",\n url: \"/settings\",\n icon: SettingsIcon,\n },\n ],\n};\n\nexport function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {\n return (\n <Sidebar collapsible=\"offcanvas\" {...props}>\n <SidebarHeader>\n <SidebarMenu>\n <SidebarMenuItem>\n <SidebarMenuButton\n asChild\n className=\"data-[slot=sidebar-menu-button]:!p-1.5\"\n >\n <Link to=\"/\">\n <span className=\"text-base font-semibold\">Xiaozhi Client</span>\n </Link>\n </SidebarMenuButton>\n </SidebarMenuItem>\n </SidebarMenu>\n </SidebarHeader>\n <SidebarContent>\n <AppSidebarNav items={data.navMain} />\n </SidebarContent>\n </Sidebar>\n );\n}\n","import * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\";\nimport * as React from \"react\";\n\nimport { buttonVariants } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\n\nconst AlertDialog = AlertDialogPrimitive.Root;\n\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger;\n\nconst AlertDialogPortal = AlertDialogPrimitive.Portal;\n\nconst AlertDialogOverlay = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Overlay\n className={cn(\n \"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n ref={ref}\n />\n));\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;\n\nconst AlertDialogContent = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>\n>(({ className, ...props }, ref) => (\n <AlertDialogPortal>\n <AlertDialogOverlay />\n <AlertDialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\",\n className\n )}\n {...props}\n />\n </AlertDialogPortal>\n));\nAlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;\n\nconst AlertDialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-2 text-center sm:text-left\",\n className\n )}\n {...props}\n />\n);\nAlertDialogHeader.displayName = \"AlertDialogHeader\";\n\nconst AlertDialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className\n )}\n {...props}\n />\n);\nAlertDialogFooter.displayName = \"AlertDialogFooter\";\n\nconst AlertDialogTitle = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Title\n ref={ref}\n className={cn(\"text-lg font-semibold\", className)}\n {...props}\n />\n));\nAlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;\n\nconst AlertDialogDescription = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nAlertDialogDescription.displayName =\n AlertDialogPrimitive.Description.displayName;\n\nconst AlertDialogAction = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Action>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Action\n ref={ref}\n className={cn(buttonVariants(), className)}\n {...props}\n />\n));\nAlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;\n\nconst AlertDialogCancel = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Cancel>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Cancel\n ref={ref}\n className={cn(\n buttonVariants({ variant: \"outline\" }),\n \"mt-2 sm:mt-0\",\n className\n )}\n {...props}\n />\n));\nAlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;\n\nexport {\n AlertDialog,\n AlertDialogPortal,\n AlertDialogOverlay,\n AlertDialogTrigger,\n AlertDialogContent,\n AlertDialogHeader,\n AlertDialogFooter,\n AlertDialogTitle,\n AlertDialogDescription,\n AlertDialogAction,\n AlertDialogCancel,\n};\n","import * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { X } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Dialog = DialogPrimitive.Root;\n\nconst DialogTrigger = DialogPrimitive.Trigger;\n\nconst DialogPortal = DialogPrimitive.Portal;\n\nconst DialogClose = DialogPrimitive.Close;\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n \"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n />\n));\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName;\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\",\n className\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\">\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPortal>\n));\nDialogContent.displayName = DialogPrimitive.Content.displayName;\n\nconst DialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-1.5 text-center sm:text-left\",\n className\n )}\n {...props}\n />\n);\nDialogHeader.displayName = \"DialogHeader\";\n\nconst DialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className\n )}\n {...props}\n />\n);\nDialogFooter.displayName = \"DialogFooter\";\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\n \"text-lg font-semibold leading-none tracking-tight\",\n className\n )}\n {...props}\n />\n));\nDialogTitle.displayName = DialogPrimitive.Title.displayName;\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nDialogDescription.displayName = DialogPrimitive.Description.displayName;\n\nexport {\n Dialog,\n DialogPortal,\n DialogOverlay,\n DialogClose,\n DialogTrigger,\n DialogContent,\n DialogHeader,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n};\n","/**\n * 配置数据统一管理 Store\n *\n * 特性:\n * - 支持 HTTP API 和 WebSocket 双重数据源\n * - 提供异步方法:getConfig()、updateConfig()、refreshConfig()\n * - 使用 Zustand 进行状态管理\n * - 提供选择器 hooks 优化组件渲染\n * - 集成 WebSocket 事件监听\n */\n\nimport { create } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport { apiClient } from \"../services/api\";\nimport { webSocketManager } from \"../services/websocket\";\nimport type {\n AppConfig,\n ConnectionConfig,\n MCPServerConfig,\n ModelScopeConfig,\n WebUIConfig,\n} from \"../types\";\n\n/**\n * 配置加载状态\n */\ninterface ConfigLoadingState {\n isLoading: boolean;\n isUpdating: boolean;\n isRefreshing: boolean;\n lastUpdated: number | null;\n lastError: Error | null;\n}\n\n/**\n * 配置 Store 状态\n */\ninterface ConfigState {\n // 配置数据\n config: AppConfig | null;\n\n // 加载状态\n loading: ConfigLoadingState;\n\n // 配置来源追踪\n lastSource: \"http\" | \"websocket\" | \"initial\" | null;\n}\n\n/**\n * 配置 Store 操作方法\n */\ninterface ConfigActions {\n // 基础操作\n setConfig: (\n config: AppConfig,\n source?: \"http\" | \"websocket\" | \"initial\"\n ) => void;\n setLoading: (loading: Partial<ConfigLoadingState>) => void;\n setError: (error: Error | null) => void;\n\n // 异步操作\n getConfig: () => Promise<AppConfig>;\n updateConfig: (config: AppConfig) => Promise<void>;\n refreshConfig: () => Promise<AppConfig>;\n reloadConfig: () => Promise<AppConfig>;\n\n // 部分更新操作\n updateMcpEndpoint: (endpoint: string | string[]) => Promise<void>;\n updateMcpServers: (servers: Record<string, MCPServerConfig>) => Promise<void>;\n updateConnectionConfig: (connection: ConnectionConfig) => Promise<void>;\n updateModelScopeConfig: (modelscope: ModelScopeConfig) => Promise<void>;\n updateWebUIConfig: (webUI: WebUIConfig) => Promise<void>;\n\n // 工具方法\n reset: () => void;\n initialize: () => Promise<void>;\n}\n\n/**\n * 完整的配置 Store 接口\n */\nexport interface ConfigStore extends ConfigState, ConfigActions {}\n\n/**\n * 初始状态\n */\nconst initialState: ConfigState = {\n config: null,\n loading: {\n isLoading: false,\n isUpdating: false,\n isRefreshing: false,\n lastUpdated: null,\n lastError: null,\n },\n lastSource: null,\n};\n\n/**\n * 创建配置 Store\n */\nexport const useConfigStore = create<ConfigStore>()(\n devtools(\n (set, get) => ({\n ...initialState,\n\n // ==================== 基础操作 ====================\n\n setConfig: (config: AppConfig, source = \"http\") => {\n console.log(`[ConfigStore] 设置配置数据,来源: ${source}`);\n set(\n (state) => ({\n config,\n lastSource: source,\n loading: {\n ...state.loading,\n lastUpdated: Date.now(),\n lastError: null,\n },\n }),\n false,\n \"setConfig\"\n );\n },\n\n setLoading: (loading: Partial<ConfigLoadingState>) => {\n set(\n (state) => ({\n loading: { ...state.loading, ...loading },\n }),\n false,\n \"setLoading\"\n );\n },\n\n setError: (error: Error | null) => {\n set(\n (state) => ({\n loading: { ...state.loading, lastError: error },\n }),\n false,\n \"setError\"\n );\n },\n\n // ==================== 异步操作 ====================\n\n getConfig: async (): Promise<AppConfig> => {\n const { config, loading } = get();\n\n // 如果已有配置且不超过5分钟,直接返回\n if (\n config &&\n loading.lastUpdated &&\n Date.now() - loading.lastUpdated < 5 * 60 * 1000\n ) {\n return config;\n }\n\n // 否则从服务器获取最新配置\n return get().refreshConfig();\n },\n\n updateConfig: async (newConfig: AppConfig): Promise<void> => {\n const { setLoading, setConfig, setError } = get();\n\n try {\n setLoading({ isUpdating: true, lastError: null });\n console.log(\"[ConfigStore] 开始更新配置\");\n\n // 通过 HTTP API 更新配置\n await apiClient.updateConfig(newConfig);\n\n // 更新本地状态\n setConfig(newConfig, \"http\");\n\n console.log(\"[ConfigStore] 配置更新成功\");\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"配置更新失败\");\n console.error(\"[ConfigStore] 配置更新失败:\", err);\n setError(err);\n throw err;\n } finally {\n setLoading({ isUpdating: false });\n }\n },\n\n refreshConfig: async (): Promise<AppConfig> => {\n const { setLoading, setConfig, setError } = get();\n\n try {\n setLoading({ isRefreshing: true, lastError: null });\n console.log(\"[ConfigStore] 开始刷新配置\");\n\n // 从服务器获取最新配置\n const config = await apiClient.getConfig();\n\n // 更新本地状态\n setConfig(config, \"http\");\n\n console.log(\"[ConfigStore] 配置刷新成功\");\n return config;\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"配置刷新失败\");\n console.error(\"[ConfigStore] 配置刷新失败:\", err);\n setError(err);\n throw err;\n } finally {\n setLoading({ isRefreshing: false });\n }\n },\n\n reloadConfig: async (): Promise<AppConfig> => {\n const { setLoading, setConfig, setError } = get();\n\n try {\n setLoading({ isRefreshing: true, lastError: null });\n console.log(\"[ConfigStore] 开始重新加载配置\");\n\n // 重新加载配置文件\n const config = await apiClient.reloadConfig();\n\n // 更新本地状态\n setConfig(config, \"http\");\n\n console.log(\"[ConfigStore] 配置重新加载成功\");\n return config;\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"配置重新加载失败\");\n console.error(\"[ConfigStore] 配置重新加载失败:\", err);\n setError(err);\n throw err;\n } finally {\n setLoading({ isRefreshing: false });\n }\n },\n\n // ==================== 部分更新操作 ====================\n\n updateMcpEndpoint: async (endpoint: string | string[]): Promise<void> => {\n const { config, updateConfig } = get();\n if (!config) {\n throw new Error(\"配置未加载,无法更新 MCP 端点\");\n }\n\n const newConfig = { ...config, mcpEndpoint: endpoint };\n await updateConfig(newConfig);\n },\n\n updateMcpServers: async (\n servers: Record<string, MCPServerConfig>\n ): Promise<void> => {\n const { config, updateConfig } = get();\n if (!config) {\n throw new Error(\"配置未加载,无法更新 MCP 服务\");\n }\n\n const newConfig = { ...config, mcpServers: servers };\n await updateConfig(newConfig);\n },\n\n updateConnectionConfig: async (\n connection: ConnectionConfig\n ): Promise<void> => {\n const { config, updateConfig } = get();\n if (!config) {\n throw new Error(\"配置未加载,无法更新连接配置\");\n }\n\n const newConfig = { ...config, connection };\n await updateConfig(newConfig);\n },\n\n updateModelScopeConfig: async (\n modelscope: ModelScopeConfig\n ): Promise<void> => {\n const { config, updateConfig } = get();\n if (!config) {\n throw new Error(\"配置未加载,无法更新 ModelScope 配置\");\n }\n\n const newConfig = { ...config, modelscope };\n await updateConfig(newConfig);\n },\n\n updateWebUIConfig: async (webUI: WebUIConfig): Promise<void> => {\n const { config, updateConfig } = get();\n if (!config) {\n throw new Error(\"配置未加载,无法更新 Web UI 配置\");\n }\n\n const newConfig = { ...config, webUI };\n await updateConfig(newConfig);\n },\n\n // ==================== 工具方法 ====================\n\n reset: () => {\n console.log(\"[ConfigStore] 重置状态\");\n set(initialState, false, \"reset\");\n },\n\n initialize: async (): Promise<void> => {\n const { setLoading, refreshConfig } = get();\n\n try {\n setLoading({ isLoading: true });\n console.log(\"[ConfigStore] 初始化配置 Store\");\n\n // 设置 WebSocket 事件监听\n webSocketManager.subscribe(\"data:configUpdate\", (config) => {\n console.log(\"[ConfigStore] 收到 WebSocket 配置更新\");\n get().setConfig(config, \"websocket\");\n });\n\n // 获取初始配置\n await refreshConfig();\n\n console.log(\"[ConfigStore] 配置 Store 初始化完成\");\n } catch (error) {\n console.error(\"[ConfigStore] 配置 Store 初始化失败:\", error);\n throw error;\n } finally {\n setLoading({ isLoading: false });\n }\n },\n }),\n {\n name: \"config-store\",\n }\n )\n);\n\n// ==================== 选择器 Hooks ====================\n\n/**\n * 获取完整配置\n */\nexport const useConfig = () => useConfigStore((state) => state.config);\n\n/**\n * 获取配置加载状态\n */\nexport const useConfigLoading = () => useConfigStore((state) => state.loading);\n\n/**\n * 获取配置是否正在加载\n */\nexport const useConfigIsLoading = () =>\n useConfigStore(\n (state) => state.loading.isLoading || state.loading.isRefreshing\n );\n\n/**\n * 获取配置是否正在更新\n */\nexport const useConfigIsUpdating = () =>\n useConfigStore((state) => state.loading.isUpdating);\n\n/**\n * 获取配置错误\n */\nexport const useConfigError = () =>\n useConfigStore((state) => state.loading.lastError);\n\n/**\n * 获取 MCP 端点\n */\nexport const useMcpEndpoint = () =>\n useConfigStore((state) => state.config?.mcpEndpoint);\n\n/**\n * 获取 MCP 服务配置\n */\nexport const useMcpServers = () =>\n useConfigStore((state) => state.config?.mcpServers);\n\n/**\n * 获取 MCP 服务工具配置\n */\nexport const useMcpServerConfig = () =>\n useConfigStore((state) => state.config?.mcpServerConfig);\n\n/**\n * 获取连接配置\n */\nexport const useConnectionConfig = () =>\n useConfigStore((state) => state.config?.connection);\n\n/**\n * 获取 ModelScope 配置\n */\nexport const useModelScopeConfig = () =>\n useConfigStore((state) => state.config?.modelscope);\n\n/**\n * 获取 Web UI 配置\n */\nexport const useWebUIConfig = () =>\n useConfigStore((state) => state.config?.webUI);\n\n/**\n * 获取配置来源\n */\nexport const useConfigSource = () =>\n useConfigStore((state) => state.lastSource);\n\n// ==================== 复合选择器 ====================\n\n/**\n * 获取配置数据和加载状态\n */\nexport const useConfigWithLoading = () =>\n useConfigStore(\n useShallow((state) => ({\n config: state.config,\n isLoading: state.loading.isLoading || state.loading.isRefreshing,\n isUpdating: state.loading.isUpdating,\n error: state.loading.lastError,\n }))\n );\n\n/**\n * 获取 MCP 相关配置\n */\nexport const useMcpConfig = () =>\n useConfigStore(\n useShallow((state) => ({\n endpoint: state.config?.mcpEndpoint,\n servers: state.config?.mcpServers,\n serverConfig: state.config?.mcpServerConfig,\n }))\n );\n\n/**\n * 获取系统配置\n */\nexport const useSystemConfig = () =>\n useConfigStore(\n useShallow((state) => ({\n connection: state.config?.connection,\n modelscope: state.config?.modelscope,\n webUI: state.config?.webUI,\n }))\n );\n\n// ==================== 操作方法 Hooks ====================\n\n/**\n * 获取配置操作方法\n */\nexport const useConfigActions = () =>\n useConfigStore(\n useShallow((state) => ({\n getConfig: state.getConfig,\n updateConfig: state.updateConfig,\n refreshConfig: state.refreshConfig,\n reloadConfig: state.reloadConfig,\n updateMcpEndpoint: state.updateMcpEndpoint,\n updateMcpServers: state.updateMcpServers,\n updateConnectionConfig: state.updateConnectionConfig,\n updateModelScopeConfig: state.updateModelScopeConfig,\n updateWebUIConfig: state.updateWebUIConfig,\n reset: state.reset,\n initialize: state.initialize,\n }))\n );\n\n/**\n * 获取配置更新方法\n */\nexport const useConfigUpdaters = () =>\n useConfigStore(\n useShallow((state) => ({\n updateConfig: state.updateConfig,\n updateMcpEndpoint: state.updateMcpEndpoint,\n updateMcpServers: state.updateMcpServers,\n updateConnectionConfig: state.updateConnectionConfig,\n updateModelScopeConfig: state.updateModelScopeConfig,\n updateWebUIConfig: state.updateWebUIConfig,\n }))\n );\n","/**\n * WebSocket 连接状态管理 Store (重构版)\n *\n * 职责:\n * - 纯 WebSocket 连接状态管理\n * - 集成 WebSocketManager 单例\n * - 提供连接控制方法\n * - 保持向后兼容性\n *\n * 注意:\n * - 配置数据管理已迁移到 stores/config.ts\n * - 状态数据管理已迁移到 stores/status.ts\n */\n\nimport { create } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport { ConnectionState, webSocketManager } from \"../services/websocket\";\n\n/**\n * 端口变更状态接口(保留用于端口切换功能)\n */\ninterface PortChangeStatus {\n status:\n | \"idle\"\n | \"checking\"\n | \"polling\"\n | \"connecting\"\n | \"completed\"\n | \"failed\";\n targetPort?: number;\n currentAttempt?: number;\n maxAttempts?: number;\n error?: string;\n timestamp: number;\n}\n\n/**\n * WebSocket 连接统计信息\n */\ninterface ConnectionStats {\n reconnectAttempts: number;\n maxReconnectAttempts: number;\n lastHeartbeat: number;\n eventListenerCount: number;\n}\n\n/**\n * WebSocket Store 状态(简化版)\n */\ninterface WebSocketState {\n // WebSocket 连接状态\n connectionState: ConnectionState;\n wsUrl: string;\n\n // 连接统计信息\n connectionStats: ConnectionStats;\n\n // 端口变更状态(保留用于端口切换功能)\n portChangeStatus?: PortChangeStatus;\n\n // 连接错误信息\n lastError: Error | null;\n\n // 连接时间戳\n connectedAt: number | null;\n disconnectedAt: number | null;\n}\n\n/**\n * WebSocket Store 操作方法(简化版)\n */\ninterface WebSocketActions {\n // 基础连接状态管理\n setConnectionState: (state: ConnectionState) => void;\n setWsUrl: (url: string) => void;\n setConnectionStats: (stats: ConnectionStats) => void;\n setLastError: (error: Error | null) => void;\n\n // 端口变更状态管理(保留用于端口切换功能)\n setPortChangeStatus: (portChangeStatus: PortChangeStatus | undefined) => void;\n\n // WebSocket 连接控制\n connect: () => Promise<void>;\n disconnect: () => void;\n reconnect: () => Promise<void>;\n send: (message: any) => boolean;\n\n // URL 管理\n updateUrl: (url: string) => void;\n\n // 工具方法\n reset: () => void;\n initialize: () => void;\n getConnectionInfo: () => {\n state: ConnectionState;\n url: string;\n stats: ConnectionStats;\n isConnected: boolean;\n };\n\n // 向后兼容的方法(废弃)\n /** @deprecated 使用 setConnectionState 替代 */\n setConnected: (connected: boolean) => void;\n}\n\nexport interface WebSocketStore extends WebSocketState, WebSocketActions {}\n\n/**\n * 初始状态(简化版)\n */\nconst initialState: WebSocketState = {\n // WebSocket 连接状态\n connectionState: ConnectionState.DISCONNECTED,\n wsUrl: \"\",\n\n // 连接统计信息\n connectionStats: {\n reconnectAttempts: 0,\n maxReconnectAttempts: 5,\n lastHeartbeat: 0,\n eventListenerCount: 0,\n },\n\n // 端口变更状态\n portChangeStatus: undefined,\n\n // 连接错误信息\n lastError: null,\n\n // 连接时间戳\n connectedAt: null,\n disconnectedAt: null,\n};\n\n/**\n * 创建 WebSocket Store(重构版)\n */\nexport const useWebSocketStore = create<WebSocketStore>()(\n devtools(\n (set, get) => ({\n ...initialState,\n\n // ==================== 基础连接状态管理 ====================\n\n setConnectionState: (connectionState: ConnectionState) => {\n console.log(\"[WebSocketStore] 更新连接状态:\", connectionState);\n\n const now = Date.now();\n const updates: Partial<WebSocketState> = { connectionState };\n\n // 更新连接时间戳\n if (connectionState === ConnectionState.CONNECTED) {\n updates.connectedAt = now;\n updates.lastError = null;\n } else if (connectionState === ConnectionState.DISCONNECTED) {\n updates.disconnectedAt = now;\n }\n\n set(updates, false, \"setConnectionState\");\n\n // 同步更新 WebSocketManager 的统计信息\n const stats = webSocketManager.getConnectionStats();\n get().setConnectionStats(stats);\n },\n\n setWsUrl: (wsUrl: string) => {\n console.log(\"[WebSocketStore] 更新 WebSocket URL:\", wsUrl);\n set({ wsUrl }, false, \"setWsUrl\");\n },\n\n setConnectionStats: (connectionStats: ConnectionStats) => {\n set({ connectionStats }, false, \"setConnectionStats\");\n },\n\n setLastError: (lastError: Error | null) => {\n console.log(\"[WebSocketStore] 更新连接错误:\", lastError?.message);\n set({ lastError }, false, \"setLastError\");\n },\n\n // ==================== 端口变更状态管理 ====================\n\n setPortChangeStatus: (portChangeStatus: PortChangeStatus | undefined) => {\n console.log(\n \"[WebSocketStore] 更新端口变更状态:\",\n portChangeStatus?.status\n );\n set({ portChangeStatus }, false, \"setPortChangeStatus\");\n },\n\n // ==================== WebSocket 连接控制 ====================\n\n connect: async (): Promise<void> => {\n try {\n console.log(\"[WebSocketStore] 开始连接 WebSocket\");\n webSocketManager.connect();\n } catch (error) {\n const err = error instanceof Error ? error : new Error(\"连接失败\");\n console.error(\"[WebSocketStore] 连接失败:\", err);\n get().setLastError(err);\n throw err;\n }\n },\n\n disconnect: () => {\n console.log(\"[WebSocketStore] 断开 WebSocket 连接\");\n webSocketManager.disconnect();\n },\n\n reconnect: async (): Promise<void> => {\n try {\n console.log(\"[WebSocketStore] 重新连接 WebSocket\");\n webSocketManager.disconnect();\n await new Promise((resolve) => setTimeout(resolve, 1000));\n webSocketManager.connect();\n } catch (error) {\n const err = error instanceof Error ? error : new Error(\"重连失败\");\n console.error(\"[WebSocketStore] 重连失败:\", err);\n get().setLastError(err);\n throw err;\n }\n },\n\n send: (message: any): boolean => {\n try {\n return webSocketManager.send(message);\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"发送消息失败\");\n console.error(\"[WebSocketStore] 发送消息失败:\", err);\n get().setLastError(err);\n return false;\n }\n },\n\n // ==================== URL 管理 ====================\n\n updateUrl: (url: string) => {\n console.log(\"[WebSocketStore] 更新 WebSocket URL:\", url);\n webSocketManager.setUrl(url);\n get().setWsUrl(url);\n },\n\n // ==================== 工具方法 ====================\n\n getConnectionInfo: () => {\n const state = get();\n return {\n state: state.connectionState,\n url: state.wsUrl,\n stats: state.connectionStats,\n isConnected: state.connectionState === ConnectionState.CONNECTED,\n };\n },\n\n reset: () => {\n console.log(\"[WebSocketStore] 重置状态\");\n set(initialState, false, \"reset\");\n },\n\n // ==================== 初始化方法 ====================\n\n initialize: () => {\n console.log(\"[WebSocketStore] 初始化 WebSocket Store\");\n\n // 设置 WebSocket 事件监听\n webSocketManager.subscribe(\"connection:connecting\", () => {\n get().setConnectionState(ConnectionState.CONNECTING);\n });\n\n webSocketManager.subscribe(\"connection:connected\", () => {\n get().setConnectionState(ConnectionState.CONNECTED);\n });\n\n webSocketManager.subscribe(\"connection:disconnected\", () => {\n get().setConnectionState(ConnectionState.DISCONNECTED);\n });\n\n webSocketManager.subscribe(\"connection:reconnecting\", () => {\n get().setConnectionState(ConnectionState.RECONNECTING);\n const stats = webSocketManager.getConnectionStats();\n get().setConnectionStats(stats);\n });\n\n webSocketManager.subscribe(\"connection:error\", ({ error }) => {\n get().setLastError(error);\n });\n\n webSocketManager.subscribe(\"system:heartbeat\", () => {\n const stats = webSocketManager.getConnectionStats();\n get().setConnectionStats(stats);\n });\n\n // 初始化连接状态\n const initialStats = webSocketManager.getConnectionStats();\n get().setConnectionStats(initialStats);\n get().setWsUrl(webSocketManager.getUrl());\n\n console.log(\"[WebSocketStore] WebSocket Store 初始化完成\");\n },\n\n // ==================== 向后兼容方法 ====================\n\n /** @deprecated 使用 setConnectionState 替代 */\n setConnected: (connected: boolean) => {\n console.warn(\n \"[WebSocketStore] setConnected 方法已废弃,请使用 setConnectionState\"\n );\n const connectionState: ConnectionState = connected\n ? ConnectionState.CONNECTED\n : ConnectionState.DISCONNECTED;\n get().setConnectionState(connectionState);\n },\n }),\n {\n name: \"websocket-store\",\n }\n )\n);\n\n// ==================== 选择器 Hooks ====================\n\n/**\n * 获取连接状态\n */\nexport const useWebSocketConnectionState = () =>\n useWebSocketStore((state) => state.connectionState);\n\n/**\n * 获取连接状态(布尔值)\n */\nexport const useWebSocketConnected = () =>\n useWebSocketStore(\n (state) => state.connectionState === ConnectionState.CONNECTED\n );\n\n/**\n * 获取 WebSocket URL\n */\nexport const useWebSocketUrl = () => useWebSocketStore((state) => state.wsUrl);\n\n/**\n * 获取连接统计信息\n */\nexport const useWebSocketConnectionStats = () =>\n useWebSocketStore((state) => state.connectionStats);\n\n/**\n * 获取端口变更状态\n */\nexport const useWebSocketPortChangeStatus = () =>\n useWebSocketStore((state) => state.portChangeStatus);\n\n/**\n * 获取最后的连接错误\n */\nexport const useWebSocketLastError = () =>\n useWebSocketStore((state) => state.lastError);\n\n/**\n * 获取连接时间戳\n */\nexport const useWebSocketConnectionTimes = () =>\n useWebSocketStore(\n useShallow((state) => ({\n connectedAt: state.connectedAt,\n disconnectedAt: state.disconnectedAt,\n }))\n );\n\n// ==================== 向后兼容的选择器(废弃) ====================\n\n// 导入兼容性选择器\nimport {\n useWebSocketConfig as useWebSocketConfigCompat,\n useWebSocketMcpEndpoint as useWebSocketMcpEndpointCompat,\n useWebSocketMcpServerConfig as useWebSocketMcpServerConfigCompat,\n useWebSocketMcpServers as useWebSocketMcpServersCompat,\n useWebSocketRestartStatus as useWebSocketRestartStatusCompat,\n useWebSocketStatus as useWebSocketStatusCompat,\n} from \"./websocket-compat\";\n\n/**\n * @deprecated 配置数据已迁移到 stores/config.ts,请使用 useConfig()\n */\nexport const useWebSocketConfig = useWebSocketConfigCompat;\n\n/**\n * @deprecated 状态数据已迁移到 stores/status.ts,请使用 useClientStatus()\n */\nexport const useWebSocketStatus = useWebSocketStatusCompat;\n\n/**\n * @deprecated 重启状态已迁移到 stores/status.ts,请使用 useRestartStatus()\n */\nexport const useWebSocketRestartStatus = useWebSocketRestartStatusCompat;\n\n/**\n * @deprecated MCP 服务器数据已迁移到 stores/config.ts,请使用 useMcpServers()\n */\nexport const useWebSocketMcpServers = useWebSocketMcpServersCompat;\n\n/**\n * @deprecated MCP 服务器配置已迁移到 stores/config.ts,请使用 useMcpServerConfig()\n */\nexport const useWebSocketMcpServerConfig = useWebSocketMcpServerConfigCompat;\n\n/**\n * @deprecated MCP 端点已迁移到 stores/config.ts,请使用 useMcpEndpoint()\n */\nexport const useWebSocketMcpEndpoint = useWebSocketMcpEndpointCompat;\n\n/**\n * @deprecated MCP 端点已迁移到 stores/config.ts,请使用 useMcpEndpoint()\n */\nexport const useMcpEndpoint = useWebSocketMcpEndpointCompat;\n\n// ==================== 复合选择器 ====================\n\n/**\n * 获取连接相关信息\n */\nexport const useWebSocketConnectionInfo = () =>\n useWebSocketStore(\n useShallow((state) => ({\n connected: state.connectionState === ConnectionState.CONNECTED,\n connectionState: state.connectionState,\n wsUrl: state.wsUrl,\n stats: state.connectionStats,\n lastError: state.lastError,\n connectedAt: state.connectedAt,\n disconnectedAt: state.disconnectedAt,\n }))\n );\n\n/**\n * 获取端口变更相关信息\n */\nexport const useWebSocketPortInfo = () =>\n useWebSocketStore(\n useShallow((state) => ({\n portChangeStatus: state.portChangeStatus,\n wsUrl: state.wsUrl,\n }))\n );\n\n// ==================== 操作方法 Hooks ====================\n\n/**\n * 获取 WebSocket 操作方法\n */\nexport const useWebSocketActions = () =>\n useWebSocketStore(\n useShallow((state) => ({\n // 连接状态管理\n setConnectionState: state.setConnectionState,\n setWsUrl: state.setWsUrl,\n setConnectionStats: state.setConnectionStats,\n setLastError: state.setLastError,\n\n // 端口变更状态管理\n setPortChangeStatus: state.setPortChangeStatus,\n\n // WebSocket 连接控制\n connect: state.connect,\n disconnect: state.disconnect,\n reconnect: state.reconnect,\n send: state.send,\n\n // URL 管理\n updateUrl: state.updateUrl,\n\n // 工具方法\n reset: state.reset,\n initialize: state.initialize,\n getConnectionInfo: state.getConnectionInfo,\n\n // 向后兼容(废弃)\n setConnected: state.setConnected,\n }))\n );\n\n/**\n * 获取连接控制方法\n */\nexport const useWebSocketControls = () =>\n useWebSocketStore(\n useShallow((state) => ({\n connect: state.connect,\n disconnect: state.disconnect,\n reconnect: state.reconnect,\n send: state.send,\n updateUrl: state.updateUrl,\n isConnected: state.connectionState === ConnectionState.CONNECTED,\n }))\n );\n\n// ==================== 向后兼容的复合选择器(废弃) ====================\n\n/**\n * @deprecated 数据已分离到不同的 stores,请使用对应的选择器\n */\nexport const useWebSocketData = () => {\n console.warn(\"useWebSocketData 已废弃,请使用对应的专门 stores\");\n return {\n config: null,\n status: null,\n };\n};\n","/**\n * 端口连通性检测工具函数\n */\n\n/**\n * 检测指定端口是否可用\n * @param port 端口号\n * @param timeout 超时时间(毫秒),默认3秒\n * @returns Promise<boolean> 端口是否可用\n */\nexport async function checkPortAvailability(\n port: number,\n timeout = 3000\n): Promise<boolean> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n // 尝试连接到服务端的健康检查端点\n // 先尝试 WebServer 的 /api/status 端点,如果失败再尝试 MCPServer 的 /health 端点\n let response: Response;\n try {\n response = await fetch(`http://localhost:${port}/api/status`, {\n method: \"GET\",\n signal: controller.signal,\n });\n } catch {\n // 如果 /api/status 失败,尝试 /health 端点\n response = await fetch(`http://localhost:${port}/health`, {\n method: \"GET\",\n signal: controller.signal,\n });\n }\n\n clearTimeout(timeoutId);\n return response.ok;\n } catch (error) {\n // 连接失败或超时\n return false;\n }\n}\n\n/**\n * 轮询检测端口直到可用或超时\n * @param port 端口号\n * @param maxAttempts 最大尝试次数,默认30次\n * @param interval 检测间隔(毫秒),默认2秒\n * @param onProgress 进度回调函数\n * @returns Promise<boolean> 是否在超时前检测到端口可用\n */\nexport async function pollPortUntilAvailable(\n port: number,\n maxAttempts = 30,\n interval = 2000,\n onProgress?: (attempt: number, maxAttempts: number) => void\n): Promise<boolean> {\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n if (onProgress) {\n onProgress(attempt, maxAttempts);\n }\n\n const isAvailable = await checkPortAvailability(port, 1000);\n if (isAvailable) {\n return true;\n }\n\n // 如果不是最后一次尝试,等待指定间隔\n if (attempt < maxAttempts) {\n await new Promise((resolve) => setTimeout(resolve, interval));\n }\n }\n\n return false;\n}\n\n/**\n * 构建 WebSocket URL\n * @param port 端口号\n * @param hostname 主机名,默认使用当前页面的 hostname\n * @returns WebSocket URL\n */\nexport function buildWebSocketUrl(port: number, hostname?: string): string {\n const protocol = window.location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const host = hostname || window.location.hostname || \"localhost\";\n\n // 如果是标准端口(80 for HTTP, 443 for HTTPS),不显示端口号\n if (\n (protocol === \"ws:\" && port === 80) ||\n (protocol === \"wss:\" && port === 443)\n ) {\n return `${protocol}//${host}`;\n }\n\n return `${protocol}//${host}:${port}`;\n}\n\n/**\n * 从 WebSocket URL 中提取端口号\n * @param url WebSocket URL\n * @returns 端口号,如果无法提取则返回 null\n */\nexport function extractPortFromUrl(url: string): number | null {\n try {\n const urlObj = new URL(url);\n const port = Number.parseInt(urlObj.port);\n return Number.isNaN(port) ? null : port;\n } catch {\n return null;\n }\n}\n","/**\n * WebSocket Hook - 重构版本(第二阶段)\n *\n * 重构内容:\n * - 移除直接的 WebSocket 实例创建,使用 WebSocketManager 单例\n * - 集成新的 config 和 status stores\n * - 使用事件总线进行消息处理\n * - 保持向后兼容性,现有组件无需修改\n *\n * 架构说明:\n * - WebSocketManager: 单例 WebSocket 连接管理\n * - ConfigStore: 配置数据统一管理\n * - StatusStore: 状态数据统一管理\n * - WebSocketStore: 纯连接状态管理\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { webSocketManager } from \"../services/websocket\";\nimport { useConfig, useConfigActions } from \"../stores/config\";\nimport {\n useClientStatus,\n useRestartStatus,\n useStatusActions,\n} from \"../stores/status\";\nimport { useWebSocketActions } from \"../stores/websocket\";\nimport type { AppConfig, ClientStatus } from \"../types\";\nimport {\n buildWebSocketUrl,\n checkPortAvailability,\n extractPortFromUrl,\n} from \"../utils/portUtils\";\n\n/**\n * 向后兼容的状态接口\n */\ninterface WebSocketState {\n connected: boolean;\n config: AppConfig | null;\n status: ClientStatus | null;\n restartStatus?: {\n status: \"restarting\" | \"completed\" | \"failed\";\n error?: string;\n timestamp: number;\n };\n}\n\n/**\n * useWebSocket Hook - 重构版本\n *\n * @deprecated 建议使用新的专用 hooks:\n * - useWebSocketConnection() 用于连接管理\n * - useConfigData() 用于配置数据\n * - useStatusData() 用于状态数据\n */\nexport function useWebSocket() {\n console.warn(\n \"[useWebSocket] 此 hook 已重构,建议使用新的专用 hooks:useWebSocketConnection()、useConfigData()、useStatusData()\"\n );\n\n // 使用新的 stores 获取数据\n const config = useConfig();\n const clientStatus = useClientStatus();\n const restartStatus = useRestartStatus();\n\n // 获取 store actions\n const webSocketActions = useWebSocketActions();\n const configActions = useConfigActions();\n const statusActions = useStatusActions();\n\n // 向后兼容的本地状态\n const [wsUrl, setWsUrl] = useState<string>(\"\");\n const isInitialized = useRef(false);\n\n // 初始化 WebSocket 连接和数据加载\n useEffect(() => {\n if (isInitialized.current) return;\n isInitialized.current = true;\n\n console.log(\"[useWebSocket] 初始化 WebSocket 连接和数据加载\");\n\n // 确保 WebSocket 连接\n if (!webSocketManager.isConnected()) {\n webSocketManager.connect();\n }\n\n // 获取当前 WebSocket URL\n const currentUrl = webSocketManager.getUrl();\n setWsUrl(currentUrl);\n\n // 初始化数据加载\n const initializeData = async () => {\n try {\n // 并行加载配置和状态数据\n await Promise.allSettled([\n configActions.getConfig(),\n statusActions.getStatus(),\n ]);\n console.log(\"[useWebSocket] 初始数据加载完成\");\n } catch (error) {\n console.error(\"[useWebSocket] 初始数据加载失败:\", error);\n }\n };\n\n initializeData();\n }, [configActions, statusActions]);\n\n // 动态获取WebSocket连接地址(向后兼容)\n const getWebSocketUrl = useCallback((configPort?: number) => {\n // 优先使用localStorage中保存的地址\n const savedUrl = localStorage.getItem(\"xiaozhi-ws-url\");\n if (savedUrl) {\n return savedUrl;\n }\n\n // 确定要使用的端口号\n let targetPort = 9999; // 默认端口\n\n // 如果传入了配置端口,使用配置端口\n if (configPort) {\n targetPort = configPort;\n } else if (window.location.port) {\n // 如果当前页面有端口号,使用当前页面的端口号\n const currentPort = Number.parseInt(window.location.port);\n if (!Number.isNaN(currentPort)) {\n targetPort = currentPort;\n }\n } else if (window.location.protocol === \"http:\" && !window.location.port) {\n // 标准 HTTP 端口 (80)\n targetPort = 80;\n } else if (window.location.protocol === \"https:\" && !window.location.port) {\n // 标准 HTTPS 端口 (443)\n targetPort = 443;\n }\n\n // 构建 WebSocket URL\n return buildWebSocketUrl(targetPort);\n }, []);\n\n // 向后兼容的状态计算\n const state: WebSocketState = {\n connected: webSocketManager.isConnected(),\n config: config,\n status: clientStatus,\n restartStatus: restartStatus || undefined,\n };\n\n // 重构后的配置更新方法\n const updateConfig = useCallback(\n async (config: AppConfig): Promise<void> => {\n console.log(\"[useWebSocket] updateConfig 调用,使用新的 configActions\");\n try {\n await configActions.updateConfig(config);\n } catch (error) {\n console.error(\"[useWebSocket] 配置更新失败:\", error);\n throw error;\n }\n },\n [configActions]\n );\n\n // 重构后的状态刷新方法\n const refreshStatus = useCallback(async () => {\n console.log(\"[useWebSocket] refreshStatus 调用,使用新的 statusActions\");\n try {\n await statusActions.refreshStatus();\n } catch (error) {\n console.error(\"[useWebSocket] 状态刷新失败:\", error);\n throw error;\n }\n }, [statusActions]);\n\n // 重构后的服务重启方法\n const restartService = useCallback(async (): Promise<void> => {\n console.log(\"[useWebSocket] restartService 调用,使用新的 statusActions\");\n try {\n await statusActions.restartService();\n } catch (error) {\n console.error(\"[useWebSocket] 服务重启失败:\", error);\n throw error;\n }\n }, [statusActions]);\n\n // 保存自定义WebSocket地址\n const setCustomWsUrl = useCallback(\n (url: string) => {\n console.log(\"[useWebSocket] setCustomWsUrl 调用,使用 WebSocketManager\");\n if (url) {\n localStorage.setItem(\"xiaozhi-ws-url\", url);\n webSocketManager.setUrl(url);\n } else {\n localStorage.removeItem(\"xiaozhi-ws-url\");\n // 恢复默认 URL\n const defaultUrl = getWebSocketUrl();\n webSocketManager.setUrl(defaultUrl);\n }\n // 更新本地状态\n setWsUrl(webSocketManager.getUrl());\n },\n [getWebSocketUrl]\n );\n\n // 端口切换核心函数\n const changePort = useCallback(\n async (newPort: number): Promise<void> => {\n const currentPort = extractPortFromUrl(wsUrl) || 9999;\n\n // 如果端口号相同,直接返回\n if (currentPort === newPort) {\n return;\n }\n\n // 更新端口切换状态\n webSocketActions.setPortChangeStatus({\n status: \"checking\",\n targetPort: newPort,\n timestamp: Date.now(),\n });\n\n try {\n // 更新端口切换状态\n webSocketActions.setPortChangeStatus({\n status: \"checking\",\n targetPort: newPort,\n timestamp: Date.now(),\n });\n\n // 检查端口可用性\n const isAvailable = await checkPortAvailability(newPort);\n if (!isAvailable) {\n throw new Error(`端口 ${newPort} 不可用`);\n }\n\n // 构建新的 WebSocket URL\n const newUrl = buildWebSocketUrl(newPort);\n\n // 更新 WebSocket URL\n webSocketManager.setUrl(newUrl);\n setWsUrl(newUrl);\n\n // 成功完成端口切换\n console.log(`[WebSocket] 端口切换到 ${newPort} 成功完成`);\n webSocketActions.setPortChangeStatus({\n status: \"completed\",\n targetPort: newPort,\n timestamp: Date.now(),\n });\n } catch (error) {\n // 端口切换失败\n const errorMessage =\n error instanceof Error ? error.message : \"端口切换失败\";\n console.error(`[WebSocket] 端口切换到 ${newPort} 失败:`, errorMessage);\n\n webSocketActions.setPortChangeStatus({\n status: \"failed\",\n targetPort: newPort,\n error: errorMessage,\n timestamp: Date.now(),\n });\n throw error;\n }\n },\n [wsUrl, webSocketActions]\n );\n\n return {\n ...state,\n updateConfig,\n refreshStatus,\n restartService,\n wsUrl,\n setCustomWsUrl,\n changePort,\n };\n}\n","import { Button } from \"@/components/ui/button\";\n\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n} from \"@/components/ui/alert-dialog\";\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport { Input } from \"@/components/ui/input\";\nimport { useWebSocket } from \"@/hooks/useWebSocket\";\nimport { useConfig, useMcpEndpoint } from \"@/stores/config\";\nimport {\n BadgeInfoIcon,\n CopyIcon,\n PlusIcon,\n SettingsIcon,\n TrashIcon,\n} from \"lucide-react\";\nimport { useMemo, useState } from \"react\";\nimport { toast } from \"sonner\";\n\nconst sliceEndpoint = (endpoint: string) => {\n return `${endpoint.slice(0, 30)}...${endpoint.slice(-10)}`;\n};\n\n// 验证接入点格式\nconst validateEndpoint = (endpoint: string): string | null => {\n if (!endpoint.trim()) {\n return \"请输入接入点地址\";\n }\n\n // 检查是否以正确的前缀开头\n const expectedPrefix = \"wss://api.xiaozhi.me/mcp/?token=\";\n if (!endpoint.startsWith(expectedPrefix)) {\n return \"接入点格式无效,请输入正确的小智服务端接入点地址\";\n }\n\n // 提取 token 部分\n const token = endpoint.substring(expectedPrefix.length);\n if (!token) {\n return \"接入点格式无效,缺少 token 参数\";\n }\n\n // 验证 JWT 格式(应该有两个点分隔的三个部分)\n const jwtParts = token.split(\".\");\n if (jwtParts.length !== 3) {\n return \"接入点格式无效,token 格式不正确\";\n }\n\n // 检查每个部分是否为有效的 base64 字符串(简单检查)\n for (const part of jwtParts) {\n if (!part || !/^[A-Za-z0-9_-]+$/.test(part)) {\n return \"接入点格式无效,token 格式不正确\";\n }\n }\n\n return null; // 验证通过\n};\n\nexport function McpEndpointSettingButton() {\n const [open, setOpen] = useState(false);\n const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);\n const [endpointToDelete, setEndpointToDelete] = useState<string>(\"\");\n const [isDeleting, setIsDeleting] = useState(false);\n const [addDialogOpen, setAddDialogOpen] = useState(false);\n const [newEndpoint, setNewEndpoint] = useState(\"\");\n const [isAdding, setIsAdding] = useState(false);\n const [validationError, setValidationError] = useState(\"\");\n\n const config = useConfig();\n const mcpEndpoint = useMcpEndpoint();\n const { updateConfig } = useWebSocket();\n\n // 复制接入点地址到剪贴板\n const handleCopy = async (endpoint: string) => {\n try {\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(endpoint);\n toast.success(\"接入点地址已复制到剪贴板\");\n } else {\n // 降级方案:使用传统的复制方法\n const textArea = document.createElement(\"textarea\");\n textArea.value = endpoint;\n textArea.style.position = \"fixed\";\n textArea.style.opacity = \"0\";\n document.body.appendChild(textArea);\n textArea.select();\n const successful = document.execCommand(\"copy\");\n document.body.removeChild(textArea);\n if (successful) {\n toast.success(\"接入点地址已复制到剪贴板\");\n } else {\n throw new Error(\"复制命令执行失败\");\n }\n }\n } catch (error) {\n console.error(\"复制失败:\", error);\n toast.error(\"复制失败,请手动复制\");\n }\n };\n\n // 删除接入点\n const handleDeleteEndpoint = async () => {\n if (!config || !endpointToDelete) {\n toast.error(\"配置数据未加载或未选择要删除的接入点\");\n return;\n }\n\n setIsDeleting(true);\n try {\n const currentEndpoints = Array.isArray(mcpEndpoint)\n ? mcpEndpoint\n : [mcpEndpoint];\n const updatedEndpoints = currentEndpoints.filter(\n (ep) => ep !== endpointToDelete && ep !== undefined\n ) as string[];\n\n // 如果删除后没有接入点了,设置为空字符串\n const newMcpEndpoint =\n updatedEndpoints.length > 0\n ? updatedEndpoints.length === 1\n ? updatedEndpoints[0]\n : updatedEndpoints\n : \"\";\n\n const updatedConfig = {\n ...config,\n mcpEndpoint: newMcpEndpoint,\n };\n\n await updateConfig(updatedConfig);\n toast.success(\"接入点已删除\");\n setDeleteConfirmOpen(false);\n setEndpointToDelete(\"\");\n } catch (error) {\n console.error(\"删除接入点失败:\", error);\n toast.error(error instanceof Error ? error.message : \"删除接入点失败\");\n } finally {\n setIsDeleting(false);\n }\n };\n\n // 添加新接入点\n const handleAddEndpoint = async () => {\n const error = validateEndpoint(newEndpoint);\n if (error) {\n setValidationError(error);\n return;\n }\n\n // 检查是否与现有接入点重复\n const currentEndpoints = Array.isArray(mcpEndpoint)\n ? mcpEndpoint\n : [mcpEndpoint];\n if (currentEndpoints.includes(newEndpoint)) {\n setValidationError(\"该接入点已存在\");\n return;\n }\n\n if (!config) {\n toast.error(\"配置数据未加载,请稍后重试\");\n return;\n }\n\n setIsAdding(true);\n try {\n // 将新接入点添加到数组的第一位\n const updatedEndpoints = [\n newEndpoint,\n ...currentEndpoints.filter((ep) => ep !== undefined && ep !== \"\"),\n ] as string[];\n\n const updatedConfig = {\n ...config,\n mcpEndpoint: updatedEndpoints,\n };\n\n await updateConfig(updatedConfig);\n toast.success(\"接入点添加成功\");\n setAddDialogOpen(false);\n setNewEndpoint(\"\");\n setValidationError(\"\");\n } catch (error) {\n console.error(\"添加接入点失败:\", error);\n toast.error(error instanceof Error ? error.message : \"添加接入点失败\");\n } finally {\n setIsAdding(false);\n }\n };\n\n // 打开添加接入点对话框\n const openAddDialog = () => {\n setNewEndpoint(\"\");\n setValidationError(\"\");\n setAddDialogOpen(true);\n };\n\n // 处理输入变化\n const handleInputChange = (value: string) => {\n setNewEndpoint(value);\n if (validationError) {\n setValidationError(\"\");\n }\n };\n\n // 打开删除确认对话框\n const openDeleteConfirm = (endpoint: string) => {\n setEndpointToDelete(endpoint);\n setDeleteConfirmOpen(true);\n };\n\n const mcpEndpoints = useMemo(() => {\n let list: string[] = [];\n if (Array.isArray(mcpEndpoint)) list = mcpEndpoint;\n if (typeof mcpEndpoint === \"string\" && mcpEndpoint.length) {\n list.push(mcpEndpoint);\n }\n return list;\n }, [mcpEndpoint]);\n\n return (\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogTrigger asChild>\n <Button variant=\"secondary\" size=\"icon\" className=\"size-8\">\n <SettingsIcon className=\"size-4\" />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-[600px]\">\n <DialogHeader className=\"mb-4\">\n <DialogTitle>配置小智服务端接入点</DialogTitle>\n <DialogDescription>\n 点击保存后,需要重启服务才会生效。\n </DialogDescription>\n </DialogHeader>\n <div className=\"flex flex-col gap-2\">\n {mcpEndpoints.map((item) => (\n <div\n key={item}\n className=\"flex items-center justify-between p-4 bg-slate-50 rounded-md font-mono\"\n >\n <span className=\"flex-1 text-ellipsis overflow-hidden whitespace-nowrap\">\n {sliceEndpoint(item)}\n </span>\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => handleCopy(item)}\n title=\"复制完整地址\"\n >\n <CopyIcon className=\"size-4\" />\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => openDeleteConfirm(item)}\n title=\"删除此接入点\"\n >\n <TrashIcon className=\"size-4 text-red-500\" />\n </Button>\n </div>\n </div>\n ))}\n {mcpEndpoints.length === 0 && (\n <div className=\"flex flex-col items-center flex-1 text-sm text-muted-foreground text-center justify-center gap-2\">\n <BadgeInfoIcon />\n <span>暂无接入点,请添加</span>\n </div>\n )}\n <div className=\"flex items-center gap-2 mt-4\">\n <Button\n size=\"icon\"\n className=\"flex-1 flex items-center gap-2\"\n onClick={openAddDialog}\n >\n <PlusIcon className=\"size-4\" />\n <span>添加小智服务端接入点</span>\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon\"\n className=\"flex-1 flex items-center gap-2\"\n onClick={() =>\n window.open(\"https://xiaozhi.me/console/agents\", \"_blank\")\n }\n >\n <span>打开小智服务端</span>\n </Button>\n </div>\n </div>\n </DialogContent>\n\n {/* 删除确认对话框 */}\n <AlertDialog open={deleteConfirmOpen} onOpenChange={setDeleteConfirmOpen}>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>确认删除接入点</AlertDialogTitle>\n <AlertDialogDescription>\n 确定要删除接入点 \"{sliceEndpoint(endpointToDelete)}\"\n 吗?此操作无法撤销。\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={isDeleting}>取消</AlertDialogCancel>\n <AlertDialogAction\n onClick={handleDeleteEndpoint}\n disabled={isDeleting}\n className=\"bg-destructive text-destructive-foreground hover:bg-destructive/90\"\n >\n {isDeleting ? \"删除中...\" : \"确定删除\"}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n\n {/* 添加接入点对话框 */}\n <Dialog open={addDialogOpen} onOpenChange={setAddDialogOpen}>\n <DialogContent className=\"sm:max-w-[500px]\">\n <DialogHeader>\n <DialogTitle>添加新的接入点</DialogTitle>\n <DialogDescription>请输入小智服务端接入点地址</DialogDescription>\n </DialogHeader>\n <div className=\"grid gap-4 py-4\">\n <div className=\"space-y-2\">\n <Input\n placeholder=\"请输入接入点地址,例如:wss://api.xiaozhi.me/mcp/?token=...\"\n value={newEndpoint}\n onChange={(e) => handleInputChange(e.target.value)}\n disabled={isAdding}\n className=\"font-mono text-sm\"\n />\n {validationError && (\n <p className=\"text-sm text-red-500\">{validationError}</p>\n )}\n </div>\n </div>\n <DialogFooter>\n <DialogClose asChild>\n <Button variant=\"outline\" disabled={isAdding}>\n 取消\n </Button>\n </DialogClose>\n <Button\n onClick={handleAddEndpoint}\n disabled={isAdding || !newEndpoint.trim()}\n >\n {isAdding ? \"添加中...\" : \"确定\"}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </Dialog>\n );\n}\n","import * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst labelVariants = cva(\n \"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n);\n\nconst Label = React.forwardRef<\n React.ElementRef<typeof LabelPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &\n VariantProps<typeof labelVariants>\n>(({ className, ...props }, ref) => (\n <LabelPrimitive.Root\n ref={ref}\n className={cn(labelVariants(), className)}\n {...props}\n />\n));\nLabel.displayName = LabelPrimitive.Root.displayName;\n\nexport { Label };\n","import type * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport {\n Controller,\n type ControllerProps,\n type FieldPath,\n type FieldValues,\n FormProvider,\n useFormContext,\n} from \"react-hook-form\";\n\nimport { Label } from \"@/components/ui/label\";\nimport { cn } from \"@/lib/utils\";\n\nconst Form = FormProvider;\n\ntype FormFieldContextValue<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = {\n name: TName;\n};\n\nconst FormFieldContext = React.createContext<FormFieldContextValue>(\n {} as FormFieldContextValue\n);\n\nconst FormField = <\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({\n ...props\n}: ControllerProps<TFieldValues, TName>) => {\n return (\n <FormFieldContext.Provider value={{ name: props.name }}>\n <Controller {...props} />\n </FormFieldContext.Provider>\n );\n};\n\nconst useFormField = () => {\n const fieldContext = React.useContext(FormFieldContext);\n const itemContext = React.useContext(FormItemContext);\n const { getFieldState, formState } = useFormContext();\n\n const fieldState = getFieldState(fieldContext.name, formState);\n\n if (!fieldContext) {\n throw new Error(\"useFormField should be used within <FormField>\");\n }\n\n const { id } = itemContext;\n\n return {\n id,\n name: fieldContext.name,\n formItemId: `${id}-form-item`,\n formDescriptionId: `${id}-form-item-description`,\n formMessageId: `${id}-form-item-message`,\n ...fieldState,\n };\n};\n\ntype FormItemContextValue = {\n id: string;\n};\n\nconst FormItemContext = React.createContext<FormItemContextValue>(\n {} as FormItemContextValue\n);\n\nconst FormItem = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => {\n const id = React.useId();\n\n return (\n <FormItemContext.Provider value={{ id }}>\n <div ref={ref} className={cn(\"space-y-2\", className)} {...props} />\n </FormItemContext.Provider>\n );\n});\nFormItem.displayName = \"FormItem\";\n\nconst FormLabel = React.forwardRef<\n React.ElementRef<typeof LabelPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>\n>(({ className, ...props }, ref) => {\n const { error, formItemId } = useFormField();\n\n return (\n <Label\n ref={ref}\n className={cn(error && \"text-destructive\", className)}\n htmlFor={formItemId}\n {...props}\n />\n );\n});\nFormLabel.displayName = \"FormLabel\";\n\nconst FormControl = React.forwardRef<\n React.ElementRef<typeof Slot>,\n React.ComponentPropsWithoutRef<typeof Slot>\n>(({ ...props }, ref) => {\n const { error, formItemId, formDescriptionId, formMessageId } =\n useFormField();\n\n return (\n <Slot\n ref={ref}\n id={formItemId}\n aria-describedby={\n !error\n ? `${formDescriptionId}`\n : `${formDescriptionId} ${formMessageId}`\n }\n aria-invalid={!!error}\n {...props}\n />\n );\n});\nFormControl.displayName = \"FormControl\";\n\nconst FormDescription = React.forwardRef<\n HTMLParagraphElement,\n React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => {\n const { formDescriptionId } = useFormField();\n\n return (\n <p\n ref={ref}\n id={formDescriptionId}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n});\nFormDescription.displayName = \"FormDescription\";\n\nconst FormMessage = React.forwardRef<\n HTMLParagraphElement,\n React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, children, ...props }, ref) => {\n const { error, formMessageId } = useFormField();\n const body = error ? String(error?.message ?? \"\") : children;\n\n if (!body) {\n return null;\n }\n\n return (\n <p\n ref={ref}\n id={formMessageId}\n className={cn(\"text-sm font-medium text-destructive\", className)}\n {...props}\n >\n {body}\n </p>\n );\n});\nFormMessage.displayName = \"FormMessage\";\n\nexport {\n useFormField,\n Form,\n FormItem,\n FormLabel,\n FormControl,\n FormDescription,\n FormMessage,\n FormField,\n};\n","import { Button } from \"@/components/ui/button\";\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport {\n Form,\n FormControl,\n FormField,\n FormItem,\n FormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { useWebSocket } from \"@/hooks/useWebSocket\";\nimport { useConfig } from \"@/stores/config\";\nimport {\n useWebSocketConnected,\n useWebSocketPortChangeStatus,\n} from \"@/stores/websocket\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { SettingsIcon } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport z from \"zod\";\n\nconst formSchema = z.object({\n port: z\n .string()\n .min(1, { message: \"端口号不能为空\" })\n .refine((val) => !Number.isNaN(Number(val)), {\n message: \"请输入有效的数字\",\n })\n .refine((val) => Number(val) >= 1 && Number(val) <= 65535, {\n message: \"端口号必须在 1-65535 之间\",\n })\n .refine((val) => Number.isInteger(Number(val)), {\n message: \"端口号必须是整数\",\n }),\n});\n\nexport function WebUrlSettingButton() {\n const [open, setOpen] = useState(false);\n const [isLoading, setIsLoading] = useState(false);\n const config = useConfig();\n const connected = useWebSocketConnected();\n const portChangeStatus = useWebSocketPortChangeStatus();\n const { changePort } = useWebSocket();\n\n const form = useForm<z.infer<typeof formSchema>>({\n resolver: zodResolver(formSchema),\n defaultValues: {\n port: \"9999\",\n },\n });\n\n // 当配置加载后,更新表单默认值\n useEffect(() => {\n if (config?.webUI?.port) {\n form.reset({\n port: config.webUI.port.toString(),\n });\n }\n }, [config, form]);\n\n // 获取按钮文本\n const getButtonText = () => {\n if (isLoading) {\n return \"处理中...\";\n }\n\n if (portChangeStatus?.status === \"checking\") {\n return \"检测端口...\";\n }\n\n if (portChangeStatus?.status === \"polling\") {\n const { currentAttempt, maxAttempts } = portChangeStatus;\n return `等待服务重启 (${currentAttempt || 0}/${maxAttempts || 45})`;\n }\n\n if (portChangeStatus?.status === \"connecting\") {\n return \"连接中...\";\n }\n\n return connected ? \"保存并重启\" : \"保存并连接\";\n };\n\n async function onSubmit(values: z.infer<typeof formSchema>) {\n const newPort = Number(values.port);\n const currentPort = config?.webUI?.port;\n\n // 如果端口号没有变化,直接关闭对话框\n if (newPort === currentPort) {\n setOpen(false);\n return;\n }\n\n console.log(\n `[WebUrlSettingButton] 开始端口切换: ${currentPort} -> ${newPort}`\n );\n setIsLoading(true);\n\n try {\n // 显示开始处理的提示\n toast.info(\n connected\n ? `正在将端口从 ${currentPort} 切换到 ${newPort}...`\n : `正在连接到端口 ${newPort}...`\n );\n\n await changePort(newPort);\n\n // 成功提示\n toast.success(\n connected\n ? `端口已成功切换到 ${newPort},页面即将刷新...`\n : `已成功连接到端口 ${newPort},页面即将刷新...`\n );\n setOpen(false);\n } catch (error) {\n console.error(\"端口切换失败:\", error);\n const errorMessage =\n error instanceof Error ? error.message : \"端口切换失败\";\n toast.error(`端口切换失败: ${errorMessage}`);\n } finally {\n setIsLoading(false);\n }\n }\n\n return (\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogTrigger asChild>\n <Button variant=\"secondary\" size=\"icon\" className=\"size-8\">\n <SettingsIcon className=\"h-4 w-4\" />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-[250px]\">\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)}>\n <DialogHeader className=\"mb-4\">\n <DialogTitle>配置服务端端口</DialogTitle>\n <DialogDescription>\n {connected\n ? \"修改端口后将自动重启服务并重新连接。\"\n : \"请输入服务端端口号,系统将尝试连接。\"}\n </DialogDescription>\n </DialogHeader>\n <div className=\"flex items-center gap-2\">\n <div>ws://{window.location.hostname}:</div>\n <div className=\"w-[100px]\">\n <FormField\n control={form.control}\n name=\"port\"\n render={({ field }) => (\n <FormItem>\n <FormControl>\n <Input\n placeholder=\"服务端端口,默认9999\"\n className=\"font-mono text-sm\"\n disabled={isLoading}\n type=\"number\"\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n </div>\n <DialogFooter className=\"mt-4\">\n <DialogClose asChild>\n <Button variant=\"outline\" disabled={isLoading}>\n 取消\n </Button>\n </DialogClose>\n <Button\n type=\"submit\"\n disabled={isLoading || portChangeStatus?.status === \"polling\"}\n >\n {getButtonText()}\n </Button>\n </DialogFooter>\n </form>\n </Form>\n </DialogContent>\n </Dialog>\n );\n}\n","import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Card = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n \"rounded-lg border bg-card text-card-foreground shadow-sm\",\n className\n )}\n {...props}\n />\n));\nCard.displayName = \"Card\";\n\nconst CardHeader = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\"flex flex-col space-y-1.5 p-6\", className)}\n {...props}\n />\n));\nCardHeader.displayName = \"CardHeader\";\n\nconst CardTitle = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n \"text-2xl font-semibold leading-none tracking-tight\",\n className\n )}\n {...props}\n />\n));\nCardTitle.displayName = \"CardTitle\";\n\nconst CardDescription = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nCardDescription.displayName = \"CardDescription\";\n\nconst CardContent = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div ref={ref} className={cn(\"p-6 pt-0\", className)} {...props} />\n));\nCardContent.displayName = \"CardContent\";\n\nconst CardFooter = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\"flex items-center p-6 pt-0\", className)}\n {...props}\n />\n));\nCardFooter.displayName = \"CardFooter\";\n\nexport {\n Card,\n CardHeader,\n CardFooter,\n CardTitle,\n CardDescription,\n CardContent,\n};\n","import { McpEndpointSettingButton } from \"@/components/McpEndpointSettingButton\";\nimport { WebUrlSettingButton } from \"@/components/WebUrlSettingButton\";\nimport {\n Card,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/components/ui/card\";\nimport { useMcpEndpoint, useMcpServers } from \"@/stores/config\";\nimport { useWebSocketConnected, useWebSocketUrl } from \"@/stores/websocket\";\n\nconst MiniCircularProgress = ({\n showValue = true,\n value = 0,\n maxValue = 100,\n size = 60,\n activeColor = \"#3b82f6\",\n inactiveColor = \"#e5e7eb\",\n symbol = \"%\",\n}) => {\n const radius = (size - 6) / 2;\n const circumference = radius * 2 * Math.PI;\n const strokeDasharray = circumference;\n const strokeDashoffset = circumference - (value / maxValue) * circumference;\n\n return (\n <div className=\"relative inline-flex items-center justify-center\">\n <svg width={size} height={size} className=\"transform -rotate-90\">\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n stroke={inactiveColor}\n strokeWidth={6}\n fill=\"none\"\n />\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n stroke={activeColor}\n strokeWidth={6}\n fill=\"none\"\n strokeDasharray={strokeDasharray}\n strokeDashoffset={strokeDashoffset}\n strokeLinecap=\"round\"\n className=\"transition-all duration-300 ease-in-out\"\n />\n </svg>\n {showValue && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <span className=\"text-xs font-medium\">\n {value}\n {symbol}\n </span>\n </div>\n )}\n </div>\n );\n};\n\nexport function DashboardStatusCard() {\n const mcpServers = useMcpServers();\n const connected = useWebSocketConnected();\n const mcpServerCount = Object.keys(mcpServers || {}).length;\n const wsUrl = useWebSocketUrl();\n const mcpEndpoint = useMcpEndpoint();\n return (\n <div className=\"*:data-[slot=card]:shadow-xs @xl/main:grid-cols-2 @5xl/main:grid-cols-4 grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:bg-gradient-to-t *:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card lg:px-6\">\n <Card className=\"@container/card\">\n <CardHeader className=\"relative\">\n <CardDescription>小智接入点</CardDescription>\n <CardTitle className=\"@[250px]/card:text-3xl text-2xl font-semibold tabular-nums\">\n {Array.isArray(mcpEndpoint)\n ? mcpEndpoint.length\n : mcpEndpoint\n ? 1\n : 0}\n </CardTitle>\n <div className=\"absolute right-4 top-4 flex flex-col items-center\">\n <MiniCircularProgress\n showValue={false}\n value={\n Array.isArray(mcpEndpoint)\n ? mcpEndpoint.length\n : mcpEndpoint\n ? 1\n : 0\n }\n maxValue={Math.max(\n Array.isArray(mcpEndpoint) ? mcpEndpoint.length : 1,\n 1\n )}\n activeColor=\"#16a34a\"\n inactiveColor=\"#f87171\"\n size={30}\n symbol=\"\"\n />\n </div>\n </CardHeader>\n <CardFooter className=\"flex-col items-end gap-1 text-sm\">\n <McpEndpointSettingButton />\n </CardFooter>\n </Card>\n <Card className=\"@container/card\">\n <CardHeader className=\"relative\">\n <CardDescription>Xiaozhi Client</CardDescription>\n <CardTitle className=\"@[250px]/card:text-3xl text-2xl font-semibold tabular-nums\">\n {connected ? \"已连接\" : \"未连接\"}\n </CardTitle>\n <div className=\"absolute right-4 top-4\">\n <MiniCircularProgress\n showValue={false}\n value={1}\n maxValue={1}\n activeColor={connected ? \"#16a34a\" : \"#f87171\"}\n inactiveColor={connected ? \"#16a34a\" : \"#f87171\"}\n size={30}\n symbol=\"\"\n />\n </div>\n </CardHeader>\n <CardFooter className=\"flex items-center justify-between gap-1 text-sm\">\n <div className=\"text-muted-foreground\">{wsUrl}</div>\n <WebUrlSettingButton />\n </CardFooter>\n </Card>\n <Card className=\"@container/card\">\n <CardHeader className=\"relative\">\n <CardDescription>MCP服务</CardDescription>\n <CardTitle className=\"@[250px]/card:text-3xl text-2xl font-semibold tabular-nums\">\n {mcpServerCount}\n </CardTitle>\n <div className=\"absolute right-4 top-4\">\n <MiniCircularProgress\n showValue={false}\n value={mcpServerCount}\n maxValue={mcpServerCount}\n activeColor=\"#16a34a\"\n inactiveColor=\"#f87171\"\n size={30}\n symbol=\"\"\n />\n </div>\n </CardHeader>\n </Card>\n </div>\n );\n}\n","import { type VariantProps, cva } from \"class-variance-authority\";\nimport type * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst badgeVariants = cva(\n \"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\",\n {\n variants: {\n variant: {\n default:\n \"border-transparent bg-primary text-primary-foreground hover:bg-primary/80\",\n secondary:\n \"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n destructive:\n \"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80\",\n outline: \"text-foreground\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n);\n\nexport interface BadgeProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof badgeVariants> {}\n\nfunction Badge({ className, variant, ...props }: BadgeProps) {\n return (\n <div className={cn(badgeVariants({ variant }), className)} {...props} />\n );\n}\n\nexport { Badge, badgeVariants };\n","/**\n * MCP 服务工具函数\n * 用于判断 MCP 服务的通信类型和其他相关操作\n */\n\n// 定义通信类型\nexport type MCPCommunicationType = \"stdio\" | \"sse\" | \"streamable-http\";\n\n// 定义 MCP 服务配置类型(与服务端保持一致)\nexport interface LocalMCPServerConfig {\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\nexport interface SSEMCPServerConfig {\n type: \"sse\";\n url: string;\n}\n\nexport interface StreamableHTTPMCPServerConfig {\n type?: \"streamable-http\"; // 可选,因为默认就是 streamable-http\n url: string;\n}\n\nexport type MCPServerConfig =\n | LocalMCPServerConfig\n | SSEMCPServerConfig\n | StreamableHTTPMCPServerConfig;\n\n/**\n * 判断 MCP 服务的通信类型\n *\n * @param serverConfig MCP 服务配置对象\n * @returns 通信类型:'stdio' | 'sse' | 'streamable-http'\n *\n * 判断逻辑:\n * 1. 如果配置对象有 command 字段 → stdio\n * 2. 如果配置对象有 type 字段且值为 \"sse\" → sse\n * 3. 如果配置对象有 url 字段但没有 type 字段,或者 type 字段不是 \"sse\" → streamable-http\n *\n * @example\n * ```typescript\n * // stdio 类型\n * const stdioConfig = {\n * command: \"node\",\n * args: [\"./mcpServers/calculator.js\"]\n * };\n * getMcpServerCommunicationType(stdioConfig); // \"stdio\"\n *\n * // sse 类型\n * const sseConfig = {\n * type: \"sse\" as const,\n * url: \"https://mcp.api-inference.modelscope.net/d3cfd34529ae4e/sse\"\n * };\n * getMcpServerCommunicationType(sseConfig); // \"sse\"\n *\n * // streamable-http 类型\n * const httpConfig = {\n * url: \"https://mcp.amap.com/mcp?key=1ec31da021b2702787841ea4ee822de3\"\n * };\n * getMcpServerCommunicationType(httpConfig); // \"streamable-http\"\n * ```\n */\nexport function getMcpServerCommunicationType(\n serverConfig: MCPServerConfig | Record<string, any>\n): MCPCommunicationType {\n // 参数验证\n if (!serverConfig || typeof serverConfig !== \"object\") {\n throw new Error(\"服务配置必须是一个有效的对象\");\n }\n\n // 1. 检查是否为 stdio 类型(有 command 字段)\n if (\"command\" in serverConfig && typeof serverConfig.command === \"string\") {\n return \"stdio\";\n }\n\n // 2. 检查是否为 sse 类型(有 type: \"sse\" 字段)\n if (\"type\" in serverConfig && serverConfig.type === \"sse\") {\n return \"sse\";\n }\n\n // 3. 检查是否为 streamable-http 类型(有 url 字段)\n if (\"url\" in serverConfig && typeof serverConfig.url === \"string\") {\n return \"streamable-http\";\n }\n\n // 如果都不匹配,抛出错误\n throw new Error(\n \"无法识别的 MCP 服务配置类型。配置必须包含 command 字段(stdio)、type: 'sse' 字段(sse)或 url 字段(streamable-http)\"\n );\n}\n\n/**\n * 检查 MCP 服务配置是否为 stdio 类型\n */\nexport function isStdioMcpServer(\n serverConfig: MCPServerConfig | Record<string, any>\n): serverConfig is LocalMCPServerConfig {\n return getMcpServerCommunicationType(serverConfig) === \"stdio\";\n}\n\n/**\n * 检查 MCP 服务配置是否为 sse 类型\n */\nexport function isSSEMcpServer(\n serverConfig: MCPServerConfig | Record<string, any>\n): serverConfig is SSEMCPServerConfig {\n return getMcpServerCommunicationType(serverConfig) === \"sse\";\n}\n\n/**\n * 检查 MCP 服务配置是否为 streamable-http 类型\n */\nexport function isStreamableHTTPMcpServer(\n serverConfig: MCPServerConfig | Record<string, any>\n): serverConfig is StreamableHTTPMCPServerConfig {\n return getMcpServerCommunicationType(serverConfig) === \"streamable-http\";\n}\n\n/**\n * 获取 MCP 服务配置的显示名称\n * 用于在 UI 中显示更友好的通信类型名称\n */\nexport function getMcpServerTypeDisplayName(\n serverConfig: MCPServerConfig | Record<string, any>\n): string {\n const type = getMcpServerCommunicationType(serverConfig);\n\n switch (type) {\n case \"stdio\":\n return \"本地进程 (stdio)\";\n case \"sse\":\n return \"服务器推送 (SSE)\";\n case \"streamable-http\":\n return \"流式 HTTP\";\n default:\n return \"未知类型\";\n }\n}\n","import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Textarea = React.forwardRef<\n HTMLTextAreaElement,\n React.ComponentProps<\"textarea\">\n>(({ className, ...props }, ref) => {\n return (\n <textarea\n className={cn(\n \"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n className\n )}\n ref={ref}\n {...props}\n />\n );\n});\nTextarea.displayName = \"Textarea\";\n\nexport { Textarea };\n","import { Button } from \"@/components/ui/button\";\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport {\n Form,\n FormControl,\n FormField,\n FormItem,\n FormMessage,\n} from \"@/components/ui/form\";\nimport { useWebSocket } from \"@/hooks/useWebSocket\";\nimport { useConfig } from \"@/stores/config\";\nimport type { MCPServerConfig } from \"@/types\";\nimport { getMcpServerCommunicationType } from \"@/utils/mcpServerUtils\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { PlusIcon } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport z from \"zod\";\nimport { Textarea } from \"./ui/textarea\";\n\nconst formSchema = z.object({\n config: z.string().min(2, {\n message: \"配置不能为空\",\n }),\n});\n\nexport function AddMcpServerButton() {\n const [open, setOpen] = useState(false);\n const [isLoading, setIsLoading] = useState(false);\n const { updateConfig } = useWebSocket();\n const config = useConfig();\n\n const form = useForm<z.infer<typeof formSchema>>({\n resolver: zodResolver(formSchema),\n defaultValues: {\n config: \"\",\n },\n });\n\n // 验证结果接口\n interface ValidationResult {\n success: boolean;\n data?: Record<string, MCPServerConfig>;\n error?: string;\n }\n\n // 验证单个 MCP 服务配置\n const validateSingleServerConfig = (\n serverName: string,\n serverConfig: any\n ): { valid: boolean; error?: string } => {\n if (!serverConfig || typeof serverConfig !== \"object\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 的配置必须是一个对象`,\n };\n }\n\n // 先进行基本字段检查,避免 getMcpServerCommunicationType 抛出错误\n const hasCommand = \"command\" in serverConfig;\n const hasType = \"type\" in serverConfig;\n const hasUrl = \"url\" in serverConfig;\n\n // 判断配置类型并验证相应字段\n if (hasCommand) {\n // stdio 类型\n if (!serverConfig.command || typeof serverConfig.command !== \"string\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 缺少必需的 command 字段或字段类型不正确`,\n };\n }\n if (!Array.isArray(serverConfig.args)) {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 的 args 字段必须是数组`,\n };\n }\n } else if (hasType && serverConfig.type === \"sse\") {\n // sse 类型\n if (!serverConfig.url || typeof serverConfig.url !== \"string\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 缺少必需的 url 字段或字段类型不正确`,\n };\n }\n } else if (hasUrl) {\n // streamable-http 类型\n if (!serverConfig.url || typeof serverConfig.url !== \"string\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 缺少必需的 url 字段或字段类型不正确`,\n };\n }\n if (serverConfig.type && serverConfig.type !== \"streamable-http\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 的 type 字段如果存在,必须是 \"streamable-http\"`,\n };\n }\n } else {\n // 无法识别的配置类型\n return {\n valid: false,\n error: `服务 \"${serverName}\" 的配置无效: 必须包含 command 字段(stdio)、type: 'sse' 字段(sse)或 url 字段(streamable-http)`,\n };\n }\n\n // 最后用工具函数验证配置是否完整\n try {\n getMcpServerCommunicationType(serverConfig);\n return { valid: true };\n } catch (error) {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 的配置无效: ${\n error instanceof Error ? error.message : \"未知错误\"\n }`,\n };\n }\n };\n\n // 验证 MCP 配置的函数\n const validateMCPConfig = (input: string): ValidationResult => {\n try {\n const trimmed = input.trim();\n if (!trimmed) {\n return { success: false, error: \"配置不能为空\" };\n }\n\n const parsed = JSON.parse(trimmed);\n\n let mcpServers: Record<string, any>;\n\n // 检查是否包含 mcpServers 层\n if (parsed.mcpServers && typeof parsed.mcpServers === \"object\") {\n mcpServers = parsed.mcpServers;\n } else if (typeof parsed === \"object\" && !Array.isArray(parsed)) {\n // 检查是否是单个服务配置\n try {\n getMcpServerCommunicationType(parsed);\n // 如果能识别类型,说明是单个服务配置,生成默认名称\n const defaultName = parsed.command\n ? parsed.command.split(\"/\").pop() || \"mcp-server\"\n : parsed.type === \"sse\"\n ? \"sse-server\"\n : \"http-server\";\n mcpServers = { [defaultName]: parsed };\n } catch {\n // 无法识别为单个服务配置,认为是多个服务的配置对象\n mcpServers = parsed;\n }\n } else {\n return { success: false, error: \"配置格式错误: 必须是对象格式\" };\n }\n\n // 验证每个服务配置\n for (const [serverName, serverConfig] of Object.entries(mcpServers)) {\n const validation = validateSingleServerConfig(serverName, serverConfig);\n if (!validation.valid) {\n return { success: false, error: validation.error };\n }\n }\n\n return { success: true, data: mcpServers };\n } catch (error) {\n return {\n success: false,\n error: `JSON 格式错误: ${\n error instanceof Error ? error.message : \"无法解析 JSON\"\n }`,\n };\n }\n };\n\n async function onSubmit(values: z.infer<typeof formSchema>) {\n if (!config) {\n toast.error(\"配置数据未加载,请稍后重试\");\n return;\n }\n\n setIsLoading(true);\n try {\n // 验证用户输入的配置\n const validation = validateMCPConfig(values.config);\n\n if (!validation.success) {\n toast.error(validation.error || \"配置验证失败\");\n return;\n }\n\n const parsedServers = validation.data!;\n\n // 检查是否有重名的服务\n const existingNames = Object.keys(parsedServers).filter(\n (name) => name in (config.mcpServers || {})\n );\n if (existingNames.length > 0) {\n toast.error(\n `服务名称冲突: 以下服务已存在: ${existingNames.join(\", \")}`\n );\n return;\n }\n\n // 更新配置\n const updatedConfig = {\n ...config,\n mcpServers: {\n ...parsedServers,\n ...config.mcpServers,\n },\n };\n\n await updateConfig(updatedConfig);\n\n // 成功反馈\n const addedCount = Object.keys(parsedServers).length;\n toast.success(\n addedCount === 1\n ? `已添加 MCP 服务 \"${Object.keys(parsedServers)[0]}\"`\n : `已添加 ${addedCount} 个 MCP 服务`\n );\n\n // 重置表单并关闭对话框\n form.reset();\n setOpen(false);\n } catch (error) {\n console.error(\"更新配置失败:\", error);\n toast.error(error instanceof Error ? error.message : \"更新配置失败\");\n } finally {\n setIsLoading(false);\n }\n }\n\n return (\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogTrigger asChild>\n <Button size=\"icon\" className=\"w-full\">\n <PlusIcon className=\"h-4 w-4\" />\n <span>添加MCP服务</span>\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-[500px]\">\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)}>\n <DialogHeader className=\"mb-4\">\n <DialogTitle>添加MCP服务</DialogTitle>\n <DialogDescription>\n 添加后,需要重启服务才会生效。\n </DialogDescription>\n </DialogHeader>\n <div className=\"grid gap-4\">\n <FormField\n control={form.control}\n name=\"config\"\n render={({ field }) => (\n <FormItem>\n <FormControl>\n <Textarea\n className=\"resize-none h-[300px] font-mono text-sm\"\n disabled={isLoading}\n placeholder={`支持三种通信方式:\n\n1. 本地进程 (stdio):\n{\n \"mcpServers\": {\n \"local-server\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@example/mcp-server\"]\n }\n }\n}\n\n2. 服务器推送 (SSE):\n{\n \"mcpServers\": {\n \"sse-server\": {\n \"type\": \"sse\",\n \"url\": \"https://example.com/sse\"\n }\n }\n}\n\n3. 流式 HTTP:\n{\n \"mcpServers\": {\n \"http-server\": {\n \"url\": \"https://example.com/mcp\"\n }\n }\n}`}\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n <DialogFooter className=\"mt-4\">\n <DialogClose asChild>\n <Button variant=\"outline\" disabled={isLoading}>\n 取消\n </Button>\n </DialogClose>\n <Button type=\"submit\" disabled={isLoading}>\n {isLoading ? \"保存中...\" : \"保存\"}\n </Button>\n </DialogFooter>\n </form>\n </Form>\n </DialogContent>\n </Dialog>\n );\n}\n","import { Button } from \"@/components/ui/button\";\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport {\n Form,\n FormControl,\n FormField,\n FormItem,\n FormMessage,\n} from \"@/components/ui/form\";\n\nimport { useWebSocket } from \"@/hooks/useWebSocket\";\nimport { useConfig } from \"@/stores/config\";\nimport type { MCPServerConfig } from \"@/types\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { SettingsIcon } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport z from \"zod\";\nimport { Textarea } from \"./ui/textarea\";\n\nconst formSchema = z.object({\n config: z.string().min(2, {\n message: \"配置不能为空\",\n }),\n});\n\nexport function McpServerSettingButton({\n mcpServer,\n mcpServerName,\n}: {\n mcpServer: MCPServerConfig;\n mcpServerName: string;\n}) {\n const [open, setOpen] = useState(false);\n const [isLoading, setIsLoading] = useState(false);\n const config = useConfig();\n const { updateConfig } = useWebSocket();\n\n const form = useForm<z.infer<typeof formSchema>>({\n resolver: zodResolver(formSchema),\n defaultValues: {\n config: JSON.stringify(mcpServer, null, 2),\n },\n });\n\n async function onSubmit(values: z.infer<typeof formSchema>) {\n if (!config) {\n toast.error(\"配置数据未加载,请稍后重试\");\n return;\n }\n\n setIsLoading(true);\n try {\n // 解析用户输入的JSON配置\n let newMcpServerConfig: MCPServerConfig;\n try {\n newMcpServerConfig = JSON.parse(values.config);\n } catch (error) {\n toast.error(\"JSON格式错误,请检查配置格式\");\n return;\n }\n\n // 验证配置格式\n if (!newMcpServerConfig || typeof newMcpServerConfig !== \"object\") {\n toast.error(\"配置格式无效\");\n return;\n }\n\n // 更新配置\n const updatedConfig = {\n ...config,\n mcpServers: {\n ...config.mcpServers,\n [mcpServerName]: newMcpServerConfig,\n },\n };\n\n await updateConfig(updatedConfig);\n toast.success(\"MCP服务器配置已更新\");\n setOpen(false);\n } catch (error) {\n console.error(\"更新配置失败:\", error);\n toast.error(error instanceof Error ? error.message : \"更新配置失败\");\n } finally {\n setIsLoading(false);\n }\n }\n\n return (\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogTrigger asChild>\n <Button variant=\"secondary\" size=\"icon\" className=\"size-8\">\n <SettingsIcon className=\"h-4 w-4\" />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-[500px]\">\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)}>\n <DialogHeader className=\"mb-4\">\n <DialogTitle>配置 {mcpServerName} MCP</DialogTitle>\n <DialogDescription>\n 点击保存后,需要重启服务才会生效。\n </DialogDescription>\n </DialogHeader>\n <div className=\"grid gap-4\">\n <FormField\n control={form.control}\n name=\"config\"\n render={({ field }) => (\n <FormItem>\n <FormControl>\n <Textarea\n placeholder=\"MCP服务配置\"\n className=\"resize-none h-[300px] font-mono text-sm\"\n disabled={isLoading}\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n <DialogFooter className=\"mt-4\">\n <DialogClose asChild>\n <Button variant=\"outline\" disabled={isLoading}>\n 取消\n </Button>\n </DialogClose>\n <Button type=\"submit\" disabled={isLoading}>\n {isLoading ? \"保存中...\" : \"保存\"}\n </Button>\n </DialogFooter>\n </form>\n </Form>\n </DialogContent>\n </Dialog>\n );\n}\n","import { Button } from \"@/components/ui/button\";\nimport { useWebSocket } from \"@/hooks/useWebSocket\";\nimport { useConfig } from \"@/stores/config\";\nimport { TrashIcon } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { toast } from \"sonner\";\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n} from \"./ui/alert-dialog\";\n\nexport function RemoveMcpServerButton({\n mcpServerName,\n}: {\n mcpServerName: string;\n}) {\n const [isLoading, setIsLoading] = useState(false);\n const { updateConfig } = useWebSocket();\n const config = useConfig();\n\n const onRemove = async () => {\n if (!config) {\n toast.error(\"配置未加载,无法删除服务\");\n return;\n }\n\n try {\n setIsLoading(true);\n\n // 创建新的服务器配置,删除指定的服务器\n const newMcpServers = { ...config.mcpServers };\n delete newMcpServers[mcpServerName];\n\n // 创建新的服务器工具配置,删除对应的工具配置\n const newMcpServerConfig = config.mcpServerConfig\n ? { ...config.mcpServerConfig }\n : undefined;\n\n if (newMcpServerConfig && mcpServerName in newMcpServerConfig) {\n delete newMcpServerConfig[mcpServerName];\n }\n\n // 构建新的配置对象\n const newConfig = {\n ...config,\n mcpServers: newMcpServers,\n mcpServerConfig: newMcpServerConfig,\n };\n\n // 更新配置\n await updateConfig(newConfig);\n\n toast.success(`MCP 服务 \"${mcpServerName}\" 已删除`);\n } catch (error) {\n console.error(\"删除 MCP 服务失败:\", error);\n toast.error(\n `删除 MCP 服务失败: ${error instanceof Error ? error.message : \"未知错误\"}`\n );\n } finally {\n setIsLoading(false);\n }\n };\n\n return (\n <AlertDialog>\n <AlertDialogTrigger asChild>\n <Button variant=\"destructive\" size=\"icon\" className=\"size-8\">\n <TrashIcon className=\"h-4 w-4\" />\n </Button>\n </AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>\n 确定要删除这个({mcpServerName})MCP服务吗?\n </AlertDialogTitle>\n <AlertDialogDescription>\n 删除后,对应的工具列表也会移除。\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>取消</AlertDialogCancel>\n <AlertDialogAction\n onClick={onRemove}\n disabled={isLoading}\n className=\"bg-destructive text-destructive-foreground hover:bg-destructive/90\"\n >\n {isLoading ? \"删除中...\" : \"确定\"}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n );\n}\n","import { Button } from \"@/components/ui/button\";\nimport { useRestartPollingStatus, useStatusStore } from \"@/stores/status\";\nimport clsx from \"clsx\";\nimport { LoaderCircleIcon, PowerIcon } from \"lucide-react\";\n\n/**\n * 重启状态接口\n */\nexport interface RestartStatus {\n status: \"restarting\" | \"completed\" | \"failed\";\n error?: string;\n timestamp: number;\n}\n\n/**\n * RestartButton 组件属性接口\n */\nexport interface RestartButtonProps {\n /** 重启状态 */\n restartStatus?: RestartStatus;\n /** 是否禁用按钮 */\n disabled?: boolean;\n /** 按钮样式变体 */\n variant?:\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\"\n | \"link\";\n /** 自定义样式类 */\n className?: string;\n /** 重启中的文本 */\n restartingText?: string;\n /** 默认文本 */\n defaultText?: string;\n}\n\n/**\n * 独立的重启按钮组件\n * 基于 ConfigEditor.tsx 中的重启服务功能实现\n */\nexport function RestartButton({\n disabled = false,\n variant = \"outline\",\n className = \"\",\n restartingText = \"重启中...\",\n defaultText = \"重启服务\",\n}: RestartButtonProps) {\n const {\n loading: { isRestarting },\n restartService,\n } = useStatusStore();\n const restartPollingStatus = useRestartPollingStatus();\n\n // 处理重启点击事件\n const handleRestart = async () => {\n try {\n await restartService();\n } catch (error) {\n console.error(\"[RestartButton] 重启失败:\", error);\n }\n };\n\n // 计算显示文本\n const getDisplayText = () => {\n if (!isRestarting) {\n return defaultText;\n }\n\n // 如果重启轮询正在进行,显示进度信息\n if (restartPollingStatus.enabled && restartPollingStatus.startTime) {\n return \"重连中...\";\n }\n\n return restartingText;\n };\n\n return (\n <Button\n type=\"button\"\n onClick={handleRestart}\n variant={variant}\n disabled={isRestarting || disabled}\n className={clsx(\"flex items-center gap-2 w-[120px]\", className)}\n >\n {!isRestarting ? (\n <PowerIcon className=\"size-4\" />\n ) : (\n <LoaderCircleIcon className=\"size-4 animate-spin\" />\n )}\n {getDisplayText()}\n </Button>\n );\n}\n","import { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card, CardContent, CardFooter } from \"@/components/ui/card\";\nimport { useConfig, useMcpServerConfig, useMcpServers } from \"@/stores/config\";\nimport type { MCPServerConfig } from \"@/types\";\nimport { getMcpServerCommunicationType } from \"@/utils/mcpServerUtils\";\nimport { CoffeeIcon, MinusIcon, PlusIcon, Wrench } from \"lucide-react\";\nimport { useMemo } from \"react\";\nimport { toast } from \"sonner\";\nimport { AddMcpServerButton } from \"./AddMcpServerButton\";\nimport { McpServerSettingButton } from \"./McpServerSettingButton\";\nimport { RemoveMcpServerButton } from \"./RemoveMcpServerButton\";\nimport { RestartButton } from \"./RestartButton\";\n\ninterface McpServerListProps {\n updateConfig?: (config: any) => Promise<void>;\n}\n\nexport function McpServerList({ updateConfig }: McpServerListProps) {\n const mcpServerConfig = useMcpServerConfig();\n const mcpServers = useMcpServers();\n const config = useConfig();\n\n const tools = useMemo(() => {\n return Object.entries(mcpServerConfig || {}).flatMap(\n ([serverName, value]) => {\n return Object.entries(value?.tools || {}).map(([toolName, tool]) => ({\n serverName,\n toolName,\n ...(tool as any),\n }));\n }\n );\n }, [mcpServerConfig]);\n\n const handleToggleTool = async (\n serverName: string,\n toolName: string,\n currentEnable: boolean\n ) => {\n if (!updateConfig) {\n toast.error(\"updateConfig 方法未提供\");\n return;\n }\n\n if (!config) {\n toast.error(\"配置未加载\");\n return;\n }\n\n try {\n // 创建新的配置对象\n const newConfig = {\n ...config,\n mcpServerConfig: {\n ...config.mcpServerConfig,\n [serverName]: {\n ...config.mcpServerConfig?.[serverName],\n tools: {\n ...config.mcpServerConfig?.[serverName]?.tools,\n [toolName]: {\n ...config.mcpServerConfig?.[serverName]?.tools?.[toolName],\n enable: !currentEnable,\n },\n },\n },\n },\n };\n\n // 更新配置\n await updateConfig(newConfig);\n\n // 显示成功提示\n const action = !currentEnable ? \"启用\" : \"禁用\";\n toast.success(`${action}工具 ${toolName} 成功`);\n } catch (error) {\n console.error(\"切换工具状态失败:\", error);\n toast.error(error instanceof Error ? error.message : \"切换工具状态失败\");\n }\n };\n\n const enabledTools = tools.filter((tool) => tool.enable);\n const disabledTools = tools.filter((tool) => !tool.enable);\n\n if (!mcpServers || Object.keys(mcpServers).length === 0) {\n return (\n <div className=\"@container/main flex flex-1 flex-col gap-2\">\n <div className=\"flex flex-col gap-4 py-4 md:gap-6 md:py-6\">\n <div className=\"flex items-center justify-between px-4 lg:px-6\">\n <div>\n <h2 className=\"text-lg font-semibold\">你的聚合 MCP 服务</h2>\n <p className=\"text-sm text-muted-foreground\">\n 在这里管理你的 MCP 服务器和工具。\n </p>\n </div>\n {/* <AddMcpServerButton /> */}\n </div>\n\n <div className=\"px-4 lg:px-6\">\n <Card className=\"border-dashed\">\n <CardContent className=\"flex flex-col items-center justify-center py-12\">\n <CoffeeIcon className=\"h-12 w-12 text-muted-foreground mb-4\" />\n <h3 className=\"text-lg font-semibold mb-2\">还没有 MCP 服务</h3>\n <p className=\"text-sm text-muted-foreground text-center mb-4\">\n 添加你的第一个 MCP 服务器来开始使用强大的工具集成功能。\n </p>\n <AddMcpServerButton />\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col gap-4 px-4 lg:px-6\">\n <h2 className=\"text-2xl font-bold\">你的聚合 MCP 服务</h2>\n <p className=\"text-sm text-muted-foreground\">\n 你可以在这里管理你的 MCP\n 服务,包括启用/禁用工具,以及查看工具的详细信息。\n 最终暴露给小智服务端和其他MCP客户端的是这里聚合MCP\n </p>\n <div className=\"*:data-[slot=card]:shadow-xs @xl/main:grid-cols-8 @5xl/main:grid-cols-8 grid grid-cols-1 gap-4 *:data-[slot=card]:bg-gradient-to-t *:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card\">\n {/* <div>{JSON.stringify(enabledTools, null, 2)}</div> */}\n <Card className=\"transition-all duration-200 col-span-3\">\n <CardContent className=\"p-4\">\n <div className=\"flex-col\">\n <h4 className=\"text-sm font-medium mb-3 flex items-center gap-2\">\n <Wrench className=\"h-4 w-4\" />\n 使用中的工具 ({enabledTools.length})\n </h4>\n <div className=\"flex-1 space-y-2\">\n {enabledTools.map((tool) => (\n <div\n key={tool.toolName}\n className=\"flex items-start justify-between p-4 bg-slate-50 rounded-md font-mono\"\n >\n <div className=\"text-md flex flex-col gap-2\">\n <div className=\"flex items-center gap-2 justify-start\">\n <Badge variant=\"secondary\" className=\"rounded-md\">\n {tool.serverName}\n </Badge>\n <span>{tool.toolName}</span>\n </div>\n <p className=\"text-sm text-muted-foreground my-2\">\n {tool.description}\n </p>\n <div className=\"flex items-center gap-4\">\n <span className=\"text-sm text-muted-foreground\">\n <span className=\"text-muted-foreground\">\n 使用次数:\n </span>{\" \"}\n <span className=\"text-primary font-bold\">\n {tool.usageCount || 0}\n </span>\n </span>\n <span className=\"text-sm text-muted-foreground\">\n <span className=\"text-muted-foreground\">\n 最后使用:\n </span>{\" \"}\n <span className=\"text-primary font-bold\">\n {tool.lastUsedTime || \"-\"}\n </span>\n </span>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 ml-4\">\n <Button\n variant=\"secondary\"\n size=\"icon\"\n className=\"size-8 hover:bg-red-500 hover:text-white\"\n onClick={() =>\n handleToggleTool(tool.serverName, tool.toolName, true)\n }\n >\n <MinusIcon size={18} />\n </Button>\n </div>\n </div>\n ))}\n </div>\n </div>\n </CardContent>\n </Card>\n <Card className=\"transition-all duration-200 col-span-3\">\n <CardContent className=\"p-4\">\n <div className=\"flex-col\">\n <h4 className=\"text-sm font-medium mb-3 flex items-center gap-2\">\n <Wrench className=\"h-4 w-4\" />\n 未使用的工具 ({disabledTools.length})\n </h4>\n {disabledTools.length === 0 && (\n <div className=\"flex-1 flex flex-col items-center gap-4 py-20 px-4 bg-slate-50 rounded-md font-mono h-full\">\n <CoffeeIcon\n strokeWidth={1.5}\n size={48}\n className=\"text-muted-foreground\"\n />\n <span className=\"text-sm text-muted-foreground\">\n 全部工具都已经启用\n </span>\n </div>\n )}\n <div className=\"flex-1 space-y-2\">\n {disabledTools.map((tool) => (\n <div\n key={tool.toolName}\n className=\"flex items-start justify-between p-4 bg-slate-50 rounded-md font-mono\"\n >\n <div className=\"text-md flex flex-col gap-2\">\n <div className=\"flex items-center gap-2 justify-start\">\n <Badge variant=\"secondary\" className=\"rounded-md\">\n {tool.serverName}\n </Badge>\n <span>{tool.toolName}</span>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {tool.description}\n </p>\n <div className=\"flex items-center gap-4\">\n <span className=\"text-sm text-muted-foreground\">\n <span className=\"text-muted-foreground\">\n 使用次数:\n </span>{\" \"}\n <span className=\"text-primary font-bold\">\n {tool.usageCount || 0}\n </span>\n </span>\n <span className=\"text-sm text-muted-foreground\">\n <span className=\"text-muted-foreground\">\n 最后使用:\n </span>{\" \"}\n <span className=\"text-primary font-bold\">\n {tool.lastUsedTime || \"-\"}\n </span>\n </span>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 ml-4\">\n <Button\n variant=\"secondary\"\n size=\"icon\"\n className=\"size-8 hover:bg-green-500 hover:text-white\"\n onClick={() =>\n handleToggleTool(\n tool.serverName,\n tool.toolName,\n false\n )\n }\n >\n <PlusIcon className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n ))}\n </div>\n </div>\n </CardContent>\n </Card>\n <div className=\"transition-all duration-200 gap-4 flex flex-col col-span-2\">\n <div className=\"flex items-center gap-2\">\n <AddMcpServerButton />\n <RestartButton />\n </div>\n {Object.entries(mcpServers || {}).map(\n ([mcpServerName, mcpServer]) => (\n <Card\n key={mcpServerName}\n className={\"transition-all duration-200\"}\n >\n <CardContent className=\"p-0\">\n <div className=\"p-4 pb-2\">\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-start gap-4 flex-1\">\n {/* <div className=\"mt-1\">{getStatusIcon(service.status)}</div> */}\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2 mb-2\">\n <h3 className=\"text-lg font-semibold\">\n {mcpServerName}\n </h3>\n </div>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 ml-4\">\n <McpServerSettingButton\n mcpServerName={mcpServerName}\n mcpServer={mcpServer as MCPServerConfig}\n />\n <RemoveMcpServerButton mcpServerName={mcpServerName} />\n </div>\n </div>\n </div>\n </CardContent>\n <CardFooter className=\"p-4 pt-2\">\n <Badge variant=\"outline\" className=\"text-xs\">\n {getMcpServerCommunicationType(mcpServer)}\n </Badge>\n </CardFooter>\n </Card>\n )\n )}\n </div>\n </div>\n </div>\n );\n}\n","import { Separator } from \"@/components/ui/separator\";\nimport { SidebarTrigger } from \"@/components/ui/sidebar\";\nimport { GithubIcon } from \"lucide-react\";\n\nexport function SiteHeader({ title }: { title: string }) {\n return (\n <header className=\"group-has-data-[collapsible=icon]/sidebar-wrapper:h-12 flex h-12 shrink-0 items-center gap-2 border-b transition-[width,height] ease-linear\">\n <div className=\"flex w-full items-center gap-1 px-4 lg:gap-2 lg:px-6\">\n <SidebarTrigger className=\"-ml-1\" />\n <Separator\n orientation=\"vertical\"\n className=\"mx-2 data-[orientation=vertical]:h-4\"\n />\n <h1 className=\"text-base font-medium\">{title}</h1>\n </div>\n <div className=\"flex w-full justify-end items-center gap-1 px-4 lg:gap-2 lg:px-6\">\n <a\n href=\"https://github.com/shenjingnan/xiaozhi-client\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <GithubIcon\n size={24}\n className=\"text-slate-500\"\n fill=\"currentColor\"\n />\n </a>\n </div>\n </header>\n );\n}\n","/**\n * 统一的网络服务管理器\n * 整合 HTTP API 客户端和 WebSocket 管理器\n */\n\nimport type { AppConfig, ClientStatus } from \"../types\";\nimport { type ApiClient, apiClient } from \"./api\";\nimport {\n type ConnectionState,\n type WebSocketManager,\n webSocketManager,\n} from \"./websocket\";\n\n/**\n * 网络服务管理器类\n */\nexport class NetworkService {\n private apiClient: ApiClient;\n private webSocketManager: WebSocketManager;\n private initialized = false;\n\n constructor() {\n this.apiClient = apiClient;\n this.webSocketManager = webSocketManager;\n }\n\n /**\n * 初始化网络服务\n */\n async initialize(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n console.log(\"[NetworkService] 初始化网络服务\");\n\n // 启动 WebSocket 连接\n this.webSocketManager.connect();\n\n this.initialized = true;\n console.log(\"[NetworkService] 网络服务初始化完成\");\n }\n\n /**\n * 销毁网络服务\n */\n destroy(): void {\n console.log(\"[NetworkService] 销毁网络服务\");\n this.webSocketManager.disconnect();\n this.initialized = false;\n }\n\n // ==================== HTTP API 方法 ====================\n\n /**\n * 获取配置 (HTTP)\n */\n async getConfig(): Promise<AppConfig> {\n return this.apiClient.getConfig();\n }\n\n /**\n * 更新配置 (HTTP)\n */\n async updateConfig(config: AppConfig): Promise<void> {\n return this.apiClient.updateConfig(config);\n }\n\n /**\n * 获取状态 (HTTP)\n */\n async getStatus(): Promise<any> {\n return this.apiClient.getStatus();\n }\n\n /**\n * 获取客户端状态 (HTTP)\n */\n async getClientStatus(): Promise<ClientStatus> {\n return this.apiClient.getClientStatus();\n }\n\n /**\n * 重启服务 (HTTP)\n */\n async restartService(): Promise<void> {\n return this.apiClient.restartService();\n }\n\n /**\n * 停止服务 (HTTP)\n */\n async stopService(): Promise<void> {\n return this.apiClient.stopService();\n }\n\n /**\n * 启动服务 (HTTP)\n */\n async startService(): Promise<void> {\n return this.apiClient.startService();\n }\n\n /**\n * 获取服务状态 (HTTP)\n */\n async getServiceStatus(): Promise<any> {\n return this.apiClient.getServiceStatus();\n }\n\n /**\n * 获取服务健康状态 (HTTP)\n */\n async getServiceHealth(): Promise<any> {\n return this.apiClient.getServiceHealth();\n }\n\n /**\n * 获取 MCP 端点 (HTTP)\n */\n async getMcpEndpoint(): Promise<string> {\n return this.apiClient.getMcpEndpoint();\n }\n\n /**\n * 获取 MCP 端点列表 (HTTP)\n */\n async getMcpEndpoints(): Promise<string[]> {\n return this.apiClient.getMcpEndpoints();\n }\n\n /**\n * 获取 MCP 服务配置 (HTTP)\n */\n async getMcpServers(): Promise<Record<string, any>> {\n return this.apiClient.getMcpServers();\n }\n\n /**\n * 获取连接配置 (HTTP)\n */\n async getConnectionConfig(): Promise<any> {\n return this.apiClient.getConnectionConfig();\n }\n\n /**\n * 重新加载配置 (HTTP)\n */\n async reloadConfig(): Promise<AppConfig> {\n return this.apiClient.reloadConfig();\n }\n\n /**\n * 获取配置文件路径 (HTTP)\n */\n async getConfigPath(): Promise<string> {\n return this.apiClient.getConfigPath();\n }\n\n /**\n * 检查配置是否存在 (HTTP)\n */\n async checkConfigExists(): Promise<boolean> {\n return this.apiClient.checkConfigExists();\n }\n\n /**\n * 获取重启状态 (HTTP)\n */\n async getRestartStatus(): Promise<any> {\n return this.apiClient.getRestartStatus();\n }\n\n /**\n * 检查客户端是否连接 (HTTP)\n */\n async checkClientConnected(): Promise<boolean> {\n return this.apiClient.checkClientConnected();\n }\n\n /**\n * 获取最后心跳时间 (HTTP)\n */\n async getLastHeartbeat(): Promise<number | null> {\n return this.apiClient.getLastHeartbeat();\n }\n\n /**\n * 获取活跃的 MCP 服务器列表 (HTTP)\n */\n async getActiveMCPServers(): Promise<string[]> {\n return this.apiClient.getActiveMCPServers();\n }\n\n /**\n * 更新客户端状态 (HTTP)\n */\n async updateClientStatus(status: Partial<ClientStatus>): Promise<void> {\n return this.apiClient.updateClientStatus(status);\n }\n\n /**\n * 设置活跃的 MCP 服务器列表 (HTTP)\n */\n async setActiveMCPServers(servers: string[]): Promise<void> {\n return this.apiClient.setActiveMCPServers(servers);\n }\n\n /**\n * 重置状态 (HTTP)\n */\n async resetStatus(): Promise<void> {\n return this.apiClient.resetStatus();\n }\n\n // ==================== WebSocket 方法 ====================\n\n /**\n * 获取 WebSocket 连接状态\n */\n getWebSocketState(): ConnectionState {\n return this.webSocketManager.getState();\n }\n\n /**\n * 检查 WebSocket 是否已连接\n */\n isWebSocketConnected(): boolean {\n return this.webSocketManager.isConnected();\n }\n\n /**\n * 设置 WebSocket URL\n */\n setWebSocketUrl(url: string): void {\n this.webSocketManager.setUrl(url);\n }\n\n /**\n * 监听 WebSocket 事件\n */\n onWebSocketEvent<\n K extends keyof import(\"./websocket\").WebSocketEventListeners,\n >(\n event: K,\n listener: import(\"./websocket\").WebSocketEventListeners[K]\n ): void {\n this.webSocketManager.on(event, listener);\n }\n\n /**\n * 移除 WebSocket 事件监听器\n */\n offWebSocketEvent<\n K extends keyof import(\"./websocket\").WebSocketEventListeners,\n >(event: K): void {\n this.webSocketManager.off(event);\n }\n\n /**\n * 重新连接 WebSocket\n */\n reconnectWebSocket(): void {\n this.webSocketManager.disconnect();\n setTimeout(() => {\n this.webSocketManager.connect();\n }, 1000);\n }\n\n // ==================== 便捷方法 ====================\n\n /**\n * 获取完整的应用状态 (HTTP + WebSocket)\n */\n async getFullAppState(): Promise<{\n config: AppConfig;\n status: any;\n webSocketConnected: boolean;\n }> {\n const [config, status] = await Promise.all([\n this.getConfig(),\n this.getStatus(),\n ]);\n\n return {\n config,\n status,\n webSocketConnected: this.isWebSocketConnected(),\n };\n }\n\n /**\n * 更新配置并等待 WebSocket 通知 (混合模式)\n */\n async updateConfigWithNotification(\n config: AppConfig,\n timeout = 5000\n ): Promise<void> {\n // 设置 WebSocket 监听器等待配置更新通知\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.webSocketManager.off(\"configUpdate\");\n reject(new Error(\"等待配置更新通知超时\"));\n }, timeout);\n\n this.webSocketManager.on(\"configUpdate\", () => {\n clearTimeout(timeoutId);\n this.webSocketManager.off(\"configUpdate\");\n resolve();\n });\n\n // 通过 HTTP API 更新配置\n this.updateConfig(config).catch((error) => {\n clearTimeout(timeoutId);\n this.webSocketManager.off(\"configUpdate\");\n reject(error);\n });\n });\n }\n\n /**\n * 重启服务并等待状态通知 (混合模式)\n */\n async restartServiceWithNotification(timeout = 30000): Promise<void> {\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.webSocketManager.off(\"restartStatus\");\n reject(new Error(\"等待重启状态通知超时\"));\n }, timeout);\n\n this.webSocketManager.on(\"restartStatus\", (status) => {\n if (status.status === \"completed\") {\n clearTimeout(timeoutId);\n this.webSocketManager.off(\"restartStatus\");\n resolve();\n } else if (status.status === \"failed\") {\n clearTimeout(timeoutId);\n this.webSocketManager.off(\"restartStatus\");\n reject(new Error(status.error || \"服务重启失败\"));\n }\n });\n\n // 通过 HTTP API 重启服务\n this.restartService().catch((error) => {\n clearTimeout(timeoutId);\n this.webSocketManager.off(\"restartStatus\");\n reject(error);\n });\n });\n }\n}\n\n// 创建默认的网络服务实例\nexport const networkService = new NetworkService();\n\n// 导出其他服务\nexport { apiClient, webSocketManager };\nexport { ConnectionState } from \"./websocket\";\n\n// 导出类型\nexport type { ApiClient, WebSocketManager };\n","/**\n * 新的网络服务 Hook\n * 使用统一的网络服务管理器,实现 HTTP 和 WebSocket 的协调使用\n */\n\nimport { useCallback, useEffect, useRef } from \"react\";\nimport { ConnectionState, networkService } from \"../services\";\nimport { useConfigStore } from \"../stores/config\";\nimport { useStatusStore } from \"../stores/status\";\nimport { useWebSocketActions } from \"../stores/websocket\";\nimport type { AppConfig, ClientStatus } from \"../types\";\n\n/**\n * 网络服务 Hook\n */\nexport function useNetworkService() {\n const webSocketActions = useWebSocketActions();\n const initializationRef = useRef(false);\n\n // 初始化网络服务\n useEffect(() => {\n if (initializationRef.current) {\n return;\n }\n\n console.log(\"[NetworkService] 初始化网络服务\");\n initializationRef.current = true;\n\n // 初始化网络服务\n networkService.initialize().catch((error) => {\n console.error(\"[NetworkService] 初始化失败:\", error);\n });\n\n // 设置 WebSocket 事件监听器\n networkService.onWebSocketEvent(\"connected\", () => {\n console.log(\"[NetworkService] WebSocket 已连接\");\n webSocketActions.setConnectionState(ConnectionState.CONNECTED);\n\n // 连接成功后立即获取初始数据\n loadInitialData();\n });\n\n networkService.onWebSocketEvent(\"disconnected\", () => {\n console.log(\"[NetworkService] WebSocket 已断开\");\n webSocketActions.setConnectionState(ConnectionState.DISCONNECTED);\n });\n\n networkService.onWebSocketEvent(\"configUpdate\", (config: AppConfig) => {\n console.log(\"[NetworkService] 收到配置更新通知\");\n useConfigStore.getState().setConfig(config, \"websocket\");\n });\n\n networkService.onWebSocketEvent(\"statusUpdate\", (status: ClientStatus) => {\n console.log(\"[NetworkService] 收到状态更新通知\");\n useStatusStore.getState().setClientStatus(status, \"websocket\");\n });\n\n networkService.onWebSocketEvent(\"restartStatus\", (restartStatus) => {\n console.log(\"[NetworkService] 收到重启状态通知:\", restartStatus);\n useStatusStore.getState().setRestartStatus(restartStatus, \"websocket\");\n });\n\n networkService.onWebSocketEvent(\"error\", (error: Error) => {\n console.error(\"[NetworkService] WebSocket 错误:\", error);\n });\n\n // 清理函数\n return () => {\n console.log(\"[NetworkService] 清理网络服务\");\n networkService.destroy();\n initializationRef.current = false;\n };\n }, [webSocketActions]);\n\n /**\n * 加载初始数据\n */\n const loadInitialData = useCallback(async () => {\n try {\n console.log(\"[NetworkService] 加载初始数据\");\n\n // 并行获取配置和状态\n const [config, status] = await Promise.all([\n networkService.getConfig(),\n networkService.getClientStatus(),\n ]);\n\n console.log(\"[NetworkService] 初始数据加载成功\");\n useConfigStore.getState().setConfig(config, \"http\");\n useStatusStore.getState().setClientStatus(status, \"http\");\n } catch (error) {\n console.error(\"[NetworkService] 加载初始数据失败:\", error);\n }\n }, []);\n\n /**\n * 获取配置\n */\n const getConfig = useCallback(async (): Promise<AppConfig> => {\n try {\n const config = await networkService.getConfig();\n useConfigStore.getState().setConfig(config, \"http\");\n return config;\n } catch (error) {\n console.error(\"[NetworkService] 获取配置失败:\", error);\n throw error;\n }\n }, []);\n\n /**\n * 更新配置\n */\n const updateConfig = useCallback(async (config: AppConfig): Promise<void> => {\n try {\n console.log(\"[NetworkService] 更新配置\");\n await networkService.updateConfig(config);\n\n // 立即更新本地状态,WebSocket 通知会进一步确认\n useConfigStore.getState().setConfig(config, \"http\");\n console.log(\"[NetworkService] 配置更新成功\");\n } catch (error) {\n console.error(\"[NetworkService] 配置更新失败:\", error);\n throw error;\n }\n }, []);\n\n /**\n * 获取状态\n */\n const getStatus = useCallback(async () => {\n try {\n const status = await networkService.getStatus();\n useStatusStore.getState().setClientStatus(status.client, \"http\");\n return status;\n } catch (error) {\n console.error(\"[NetworkService] 获取状态失败:\", error);\n throw error;\n }\n }, []);\n\n /**\n * 刷新状态\n */\n const refreshStatus = useCallback(async (): Promise<void> => {\n try {\n await getStatus();\n } catch (error) {\n console.error(\"[NetworkService] 刷新状态失败:\", error);\n }\n }, [getStatus]);\n\n /**\n * 重启服务\n */\n const restartService = useCallback(async (): Promise<void> => {\n try {\n console.log(\"[NetworkService] 重启服务\");\n await networkService.restartService();\n console.log(\"[NetworkService] 重启请求已发送\");\n } catch (error) {\n console.error(\"[NetworkService] 重启服务失败:\", error);\n throw error;\n }\n }, []);\n\n /**\n * 重启服务并等待完成通知\n */\n const restartServiceWithNotification = useCallback(\n async (timeout = 30000): Promise<void> => {\n try {\n console.log(\"[NetworkService] 重启服务并等待通知\");\n await networkService.restartServiceWithNotification(timeout);\n console.log(\"[NetworkService] 服务重启完成\");\n } catch (error) {\n console.error(\"[NetworkService] 重启服务失败:\", error);\n throw error;\n }\n },\n []\n );\n\n /**\n * 更新配置并等待通知\n */\n const updateConfigWithNotification = useCallback(\n async (config: AppConfig, timeout = 5000): Promise<void> => {\n try {\n console.log(\"[NetworkService] 更新配置并等待通知\");\n await networkService.updateConfigWithNotification(config, timeout);\n console.log(\"[NetworkService] 配置更新完成\");\n } catch (error) {\n console.error(\"[NetworkService] 配置更新失败:\", error);\n throw error;\n }\n },\n []\n );\n\n /**\n * 设置自定义 WebSocket URL\n */\n const setCustomWsUrl = useCallback(\n (url: string): void => {\n console.log(\"[NetworkService] 设置自定义 WebSocket URL:\", url);\n networkService.setWebSocketUrl(url);\n webSocketActions.setWsUrl(url);\n },\n [webSocketActions]\n );\n\n /**\n * 端口切换功能 (保留向后兼容)\n */\n const changePort = useCallback(\n async (newPort: number): Promise<void> => {\n try {\n console.log(`[NetworkService] 切换到端口 ${newPort}`);\n\n // 更新端口变更状态\n webSocketActions.setPortChangeStatus({\n status: \"checking\",\n targetPort: newPort,\n timestamp: Date.now(),\n });\n\n // 构建新的 WebSocket URL\n const protocol = window.location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const hostname = window.location.hostname;\n const newUrl = `${protocol}//${hostname}:${newPort}`;\n\n // 重启服务\n await restartService();\n\n // 等待一段时间让服务重启\n await new Promise((resolve) => setTimeout(resolve, 5000));\n\n // 更新 WebSocket URL\n setCustomWsUrl(newUrl);\n\n // 重新加载页面\n window.location.reload();\n } catch (error) {\n console.error(\"[NetworkService] 端口切换失败:\", error);\n webSocketActions.setPortChangeStatus({\n status: \"failed\",\n targetPort: newPort,\n error: error instanceof Error ? error.message : \"端口切换失败\",\n timestamp: Date.now(),\n });\n throw error;\n }\n },\n [webSocketActions, restartService, setCustomWsUrl]\n );\n\n /**\n * 获取当前 WebSocket URL\n */\n const getWebSocketUrl = useCallback((): string => {\n // 从 localStorage 获取自定义 URL\n const savedUrl = localStorage.getItem(\"xiaozhi-ws-url\");\n if (savedUrl) {\n return savedUrl;\n }\n\n // 根据当前页面 URL 构建 WebSocket URL\n const protocol = window.location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const hostname = window.location.hostname;\n const port = window.location.port;\n return `${protocol}//${hostname}${port ? `:${port}` : \"\"}`;\n }, []);\n\n return {\n // 数据操作方法 (HTTP)\n getConfig,\n updateConfig,\n getStatus,\n refreshStatus,\n restartService,\n\n // 混合模式方法 (HTTP + WebSocket)\n updateConfigWithNotification,\n restartServiceWithNotification,\n\n // WebSocket 管理\n setCustomWsUrl,\n getWebSocketUrl,\n\n // 端口切换 (向后兼容)\n changePort,\n\n // 工具方法\n loadInitialData,\n\n // 网络服务状态\n isWebSocketConnected: () => networkService.isWebSocketConnected(),\n getWebSocketState: () => networkService.getWebSocketState(),\n };\n}\n","/**\n * Stores 统一初始化和管理\n *\n * 这个文件负责:\n * - 初始化所有 stores\n * - 设置 stores 之间的协调\n * - 提供统一的初始化入口\n */\n\nimport { useConfigStore } from \"./config\";\nimport { useStatusStore } from \"./status\";\nimport { useWebSocketStore } from \"./websocket\";\n\n/**\n * 初始化所有 stores\n *\n * 这个函数应该在应用启动时调用一次\n */\nexport async function initializeStores(): Promise<void> {\n console.log(\"[Stores] 开始初始化所有 stores\");\n\n try {\n // 1. 首先初始化 WebSocket store(建立连接管理)\n console.log(\"[Stores] 初始化 WebSocket store\");\n useWebSocketStore.getState().initialize();\n\n // 2. 初始化配置 store(设置配置数据管理和 WebSocket 监听)\n console.log(\"[Stores] 初始化配置 store\");\n await useConfigStore.getState().initialize();\n\n // 3. 初始化状态 store(设置状态数据管理和 WebSocket 监听)\n console.log(\"[Stores] 初始化状态 store\");\n await useStatusStore.getState().initialize();\n\n console.log(\"[Stores] 所有 stores 初始化完成\");\n } catch (error) {\n console.error(\"[Stores] Stores 初始化失败:\", error);\n throw error;\n }\n}\n\n/**\n * 重置所有 stores\n *\n * 用于测试或需要完全重置应用状态时\n */\nexport function resetAllStores(): void {\n console.log(\"[Stores] 重置所有 stores\");\n\n useWebSocketStore.getState().reset();\n useConfigStore.getState().reset();\n useStatusStore.getState().reset();\n\n console.log(\"[Stores] 所有 stores 已重置\");\n}\n\n/**\n * 获取所有 stores 的状态摘要\n *\n * 用于调试和监控\n */\nexport function getStoresStatus() {\n const websocketState = useWebSocketStore.getState();\n const configState = useConfigStore.getState();\n const statusState = useStatusStore.getState();\n\n return {\n websocket: {\n connectionState: websocketState.connectionState,\n url: websocketState.wsUrl,\n connected: websocketState.connectionState === \"connected\",\n lastError: websocketState.lastError?.message,\n },\n config: {\n hasConfig: !!configState.config,\n isLoading: configState.loading.isLoading,\n isUpdating: configState.loading.isUpdating,\n lastUpdated: configState.loading.lastUpdated,\n lastError: configState.loading.lastError?.message,\n source: configState.lastSource,\n },\n status: {\n hasStatus: !!statusState.clientStatus,\n isLoading: statusState.loading.isLoading,\n isRestarting: statusState.loading.isRestarting,\n pollingEnabled: statusState.polling.enabled,\n lastUpdated: statusState.loading.lastUpdated,\n lastError: statusState.loading.lastError?.message,\n source: statusState.lastSource,\n },\n };\n}\n\n// 导出所有 stores 以便统一访问\nexport { useWebSocketStore } from \"./websocket\";\nexport { useConfigStore } from \"./config\";\nexport { useStatusStore } from \"./status\";\n\n// 导出所有选择器 hooks(避免命名冲突)\nexport {\n // WebSocket store exports\n useWebSocketConnectionState,\n useWebSocketConnected,\n useWebSocketUrl,\n useWebSocketConnectionStats,\n useWebSocketPortChangeStatus,\n useWebSocketLastError,\n useWebSocketConnectionTimes,\n useWebSocketConnectionInfo,\n useWebSocketPortInfo,\n useWebSocketActions,\n useWebSocketControls,\n useWebSocketData,\n // 废弃的导出(向后兼容)\n useWebSocketConfig,\n useWebSocketStatus,\n useWebSocketRestartStatus,\n useWebSocketMcpServers,\n useWebSocketMcpServerConfig,\n} from \"./websocket\";\n\nexport {\n // Config store exports\n useConfig,\n useConfigLoading,\n useConfigIsLoading,\n useConfigIsUpdating,\n useConfigError,\n useMcpEndpoint,\n useMcpServers,\n useMcpServerConfig,\n useConnectionConfig,\n useModelScopeConfig,\n useWebUIConfig,\n useConfigSource,\n useConfigWithLoading,\n useMcpConfig,\n useSystemConfig,\n useConfigActions,\n useConfigUpdaters,\n} from \"./config\";\n\nexport {\n // Status store exports\n useClientStatus,\n useRestartStatus,\n useServiceStatus,\n useServiceHealth,\n useFullStatus,\n useStatusLoading,\n useStatusIsLoading,\n useStatusIsRestarting,\n useStatusError,\n usePollingConfig,\n usePollingEnabled,\n useStatusSource,\n useConnectionStatus,\n useStatusMcpEndpoint,\n useActiveMcpServers,\n useLastHeartbeat,\n useStatusWithLoading,\n useServiceInfo,\n useConnectionInfo,\n useStatusActions,\n usePollingActions,\n} from \"./status\";\n","import {\n type ReactNode,\n createContext,\n useContext,\n useEffect,\n useState,\n} from \"react\";\nimport { useNetworkService } from \"../hooks/useNetworkService\";\nimport { initializeStores } from \"../stores\";\nimport type { AppConfig } from \"../types\";\n\ninterface NetworkServiceContextType {\n // HTTP API 方法\n getConfig: () => Promise<AppConfig>;\n updateConfig: (config: AppConfig) => Promise<void>;\n getStatus: () => Promise<any>;\n refreshStatus: () => Promise<void>;\n restartService: () => Promise<void>;\n\n // 混合模式方法 (HTTP + WebSocket)\n updateConfigWithNotification: (\n config: AppConfig,\n timeout?: number\n ) => Promise<void>;\n restartServiceWithNotification: (timeout?: number) => Promise<void>;\n\n // WebSocket 管理\n setCustomWsUrl: (url: string) => void;\n getWebSocketUrl: () => string;\n\n // 端口切换 (向后兼容)\n changePort: (newPort: number) => Promise<void>;\n\n // 工具方法\n loadInitialData: () => Promise<void>;\n isWebSocketConnected: () => boolean;\n getWebSocketState: () => any;\n}\n\nconst NetworkServiceContext = createContext<NetworkServiceContextType | null>(\n null\n);\n\ninterface NetworkServiceProviderProps {\n children: ReactNode;\n}\n\nexport function NetworkServiceProvider({\n children,\n}: NetworkServiceProviderProps) {\n const networkService = useNetworkService();\n const [storesInitialized, setStoresInitialized] = useState(false);\n\n // 初始化 stores\n useEffect(() => {\n let mounted = true;\n\n const initStores = async () => {\n try {\n console.log(\"[WebSocketProvider] 开始初始化 stores\");\n await initializeStores();\n\n if (mounted) {\n setStoresInitialized(true);\n console.log(\"[WebSocketProvider] Stores 初始化完成\");\n }\n } catch (error) {\n console.error(\"[WebSocketProvider] Stores 初始化失败:\", error);\n // 即使初始化失败,也允许应用继续运行\n if (mounted) {\n setStoresInitialized(true);\n }\n }\n };\n\n initStores();\n\n return () => {\n mounted = false;\n };\n }, []);\n\n // 在 stores 初始化完成前显示加载状态(可选)\n if (!storesInitialized) {\n return (\n <div className=\"flex items-center justify-center min-h-screen\">\n <div className=\"text-center\">\n <div className=\"animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto mb-2\" />\n <p className=\"text-sm text-gray-600\">正在初始化应用...</p>\n </div>\n </div>\n );\n }\n\n return (\n <NetworkServiceContext.Provider value={networkService}>\n {children}\n </NetworkServiceContext.Provider>\n );\n}\n\nexport function useNetworkServiceActions() {\n const context = useContext(NetworkServiceContext);\n if (!context) {\n throw new Error(\n \"useNetworkServiceActions must be used within a NetworkServiceProvider\"\n );\n }\n return context;\n}\n\n// 向后兼容的别名\nexport const WebSocketProvider = NetworkServiceProvider;\nexport const useWebSocketActions = useNetworkServiceActions;\n","import { AppSidebar } from \"@/components/AppSidebar\";\nimport { DashboardStatusCard } from \"@/components/DashboardStatusCard\";\nimport { McpServerList } from \"@/components/McpServerList\";\nimport { SiteHeader } from \"@/components/SiteHeder\";\nimport { SidebarInset, SidebarProvider } from \"@/components/ui/sidebar\";\nimport { useWebSocketActions } from \"@/providers/WebSocketProvider\";\n\nexport default function DashboardPage() {\n // 从 WebSocketProvider 获取操作方法\n const { updateConfig } = useWebSocketActions();\n\n return (\n <SidebarProvider>\n <AppSidebar variant=\"inset\" />\n <SidebarInset>\n <SiteHeader title=\"看板\" />\n <div className=\"flex flex-1 flex-col\">\n <div className=\"@container/main flex flex-1 flex-col gap-2\">\n <div className=\"flex flex-col gap-4 py-4 md:gap-6 md:py-6\">\n <DashboardStatusCard />\n <McpServerList updateConfig={updateConfig} />\n </div>\n </div>\n </div>\n </SidebarInset>\n </SidebarProvider>\n );\n}\n","import { AppSidebar } from \"@/components/AppSidebar\";\nimport { RestartButton } from \"@/components/RestartButton\";\nimport { SiteHeader } from \"@/components/SiteHeder\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { SidebarInset, SidebarProvider } from \"@/components/ui/sidebar\";\nimport { useWebSocket } from \"@/hooks/useWebSocket\";\nimport { useConfig } from \"@/stores/config\";\nimport type { AppConfig } from \"@/types\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { useEffect, useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport z from \"zod\";\n\nconst formSchema = z.object({\n modelscope: z.object({\n apiKey: z\n .string()\n .min(2, {\n message: \"API Key不能为空\",\n })\n .optional(),\n }),\n connection: z.object({\n heartbeatInterval: z.number().min(1000, {\n message: \"心跳间隔不能小于1000毫秒\",\n }),\n heartbeatTimeout: z.number().min(1000, {\n message: \"心跳超时不能小于1000毫秒\",\n }),\n reconnectInterval: z.number().min(1000, {\n message: \"重连间隔不能小于1000毫秒\",\n }),\n }),\n});\n\nexport default function SettingsPage() {\n const config = useConfig();\n const { updateConfig } = useWebSocket();\n const [isLoading, setIsLoading] = useState(false);\n const form = useForm<z.infer<typeof formSchema>>({\n resolver: zodResolver(formSchema),\n defaultValues: {\n modelscope: {\n apiKey: config?.modelscope?.apiKey || \"\",\n },\n connection: {\n heartbeatInterval: config?.connection?.heartbeatInterval || 30000,\n heartbeatTimeout: config?.connection?.heartbeatTimeout || 10000,\n reconnectInterval: config?.connection?.reconnectInterval || 5000,\n },\n },\n });\n\n useEffect(() => {\n form.reset({\n modelscope: {\n apiKey: config?.modelscope?.apiKey || \"\",\n },\n connection: {\n heartbeatInterval: config?.connection?.heartbeatInterval || 30000,\n heartbeatTimeout: config?.connection?.heartbeatTimeout || 10000,\n reconnectInterval: config?.connection?.reconnectInterval || 5000,\n },\n });\n }, [config, form.reset]);\n\n async function onSubmit(values: z.infer<typeof formSchema>) {\n if (!config) {\n toast.error(\"配置数据未加载,请稍后重试\");\n return;\n }\n\n setIsLoading(true);\n try {\n const newConfig: AppConfig = {\n ...config,\n modelscope: {\n apiKey: values.modelscope.apiKey,\n },\n connection: {\n heartbeatInterval: values.connection.heartbeatInterval,\n heartbeatTimeout: values.connection.heartbeatTimeout,\n reconnectInterval: values.connection.reconnectInterval,\n },\n };\n\n await updateConfig(newConfig);\n toast.success(\"配置已更新\");\n } catch (error) {\n console.error(\"更新配置失败:\", error);\n toast.error(error instanceof Error ? error.message : \"更新配置失败\");\n } finally {\n setIsLoading(false);\n }\n }\n\n return (\n <SidebarProvider>\n <AppSidebar variant=\"inset\" />\n <SidebarInset>\n <SiteHeader title=\"设置\" />\n <div className=\"flex flex-1 flex-col p-4\">\n <div className=\"@container/main flex flex-1 flex-col gap-2 items-center\">\n <div className=\"flex flex-col gap-4 py-4 md:gap-6 md:py-6 w-[600px]\">\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)}>\n <div className=\"grid gap-4\">\n <FormField\n control={form.control}\n name=\"modelscope.apiKey\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>魔搭社区 API Key</FormLabel>\n <div className=\"flex gap-2\">\n <FormControl>\n <Input\n placeholder=\"魔搭社区 API Key\"\n className=\"font-mono text-sm\"\n type=\"password\"\n disabled={isLoading}\n {...field}\n />\n </FormControl>\n <Button\n variant=\"outline\"\n onClick={() => {\n window.open(\n \"https://www.modelscope.cn/my/myaccesstoken\",\n \"_blank\"\n );\n }}\n >\n 打开魔搭社区\n </Button>\n </div>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"connection.heartbeatInterval\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>心跳间隔(毫秒)</FormLabel>\n <div className=\"flex gap-2 items-center\">\n <FormControl>\n <Input\n placeholder=\"心跳间隔(毫秒)\"\n className=\"font-mono text-sm\"\n type=\"number\"\n disabled={isLoading}\n {...field}\n value={field.value || \"\"}\n onChange={(e) => {\n const value = e.target.value;\n field.onChange(\n value === \"\" ? \"\" : Number(value)\n );\n }}\n />\n </FormControl>\n <span className=\"text-sm text-muted-foreground w-[50px]\">\n 毫秒\n </span>\n </div>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"connection.heartbeatTimeout\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>心跳超时(毫秒)</FormLabel>\n <div className=\"flex gap-2 items-center\">\n <FormControl>\n <Input\n placeholder=\"心跳超时(毫秒)\"\n className=\"font-mono text-sm\"\n type=\"number\"\n disabled={isLoading}\n {...field}\n value={field.value || \"\"}\n onChange={(e) => {\n const value = e.target.value;\n field.onChange(\n value === \"\" ? \"\" : Number(value)\n );\n }}\n />\n </FormControl>\n <span className=\"text-sm text-muted-foreground w-[50px]\">\n 毫秒\n </span>\n </div>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"connection.reconnectInterval\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>重连间隔(毫秒)</FormLabel>\n <div className=\"flex gap-2 items-center\">\n <FormControl>\n <Input\n placeholder=\"重连间隔(毫秒)\"\n className=\"font-mono text-sm\"\n type=\"number\"\n disabled={isLoading}\n {...field}\n value={field.value || \"\"}\n onChange={(e) => {\n const value = e.target.value;\n field.onChange(\n value === \"\" ? \"\" : Number(value)\n );\n }}\n />\n </FormControl>\n <span className=\"text-sm text-muted-foreground w-[50px]\">\n 毫秒\n </span>\n </div>\n <FormMessage />\n </FormItem>\n )}\n />\n <div className=\"flex gap-2 justify-end\">\n <Button\n type=\"submit\"\n disabled={isLoading}\n className=\"flex-1\"\n >\n {isLoading ? \"保存中...\" : \"保存\"}\n </Button>\n <RestartButton />\n </div>\n </div>\n </form>\n </Form>\n {/* <DashboardWithStore /> */}\n {/* ModelScope APIKey */}\n </div>\n </div>\n </div>\n </SidebarInset>\n </SidebarProvider>\n );\n}\n","import { Toaster } from \"@/components/ui/sonner\";\nimport { RestartNotificationProvider } from \"@/hooks/useRestartNotifications\";\nimport DashboardPage from \"@/pages/DashboardPage\";\nimport SettingsPage from \"@/pages/SettingsPage\";\nimport { WebSocketProvider } from \"@/providers/WebSocketProvider\";\nimport { Navigate, Route, Routes } from \"react-router-dom\";\n\nfunction App() {\n return (\n <WebSocketProvider>\n {/* 重启通知管理器 - 全局监听重启状态变化 */}\n <RestartNotificationProvider />\n\n <Routes>\n <Route path=\"/\" element={<Navigate to=\"/dashboard\" />} />\n <Route path=\"/dashboard\" element={<DashboardPage />} />\n <Route path=\"/settings\" element={<SettingsPage />} />\n </Routes>\n\n {/* Toast 通知容器 */}\n <Toaster\n richColors\n toastOptions={{\n classNames: {\n description: \"group-[.toast]:text-muted-foreground\",\n actionButton:\n \"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground\",\n cancelButton: \"group-[.toast]:bg-white group-[.toast]:text-black\",\n error:\n \"group toast group-[.toaster]:bg-red group-[.toaster]:text-red-600 dark:group-[.toaster]:text-foreground group-[.toaster]:shadow-lg\",\n success:\n \"group toast group-[.toaster]:bg-green group-[.toaster]:text-green-600 dark:group-[.toaster]:text-foreground group-[.toaster]:shadow-lg\",\n warning:\n \"group toast group-[.toaster]:bg-yellow group-[.toaster]:text-yellow-600 dark:group-[.toaster]:text-foreground group-[.toaster]:shadow-lg\",\n info: \"group toast group-[.toaster]:bg-blue group-[.toaster]:text-blue-600 dark:group-[.toaster]:text-foreground group-[.toaster]:shadow-lg\",\n },\n }}\n />\n </WebSocketProvider>\n );\n}\n\nexport default App;\n","import React from \"react\";\nimport ReactDOM from \"react-dom/client\";\nimport { BrowserRouter } from \"react-router-dom\";\nimport App from \"./App\";\nimport \"./index.css\";\n\nReactDOM.createRoot(document.getElementById(\"root\")!).render(\n <React.StrictMode>\n <BrowserRouter>\n <App />\n </BrowserRouter>\n </React.StrictMode>\n);\n"],"names":["ApiClient","baseUrl","__publicField","protocol","hostname","port","endpoint","options","url","defaultOptions","response","errorMessage","_a","config","status","servers","apiClient","ConnectionState","EventBus","event","listener","eventListeners","data","error","total","listeners","_WebSocketManager","_b","savedUrl","message","messageStr","heartbeatMessage","WebSocketManager","webSocketManager","initialState","pollingTimer","restartPollingTimer","useStatusStore","create","devtools","set","get","source","state","health","loading","fullStatus","setLoading","setFullStatus","setError","polling","err","newRetries","setRestartStatus","startRestartPolling","interval","refreshStatus","restartPolling","startTime","currentState","currentRestartPolling","elapsed","attempts","useClientStatus","useRestartStatus","useRestartPollingStatus","useStatusActions","useShallow","useRestartNotifications","restartStatus","restartPollingStatus","lastNotifiedStatus","useRef","lastNotifiedTimestamp","useEffect","timestamp","toast","successMessage","failureMessage","description","RestartNotificationProvider","cn","inputs","twMerge","clsx","buttonVariants","cva","Button","React.forwardRef","className","variant","size","asChild","props","ref","Comp","Slot","jsx","Input","type","Separator","orientation","decorative","SeparatorPrimitive.Root","Sheet","SheetPrimitive.Root","SheetPortal","SheetPrimitive.Portal","SheetOverlay","SheetPrimitive.Overlay","sheetVariants","SheetContent","side","children","jsxs","SheetPrimitive.Content","SheetPrimitive.Close","X","SheetHeader","SheetTitle","SheetPrimitive.Title","SheetDescription","SheetPrimitive.Description","Skeleton","TooltipProvider","TooltipPrimitive.Provider","Tooltip","TooltipPrimitive.Root","TooltipTrigger","TooltipPrimitive.Trigger","TooltipContent","sideOffset","TooltipPrimitive.Content","MOBILE_BREAKPOINT","useIsMobile","isMobile","setIsMobile","React.useState","React.useEffect","mql","onChange","SIDEBAR_COOKIE_NAME","SIDEBAR_COOKIE_MAX_AGE","SIDEBAR_WIDTH","SIDEBAR_WIDTH_MOBILE","SIDEBAR_WIDTH_ICON","SIDEBAR_KEYBOARD_SHORTCUT","SidebarContext","React.createContext","useSidebar","context","React.useContext","SidebarProvider","defaultOpen","openProp","setOpenProp","style","openMobile","setOpenMobile","_open","_setOpen","open","setOpen","React.useCallback","value","openState","toggleSidebar","handleKeyDown","contextValue","React.useMemo","Sidebar","collapsible","SidebarTrigger","onClick","PanelLeft","SidebarRail","SidebarInset","SidebarInput","SidebarHeader","SidebarFooter","SidebarSeparator","SidebarContent","SidebarGroup","SidebarGroupLabel","SidebarGroupAction","SidebarGroupContent","SidebarMenu","SidebarMenuItem","sidebarMenuButtonVariants","SidebarMenuButton","isActive","tooltip","button","SidebarMenuAction","showOnHover","SidebarMenuBadge","SidebarMenuSkeleton","showIcon","width","SidebarMenuSub","SidebarMenuSubItem","SidebarMenuSubButton","AppSidebarNav","items","location","useLocation","item","Link","LayoutDashboardIcon","SettingsIcon","AppSidebar","AlertDialog","AlertDialogPrimitive.Root","AlertDialogTrigger","AlertDialogPrimitive.Trigger","AlertDialogPortal","AlertDialogPrimitive.Portal","AlertDialogOverlay","AlertDialogPrimitive.Overlay","AlertDialogContent","AlertDialogPrimitive.Content","AlertDialogHeader","AlertDialogFooter","AlertDialogTitle","AlertDialogPrimitive.Title","AlertDialogDescription","AlertDialogPrimitive.Description","AlertDialogAction","AlertDialogPrimitive.Action","AlertDialogCancel","AlertDialogPrimitive.Cancel","Dialog","DialogPrimitive.Root","DialogTrigger","DialogPrimitive.Trigger","DialogPortal","DialogPrimitive.Portal","DialogClose","DialogPrimitive.Close","DialogOverlay","DialogPrimitive.Overlay","DialogContent","DialogPrimitive.Content","DialogHeader","DialogFooter","DialogTitle","DialogPrimitive.Title","DialogDescription","DialogPrimitive.Description","useConfigStore","newConfig","setConfig","updateConfig","connection","modelscope","webUI","refreshConfig","useConfig","useMcpEndpoint","useMcpServers","useMcpServerConfig","useConfigActions","useWebSocketStore","connectionState","now","updates","stats","wsUrl","connectionStats","lastError","portChangeStatus","resolve","initialStats","connected","useWebSocketConnected","useWebSocketUrl","useWebSocketPortChangeStatus","useWebSocketActions","checkPortAvailability","timeout","controller","timeoutId","buildWebSocketUrl","host","extractPortFromUrl","urlObj","useWebSocket","clientStatus","webSocketActions","configActions","statusActions","setWsUrl","useState","isInitialized","currentUrl","getWebSocketUrl","useCallback","configPort","targetPort","currentPort","restartService","setCustomWsUrl","defaultUrl","changePort","newPort","newUrl","sliceEndpoint","validateEndpoint","expectedPrefix","token","jwtParts","part","McpEndpointSettingButton","deleteConfirmOpen","setDeleteConfirmOpen","endpointToDelete","setEndpointToDelete","isDeleting","setIsDeleting","addDialogOpen","setAddDialogOpen","newEndpoint","setNewEndpoint","isAdding","setIsAdding","validationError","setValidationError","mcpEndpoint","handleCopy","textArea","successful","handleDeleteEndpoint","updatedEndpoints","ep","newMcpEndpoint","updatedConfig","handleAddEndpoint","currentEndpoints","openAddDialog","handleInputChange","openDeleteConfirm","mcpEndpoints","useMemo","list","CopyIcon","TrashIcon","BadgeInfoIcon","PlusIcon","e","labelVariants","Label","LabelPrimitive.Root","Form","FormProvider","FormFieldContext","FormField","Controller","useFormField","fieldContext","itemContext","FormItemContext","getFieldState","formState","useFormContext","fieldState","id","FormItem","React.useId","FormLabel","formItemId","FormControl","formDescriptionId","formMessageId","FormDescription","FormMessage","body","formSchema","z","val","WebUrlSettingButton","isLoading","setIsLoading","form","useForm","zodResolver","getButtonText","currentAttempt","maxAttempts","onSubmit","values","field","Card","CardHeader","CardTitle","CardDescription","CardContent","CardFooter","MiniCircularProgress","showValue","maxValue","activeColor","inactiveColor","symbol","radius","circumference","strokeDasharray","strokeDashoffset","DashboardStatusCard","mcpServers","mcpServerCount","badgeVariants","Badge","getMcpServerCommunicationType","serverConfig","Textarea","AddMcpServerButton","validateSingleServerConfig","serverName","hasCommand","hasType","hasUrl","validateMCPConfig","input","trimmed","parsed","validation","parsedServers","existingNames","name","addedCount","McpServerSettingButton","mcpServer","mcpServerName","newMcpServerConfig","RemoveMcpServerButton","onRemove","newMcpServers","RestartButton","disabled","restartingText","defaultText","isRestarting","handleRestart","getDisplayText","LoaderCircleIcon","PowerIcon","McpServerList","mcpServerConfig","tools","toolName","tool","handleToggleTool","currentEnable","_c","_f","_e","_d","action","enabledTools","disabledTools","CoffeeIcon","Wrench","MinusIcon","SiteHeader","title","GithubIcon","NetworkService","reject","networkService","useNetworkService","initializationRef","loadInitialData","getConfig","getStatus","restartServiceWithNotification","updateConfigWithNotification","initializeStores","NetworkServiceContext","createContext","NetworkServiceProvider","storesInitialized","setStoresInitialized","mounted","useNetworkServiceActions","useContext","WebSocketProvider","DashboardPage","SettingsPage","App","Routes","Route","Navigate","Toaster","ReactDOM","React","BrowserRouter"],"mappings":"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"}
|