diff --git a/package.json b/package.json index 71a0a310..da9a3701 100644 --- a/package.json +++ b/package.json @@ -43,17 +43,14 @@ "@tauri-apps/plugin-clipboard-manager": "^2.3.0", "@tauri-apps/plugin-dialog": "^2.4.0", "@tauri-apps/plugin-fs": "^2.4.2", - "@tauri-apps/plugin-notification": "^2.3.1", "@tauri-apps/plugin-process": "^2.3.0", "@tauri-apps/plugin-shell": "2.3.1", "@tauri-apps/plugin-updater": "2.9.0", "@types/json-schema": "^7.0.15", "ahooks": "^3.9.5", "axios": "^1.12.2", - "cli-color": "^2.0.4", "dayjs": "1.11.18", "foxact": "^0.2.49", - "glob": "^11.0.3", "i18next": "^25.5.2", "js-yaml": "^4.1.0", "json-schema": "^0.4.0", @@ -70,13 +67,14 @@ "react-monaco-editor": "0.59.0", "react-router-dom": "7.9.1", "react-virtuoso": "^4.14.0", - "sockette": "^2.0.6", "swr": "^2.3.6", - "tar": "^7.4.3", "types-pac": "^1.0.3", "zustand": "^5.0.8" }, "devDependencies": { + "tar": "^7.4.3", + "glob": "^11.0.3", + "cli-color": "^2.0.4", "@actions/github": "^6.0.1", "@eslint/js": "^9.35.0", "@tauri-apps/cli": "2.8.4", @@ -96,9 +94,7 @@ "jiti": "^2.5.1", "meta-json-schema": "^1.19.13", "node-fetch": "^3.3.2", - "path": "^0.12.7", "prettier": "^3.6.2", - "process": "^0.11.10", "sass": "^1.92.1", "terser": "^5.44.0", "typescript": "^5.9.2", diff --git a/src/App.tsx b/src/App.tsx index 3ca17dc0..2ef77bb4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,9 +1,7 @@ import { AppDataProvider } from "./providers/app-data-provider"; import Layout from "./pages/_layout"; -import { useNotificationPermission } from "./hooks/useNotificationPermission"; function App() { - useNotificationPermission(); return ( diff --git a/src/components/base/base-loading-overlay.tsx b/src/components/base/base-loading-overlay.tsx index 036250b9..faba8f5f 100644 --- a/src/components/base/base-loading-overlay.tsx +++ b/src/components/base/base-loading-overlay.tsx @@ -1,7 +1,7 @@ import React from "react"; import { Box, CircularProgress } from "@mui/material"; -export interface BaseLoadingOverlayProps { +interface BaseLoadingOverlayProps { isLoading: boolean; } @@ -29,5 +29,3 @@ export const BaseLoadingOverlay: React.FC = ({ ); }; - -export default BaseLoadingOverlay; diff --git a/src/components/home/current-proxy-card.tsx b/src/components/home/current-proxy-card.tsx index c20f9a74..84564254 100644 --- a/src/components/home/current-proxy-card.tsx +++ b/src/components/home/current-proxy-card.tsx @@ -44,7 +44,7 @@ interface ProxyOption { } // 排序类型: 默认 | 按延迟 | 按字母 -export type ProxySortType = 0 | 1 | 2; +type ProxySortType = 0 | 1 | 2; function convertDelayColor(delayValue: number) { const colorStr = delayManager.formatDelayColor(delayValue); diff --git a/src/components/home/enhanced-canvas-traffic-graph.tsx b/src/components/home/enhanced-canvas-traffic-graph.tsx index d42896cf..a009e28d 100644 --- a/src/components/home/enhanced-canvas-traffic-graph.tsx +++ b/src/components/home/enhanced-canvas-traffic-graph.tsx @@ -17,7 +17,7 @@ import { } from "@/hooks/use-traffic-monitor"; // 流量数据项接口 -export interface ITrafficItem { +interface ITrafficItem { up: number; down: number; timestamp?: number; diff --git a/src/components/home/enhanced-card.tsx b/src/components/home/enhanced-card.tsx index 68548a54..59203c66 100644 --- a/src/components/home/enhanced-card.tsx +++ b/src/components/home/enhanced-card.tsx @@ -2,7 +2,7 @@ import { Box, Typography, alpha, useTheme } from "@mui/material"; import { ReactNode } from "react"; // 自定义卡片组件接口 -export interface EnhancedCardProps { +interface EnhancedCardProps { title: ReactNode; icon: ReactNode; action?: ReactNode; diff --git a/src/components/home/home-profile-card.tsx b/src/components/home/home-profile-card.tsx index 5d3f54e4..321ee82a 100644 --- a/src/components/home/home-profile-card.tsx +++ b/src/components/home/home-profile-card.tsx @@ -55,7 +55,7 @@ interface ProfileExtra { expire: number; } -export interface ProfileItem { +interface ProfileItem { uid: string; type?: "local" | "remote" | "merge" | "script"; name?: string; @@ -68,7 +68,7 @@ export interface ProfileItem { option?: any; } -export interface HomeProfileCardProps { +interface HomeProfileCardProps { current: ProfileItem | null | undefined; onProfileUpdated?: () => void; } diff --git a/src/components/setting/mods/backup-config-viewer.tsx b/src/components/setting/mods/backup-config-viewer.tsx index d6cb9e51..cddf40aa 100644 --- a/src/components/setting/mods/backup-config-viewer.tsx +++ b/src/components/setting/mods/backup-config-viewer.tsx @@ -17,7 +17,7 @@ import VisibilityOff from "@mui/icons-material/VisibilityOff"; import { saveWebdavConfig, createWebdavBackup } from "@/services/cmds"; import { showNotice } from "@/services/noticeService"; -export interface BackupConfigViewerProps { +interface BackupConfigViewerProps { onBackupSuccess: () => Promise; onSaveSuccess: () => Promise; onRefresh: () => Promise; diff --git a/src/components/setting/mods/backup-table-viewer.tsx b/src/components/setting/mods/backup-table-viewer.tsx index b6fb1d6c..595496d0 100644 --- a/src/components/setting/mods/backup-table-viewer.tsx +++ b/src/components/setting/mods/backup-table-viewer.tsx @@ -33,7 +33,7 @@ export type BackupFile = IWebDavFile & { export const DEFAULT_ROWS_PER_PAGE = 5; -export interface BackupTableViewerProps { +interface BackupTableViewerProps { datasource: BackupFile[]; page: number; onPageChange: ( diff --git a/src/hooks/use-log-data.ts b/src/hooks/use-log-data.ts index 91c86d8f..441b3f16 100644 --- a/src/hooks/use-log-data.ts +++ b/src/hooks/use-log-data.ts @@ -2,12 +2,10 @@ import { useGlobalLogData, clearGlobalLogs, LogLevel, - ILogItem, } from "@/services/global-log-service"; // 为了向后兼容,导出相同的类型 export type { LogLevel }; -export type { ILogItem }; export const useLogData = useGlobalLogData; diff --git a/src/hooks/use-proxy-selection.ts b/src/hooks/use-proxy-selection.ts index bb3a4674..49061a52 100644 --- a/src/hooks/use-proxy-selection.ts +++ b/src/hooks/use-proxy-selection.ts @@ -28,7 +28,7 @@ const cleanupConnections = async (previousProxy: string) => { } }; -export interface ProxySelectionOptions { +interface ProxySelectionOptions { onSuccess?: () => void; onError?: (error: any) => void; enableConnectionCleanup?: boolean; diff --git a/src/hooks/useNotificationPermission.ts b/src/hooks/useNotificationPermission.ts deleted file mode 100644 index 45869529..00000000 --- a/src/hooks/useNotificationPermission.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { setupNotificationPermission } from "../utils/notification-permission"; -import { useEffect } from "react"; - -export function useNotificationPermission() { - useEffect(() => { - setupNotificationPermission(); - }, []); -} diff --git a/src/pages/home.tsx b/src/pages/home.tsx index e29ad15a..db59c322 100644 --- a/src/pages/home.tsx +++ b/src/pages/home.tsx @@ -201,7 +201,7 @@ const HomeSettingsDialog = ({ ); }; -export const HomePage = () => { +const HomePage = () => { const { t } = useTranslation(); const { verge } = useVerge(); const { current, mutateProfiles } = useProfiles(); diff --git a/src/services/global-log-service.ts b/src/services/global-log-service.ts index 75495962..7b6a3928 100644 --- a/src/services/global-log-service.ts +++ b/src/services/global-log-service.ts @@ -12,7 +12,7 @@ const MAX_LOG_NUM = 1000; export type LogLevel = "debug" | "info" | "warning" | "error" | "all"; -export interface ILogItem { +interface ILogItem { time?: string; type: string; payload: string; diff --git a/src/services/ipc-log-service.ts b/src/services/ipc-log-service.ts index 8d6df9ae..f98464aa 100644 --- a/src/services/ipc-log-service.ts +++ b/src/services/ipc-log-service.ts @@ -7,9 +7,9 @@ import { } from "@/services/cmds"; import dayjs from "dayjs"; -export type LogLevel = "debug" | "info" | "warning" | "error" | "all"; +type LogLevel = "debug" | "info" | "warning" | "error" | "all"; -export interface ILogItem { +interface ILogItem { time?: string; type: string; payload: string; diff --git a/src/services/noticeService.ts b/src/services/noticeService.ts index f5ef04ca..0a3505da 100644 --- a/src/services/noticeService.ts +++ b/src/services/noticeService.ts @@ -1,6 +1,6 @@ import { ReactNode } from "react"; -export interface NoticeItem { +interface NoticeItem { id: number; type: "success" | "error" | "info"; message: ReactNode; diff --git a/src/utils/notification-permission.ts b/src/utils/notification-permission.ts deleted file mode 100644 index 226f3025..00000000 --- a/src/utils/notification-permission.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { - isPermissionGranted, - requestPermission, -} from "@tauri-apps/plugin-notification"; - -export async function setupNotificationPermission() { - let permission = await isPermissionGranted(); - if (!permission) { - const result = await requestPermission(); - permission = result === "granted"; - } - if (permission) { - console.log("通知权限已授予"); - } else { - console.log("通知权限被拒绝"); - } -} diff --git a/src/utils/websocket.ts b/src/utils/websocket.ts deleted file mode 100644 index 624fbb54..00000000 --- a/src/utils/websocket.ts +++ /dev/null @@ -1,262 +0,0 @@ -import Sockette, { type SocketteOptions } from "sockette"; - -/** - * A wrapper of Sockette that will automatically reconnect up to `maxError` before emitting an error event. - */ -export const createSockette = ( - url: string, - opt: SocketteOptions, - maxError = 10, -) => { - let remainRetryCount = maxError; - - return new Sockette(url, { - ...opt, - // Sockette has a built-in reconnect when ECONNREFUSED feature - // Use maxError if opt.maxAttempts is not specified - maxAttempts: opt.maxAttempts ?? maxError, - onmessage(this: Sockette, ev) { - remainRetryCount = maxError; // reset counter - opt.onmessage?.call(this, ev); - }, - onerror(this: Sockette, ev) { - remainRetryCount -= 1; - - if (remainRetryCount >= 0) { - if (this instanceof Sockette) { - this.close(); - this.reconnect(); - } - } else { - opt.onerror?.call(this, ev); - } - }, - onmaximum(this: Sockette, ev) { - opt.onmaximum?.call(this, ev); - // onmaximum will be fired when Sockette reaches built-in reconnect limit, - // We will also set remainRetryCount to 0 to prevent further reconnect. - remainRetryCount = 0; - }, - }); -}; - -/** - * 创建一个支持认证的WebSocket连接 - * 使用标准的URL参数方式添加token - * - * 注意:mihomo服务器对WebSocket的认证支持不佳,使用URL参数方式传递token - */ -export const createAuthSockette = ( - baseUrl: string, - secret: string, - opt: SocketteOptions, - maxError = 10, -) => { - // 确保baseUrl格式正确 - let url = baseUrl; - if (!url.startsWith("ws://") && !url.startsWith("wss://")) { - url = `ws://${url}`; - } - - // 重试控制 - let reconnectAttempts = 0; - const MAX_RECONNECT = maxError; - let reconnectTimeout: any = null; - let ws: WebSocket | null = null; - - // 使用URL API解析和构建URL - try { - const urlObj = new URL(url); - - // 添加token参数(如果有secret) - if (secret) { - urlObj.searchParams.delete("token"); - urlObj.searchParams.append("token", secret); - } - - url = urlObj.toString(); - console.log(`[WebSocket] 创建连接: ${url.replace(secret || "", "***")}`); - } catch (e) { - console.error(`[WebSocket] URL格式错误: ${url}`, e); - if (opt.onerror) { - // 使用任意类型避免类型错误 - const anyOpt = opt as any; - anyOpt.onerror( - new ErrorEvent("error", { message: `URL格式错误: ${e}` } as any), - ); - } - return createDummySocket(); - } - - function connect() { - try { - ws = new WebSocket(url); - - ws.onopen = function (event) { - console.log( - `[WebSocket] 连接成功: ${url.replace(secret || "", "***")}`, - ); - reconnectAttempts = 0; // 重置重连计数 - if (opt.onopen) { - // 使用任意类型避免类型错误 - const anyOpt = opt as any; - anyOpt.onopen(event); - } - }; - - ws.onmessage = function (event) { - if (opt.onmessage) { - // 使用任意类型避免类型错误 - const anyOpt = opt as any; - anyOpt.onmessage(event); - } - }; - - ws.onerror = function (event) { - console.error( - `[WebSocket] 连接错误: ${url.replace(secret || "", "***")}`, - ); - // 错误处理 - if (reconnectAttempts < MAX_RECONNECT) { - scheduleReconnect(); - } else if (opt.onerror) { - // 使用任意类型避免类型错误 - const anyOpt = opt as any; - anyOpt.onerror(event); - } - }; - - ws.onclose = function (event) { - console.log( - `[WebSocket] 连接关闭: ${url.replace(secret || "", "***")}, 代码: ${event.code}`, - ); - - // 如果不是正常关闭(1000, 1001),尝试重连 - if ( - event.code !== 1000 && - event.code !== 1001 && - reconnectAttempts < MAX_RECONNECT - ) { - scheduleReconnect(); - } else { - if (opt.onclose) { - // 使用任意类型避免类型错误 - const anyOpt = opt as any; - anyOpt.onclose(event); - } - - // 如果已达到最大重试次数 - if (reconnectAttempts >= MAX_RECONNECT && opt.onmaximum) { - console.error( - `[WebSocket] 达到最大重试次数: ${url.replace(secret || "", "***")}`, - ); - const anyOpt = opt as any; - anyOpt.onmaximum(event); - } - } - }; - } catch (error) { - console.error(`[WebSocket] 创建连接失败:`, error); - if (opt.onerror) { - // 使用任意类型避免类型错误 - const anyOpt = opt as any; - anyOpt.onerror( - new ErrorEvent("error", { message: `创建连接失败: ${error}` } as any), - ); - } - } - } - - function scheduleReconnect() { - if (reconnectTimeout) { - clearTimeout(reconnectTimeout); - } - - reconnectAttempts++; - const delay = Math.min(1000 * Math.pow(1.5, reconnectAttempts), 10000); // 指数退避,最大10秒 - - console.log( - `[WebSocket] 计划重连 (${reconnectAttempts}/${MAX_RECONNECT}) 延迟: ${delay}ms`, - ); - - reconnectTimeout = setTimeout(() => { - console.log( - `[WebSocket] 尝试重连 (${reconnectAttempts}/${MAX_RECONNECT})`, - ); - cleanup(); - connect(); - }, delay); - } - - function cleanup() { - if (ws) { - // 移除所有事件监听器 - ws.onopen = null; - ws.onmessage = null; - ws.onerror = null; - ws.onclose = null; - - // 如果连接仍然打开,关闭它 - if ( - ws.readyState === WebSocket.OPEN || - ws.readyState === WebSocket.CONNECTING - ) { - try { - ws.close(); - } catch (e) { - console.error("[WebSocket] 关闭连接时出错:", e); - } - } - - ws = null; - } - - // 清除重连计时器 - if (reconnectTimeout) { - clearTimeout(reconnectTimeout); - reconnectTimeout = null; - } - } - - // 创建一个类似Sockette的接口对象 - const socketLike = { - ws, - close: () => { - console.log( - `[WebSocket] 手动关闭连接: ${url.replace(secret || "", "***")}`, - ); - cleanup(); - }, - reconnect: () => { - cleanup(); - connect(); - }, - json: (data: any) => { - if (ws && ws.readyState === WebSocket.OPEN) { - ws.send(JSON.stringify(data)); - } - }, - send: (data: string) => { - if (ws && ws.readyState === WebSocket.OPEN) { - ws.send(data); - } - }, - open: connect, - }; - - // 立即连接 - connect(); - - return socketLike; -}; - -// 创建一个空的WebSocket对象 -function createDummySocket() { - return { - close: () => {}, - reconnect: () => {}, - json: () => {}, - send: () => {}, - open: () => {}, - }; -} diff --git a/vite.config.mts b/vite.config.mts index 192b2040..b33edbca 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -112,7 +112,6 @@ export default defineConfig({ id.includes("@tauri-apps/plugin-dialog") || id.includes("@tauri-apps/plugin-fs") || id.includes("@tauri-apps/plugin-global-shortcut") || - id.includes("@tauri-apps/plugin-notification") || id.includes("@tauri-apps/plugin-process") || id.includes("@tauri-apps/plugin-shell") || id.includes("@tauri-apps/plugin-updater")