Compare commits

...

2 Commits

Author SHA1 Message Date
ezequielnick
a81f970baa chore: udpate deps2 2025-08-03 21:29:13 +08:00
ezequielnick
bc54d87389 chore: update deps 2025-08-03 20:15:40 +08:00
13 changed files with 4158 additions and 5259 deletions

View File

@ -23,75 +23,76 @@
"build:linux": "electron-vite build && electron-builder --publish never --linux" "build:linux": "electron-vite build && electron-builder --publish never --linux"
}, },
"dependencies": { "dependencies": {
"@electron-toolkit/preload": "^3.0.1", "@electron-toolkit/preload": "^3.0.2",
"@electron-toolkit/utils": "^3.0.0", "@electron-toolkit/utils": "^4.0.0",
"@heroui/react": "^2.6.14", "@heroui/react": "^2.8.2",
"@mihomo-party/sysproxy": "^2.0.7", "@mihomo-party/sysproxy": "^2.0.8",
"@mihomo-party/sysproxy-darwin-arm64": "^2.0.7", "@mihomo-party/sysproxy-darwin-arm64": "^2.0.8",
"@types/crypto-js": "^4.2.2", "@types/crypto-js": "^4.2.2",
"adm-zip": "^0.5.16", "adm-zip": "^0.5.16",
"axios": "^1.7.7", "axios": "^1.11.0",
"chokidar": "^4.0.1", "chart.js": "^4.5.0",
"chokidar": "^4.0.3",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"express": "^5.0.1", "express": "^5.1.0",
"i18next": "^24.2.2", "i18next": "^25.3.2",
"iconv-lite": "^0.6.3", "iconv-lite": "^0.6.3",
"react-i18next": "^15.4.0", "react-chartjs-2": "^5.3.0",
"webdav": "^5.7.1", "react-i18next": "^15.6.1",
"ws": "^8.18.0", "webdav": "^5.8.0",
"yaml": "^2.6.0" "ws": "^8.18.3",
"yaml": "^2.8.0"
}, },
"devDependencies": { "devDependencies": {
"@dnd-kit/core": "^6.1.0", "@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0", "@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2", "@dnd-kit/utilities": "^3.2.2",
"@electron-toolkit/eslint-config-prettier": "^2.0.0", "@electron-toolkit/eslint-config-prettier": "^3.0.0",
"@electron-toolkit/eslint-config-ts": "^2.0.0", "@electron-toolkit/eslint-config-ts": "^3.1.0",
"@electron-toolkit/tsconfig": "^1.0.1", "@electron-toolkit/tsconfig": "^1.0.1",
"@types/adm-zip": "^0.5.6", "@types/adm-zip": "^0.5.7",
"@types/express": "^5.0.0", "@types/express": "^5.0.3",
"@types/node": "^22.13.1", "@types/node": "^24.1.0",
"@types/pubsub-js": "^1.8.6", "@types/pubsub-js": "^1.8.6",
"@types/react": "^19.0.4", "@types/react": "^19.1.9",
"@types/react-dom": "^19.0.2", "@types/react-dom": "^19.1.7",
"@types/ws": "^8.5.13", "@types/ws": "^8.18.1",
"@vitejs/plugin-react": "^4.3.3", "@vitejs/plugin-react": "^4.7.0",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.21",
"cron-validator": "^1.3.1", "cron-validator": "^1.4.0",
"driver.js": "^1.3.5", "driver.js": "^1.3.6",
"electron": "^34.0.2", "electron": "^37.2.5",
"electron-builder": "25.1.8", "electron-builder": "26.0.12",
"electron-vite": "^2.3.0", "electron-vite": "^4.0.0",
"electron-window-state": "^5.0.3", "electron-window-state": "^5.0.3",
"eslint": "8.57.1", "eslint": "9.32.0",
"eslint-plugin-react": "^7.37.2", "eslint-plugin-react": "^7.37.5",
"form-data": "^4.0.1", "form-data": "^4.0.4",
"framer-motion": "12.0.11", "framer-motion": "12.23.12",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"meta-json-schema": "^1.18.9", "meta-json-schema": "^1.19.12",
"monaco-yaml": "^5.2.3", "monaco-yaml": "^5.4.0",
"nanoid": "^5.0.8", "nanoid": "^5.1.5",
"next-themes": "^0.4.3", "next-themes": "^0.4.6",
"postcss": "^8.4.47", "postcss": "^8.5.6",
"prettier": "^3.3.3", "prettier": "^3.6.2",
"pubsub-js": "^1.9.5", "pubsub-js": "^1.9.5",
"react": "^19.0.0", "react": "^19.1.1",
"react-dom": "^19.0.0", "react-dom": "^19.1.1",
"react-error-boundary": "^5.0.0", "react-error-boundary": "^6.0.0",
"react-icons": "^5.3.0", "react-icons": "^5.5.0",
"react-markdown": "^9.0.1", "react-markdown": "^10.1.0",
"react-monaco-editor": "^0.58.0", "react-monaco-editor": "^0.59.0",
"react-router-dom": "^7.1.5", "react-router-dom": "^7.7.1",
"react-virtuoso": "^4.12.0", "react-virtuoso": "^4.13.0",
"recharts": "^2.13.3", "swr": "^2.3.4",
"swr": "^2.2.5", "tailwindcss": "^4.1.11",
"tailwindcss": "^3.4.17",
"tar": "^7.4.3", "tar": "^7.4.3",
"tsx": "^4.19.2", "tsx": "^4.20.3",
"types-pac": "^1.0.3", "types-pac": "^1.0.3",
"typescript": "^5.6.3", "typescript": "^5.9.2",
"vite": "^6.0.7", "vite": "^7.0.6",
"vite-plugin-monaco-editor": "^1.1.0" "vite-plugin-monaco-editor": "^1.1.0"
}, },
"packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c" "packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c"

9139
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -373,13 +373,13 @@ const App: React.FC = () => {
setSiderWidthValue(e.clientX) setSiderWidthValue(e.clientX)
} }
}} }}
className={`w-full h-[100vh] flex ${resizing ? 'cursor-ew-resize' : ''}`} className={`w-full h-screen flex ${resizing ? 'cursor-ew-resize' : ''}`}
> >
{siderWidthValue === narrowWidth ? ( {siderWidthValue === narrowWidth ? (
<div style={{ width: `${narrowWidth}px` }} className="side h-full"> <div style={{ width: `${narrowWidth}px` }} className="side h-full">
<div className="app-drag flex justify-center items-center z-40 bg-transparent h-[49px]"> <div className="app-drag flex justify-center items-center z-40 bg-transparent h-[49px]">
{platform !== 'darwin' && ( {platform !== 'darwin' && (
<MihomoIcon className="h-[32px] leading-[32px] text-lg mx-[1px]" /> <MihomoIcon className="h-[32px] leading-[32px] text-lg mx-px" />
)} )}
<UpdaterButton iconOnly={true} /> <UpdaterButton iconOnly={true} />
</div> </div>
@ -417,7 +417,7 @@ const App: React.FC = () => {
className={`flex justify-between p-2 ${!useWindowFrame && platform === 'darwin' ? 'ml-[60px]' : ''}`} className={`flex justify-between p-2 ${!useWindowFrame && platform === 'darwin' ? 'ml-[60px]' : ''}`}
> >
<div className="flex ml-1"> <div className="flex ml-1">
<MihomoIcon className="h-[32px] leading-[32px] text-lg mx-[1px]" /> <MihomoIcon className="h-[32px] leading-[32px] text-lg mx-px" />
<h3 className="text-lg font-bold leading-[32px]">ihomo Party</h3> <h3 className="text-lg font-bold leading-[32px]">ihomo Party</h3>
</div> </div>
<UpdaterButton /> <UpdaterButton />

View File

@ -59,9 +59,9 @@ const FloatingApp: React.FC = () => {
}, []) }, [])
return ( return (
<div className="app-drag h-[100vh] w-[100vw] overflow-hidden"> <div className="app-drag h-screen w-screen overflow-hidden">
<div className="floating-bg border-1 border-divider flex rounded-full bg-content1 h-[calc(100%-2px)] w-[calc(100%-2px)]"> <div className="floating-bg border-1 border-divider flex rounded-full bg-content1 h-[calc(100%-2px)] w-[calc(100%-2px)]">
<div className="flex justify-center items-center h-[100%] aspect-square"> <div className="flex justify-center items-center h-full aspect-square">
<div <div
onContextMenu={(e) => { onContextMenu={(e) => {
e.preventDefault() e.preventDefault()

View File

@ -1,6 +1,4 @@
@tailwind base; @import 'tailwindcss';
@tailwind components;
@tailwind utilities;
.floating-text { .floating-text {
font-family: font-family:

View File

@ -1,6 +1,4 @@
@tailwind base; @import 'tailwindcss';
@tailwind components;
@tailwind utilities;
@font-face { @font-face {
font-family: 'Noto Color Emoji'; font-family: 'Noto Color Emoji';

View File

@ -1,5 +1,7 @@
.border-switch { .border-switch {
input[type='checkbox'] { overflow: hidden;
width: 100%; }
}
.border-switch input[type='checkbox'] {
width: 100%;
} }

View File

@ -2,16 +2,29 @@ import { Button, Card, CardBody, CardFooter, Tooltip } from '@heroui/react'
import { FaCircleArrowDown, FaCircleArrowUp } from 'react-icons/fa6' import { FaCircleArrowDown, FaCircleArrowUp } from 'react-icons/fa6'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { calcTraffic } from '@renderer/utils/calc' import { calcTraffic } from '@renderer/utils/calc'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState, useMemo } from 'react'
import { useSortable } from '@dnd-kit/sortable' import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities'
import { IoLink } from 'react-icons/io5' import { IoLink } from 'react-icons/io5'
import { useTheme } from 'next-themes'
import { useAppConfig } from '@renderer/hooks/use-app-config' import { useAppConfig } from '@renderer/hooks/use-app-config'
import { platform } from '@renderer/utils/init' import { platform } from '@renderer/utils/init'
import { Area, AreaChart, ResponsiveContainer } from 'recharts' import { Line } from 'react-chartjs-2'
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Filler,
ChartOptions,
ScriptableContext
} from 'chart.js'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
// 注册 Chart.js 组件
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Filler)
let currentUpload: number | undefined = undefined let currentUpload: number | undefined = undefined
let currentDownload: number | undefined = undefined let currentDownload: number | undefined = undefined
let hasShowTraffic = false let hasShowTraffic = false
@ -21,10 +34,9 @@ interface Props {
iconOnly?: boolean iconOnly?: boolean
} }
const ConnCard: React.FC<Props> = (props) => { const ConnCard: React.FC<Props> = (props) => {
const { theme = 'system', systemTheme = 'dark' } = useTheme()
const { iconOnly } = props const { iconOnly } = props
const { appConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { showTraffic = false, connectionCardStatus = 'col-span-2', customTheme } = appConfig || {} const { showTraffic = false, connectionCardStatus = 'col-span-2' } = appConfig || {}
const location = useLocation() const location = useLocation()
const navigate = useNavigate() const navigate = useNavigate()
const match = location.pathname.includes('/connections') const match = location.pathname.includes('/connections')
@ -43,25 +55,69 @@ const ConnCard: React.FC<Props> = (props) => {
id: 'connection' id: 'connection'
}) })
const [series, setSeries] = useState(Array(10).fill(0)) const [series, setSeries] = useState(Array(10).fill(0))
const [chartColor, setChartColor] = useState('rgba(255,255,255)')
useEffect(() => { // Chart.js 配置
setChartColor( const chartData = useMemo(() => {
match return {
? `hsla(${getComputedStyle(document.documentElement).getPropertyValue('--heroui-primary-foreground')})` labels: Array(10).fill(''),
: `hsla(${getComputedStyle(document.documentElement).getPropertyValue('--heroui-foreground')})` datasets: [
) {
}, [theme, systemTheme, match]) data: series,
fill: true,
backgroundColor: (context: ScriptableContext<'line'>) => {
const chart = context.chart
const { ctx, chartArea } = chart
if (!chartArea) {
return 'transparent'
}
useEffect(() => { const gradient = ctx.createLinearGradient(0, chartArea.top, 0, chartArea.bottom)
setTimeout(() => {
setChartColor( // 颜色处理
match const isMatch = location.pathname.includes('/connections')
? `hsla(${getComputedStyle(document.documentElement).getPropertyValue('--heroui-primary-foreground')})` const baseColor = isMatch ? '6, 182, 212' : '161, 161, 170' // primary vs foreground 的近似 RGB 值
: `hsla(${getComputedStyle(document.documentElement).getPropertyValue('--heroui-foreground')})`
) gradient.addColorStop(0, `rgba(${baseColor}, 0.8)`)
}, 200) gradient.addColorStop(1, `rgba(${baseColor}, 0)`)
}, [customTheme]) return gradient
},
borderColor: 'transparent',
pointRadius: 0,
pointHoverRadius: 0,
tension: 0.4
}
]
}
}, [series, location.pathname])
const chartOptions: ChartOptions<'line'> = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
x: {
display: false
},
y: {
display: false
}
},
elements: {
line: {
borderWidth: 0
}
},
interaction: {
intersect: false
},
animation: {
duration: 0
}
}
const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null
useEffect(() => { useEffect(() => {
@ -168,30 +224,9 @@ const ConnCard: React.FC<Props> = (props) => {
</h3> </h3>
</CardFooter> </CardFooter>
</Card> </Card>
<ResponsiveContainer <div className="w-full h-full absolute top-0 left-0 pointer-events-none overflow-hidden rounded-[14px]">
height="100%" <Line data={chartData} options={chartOptions} />
width="100%" </div>
className="w-full h-full absolute top-0 left-0 pointer-events-none overflow-hidden rounded-[14px]"
>
<AreaChart
data={series.map((v) => ({ traffic: v }))}
margin={{ top: 50, right: 0, left: 0, bottom: 0 }}
>
<defs>
<linearGradient id="gradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor={chartColor} stopOpacity={0.8} />
<stop offset="100%" stopColor={chartColor} stopOpacity={0} />
</linearGradient>
</defs>
<Area
isAnimationActive={false}
type="monotone"
dataKey="traffic"
stroke="none"
fill="url(#gradient)"
/>
</AreaChart>
</ResponsiveContainer>
</> </>
) : ( ) : (
<Card <Card

View File

@ -273,7 +273,7 @@ const Connections: React.FC = () => {
</div> </div>
<Divider /> <Divider />
</div> </div>
<div className="h-[calc(100vh-100px)] mt-[1px]"> <div className="h-[calc(100vh-100px)] mt-px">
<Virtuoso <Virtuoso
data={filteredConnections} data={filteredConnections}
itemContent={(i, connection) => ( itemContent={(i, connection) => (

View File

@ -264,7 +264,7 @@ const DNS: React.FC = () => {
{[...values.nameserverPolicy, { domain: '', value: '' }].map( {[...values.nameserverPolicy, { domain: '', value: '' }].map(
({ domain, value }, index) => ( ({ domain, value }, index) => (
<div key={index} className="flex mb-2"> <div key={index} className="flex mb-2">
<div className="flex-[4]"> <div className="flex-4">
<Input <Input
size="sm" size="sm"
fullWidth fullWidth
@ -281,7 +281,7 @@ const DNS: React.FC = () => {
/> />
</div> </div>
<span className="mx-2">:</span> <span className="mx-2">:</span>
<div className="flex-[6] flex"> <div className="flex-6 flex">
<Input <Input
size="sm" size="sm"
fullWidth fullWidth
@ -332,7 +332,7 @@ const DNS: React.FC = () => {
<h3 className="mb-2">{t('dns.customHosts.list')}</h3> <h3 className="mb-2">{t('dns.customHosts.list')}</h3>
{[...values.hosts, { domain: '', value: '' }].map(({ domain, value }, index) => ( {[...values.hosts, { domain: '', value: '' }].map(({ domain, value }, index) => (
<div key={index} className="flex mb-2"> <div key={index} className="flex mb-2">
<div className="flex-[4]"> <div className="flex-4">
<Input <Input
size="sm" size="sm"
fullWidth fullWidth
@ -349,7 +349,7 @@ const DNS: React.FC = () => {
/> />
</div> </div>
<span className="mx-2">:</span> <span className="mx-2">:</span>
<div className="flex-[6] flex"> <div className="flex-6 flex">
<Input <Input
size="sm" size="sm"
fullWidth fullWidth

View File

@ -109,7 +109,7 @@ const Logs: React.FC = () => {
</div> </div>
<Divider /> <Divider />
</div> </div>
<div className="h-[calc(100vh-100px)] mt-[1px]"> <div className="h-[calc(100vh-100px)] mt-px">
<Virtuoso <Virtuoso
ref={virtuosoRef} ref={virtuosoRef}
data={filteredLogs} data={filteredLogs}

View File

@ -652,7 +652,7 @@ const Mihomo: React.FC = () => {
const [user, pass] = auth.split(':') const [user, pass] = auth.split(':')
return ( return (
<div key={index} className="flex mb-2"> <div key={index} className="flex mb-2">
<div className="flex-[4]"> <div className="flex-4">
<Input <Input
size="sm" size="sm"
fullWidth fullWidth
@ -672,7 +672,7 @@ const Mihomo: React.FC = () => {
/> />
</div> </div>
<span className="mx-2">:</span> <span className="mx-2">:</span>
<div className="flex-[6] flex"> <div className="flex-6 flex">
<Input <Input
size="sm" size="sm"
fullWidth fullWidth

View File

@ -38,7 +38,7 @@ const Rules: React.FC = () => {
</div> </div>
<Divider /> <Divider />
</div> </div>
<div className="h-[calc(100vh-100px)] mt-[1px]"> <div className="h-[calc(100vh-100px)] mt-px">
<Virtuoso <Virtuoso
data={filteredRules} data={filteredRules}
itemContent={(i, rule) => ( itemContent={(i, rule) => (