diff --git a/.github/update.log b/.github/update.log index 8685efdaa8..9bdb4c6ab4 100644 --- a/.github/update.log +++ b/.github/update.log @@ -947,3 +947,4 @@ Update On Sun Mar 16 19:33:44 CET 2025 Update On Mon Mar 17 19:35:42 CET 2025 Update On Tue Mar 18 19:36:24 CET 2025 Update On Wed Mar 19 19:36:39 CET 2025 +Update On Thu Mar 20 19:36:35 CET 2025 diff --git a/clash-meta/adapter/outbound/mieru.go b/clash-meta/adapter/outbound/mieru.go index 7aab2f58d8..d2209442f8 100644 --- a/clash-meta/adapter/outbound/mieru.go +++ b/clash-meta/adapter/outbound/mieru.go @@ -8,11 +8,13 @@ import ( "strconv" "sync" + CN "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" C "github.com/metacubex/mihomo/constant" mieruclient "github.com/enfein/mieru/v3/apis/client" + mierucommon "github.com/enfein/mieru/v3/apis/common" mierumodel "github.com/enfein/mieru/v3/apis/model" mierupb "github.com/enfein/mieru/v3/pkg/appctl/appctlpb" "google.golang.org/protobuf/proto" @@ -32,6 +34,7 @@ type MieruOption struct { Port int `proxy:"port,omitempty"` PortRange string `proxy:"port-range,omitempty"` Transport string `proxy:"transport"` + UDP bool `proxy:"udp,omitempty"` UserName string `proxy:"username"` Password string `proxy:"password"` Multiplexing string `proxy:"multiplexing,omitempty"` @@ -50,6 +53,23 @@ func (m *Mieru) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d return NewConn(c, m), nil } +// ListenPacketContext implements C.ProxyAdapter +func (m *Mieru) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { + if err := m.ensureClientIsRunning(opts...); err != nil { + return nil, err + } + c, err := m.client.DialContext(ctx, metadata.UDPAddr()) + if err != nil { + return nil, fmt.Errorf("dial to %s failed: %w", metadata.UDPAddr(), err) + } + return newPacketConn(CN.NewRefPacketConn(CN.NewThreadSafePacketConn(mierucommon.NewUDPAssociateWrapper(mierucommon.NewPacketOverStreamTunnel(c))), m), m), nil +} + +// SupportUOT implements C.ProxyAdapter +func (m *Mieru) SupportUOT() bool { + return true +} + // ProxyInfo implements C.ProxyAdapter func (m *Mieru) ProxyInfo() C.ProxyInfo { info := m.Base.ProxyInfo() @@ -113,7 +133,7 @@ func NewMieru(option MieruOption) (*Mieru, error) { addr: addr, iface: option.Interface, tp: C.Mieru, - udp: false, + udp: option.UDP, xudp: false, rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), diff --git a/clash-meta/docs/config.yaml b/clash-meta/docs/config.yaml index 263d67b65c..fc11dca6eb 100644 --- a/clash-meta/docs/config.yaml +++ b/clash-meta/docs/config.yaml @@ -879,6 +879,7 @@ proxies: # socks5 port: 2999 # port-range: 2090-2099 #(不可同时填写 port 和 port-range) transport: TCP # 只支持 TCP + udp: true # 支持 UDP over TCP username: user password: password # 可以使用的值包括 MULTIPLEXING_OFF, MULTIPLEXING_LOW, MULTIPLEXING_MIDDLE, MULTIPLEXING_HIGH。其中 MULTIPLEXING_OFF 会关闭多路复用功能。默认值为 MULTIPLEXING_LOW。 diff --git a/clash-meta/go.mod b/clash-meta/go.mod index 4787606ab5..b327e196c2 100644 --- a/clash-meta/go.mod +++ b/clash-meta/go.mod @@ -7,7 +7,7 @@ require ( github.com/bahlo/generic-list-go v0.2.0 github.com/coreos/go-iptables v0.8.0 github.com/dlclark/regexp2 v1.11.5 - github.com/enfein/mieru/v3 v3.12.0 + github.com/enfein/mieru/v3 v3.13.0 github.com/go-chi/chi/v5 v5.2.1 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 diff --git a/clash-meta/go.sum b/clash-meta/go.sum index c7711bead1..9cfd6e0e1f 100644 --- a/clash-meta/go.sum +++ b/clash-meta/go.sum @@ -28,8 +28,8 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/enfein/mieru/v3 v3.12.0 h1:sV3moozWpRjjqwqFZJjGtMB0EacN8+D7BpjzsmacsXM= -github.com/enfein/mieru/v3 v3.12.0/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM= +github.com/enfein/mieru/v3 v3.13.0 h1:eGyxLGkb+lut9ebmx+BGwLJ5UMbEc/wGIYO0AXEKy98= +github.com/enfein/mieru/v3 v3.13.0/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= diff --git a/clash-nyanpasu/backend/Cargo.lock b/clash-nyanpasu/backend/Cargo.lock index 41c8888ec6..b44ad4ec51 100644 --- a/clash-nyanpasu/backend/Cargo.lock +++ b/clash-nyanpasu/backend/Cargo.lock @@ -984,9 +984,9 @@ dependencies = [ [[package]] name = "backon" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49fef586913a57ff189f25c9b3d034356a5bf6b3fa9a7f067588fe1698ba1f5d" +checksum = "970d91570c01a8a5959b36ad7dd1c30642df24b6b3068710066f6809f7033bb7" dependencies = [ "fastrand 2.3.0", "gloo-timers", @@ -2090,7 +2090,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -5557,7 +5557,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -8542,9 +8542,9 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "reqwest" -version = "0.12.14" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e327e510263980e231de548a33e63d34962d29ae61b467389a1a09627a254" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ "base64 0.22.1", "bytes", @@ -12609,7 +12609,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] diff --git a/clash-nyanpasu/frontend/interface/package.json b/clash-nyanpasu/frontend/interface/package.json index 9d4a8af5b3..8b1bb296af 100644 --- a/clash-nyanpasu/frontend/interface/package.json +++ b/clash-nyanpasu/frontend/interface/package.json @@ -22,6 +22,6 @@ }, "devDependencies": { "@types/lodash-es": "4.17.12", - "@types/react": "19.0.11" + "@types/react": "19.0.12" } } diff --git a/clash-nyanpasu/frontend/nyanpasu/package.json b/clash-nyanpasu/frontend/nyanpasu/package.json index 5235946061..3a63172c66 100644 --- a/clash-nyanpasu/frontend/nyanpasu/package.json +++ b/clash-nyanpasu/frontend/nyanpasu/package.json @@ -67,7 +67,7 @@ "@tauri-apps/plugin-process": "2.2.0", "@tauri-apps/plugin-shell": "2.2.0", "@tauri-apps/plugin-updater": "2.6.1", - "@types/react": "19.0.11", + "@types/react": "19.0.12", "@types/react-dom": "19.0.4", "@types/validator": "13.12.2", "@vitejs/plugin-legacy": "6.0.2", diff --git a/clash-nyanpasu/frontend/ui/package.json b/clash-nyanpasu/frontend/ui/package.json index 7dd5fd30f7..6237595032 100644 --- a/clash-nyanpasu/frontend/ui/package.json +++ b/clash-nyanpasu/frontend/ui/package.json @@ -24,7 +24,7 @@ "@radix-ui/react-scroll-area": "1.2.3", "@tauri-apps/api": "2.3.0", "@types/d3": "7.4.3", - "@types/react": "19.0.11", + "@types/react": "19.0.12", "@vitejs/plugin-react": "4.3.4", "ahooks": "3.8.4", "d3": "7.9.0", diff --git a/clash-nyanpasu/manifest/version.json b/clash-nyanpasu/manifest/version.json index e8ae34bf51..76d952f5c9 100644 --- a/clash-nyanpasu/manifest/version.json +++ b/clash-nyanpasu/manifest/version.json @@ -2,10 +2,10 @@ "manifest_version": 1, "latest": { "mihomo": "v1.19.3", - "mihomo_alpha": "alpha-dcef787", + "mihomo_alpha": "alpha-4f8b70c", "clash_rs": "v0.7.6", "clash_premium": "2023-09-05-gdcc8d87", - "clash_rs_alpha": "0.7.6-alpha+sha.3cc3aa2" + "clash_rs_alpha": "0.7.6-alpha+sha.153fb70" }, "arch_template": { "mihomo": { @@ -69,5 +69,5 @@ "linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf" } }, - "updated_at": "2025-03-18T22:22:45.612Z" + "updated_at": "2025-03-19T22:20:51.520Z" } diff --git a/clash-nyanpasu/package.json b/clash-nyanpasu/package.json index 77c8062de6..e5e82814b0 100644 --- a/clash-nyanpasu/package.json +++ b/clash-nyanpasu/package.json @@ -109,7 +109,7 @@ "typescript": "5.8.2", "typescript-eslint": "8.26.1" }, - "packageManager": "pnpm@10.6.4", + "packageManager": "pnpm@10.6.5", "engines": { "node": "22.14.0" }, diff --git a/clash-nyanpasu/pnpm-lock.yaml b/clash-nyanpasu/pnpm-lock.yaml index d711816f07..8cf2d7a9e3 100644 --- a/clash-nyanpasu/pnpm-lock.yaml +++ b/clash-nyanpasu/pnpm-lock.yaml @@ -203,8 +203,8 @@ importers: specifier: 4.17.12 version: 4.17.12 '@types/react': - specifier: 19.0.11 - version: 19.0.11 + specifier: 19.0.12 + version: 19.0.12 frontend/nyanpasu: dependencies: @@ -219,7 +219,7 @@ importers: version: 3.2.2(react@19.0.0) '@emotion/styled': specifier: 11.14.0 - version: 11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) + version: 11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) '@juggle/resize-observer': specifier: 3.4.0 version: 3.4.0 @@ -228,13 +228,13 @@ importers: version: 0.3.0 '@mui/icons-material': specifier: 6.4.8 - version: 6.4.8(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) + version: 6.4.8(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) '@mui/lab': specifier: 6.0.0-beta.31 - version: 6.0.0-beta.31(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 6.0.0-beta.31(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@mui/material': specifier: 6.4.8 - version: 6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@nyanpasu/interface': specifier: workspace:^ version: link:../interface @@ -273,19 +273,19 @@ importers: version: 24.2.3(typescript@5.8.2) jotai: specifier: 2.12.2 - version: 2.12.2(@types/react@19.0.11)(react@19.0.0) + version: 2.12.2(@types/react@19.0.12)(react@19.0.0) json-schema: specifier: 0.4.0 version: 0.4.0 material-react-table: specifier: 3.2.1 - version: 3.2.1(384a497145485e68a86cdc9cfd7e9248) + version: 3.2.1(2b16bb54767bc65498934150c630abc0) monaco-editor: specifier: 0.52.2 version: 0.52.2 mui-color-input: specifier: 5.0.1 - version: 5.0.1(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 5.0.1(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: specifier: 19.0.0 version: 19.0.0 @@ -300,13 +300,13 @@ importers: version: 1.6.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react-hook-form-mui: specifier: 7.5.0 - version: 7.5.0(d916abb5390aa3b2a5543e30cee3d1b4) + version: 7.5.0(fee9744d8c06e9649bd4f90cf648ba7a) react-i18next: specifier: 15.4.1 version: 15.4.1(i18next@24.2.3(typescript@5.8.2))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react-markdown: specifier: 10.1.0 - version: 10.1.0(@types/react@19.0.11)(react@19.0.0) + version: 10.1.0(@types/react@19.0.12)(react@19.0.0) react-split-grid: specifier: 1.0.4 version: 1.0.4(react@19.0.0) @@ -331,7 +331,7 @@ importers: version: 11.13.5 '@emotion/react': specifier: 11.14.0 - version: 11.14.0(@types/react@19.0.11)(react@19.0.0) + version: 11.14.0(@types/react@19.0.12)(react@19.0.0) '@iconify/json': specifier: 2.2.318 version: 2.2.318 @@ -375,11 +375,11 @@ importers: specifier: 2.6.1 version: 2.6.1 '@types/react': - specifier: 19.0.11 - version: 19.0.11 + specifier: 19.0.12 + version: 19.0.12 '@types/react-dom': specifier: 19.0.4 - version: 19.0.4(@types/react@19.0.11) + version: 19.0.4(@types/react@19.0.12) '@types/validator': specifier: 13.12.2 version: 13.12.2 @@ -454,19 +454,19 @@ importers: version: 0.3.0 '@mui/icons-material': specifier: 6.4.8 - version: 6.4.8(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) + version: 6.4.8(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) '@mui/lab': specifier: 6.0.0-beta.31 - version: 6.0.0-beta.31(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 6.0.0-beta.31(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@mui/material': specifier: 6.4.8 - version: 6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-portal': specifier: 1.1.4 - version: 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-scroll-area': specifier: 1.2.3 - version: 1.2.3(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.2.3(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@tauri-apps/api': specifier: 2.3.0 version: 2.3.0 @@ -474,8 +474,8 @@ importers: specifier: 7.4.3 version: 7.4.3 '@types/react': - specifier: 19.0.11 - version: 19.0.11 + specifier: 19.0.12 + version: 19.0.12 '@vitejs/plugin-react': specifier: 4.3.4 version: 4.3.4(vite@6.2.2(@types/node@22.13.10)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.2)(sass-embedded@1.85.1)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)) @@ -515,7 +515,7 @@ importers: devDependencies: '@emotion/react': specifier: 11.14.0 - version: 11.14.0(@types/react@19.0.11)(react@19.0.0) + version: 11.14.0(@types/react@19.0.12)(react@19.0.0) '@types/d3-interpolate-path': specifier: 2.0.3 version: 2.0.3 @@ -3175,8 +3175,8 @@ packages: peerDependencies: '@types/react': '*' - '@types/react@19.0.11': - resolution: {integrity: sha512-vrdxRZfo9ALXth6yPfV16PYTLZwsUWhVjjC+DkfE5t1suNSbBrWC9YqSuuxJZ8Ps6z1o2ycRpIqzZJIgklq4Tw==} + '@types/react@19.0.12': + resolution: {integrity: sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==} '@types/responselike@1.0.3': resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} @@ -9234,7 +9234,7 @@ snapshots: '@emotion/memoize@0.9.0': {} - '@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0)': + '@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0)': dependencies: '@babel/runtime': 7.26.0 '@emotion/babel-plugin': 11.13.5 @@ -9246,7 +9246,7 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 19.0.0 optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 transitivePeerDependencies: - supports-color @@ -9260,18 +9260,18 @@ snapshots: '@emotion/sheet@1.4.0': {} - '@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0)': + '@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0)': dependencies: '@babel/runtime': 7.26.0 '@emotion/babel-plugin': 11.13.5 '@emotion/is-prop-valid': 1.3.0 - '@emotion/react': 11.14.0(@types/react@19.0.11)(react@19.0.0) + '@emotion/react': 11.14.0(@types/react@19.0.12)(react@19.0.0) '@emotion/serialize': 1.3.3 '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.0.0) '@emotion/utils': 1.4.2 react: 19.0.0 optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 transitivePeerDependencies: - supports-color @@ -9562,56 +9562,56 @@ snapshots: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - '@mui/base@5.0.0-beta.70(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@mui/base@5.0.0-beta.70(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@babel/runtime': 7.26.10 '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@mui/types': 7.2.24(@types/react@19.0.11) - '@mui/utils': 6.4.8(@types/react@19.0.11)(react@19.0.0) + '@mui/types': 7.2.24(@types/react@19.0.12) + '@mui/utils': 6.4.8(@types/react@19.0.12)(react@19.0.0) '@popperjs/core': 2.11.8 clsx: 2.1.1 prop-types: 15.8.1 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 '@mui/core-downloads-tracker@6.4.8': {} - '@mui/icons-material@6.4.8(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.11)(react@19.0.0)': + '@mui/icons-material@6.4.8(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.12)(react@19.0.0)': dependencies: '@babel/runtime': 7.26.10 - '@mui/material': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@mui/material': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 - '@mui/lab@6.0.0-beta.31(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@mui/lab@6.0.0-beta.31(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@babel/runtime': 7.26.10 - '@mui/base': 5.0.0-beta.70(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@mui/material': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@mui/system': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) - '@mui/types': 7.2.24(@types/react@19.0.11) - '@mui/utils': 6.4.8(@types/react@19.0.11)(react@19.0.0) + '@mui/base': 5.0.0-beta.70(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@mui/material': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@mui/system': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) + '@mui/types': 7.2.24(@types/react@19.0.12) + '@mui/utils': 6.4.8(@types/react@19.0.12)(react@19.0.0) clsx: 2.1.1 prop-types: 15.8.1 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.0.11)(react@19.0.0) - '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) - '@types/react': 19.0.11 + '@emotion/react': 11.14.0(@types/react@19.0.12)(react@19.0.0) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) + '@types/react': 19.0.12 - '@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@babel/runtime': 7.26.10 '@mui/core-downloads-tracker': 6.4.8 - '@mui/system': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) - '@mui/types': 7.2.24(@types/react@19.0.11) - '@mui/utils': 6.4.8(@types/react@19.0.11)(react@19.0.0) + '@mui/system': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) + '@mui/types': 7.2.24(@types/react@19.0.12) + '@mui/utils': 6.4.8(@types/react@19.0.12)(react@19.0.0) '@popperjs/core': 2.11.8 - '@types/react-transition-group': 4.4.12(@types/react@19.0.11) + '@types/react-transition-group': 4.4.12(@types/react@19.0.12) clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 @@ -9620,29 +9620,29 @@ snapshots: react-is: 19.0.0 react-transition-group: 4.4.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.0.11)(react@19.0.0) - '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) - '@types/react': 19.0.11 + '@emotion/react': 11.14.0(@types/react@19.0.12)(react@19.0.0) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) + '@types/react': 19.0.12 - '@mui/private-theming@5.16.6(@types/react@19.0.11)(react@19.0.0)': + '@mui/private-theming@5.16.6(@types/react@19.0.12)(react@19.0.0)': dependencies: '@babel/runtime': 7.26.10 - '@mui/utils': 5.16.6(@types/react@19.0.11)(react@19.0.0) + '@mui/utils': 5.16.6(@types/react@19.0.12)(react@19.0.0) prop-types: 15.8.1 react: 19.0.0 optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 - '@mui/private-theming@6.4.8(@types/react@19.0.11)(react@19.0.0)': + '@mui/private-theming@6.4.8(@types/react@19.0.12)(react@19.0.0)': dependencies: '@babel/runtime': 7.26.10 - '@mui/utils': 6.4.8(@types/react@19.0.11)(react@19.0.0) + '@mui/utils': 6.4.8(@types/react@19.0.12)(react@19.0.0) prop-types: 15.8.1 react: 19.0.0 optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 - '@mui/styled-engine@5.16.6(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(react@19.0.0)': + '@mui/styled-engine@5.16.6(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(react@19.0.0)': dependencies: '@babel/runtime': 7.26.10 '@emotion/cache': 11.14.0 @@ -9650,10 +9650,10 @@ snapshots: prop-types: 15.8.1 react: 19.0.0 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.0.11)(react@19.0.0) - '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) + '@emotion/react': 11.14.0(@types/react@19.0.12)(react@19.0.0) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) - '@mui/styled-engine@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(react@19.0.0)': + '@mui/styled-engine@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(react@19.0.0)': dependencies: '@babel/runtime': 7.26.10 '@emotion/cache': 11.14.0 @@ -9663,85 +9663,85 @@ snapshots: prop-types: 15.8.1 react: 19.0.0 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.0.11)(react@19.0.0) - '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) + '@emotion/react': 11.14.0(@types/react@19.0.12)(react@19.0.0) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) - '@mui/system@5.16.7(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0)': + '@mui/system@5.16.7(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0)': dependencies: '@babel/runtime': 7.26.10 - '@mui/private-theming': 5.16.6(@types/react@19.0.11)(react@19.0.0) - '@mui/styled-engine': 5.16.6(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(react@19.0.0) - '@mui/types': 7.2.24(@types/react@19.0.11) - '@mui/utils': 5.16.6(@types/react@19.0.11)(react@19.0.0) + '@mui/private-theming': 5.16.6(@types/react@19.0.12)(react@19.0.0) + '@mui/styled-engine': 5.16.6(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(react@19.0.0) + '@mui/types': 7.2.24(@types/react@19.0.12) + '@mui/utils': 5.16.6(@types/react@19.0.12)(react@19.0.0) clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 react: 19.0.0 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.0.11)(react@19.0.0) - '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) - '@types/react': 19.0.11 + '@emotion/react': 11.14.0(@types/react@19.0.12)(react@19.0.0) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) + '@types/react': 19.0.12 - '@mui/system@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0)': + '@mui/system@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0)': dependencies: '@babel/runtime': 7.26.10 - '@mui/private-theming': 6.4.8(@types/react@19.0.11)(react@19.0.0) - '@mui/styled-engine': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(react@19.0.0) - '@mui/types': 7.2.24(@types/react@19.0.11) - '@mui/utils': 6.4.8(@types/react@19.0.11)(react@19.0.0) + '@mui/private-theming': 6.4.8(@types/react@19.0.12)(react@19.0.0) + '@mui/styled-engine': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(react@19.0.0) + '@mui/types': 7.2.24(@types/react@19.0.12) + '@mui/utils': 6.4.8(@types/react@19.0.12)(react@19.0.0) clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 react: 19.0.0 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.0.11)(react@19.0.0) - '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) - '@types/react': 19.0.11 + '@emotion/react': 11.14.0(@types/react@19.0.12)(react@19.0.0) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) + '@types/react': 19.0.12 - '@mui/types@7.2.24(@types/react@19.0.11)': + '@mui/types@7.2.24(@types/react@19.0.12)': optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 - '@mui/utils@5.16.6(@types/react@19.0.11)(react@19.0.0)': + '@mui/utils@5.16.6(@types/react@19.0.12)(react@19.0.0)': dependencies: '@babel/runtime': 7.26.10 - '@mui/types': 7.2.24(@types/react@19.0.11) + '@mui/types': 7.2.24(@types/react@19.0.12) '@types/prop-types': 15.7.14 clsx: 2.1.1 prop-types: 15.8.1 react: 19.0.0 react-is: 18.3.1 optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 - '@mui/utils@6.4.8(@types/react@19.0.11)(react@19.0.0)': + '@mui/utils@6.4.8(@types/react@19.0.12)(react@19.0.0)': dependencies: '@babel/runtime': 7.26.10 - '@mui/types': 7.2.24(@types/react@19.0.11) + '@mui/types': 7.2.24(@types/react@19.0.12) '@types/prop-types': 15.7.14 clsx: 2.1.1 prop-types: 15.8.1 react: 19.0.0 react-is: 19.0.0 optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 - '@mui/x-date-pickers@7.9.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.11)(dayjs@1.11.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@mui/x-date-pickers@7.9.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.12)(dayjs@1.11.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@babel/runtime': 7.26.10 - '@mui/base': 5.0.0-beta.70(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@mui/material': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@mui/system': 5.16.7(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) - '@mui/utils': 5.16.6(@types/react@19.0.11)(react@19.0.0) - '@types/react-transition-group': 4.4.12(@types/react@19.0.11) + '@mui/base': 5.0.0-beta.70(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@mui/material': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@mui/system': 5.16.7(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) + '@mui/utils': 5.16.6(@types/react@19.0.12)(react@19.0.0) + '@types/react-transition-group': 4.4.12(@types/react@19.0.12) clsx: 2.1.1 prop-types: 15.8.1 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) react-transition-group: 4.4.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.0.11)(react@19.0.0) - '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) + '@emotion/react': 11.14.0(@types/react@19.0.12)(react@19.0.0) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) dayjs: 1.11.13 transitivePeerDependencies: - '@types/react' @@ -10039,88 +10039,88 @@ snapshots: '@radix-ui/primitive@1.1.1': {} - '@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.11)(react@19.0.0)': + '@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.12)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 - '@radix-ui/react-context@1.1.1(@types/react@19.0.11)(react@19.0.0)': + '@radix-ui/react-context@1.1.1(@types/react@19.0.12)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 - '@radix-ui/react-direction@1.1.0(@types/react@19.0.11)(react@19.0.0)': + '@radix-ui/react-direction@1.1.0(@types/react@19.0.12)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 - '@radix-ui/react-portal@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-portal@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.12)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.11 - '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@types/react': 19.0.12 + '@types/react-dom': 19.0.4(@types/react@19.0.12) - '@radix-ui/react-presence@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-presence@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.11)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.12)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.12)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.11 - '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@types/react': 19.0.12 + '@types/react-dom': 19.0.4(@types/react@19.0.12) - '@radix-ui/react-primitive@2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-primitive@2.0.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-slot': 1.1.2(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-slot': 1.1.2(@types/react@19.0.12)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.11 - '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@types/react': 19.0.12 + '@types/react-dom': 19.0.4(@types/react@19.0.12) - '@radix-ui/react-scroll-area@1.2.3(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-scroll-area@1.2.3(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/number': 1.1.0 '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.11)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.11)(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.11)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.12)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.12)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@19.0.12)(react@19.0.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.12)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.12)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.11 - '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@types/react': 19.0.12 + '@types/react-dom': 19.0.4(@types/react@19.0.12) - '@radix-ui/react-slot@1.1.2(@types/react@19.0.11)(react@19.0.0)': + '@radix-ui/react-slot@1.1.2(@types/react@19.0.12)(react@19.0.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.12)(react@19.0.0) react: 19.0.0 optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 - '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.11)(react@19.0.0)': + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.12)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 - '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.0.11)(react@19.0.0)': + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.0.12)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 '@rollup/pluginutils@4.2.1': dependencies: @@ -10946,15 +10946,15 @@ snapshots: '@types/prop-types@15.7.14': {} - '@types/react-dom@19.0.4(@types/react@19.0.11)': + '@types/react-dom@19.0.4(@types/react@19.0.12)': dependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 - '@types/react-transition-group@4.4.12(@types/react@19.0.11)': + '@types/react-transition-group@4.4.12(@types/react@19.0.12)': dependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 - '@types/react@19.0.11': + '@types/react@19.0.12': dependencies: csstype: 3.1.3 @@ -13665,9 +13665,9 @@ snapshots: jju@1.4.0: {} - jotai@2.12.2(@types/react@19.0.11)(react@19.0.0): + jotai@2.12.2(@types/react@19.0.12)(react@19.0.0): optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 react: 19.0.0 js-cookie@2.2.1: {} @@ -13967,13 +13967,13 @@ snapshots: escape-string-regexp: 4.0.0 optional: true - material-react-table@3.2.1(384a497145485e68a86cdc9cfd7e9248): + material-react-table@3.2.1(2b16bb54767bc65498934150c630abc0): dependencies: - '@emotion/react': 11.14.0(@types/react@19.0.11)(react@19.0.0) - '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) - '@mui/icons-material': 6.4.8(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) - '@mui/material': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@mui/x-date-pickers': 7.9.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.11)(dayjs@1.11.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@emotion/react': 11.14.0(@types/react@19.0.12)(react@19.0.0) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) + '@mui/icons-material': 6.4.8(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) + '@mui/material': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@mui/x-date-pickers': 7.9.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.12)(dayjs@1.11.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@tanstack/match-sorter-utils': 8.19.4 '@tanstack/react-table': 8.20.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@tanstack/react-virtual': 3.11.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -14324,16 +14324,16 @@ snapshots: muggle-string@0.4.1: {} - mui-color-input@5.0.1(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + mui-color-input@5.0.1(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: '@ctrl/tinycolor': 4.1.0 - '@emotion/react': 11.14.0(@types/react@19.0.11)(react@19.0.0) - '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) - '@mui/material': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@emotion/react': 11.14.0(@types/react@19.0.12)(react@19.0.0) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) + '@mui/material': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.11 + '@types/react': 19.0.12 nano-css@5.6.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: @@ -14889,14 +14889,14 @@ snapshots: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - react-hook-form-mui@7.5.0(d916abb5390aa3b2a5543e30cee3d1b4): + react-hook-form-mui@7.5.0(fee9744d8c06e9649bd4f90cf648ba7a): dependencies: - '@mui/material': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@mui/material': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 react-hook-form: 7.52.1(react@19.0.0) optionalDependencies: - '@mui/icons-material': 6.4.8(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.11)(react@19.0.0) - '@mui/x-date-pickers': 7.9.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react@19.0.0))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.11)(dayjs@1.11.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@mui/icons-material': 6.4.8(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) + '@mui/x-date-pickers': 7.9.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.12)(dayjs@1.11.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react-hook-form@7.52.1(react@19.0.0): dependencies: @@ -14917,11 +14917,11 @@ snapshots: react-is@19.0.0: {} - react-markdown@10.1.0(@types/react@19.0.11)(react@19.0.0): + react-markdown@10.1.0(@types/react@19.0.12)(react@19.0.0): dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.3 - '@types/react': 19.0.11 + '@types/react': 19.0.12 devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.0 html-url-attributes: 3.0.0 diff --git a/clash-verge-rev/.github/ISSUE_TEMPLATE/bug_report.yml b/clash-verge-rev/.github/ISSUE_TEMPLATE/bug_report.yml index 66564bb761..f1cfd004ef 100644 --- a/clash-verge-rev/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/clash-verge-rev/.github/ISSUE_TEMPLATE/bug_report.yml @@ -55,17 +55,6 @@ body: description: 请提供你的操作系统版本,Linux请额外提供桌面环境及窗口系统 / Please provide your OS version, for Linux, please also provide the desktop environment and window system validations: required: true - - type: checkboxes - id: os-labels - attributes: - label: 系统标签 / OS Labels - description: 请选择受影响的操作系统(至少选择一个) / Please select the affected operating system(s) (select at least one) - options: - - label: windows - - label: macos - - label: linux - validations: - required: true - type: textarea attributes: label: 日志 / Log diff --git a/clash-verge-rev/.github/workflows/alpha.yml b/clash-verge-rev/.github/workflows/alpha.yml index 6258de309b..9e206d38ec 100644 --- a/clash-verge-rev/.github/workflows/alpha.yml +++ b/clash-verge-rev/.github/workflows/alpha.yml @@ -25,7 +25,7 @@ jobs: with: fetch-depth: 2 - - name: Check if commit changed + - name: Check if version changed id: check run: | # For manual workflow_dispatch, always run @@ -34,21 +34,164 @@ jobs: exit 0 fi - # Check if current commit is different from the previous one - CURRENT_COMMIT=$(git rev-parse HEAD) - PREVIOUS_COMMIT=$(git rev-parse HEAD~1) + # Store current version from package.json + CURRENT_VERSION=$(cat package.json | jq -r '.version') + echo "Current version: $CURRENT_VERSION" - if [ "$CURRENT_COMMIT" != "$PREVIOUS_COMMIT" ]; then - echo "New commit detected: $CURRENT_COMMIT" + # Get the previous commit's package.json version + git checkout HEAD~1 package.json + PREVIOUS_VERSION=$(cat package.json | jq -r '.version') + echo "Previous version: $PREVIOUS_VERSION" + + # Reset back to current commit + git checkout HEAD package.json + + # Check if version changed + if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then + echo "Version changed from $PREVIOUS_VERSION to $CURRENT_VERSION" echo "should_run=true" >> $GITHUB_OUTPUT else - echo "No new commits since last run" + echo "Version unchanged: $CURRENT_VERSION" echo "should_run=false" >> $GITHUB_OUTPUT fi - alpha: + delete_old_assets: needs: check_commit if: ${{ needs.check_commit.outputs.should_run == 'true' }} + runs-on: ubuntu-latest + steps: + - name: Delete Old Alpha Release Assets + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const releaseTag = 'alpha'; + + try { + // Get the release by tag name + const { data: release } = await github.rest.repos.getReleaseByTag({ + owner: context.repo.owner, + repo: context.repo.repo, + tag: releaseTag + }); + + console.log(`Found release with ID: ${release.id}`); + + // Delete each asset + if (release.assets && release.assets.length > 0) { + console.log(`Deleting ${release.assets.length} assets`); + + for (const asset of release.assets) { + console.log(`Deleting asset: ${asset.name} (${asset.id})`); + await github.rest.repos.deleteReleaseAsset({ + owner: context.repo.owner, + repo: context.repo.repo, + asset_id: asset.id + }); + } + + console.log('All assets deleted successfully'); + } else { + console.log('No assets found to delete'); + } + } catch (error) { + if (error.status === 404) { + console.log('Release not found, nothing to delete'); + } else { + console.error('Error:', error); + throw error; + } + } + + update_tag: + name: Update tag + runs-on: ubuntu-latest + needs: delete_old_assets + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Fetch Alpha update logs + id: fetch_alpha_logs + run: | + # Check if UPDATELOG.md exists + if [ -f "UPDATELOG.md" ]; then + # Extract the section starting with ## and containing -alpha until the next ## or end of file + ALPHA_LOGS=$(awk '/^## .*-alpha/{flag=1; print; next} /^## /{flag=0} flag' UPDATELOG.md) + + if [ -n "$ALPHA_LOGS" ]; then + echo "Found alpha update logs" + echo "ALPHA_LOGS<> $GITHUB_ENV + echo "$ALPHA_LOGS" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + else + echo "No alpha sections found in UPDATELOG.md" + fi + else + echo "UPDATELOG.md file not found" + fi + shell: bash + + - name: Set Env + run: | + echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV + shell: bash + + - run: | + # 检查 ALPHA_LOGS 是否存在,如果不存在则使用默认消息 + if [ -z "$ALPHA_LOGS" ]; then + echo "No alpha logs found, using default message" + ALPHA_LOGS="More new features are now supported. Check for detailed changelog soon." + else + echo "Using found alpha logs" + fi + + # 生成 release.txt 文件 + cat > release.txt << EOF + $ALPHA_LOGS + + ## 我应该下载哪个版本? + + ### MacOS + - MacOS intel芯片: x64.dmg + - MacOS apple M芯片: aarch64.dmg + + ### Linux + - Linux 64位: amd64.deb/amd64.rpm + - Linux arm64 architecture: arm64.deb/aarch64.rpm + - Linux armv7架构: armhf.deb/armhfp.rpm + + ### Windows (不再支持Win7) + #### 正常版本(推荐) + - 64位: x64-setup.exe + - arm64架构: arm64-setup.exe + #### 便携版问题很多不再提供 + #### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用) + - 64位: x64_fixed_webview2-setup.exe + - arm64架构: arm64_fixed_webview2-setup.exe + + ### FAQ + + - [常见问题](https://clash-verge-rev.github.io/faq/windows.html) + + ### 稳定机场VPN推荐 + - [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6) + + Created at ${{ env.BUILDTIME }}. + EOF + + - name: Upload Release + uses: softprops/action-gh-release@v2 + with: + tag_name: alpha + name: "Clash Verge Rev Alpha" + body_path: release.txt + prerelease: true + token: ${{ secrets.GITHUB_TOKEN }} + generate_release_notes: true + + alpha: + needs: update_tag strategy: fail-fast: false matrix: @@ -86,7 +229,7 @@ jobs: if: matrix.os == 'ubuntu-22.04' run: | sudo apt-get update - sudo apt-get install -y libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf + sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf - name: Install Node uses: actions/setup-node@v4 @@ -103,9 +246,6 @@ jobs: pnpm i pnpm check ${{ matrix.target }} - - name: Alpha Version update - run: pnpm run fix-alpha-version - - name: Tauri build uses: tauri-apps/tauri-action@v0 env: @@ -129,8 +269,7 @@ jobs: args: --target ${{ matrix.target }} alpha-for-linux-arm: - needs: check_commit - if: ${{ needs.check_commit.outputs.should_run == 'true' }} + needs: update_tag strategy: fail-fast: false matrix: @@ -196,6 +335,7 @@ jobs: sudo apt update sudo apt install -y \ + libxslt1.1:${{ matrix.arch }} \ libwebkit2gtk-4.1-dev:${{ matrix.arch }} \ libayatana-appindicator3-dev:${{ matrix.arch }} \ libssl-dev:${{ matrix.arch }} \ @@ -244,7 +384,6 @@ jobs: with: tag_name: alpha name: "Clash Verge Rev Alpha" - body: "More new features are now supported." prerelease: true token: ${{ secrets.GITHUB_TOKEN }} files: | @@ -252,8 +391,7 @@ jobs: src-tauri/target/${{ matrix.target }}/release/bundle/rpm/*.rpm alpha-for-fixed-webview2: - needs: check_commit - if: ${{ needs.check_commit.outputs.should_run == 'true' }} + needs: update_tag strategy: fail-fast: false matrix: @@ -338,7 +476,6 @@ jobs: with: tag_name: alpha name: "Clash Verge Rev Alpha" - body: "More new features are now supported." prerelease: true token: ${{ secrets.GITHUB_TOKEN }} files: src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*setup* @@ -347,66 +484,3 @@ jobs: run: pnpm portable-fixed-webview2 ${{ matrix.target }} --alpha env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - update_tag: - name: Update tag - runs-on: ubuntu-latest - needs: [check_commit, alpha, alpha-for-linux-arm, alpha-for-fixed-webview2] - if: ${{ needs.check_commit.outputs.should_run == 'true' }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set Env - run: | - echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV - shell: bash - - # - name: Update Tag - # uses: richardsimko/update-tag@v1 - # with: - # tag_name: alpha - # env: - # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - run: | - cat > release.txt << 'EOF' - ## 我应该下载哪个版本? - - ### MacOS - - MacOS intel芯片: x64.dmg - - MacOS apple M芯片: aarch64.dmg - - ### Linux - - Linux 64位: amd64.deb/amd64.rpm - - Linux arm64 architecture: arm64.deb/aarch64.rpm - - Linux armv7架构: armhf.deb/armhfp.rpm - - ### Windows (不再支持Win7) - #### 正常版本(推荐) - - 64位: x64-setup.exe - - arm64架构: arm64-setup.exe - #### 便携版问题很多不再提供 - #### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用) - - 64位: x64_fixed_webview2-setup.exe - - arm64架构: arm64_fixed_webview2-setup.exe - - ### FAQ - - - [常见问题](https://clash-verge-rev.github.io/faq/windows.html) - - ### 稳定机场VPN推荐 - - [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6) - - Created at ${{ env.BUILDTIME }}. - EOF - - - name: Upload Release - uses: softprops/action-gh-release@v2 - with: - tag_name: alpha - name: "Clash Verge Rev Alpha" - body_path: release.txt - prerelease: true - token: ${{ secrets.GITHUB_TOKEN }} - generate_release_notes: true diff --git a/clash-verge-rev/.github/workflows/release.yml b/clash-verge-rev/.github/workflows/release.yml index 4ad137ff44..4eacd166ee 100644 --- a/clash-verge-rev/.github/workflows/release.yml +++ b/clash-verge-rev/.github/workflows/release.yml @@ -50,7 +50,7 @@ jobs: if: matrix.os == 'ubuntu-22.04' run: | sudo apt-get update - sudo apt-get install -y libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf + sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf - name: Install Node uses: actions/setup-node@v4 @@ -153,6 +153,7 @@ jobs: sudo apt update sudo apt install -y \ + libxslt1.1:${{ matrix.arch }} \ libwebkit2gtk-4.1-dev:${{ matrix.arch }} \ libayatana-appindicator3-dev:${{ matrix.arch }} \ libssl-dev:${{ matrix.arch }} \ diff --git a/clash-verge-rev/UPDATELOG.md b/clash-verge-rev/UPDATELOG.md index 3be0e88fdc..68f5725d64 100644 --- a/clash-verge-rev/UPDATELOG.md +++ b/clash-verge-rev/UPDATELOG.md @@ -1,3 +1,90 @@ +## v2.2.1 + +**发行代号:拓** + +代号释义: 本次发布在功能上的大幅扩展。新首页设计为用户带来全新交互体验,DNS 覆写功能增强网络控制能力,解锁测试页面助力内容访问自由度提升,轻量模式提供灵活使用选择。此外,macOS 应用菜单集成、sidecar 模式、诊断信息导出等新特性进一步丰富了软件的适用场景。这些新增功能显著拓宽了 Clash Verge 的功能边界,为用户提供了更强大的工具和可能性。 + +#### 修复 +1. **首页** + - 修复 Direct 模式首页无法渲染。 + - 修复 首页启用轻量模式导致 ClashVergeRev 从托盘退出。 +2. **系统** + - 修复 MacOS 无法使用快捷键粘贴/选择/复制订阅地址。 + + +## v2.2.0 + +**发行代号:拓** + +代号释义: 本次发布在功能上的大幅扩展。新首页设计为用户带来全新交互体验,DNS 覆写功能增强网络控制能力,解锁测试页面助力内容访问自由度提升,轻量模式提供灵活使用选择。此外,macOS 应用菜单集成、sidecar 模式、诊断信息导出等新特性进一步丰富了软件的适用场景。这些新增功能显著拓宽了 Clash Verge 的功能边界,为用户提供了更强大的工具和可能性。 + +#### 新增功能 +1. **首页** + - 新增首页功能,默认启动页面改为首页。 + - 首页流量图卡片显示上传/下载名称。 + - 首页支持轻量模式切换。 + - 流量统计数据持久保存。 + - 限制首页配置文件卡片URL长度。 + +2. **DNS 设置与覆写** + - 默认启用 DNS 设置。 + - 新增 DNS 覆写功能。 + +3. **解锁测试** + - 新增解锁测试页面。 + +4. **轻量模式** + - 新增轻量模式及设置。 + - 添加自动轻量模式定时器。 + +5. **系统支持** + - macOS 支持 CMD+W 关闭窗口。 + - 新增 macOS 应用菜单。 + - 添加管理员权限提示。 + - 新增 sidecar 模式。 + +6. **其他** + - 增强延迟测试日志和错误处理。 + - 添加诊断信息导出。 + - 新增代理命令。 + +#### 修复 +1. **系统** + - 修复 Windows 热键崩溃。 + - 修复 macOS 无框标题。 + - 修复 macOS 静默启动崩溃。 + - 修复 Windows/Linux 运行时崩溃。 + - 修复 Netflix 检测错误。 + - 修复服务模式检测失败。 + +2. **性能** + - 优化小数值速度更新。 + - 增加请求超时至 60 秒。 + - 修复代理节点选择同步。 + - 优化修改verge配置性能。 + +3. **构建** + - 修复构建失败问题。 + +#### 优化 +1. **性能** + - 优化首页组件性能。 + - 优化流量图表资源使用。 + - 提升代理组列表滚动性能。 + - 加快应用退出速度。 + - 加快进入轻量模式速度。 + +2. **重构** + - 优化定时器管理。 + - 重构 MihomoManager 处理流量。 + - 优化 WebSocket 连接。 + +3. **其他** + - 更新依赖。 + - 默认 TUN 堆栈改为 gvisor。 + +--- + ## v2.1.2 **发行代号:臻** diff --git a/clash-verge-rev/package.json b/clash-verge-rev/package.json index b3b3ca074a..e73558af9a 100644 --- a/clash-verge-rev/package.json +++ b/clash-verge-rev/package.json @@ -1,6 +1,6 @@ { "name": "clash-verge", - "version": "2.1.3-alpha", + "version": "2.2.1", "license": "GPL-3.0-only", "scripts": { "dev": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev -- --profile fast-dev", diff --git a/clash-verge-rev/src-tauri/Cargo.lock b/clash-verge-rev/src-tauri/Cargo.lock index 8f29d86c2a..e44b3c80c0 100644 --- a/clash-verge-rev/src-tauri/Cargo.lock +++ b/clash-verge-rev/src-tauri/Cargo.lock @@ -1132,7 +1132,7 @@ dependencies = [ [[package]] name = "clash-verge" -version = "2.1.2" +version = "2.2.1" dependencies = [ "ab_glyph", "aes-gcm", diff --git a/clash-verge-rev/src-tauri/Cargo.toml b/clash-verge-rev/src-tauri/Cargo.toml index 39aef01b8e..2b1c18a19c 100755 --- a/clash-verge-rev/src-tauri/Cargo.toml +++ b/clash-verge-rev/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clash-verge" -version = "2.1.2" +version = "2.2.1" description = "clash verge" authors = ["zzzgydi", "wonfen", "MystiPanda"] license = "GPL-3.0-only" diff --git a/clash-verge-rev/src-tauri/src/cmd/lighteweight.rs b/clash-verge-rev/src-tauri/src/cmd/lighteweight.rs new file mode 100644 index 0000000000..ac71ad37d0 --- /dev/null +++ b/clash-verge-rev/src-tauri/src/cmd/lighteweight.rs @@ -0,0 +1,9 @@ +use crate::module::lightweight; + +use super::CmdResult; + +#[tauri::command] +pub async fn entry_lightweight_mode() -> CmdResult { + lightweight::entry_lightweight_mode(); + Ok(()) +} diff --git a/clash-verge-rev/src-tauri/src/cmd/mod.rs b/clash-verge-rev/src-tauri/src/cmd/mod.rs index 7be1663c21..6137454668 100644 --- a/clash-verge-rev/src-tauri/src/cmd/mod.rs +++ b/clash-verge-rev/src-tauri/src/cmd/mod.rs @@ -17,6 +17,7 @@ pub mod uwp; pub mod validate; pub mod verge; pub mod webdav; +pub mod lighteweight; // Re-export all command functions for backwards compatibility pub use app::*; @@ -32,3 +33,4 @@ pub use uwp::*; pub use validate::*; pub use verge::*; pub use webdav::*; +pub use lighteweight::*; diff --git a/clash-verge-rev/src-tauri/src/config/verge.rs b/clash-verge-rev/src-tauri/src/config/verge.rs index f124365bdf..26ad2368e1 100644 --- a/clash-verge-rev/src-tauri/src/config/verge.rs +++ b/clash-verge-rev/src-tauri/src/config/verge.rs @@ -189,14 +189,11 @@ pub struct IVerge { pub enable_tray_speed: Option, - /// 轻量模式 - 只保留内核运行 - pub enable_lite_mode: Option, - /// 自动进入轻量模式 - pub auto_enter_lite_mode: Option, + pub enable_auto_light_weight_mode: Option, /// 自动进入轻量模式的延迟(分钟) - pub auto_enter_lite_mode_delay: Option, + pub auto_light_weight_minutes: Option, } #[derive(Default, Debug, Clone, Deserialize, Serialize)] @@ -299,9 +296,8 @@ impl IVerge { webdav_password: None, enable_tray_speed: Some(true), enable_global_hotkey: Some(true), - enable_lite_mode: Some(false), - auto_enter_lite_mode: Some(false), - auto_enter_lite_mode_delay: Some(10), + enable_auto_light_weight_mode: Some(false), + auto_light_weight_minutes: Some(10), enable_dns_settings: Some(true), home_cards: None, ..Self::default() @@ -385,9 +381,8 @@ impl IVerge { patch!(webdav_username); patch!(webdav_password); patch!(enable_tray_speed); - patch!(enable_lite_mode); - patch!(auto_enter_lite_mode); - patch!(auto_enter_lite_mode_delay); + patch!(enable_auto_light_weight_mode); + patch!(auto_light_weight_minutes); patch!(enable_dns_settings); patch!(home_cards); } @@ -478,9 +473,8 @@ pub struct IVergeResponse { pub webdav_username: Option, pub webdav_password: Option, pub enable_tray_speed: Option, - pub enable_lite_mode: Option, - pub auto_enter_lite_mode: Option, - pub auto_enter_lite_mode_delay: Option, + pub enable_auto_light_weight_mode: Option, + pub auto_light_weight_minutes: Option, pub enable_dns_settings: Option, pub home_cards: Option, } @@ -545,9 +539,8 @@ impl From for IVergeResponse { webdav_username: verge.webdav_username, webdav_password: verge.webdav_password, enable_tray_speed: verge.enable_tray_speed, - enable_lite_mode: verge.enable_lite_mode, - auto_enter_lite_mode: verge.auto_enter_lite_mode, - auto_enter_lite_mode_delay: verge.auto_enter_lite_mode_delay, + enable_auto_light_weight_mode: verge.enable_auto_light_weight_mode, + auto_light_weight_minutes: verge.auto_light_weight_minutes, enable_dns_settings: verge.enable_dns_settings, home_cards: verge.home_cards, } diff --git a/clash-verge-rev/src-tauri/src/core/handle.rs b/clash-verge-rev/src-tauri/src/core/handle.rs index bef8c52f6a..4fabfaba86 100644 --- a/clash-verge-rev/src-tauri/src/core/handle.rs +++ b/clash-verge-rev/src-tauri/src/core/handle.rs @@ -41,18 +41,6 @@ impl Handle { window } - pub fn destroy_window(&self) -> Result<(), String> { - if let Some(window) = self.get_window() { - log_err!(window.close()); - } - if let Some(window) = Self::global().get_window() { - if let Some(webview) = window.get_webview_window("main") { - log_err!(webview.destroy()); - } - } - Ok(()) - } - pub fn refresh_clash() { if let Some(window) = Self::global().get_window() { log_err!(window.emit("verge://refresh-clash-config", "yes")); diff --git a/clash-verge-rev/src-tauri/src/core/hotkey.rs b/clash-verge-rev/src-tauri/src/core/hotkey.rs index bef1540998..f2e48f5fae 100755 --- a/clash-verge-rev/src-tauri/src/core/hotkey.rs +++ b/clash-verge-rev/src-tauri/src/core/hotkey.rs @@ -148,6 +148,8 @@ impl Hotkey { "toggle_system_proxy" => || feat::toggle_system_proxy(), "toggle_tun_mode" => || feat::toggle_tun_mode(None), "quit" => || feat::quit(Some(0)), + #[cfg(target_os = "macos")] + "hide" => || feat::hide(), _ => { println!("Invalid function: {}", func); @@ -176,15 +178,13 @@ impl Hotkey { println!("Executing function directly"); log::info!(target: "app", "Executing function directly"); - // 获取轻量模式状态和全局热键状态 - let is_lite_mode = Config::verge().latest().enable_lite_mode.unwrap_or(false); + // 获取全局热键状态 let is_enable_global_hotkey = Config::verge() .latest() .enable_global_hotkey .unwrap_or(true); - // 在轻量模式下或配置了全局热键时,始终执行热键功能 - if is_lite_mode || is_enable_global_hotkey { + if is_enable_global_hotkey { f(); } else if let Some(window) = app_handle.get_webview_window("main") { // 非轻量模式且未启用全局热键时,只在窗口可见且有焦点的情况下响应热键 diff --git a/clash-verge-rev/src-tauri/src/core/service.rs b/clash-verge-rev/src-tauri/src/core/service.rs index 8108ecc800..c58d994b86 100644 --- a/clash-verge-rev/src-tauri/src/core/service.rs +++ b/clash-verge-rev/src-tauri/src/core/service.rs @@ -297,7 +297,7 @@ pub async fn is_service_running() -> Result { let resp = check_service().await?; // 检查服务状态码和消息 - if resp.code == 200 && resp.msg == "success" && resp.data.is_some() { + if resp.code == 0 && resp.msg == "ok" && resp.data.is_some() { Ok(true) } else { Ok(false) diff --git a/clash-verge-rev/src-tauri/src/core/timer.rs b/clash-verge-rev/src-tauri/src/core/timer.rs index 080fdf42ba..9a38c69812 100644 --- a/clash-verge-rev/src-tauri/src/core/timer.rs +++ b/clash-verge-rev/src-tauri/src/core/timer.rs @@ -8,24 +8,25 @@ use std::{collections::HashMap, sync::Arc}; type TaskID = u64; #[derive(Debug, Clone)] -struct TimerTask { - task_id: TaskID, - interval_minutes: u64, - __last_run: i64, // Timestamp of last execution +pub struct TimerTask { + pub task_id: TaskID, + pub interval_minutes: u64, + #[allow(unused)] + pub last_run: i64, // Timestamp of last execution } pub struct Timer { /// cron manager - delay_timer: Arc>, + pub delay_timer: Arc>, /// save the current state - using RwLock for better read concurrency - timer_map: Arc>>, + pub timer_map: Arc>>, /// increment id - kept as mutex since it's just a counter - timer_count: Arc>, + pub timer_count: Arc>, /// Flag to mark if timer is initialized - atomic for better performance - initialized: Arc, + pub initialized: Arc, } impl Timer { @@ -139,7 +140,7 @@ impl Timer { let task = TimerTask { task_id: tid, interval_minutes: interval, - __last_run: chrono::Local::now().timestamp(), + last_run: chrono::Local::now().timestamp(), }; timer_map.insert(uid.clone(), task); @@ -161,7 +162,7 @@ impl Timer { let task = TimerTask { task_id: tid, interval_minutes: interval, - __last_run: chrono::Local::now().timestamp(), + last_run: chrono::Local::now().timestamp(), }; timer_map.insert(uid.clone(), task); diff --git a/clash-verge-rev/src-tauri/src/feat/config.rs b/clash-verge-rev/src-tauri/src/feat/config.rs index 88eb225bc8..3c01387c4f 100644 --- a/clash-verge-rev/src-tauri/src/feat/config.rs +++ b/clash-verge-rev/src-tauri/src/feat/config.rs @@ -2,7 +2,7 @@ use crate::{ config::{Config, IVerge}, core::{handle, hotkey, sysopt, tray, CoreManager}, log_err, - utils::resolve, + module::lightweight, }; use anyhow::Result; use serde_yaml::Mapping; @@ -54,6 +54,7 @@ enum UpdateFlags { SystrayMenu = 1 << 7, SystrayTooltip = 1 << 8, SystrayClickBehavior = 1 << 9, + LighteWeight = 1 << 10, } /// Patch Verge configuration @@ -68,7 +69,6 @@ pub async fn patch_verge(patch: IVerge, not_save_file: bool) -> Result<()> { let proxy_bypass = patch.system_proxy_bypass; let language = patch.language; let mixed_port = patch.verge_mixed_port; - let lite_mode = patch.enable_lite_mode; #[cfg(target_os = "macos")] let tray_icon = patch.tray_icon; #[cfg(not(target_os = "macos"))] @@ -92,7 +92,7 @@ pub async fn patch_verge(patch: IVerge, not_save_file: bool) -> Result<()> { let enable_global_hotkey = patch.enable_global_hotkey; let tray_event = patch.tray_event; let home_cards = patch.home_cards.clone(); - + let enable_auto_light_weight = patch.enable_auto_light_weight_mode; let res: std::result::Result<(), anyhow::Error> = { // Initialize with no flags set let mut update_flags: i32 = UpdateFlags::None as i32; @@ -158,6 +158,10 @@ pub async fn patch_verge(patch: IVerge, not_save_file: bool) -> Result<()> { update_flags |= UpdateFlags::SystrayClickBehavior as i32; } + if enable_auto_light_weight.is_some() { + update_flags |= UpdateFlags::LighteWeight as i32; + } + // Process updates based on flags if (update_flags & (UpdateFlags::RestartCore as i32)) != 0 { CoreManager::global().restart_core().await?; @@ -191,13 +195,11 @@ pub async fn patch_verge(patch: IVerge, not_save_file: bool) -> Result<()> { if (update_flags & (UpdateFlags::SystrayClickBehavior as i32)) != 0 { tray::Tray::global().update_click_behavior()?; } - - // Handle lite mode switch - if let Some(enable) = lite_mode { - if enable { - handle::Handle::global().destroy_window().ok(); + if (update_flags & (UpdateFlags::LighteWeight as i32)) != 0 { + if enable_auto_light_weight.unwrap() { + lightweight::enable_auto_light_weight_mode(); } else { - resolve::create_window(); + lightweight::disable_auto_light_weight_mode(); } } diff --git a/clash-verge-rev/src-tauri/src/feat/window.rs b/clash-verge-rev/src-tauri/src/feat/window.rs index 253c1f7105..3c235e14a5 100644 --- a/clash-verge-rev/src-tauri/src/feat/window.rs +++ b/clash-verge-rev/src-tauri/src/feat/window.rs @@ -1,3 +1,5 @@ +#[cfg(target_os = "macos")] +use crate::AppHandleManager; use crate::{ config::Config, core::{handle, sysopt, CoreManager}, @@ -137,3 +139,13 @@ pub fn quit(code: Option) { app_handle.exit(code.unwrap_or(0)); }); } + +#[cfg(target_os = "macos")] +pub fn hide() { + if let Some(window) = handle::Handle::global().get_window() { + if window.is_visible().unwrap_or(false) { + AppHandleManager::global().set_activation_policy_accessory(); + let _ = window.hide(); + } + } +} diff --git a/clash-verge-rev/src-tauri/src/lib.rs b/clash-verge-rev/src-tauri/src/lib.rs index 26f682b1b3..20013f245c 100644 --- a/clash-verge-rev/src-tauri/src/lib.rs +++ b/clash-verge-rev/src-tauri/src/lib.rs @@ -11,12 +11,9 @@ use crate::{ }; use config::Config; use std::sync::{Mutex, Once}; +use tauri::AppHandle; #[cfg(target_os = "macos")] use tauri::Manager; -use tauri::{ - menu::{Menu, MenuItem, Submenu}, - AppHandle, -}; use tauri_plugin_autostart::MacosLauncher; use tauri_plugin_deep_link::DeepLinkExt; @@ -215,6 +212,8 @@ pub fn run() { // media unlock checker cmd::get_unlock_items, cmd::check_media_unlock, + // light-weight model + cmd::entry_lightweight_mode, ]); #[cfg(debug_assertions)] @@ -225,18 +224,7 @@ pub fn run() { // Macos Application Menu #[cfg(target_os = "macos")] { - builder = builder.menu(|handle| { - Menu::with_items( - handle, - &[&Submenu::with_items( - handle, - "Menu", - true, - &[&MenuItem::new(handle, "Clash Verge", true, None::).unwrap()], - ) - .unwrap()], - ) - }); + // Temporary Achived due to cannot CMD+C/V/A } let app = builder @@ -248,11 +236,12 @@ pub fn run() { AppHandleManager::global().init(app_handle.clone()); #[cfg(target_os = "macos")] { - let main_window = AppHandleManager::global() + if let Some(window) = AppHandleManager::global() .get_handle() .get_webview_window("main") - .unwrap(); - let _ = main_window.set_title("Clash Verge"); + { + let _ = window.set_title("Clash Verge"); + } } } #[cfg(target_os = "macos")] @@ -283,46 +272,12 @@ pub fn run() { api.prevent_close(); let window = core::handle::Handle::global().get_window().unwrap(); let _ = window.hide(); - - // 检查是否启用了自动进入 Lite Mode - let verge = crate::config::Config::verge(); - let verge_config = verge.latest(); - let auto_enter_lite_mode = verge_config.auto_enter_lite_mode.unwrap_or(false); - - if auto_enter_lite_mode { - let delay_minutes = verge_config.auto_enter_lite_mode_delay.unwrap_or(10); - let app_handle_clone = app_handle.clone(); - println!("自动进入 Lite Mode 已启用"); - // 启动一个线程,在指定延迟后启用 Lite Mode - std::thread::spawn(move || { - println!("等待 {} 分钟后自动进入 Lite Mode", delay_minutes); - std::thread::sleep(std::time::Duration::from_secs(delay_minutes as u64 * 60)); - println!("Lite Mode 倒计时结束"); - - // 延迟后检查窗口是否仍然隐藏,如果是,则启用 Lite Mode - let window_opt = app_handle_clone.get_webview_window("main"); - if let Some(window) = window_opt { - if !window.is_visible().unwrap_or(true) { - println!("倒计时结束,正在进入 Lite Mode..."); - // 应用 Lite Mode - if let Err(e) = tauri::async_runtime::block_on(crate::feat::patch_verge( - crate::config::IVerge { - enable_lite_mode: Some(true), - ..Default::default() - }, - false - )) { - println!("Lite Mode 进入失败: {:?}", e); - } - } - } - }); - } } tauri::WindowEvent::Focused(true) => { #[cfg(target_os = "macos")] { log_err!(hotkey::Hotkey::global().register("CMD+Q", "quit")); + log_err!(hotkey::Hotkey::global().register("CMD+W", "hide")); } #[cfg(not(target_os = "macos"))] @@ -343,6 +298,7 @@ pub fn run() { #[cfg(target_os = "macos")] { log_err!(hotkey::Hotkey::global().unregister("CMD+Q")); + log_err!(hotkey::Hotkey::global().unregister("CMD+W")); } #[cfg(not(target_os = "macos"))] { @@ -362,6 +318,7 @@ pub fn run() { #[cfg(target_os = "macos")] { log_err!(hotkey::Hotkey::global().unregister("CMD+Q")); + log_err!(hotkey::Hotkey::global().unregister("CMD+W")); } #[cfg(not(target_os = "macos"))] diff --git a/clash-verge-rev/src-tauri/src/module/lightweight.rs b/clash-verge-rev/src-tauri/src/module/lightweight.rs new file mode 100644 index 0000000000..a853d611b6 --- /dev/null +++ b/clash-verge-rev/src-tauri/src/module/lightweight.rs @@ -0,0 +1,137 @@ +use anyhow::{Context, Result}; +use delay_timer::prelude::TaskBuilder; +use tauri::{Listener, Manager}; + +use crate::{ + config::Config, + core::{handle, timer::Timer}, + log_err, +}; + +const LIGHT_WEIGHT_TASK_UID: &str = "light_weight_task"; + +pub fn enable_auto_light_weight_mode() { + println!("[lightweight_mode] 开启自动轻量模式"); + log::info!(target: "app", "[lightweight_mode] 开启自动轻量模式"); + setup_window_close_listener(); + setup_webview_focus_listener(); +} + +pub fn disable_auto_light_weight_mode() { + println!("[lightweight_mode] 关闭自动轻量模式"); + log::info!(target: "app", "[lightweight_mode] 关闭自动轻量模式"); + let _ = cancel_light_weight_timer(); + cancel_window_close_listener(); +} + +pub fn entry_lightweight_mode() { + if let Some(window) = handle::Handle::global().get_window() { + if let Some(webview) = window.get_webview_window("main") { + let _ = webview.destroy(); + let _ = window.hide(); + println!("[lightweight_mode] 轻量模式已开启"); + log::info!(target: "app", "[lightweight_mode] 轻量模式已开启"); + } + } + let _ = cancel_light_weight_timer(); +} + +fn setup_window_close_listener() -> u32 { + if let Some(window) = handle::Handle::global().get_window() { + let handler = window.listen("tauri://close-requested", move |_event| { + let _ = setup_light_weight_timer(); + println!("[lightweight_mode] 监听到关闭请求,开始轻量模式计时"); + log::info!(target: "app", "[lightweight_mode] 监听到关闭请求,开始轻量模式计时"); + }); + return handler; + } + 0 +} + +fn setup_webview_focus_listener() -> u32 { + if let Some(window) = handle::Handle::global().get_window() { + let handler = window.listen("tauri://focus", move |_event| { + log_err!(cancel_light_weight_timer()); + println!("[lightweight_mode] 监听到窗口获得焦点,取消轻量模式计时"); + log::info!(target: "app", "[lightweight_mode] 监听到窗口获得焦点,取消轻量模式计时"); + }); + return handler; + } + 0 +} + +fn cancel_window_close_listener() { + if let Some(window) = handle::Handle::global().get_window() { + window.unlisten(setup_window_close_listener()); + println!("[lightweight_mode] 取消了窗口关闭监听"); + log::info!(target: "app", "[lightweight_mode] 取消了窗口关闭监听"); + } +} + +fn setup_light_weight_timer() -> Result<()> { + Timer::global().init()?; + + let mut timer_map = Timer::global().timer_map.write(); + let delay_timer = Timer::global().delay_timer.write(); + let mut timer_count = Timer::global().timer_count.lock(); + + let task_id = *timer_count; + *timer_count += 1; + + let once_by_minutes = Config::verge() + .latest() + .auto_light_weight_minutes + .clone() + .unwrap_or(10); + + let task = TaskBuilder::default() + .set_task_id(task_id) + .set_maximum_parallel_runnable_num(1) + .set_frequency_once_by_minutes(once_by_minutes) + .spawn_async_routine(move || async move { + println!("[lightweight_mode] 计时器到期,开始进入轻量模式"); + log::info!(target: "app", + "[lightweight_mode] 计时器到期,开始进入轻量模式" + ); + entry_lightweight_mode(); + }) + .context("failed to create light weight timer task")?; + + delay_timer + .add_task(task) + .context("failed to add light weight timer task")?; + + let timer_task = crate::core::timer::TimerTask { + task_id, + interval_minutes: once_by_minutes, + last_run: chrono::Local::now().timestamp(), + }; + + timer_map.insert(LIGHT_WEIGHT_TASK_UID.to_string(), timer_task); + + println!( + "[lightweight_mode] 轻量模式计时器已设置,{} 分钟后将自动进入轻量模式", + once_by_minutes + ); + log::info!(target: "app", + "[lightweight_mode] 轻量模式计时器已设置,{} 分钟后将自动进入轻量模式", + once_by_minutes + ); + + Ok(()) +} + +fn cancel_light_weight_timer() -> Result<()> { + let mut timer_map = Timer::global().timer_map.write(); + let delay_timer = Timer::global().delay_timer.write(); + + if let Some(task) = timer_map.remove(LIGHT_WEIGHT_TASK_UID) { + delay_timer + .remove_task(task.task_id) + .context("failed to remove light weight timer task")?; + println!("[lightweight_mode] 轻量模式计时器已取消"); + log::info!(target: "app", "[lightweight_mode] 轻量模式计时器已取消"); + } + + Ok(()) +} diff --git a/clash-verge-rev/src-tauri/src/module/mod.rs b/clash-verge-rev/src-tauri/src/module/mod.rs index c07cdce01f..d122a47b1e 100644 --- a/clash-verge-rev/src-tauri/src/module/mod.rs +++ b/clash-verge-rev/src-tauri/src/module/mod.rs @@ -1,2 +1,3 @@ pub mod mihomo; pub mod sysinfo; +pub mod lightweight; \ No newline at end of file diff --git a/clash-verge-rev/src-tauri/src/module/sysinfo.rs b/clash-verge-rev/src-tauri/src/module/sysinfo.rs index d22132b08e..6cf4913eaf 100644 --- a/clash-verge-rev/src-tauri/src/module/sysinfo.rs +++ b/clash-verge-rev/src-tauri/src/module/sysinfo.rs @@ -26,7 +26,7 @@ impl PlatformSpecification { let system_name = System::name().unwrap_or("Null".into()); let system_version = System::long_os_version().unwrap_or("Null".into()); let system_kernel_version = System::kernel_version().unwrap_or("Null".into()); - let system_arch = std::env::consts::ARCH.to_string(); + let system_arch = System::cpu_arch(); let handler = handle::Handle::global().app_handle().unwrap(); let config = handler.config(); diff --git a/clash-verge-rev/src-tauri/src/utils/resolve.rs b/clash-verge-rev/src-tauri/src/utils/resolve.rs index 97124f0ac3..80edfaa563 100644 --- a/clash-verge-rev/src-tauri/src/utils/resolve.rs +++ b/clash-verge-rev/src-tauri/src/utils/resolve.rs @@ -1,11 +1,7 @@ #[cfg(target_os = "macos")] use crate::AppHandleManager; use crate::{ - config::{Config, IVerge, PrfItem}, - core::*, - log_err, - utils::{error, init, server}, - wrap_err, + config::{Config, IVerge, PrfItem}, core::*, log_err, module::lightweight, utils::{error, init, server}, wrap_err }; use anyhow::{bail, Result}; use once_cell::sync::OnceCell; @@ -115,6 +111,11 @@ pub async fn resolve_setup(app: &mut App) { log_err!(tray::Tray::global().update_part()); log_err!(timer::Timer::global().init()); + + let enable_auto_light_weight_mode = { Config::verge().data().enable_auto_light_weight_mode }; + if enable_auto_light_weight_mode.unwrap_or(false) { + lightweight::enable_auto_light_weight_mode(); + } } /// reset system proxy diff --git a/clash-verge-rev/src-tauri/tauri.conf.json b/clash-verge-rev/src-tauri/tauri.conf.json index bd039c3221..39584e21dc 100755 --- a/clash-verge-rev/src-tauri/tauri.conf.json +++ b/clash-verge-rev/src-tauri/tauri.conf.json @@ -1,5 +1,5 @@ { - "version": "2.1.3-alpha", + "version": "2.2.1", "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "bundle": { "active": true, diff --git a/clash-verge-rev/src/components/home/current-proxy-card.tsx b/clash-verge-rev/src/components/home/current-proxy-card.tsx index c65282523a..93a45c2729 100644 --- a/clash-verge-rev/src/components/home/current-proxy-card.tsx +++ b/clash-verge-rev/src/components/home/current-proxy-card.tsx @@ -53,11 +53,16 @@ function convertDelayColor(delayValue: number) { const mainColor = colorStr.split(".")[0]; switch (mainColor) { - case "success": return "success"; - case "warning": return "warning"; - case "error": return "error"; - case "primary": return "primary"; - default: return "default"; + case "success": + return "success"; + case "warning": + return "warning"; + case "error": + return "error"; + case "primary": + return "primary"; + default: + return "default"; } } @@ -79,7 +84,7 @@ function getSignalIcon(delay: number) { // 简单的防抖函数 function debounce(fn: Function, ms = 100) { let timeoutId: ReturnType; - return function(this: any, ...args: any[]) { + return function (this: any, ...args: any[]) { clearTimeout(timeoutId); timeoutId = setTimeout(() => fn.apply(this, args), ms); }; @@ -87,7 +92,8 @@ function debounce(fn: Function, ms = 100) { export const CurrentProxyCard = () => { const { t } = useTranslation(); - const { currentProxy, primaryGroupName, mode, refreshProxy } = useCurrentProxy(); + const { currentProxy, primaryGroupName, mode, refreshProxy } = + useCurrentProxy(); const navigate = useNavigate(); const theme = useTheme(); const { verge } = useVerge(); @@ -135,133 +141,141 @@ export const CurrentProxyCard = () => { useEffect(() => { // 根据模式确定初始组 if (isGlobalMode) { - setState(prev => ({ + setState((prev) => ({ ...prev, selection: { ...prev.selection, - group: "GLOBAL" - } + group: "GLOBAL", + }, })); } else if (isDirectMode) { - setState(prev => ({ + setState((prev) => ({ ...prev, selection: { ...prev.selection, - group: "DIRECT" - } + group: "DIRECT", + }, })); } else { const savedGroup = localStorage.getItem(STORAGE_KEY_GROUP); - setState(prev => ({ + setState((prev) => ({ ...prev, selection: { ...prev.selection, - group: savedGroup || primaryGroupName || "" - } + group: savedGroup || primaryGroupName || "", + }, })); } }, [isGlobalMode, isDirectMode, primaryGroupName]); // 带锁的代理数据获取函数,防止并发请求 - const fetchProxyData = useCallback(async (force = false) => { - // 防止重复请求 - if (isRefreshingRef.current) { - pendingRefreshRef.current = true; - return; - } - - // 检查刷新间隔 - const now = Date.now(); - if (!force && now - lastRefreshRef.current < 1000) { - return; - } - - isRefreshingRef.current = true; - lastRefreshRef.current = now; - - try { - const data = await getProxies(); - - // 过滤和格式化组 - const filteredGroups = data.groups - .filter(g => g.name !== "DIRECT" && g.name !== "REJECT") - .map(g => ({ - name: g.name, - now: g.now || "", - all: g.all.map(p => p.name), - })); - - // 使用函数式更新确保状态更新的原子性 - setState(prev => { - let newProxy = ""; - let newDisplayProxy = null; - let newGroup = prev.selection.group; - - // 根据模式确定新代理 - if (isDirectMode) { - newGroup = "DIRECT"; - newProxy = "DIRECT"; - newDisplayProxy = data.records?.DIRECT || null; - } else if (isGlobalMode && data.global) { - newGroup = "GLOBAL"; - newProxy = data.global.now || ""; - newDisplayProxy = data.records?.[newProxy] || null; - } else { - // 普通模式 - 检查当前选择的组是否存在 - const currentGroup = filteredGroups.find(g => g.name === prev.selection.group); - - // 如果当前组不存在或为空,自动选择第一个组 - if (!currentGroup && filteredGroups.length > 0) { - newGroup = filteredGroups[0].name; - const firstGroup = filteredGroups[0]; - newProxy = firstGroup.now; - newDisplayProxy = data.records?.[newProxy] || null; - - // 保存到本地存储 - if (!isGlobalMode && !isDirectMode) { - localStorage.setItem(STORAGE_KEY_GROUP, newGroup); - if (newProxy) { - localStorage.setItem(STORAGE_KEY_PROXY, newProxy); - } - } - } else if (currentGroup) { - // 使用当前组的代理 - newProxy = currentGroup.now; - newDisplayProxy = data.records?.[newProxy] || null; - } - } - - // 返回新状态 - return { - proxyData: { - groups: filteredGroups, - records: data.records || {}, - globalProxy: data.global?.now || "", - directProxy: data.records?.DIRECT || null, - }, - selection: { - group: newGroup, - proxy: newProxy - }, - displayProxy: newDisplayProxy - }; - }); - } catch (error) { - console.error("获取代理信息失败", error); - } finally { - isRefreshingRef.current = false; - - // 处理待处理的刷新请求 - if (pendingRefreshRef.current) { - pendingRefreshRef.current = false; - setTimeout(() => fetchProxyData(), 100); + const fetchProxyData = useCallback( + async (force = false) => { + // 防止重复请求 + if (isRefreshingRef.current) { + pendingRefreshRef.current = true; + return; } - } - }, [isGlobalMode, isDirectMode]); + + // 检查刷新间隔 + const now = Date.now(); + if (!force && now - lastRefreshRef.current < 1000) { + return; + } + + isRefreshingRef.current = true; + lastRefreshRef.current = now; + + try { + const data = await getProxies(); + + // 过滤和格式化组 + const filteredGroups = data.groups + .filter((g) => g.name !== "DIRECT" && g.name !== "REJECT") + .map((g) => ({ + name: g.name, + now: g.now || "", + all: g.all.map((p) => p.name), + })); + + // 使用函数式更新确保状态更新的原子性 + setState((prev) => { + let newProxy = ""; + let newDisplayProxy = null; + let newGroup = prev.selection.group; + + // 根据模式确定新代理 + if (isDirectMode) { + newGroup = "DIRECT"; + newProxy = "DIRECT"; + newDisplayProxy = data.records?.DIRECT || null; + } else if (isGlobalMode && data.global) { + newGroup = "GLOBAL"; + newProxy = data.global.now || ""; + newDisplayProxy = data.records?.[newProxy] || null; + } else { + // 普通模式 - 检查当前选择的组是否存在 + const currentGroup = filteredGroups.find( + (g) => g.name === prev.selection.group, + ); + + // 如果当前组不存在或为空,自动选择第一个组 + if (!currentGroup && filteredGroups.length > 0) { + newGroup = filteredGroups[0].name; + const firstGroup = filteredGroups[0]; + newProxy = firstGroup.now; + newDisplayProxy = data.records?.[newProxy] || null; + + // 保存到本地存储 + if (!isGlobalMode && !isDirectMode) { + localStorage.setItem(STORAGE_KEY_GROUP, newGroup); + if (newProxy) { + localStorage.setItem(STORAGE_KEY_PROXY, newProxy); + } + } + } else if (currentGroup) { + // 使用当前组的代理 + newProxy = currentGroup.now; + newDisplayProxy = data.records?.[newProxy] || null; + } + } + + // 返回新状态 + return { + proxyData: { + groups: filteredGroups, + records: data.records || {}, + globalProxy: data.global?.now || "", + directProxy: data.records?.DIRECT || null, + }, + selection: { + group: newGroup, + proxy: newProxy, + }, + displayProxy: newDisplayProxy, + }; + }); + } catch (error) { + console.error("获取代理信息失败", error); + } finally { + isRefreshingRef.current = false; + + // 处理待处理的刷新请求 + if (pendingRefreshRef.current) { + pendingRefreshRef.current = false; + setTimeout(() => fetchProxyData(), 100); + } + } + }, + [isGlobalMode, isDirectMode], + ); // 响应 currentProxy 变化 useEffect(() => { - if (currentProxy && (!state.displayProxy || currentProxy.name !== state.displayProxy.name)) { + if ( + currentProxy && + (!state.displayProxy || currentProxy.name !== state.displayProxy.name) + ) { fetchProxyData(true); } }, [currentProxy, fetchProxyData, state.displayProxy]); @@ -269,11 +283,11 @@ export const CurrentProxyCard = () => { // 平滑的定期刷新,使用固定间隔 useEffect(() => { fetchProxyData(); - + const intervalId = setInterval(() => { fetchProxyData(); }, 3000); // 使用固定的3秒间隔,平衡响应速度和性能 - + return () => clearInterval(intervalId); }, [fetchProxyData]); @@ -285,14 +299,16 @@ export const CurrentProxyCard = () => { if (isGlobalMode && state.proxyData.records) { // 全局模式下的选项 return Object.keys(state.proxyData.records) - .filter(name => name !== "DIRECT" && name !== "REJECT") - .map(name => ({ name })); + .filter((name) => name !== "DIRECT" && name !== "REJECT") + .map((name) => ({ name })); } - + // 普通模式 - const group = state.proxyData.groups.find(g => g.name === state.selection.group); + const group = state.proxyData.groups.find( + (g) => g.name === state.selection.group, + ); if (group) { - return group.all.map(name => ({ name })); + return group.all.map((name) => ({ name })); } return []; }, [isDirectMode, isGlobalMode, state.proxyData, state.selection.group]); @@ -302,88 +318,103 @@ export const CurrentProxyCard = () => { debounce((updateFn: (prev: ProxyState) => ProxyState) => { setState(updateFn); }, 50), - [] + [], ); // 处理代理组变更 - const handleGroupChange = useCallback((event: SelectChangeEvent) => { - if (isGlobalMode || isDirectMode) return; - - const newGroup = event.target.value; - - // 保存到本地存储 - localStorage.setItem(STORAGE_KEY_GROUP, newGroup); - - // 获取该组当前选中的代理 - setState(prev => { - const group = prev.proxyData.groups.find(g => g.name === newGroup); - if (group) { + const handleGroupChange = useCallback( + (event: SelectChangeEvent) => { + if (isGlobalMode || isDirectMode) return; + + const newGroup = event.target.value; + + // 保存到本地存储 + localStorage.setItem(STORAGE_KEY_GROUP, newGroup); + + // 获取该组当前选中的代理 + setState((prev) => { + const group = prev.proxyData.groups.find((g) => g.name === newGroup); + if (group) { + return { + ...prev, + selection: { + group: newGroup, + proxy: group.now, + }, + displayProxy: prev.proxyData.records[group.now] || null, + }; + } return { ...prev, selection: { + ...prev.selection, group: newGroup, - proxy: group.now }, - displayProxy: prev.proxyData.records[group.now] || null }; - } - return { + }); + }, + [isGlobalMode, isDirectMode], + ); + + // 处理代理节点变更 + const handleProxyChange = useCallback( + async (event: SelectChangeEvent) => { + if (isDirectMode) return; + + const newProxy = event.target.value; + const currentGroup = state.selection.group; + const previousProxy = state.selection.proxy; + + // 立即更新UI,优化体验 + debouncedSetState((prev: ProxyState) => ({ ...prev, selection: { ...prev.selection, - group: newGroup - } - }; - }); - }, [isGlobalMode, isDirectMode]); + proxy: newProxy, + }, + displayProxy: prev.proxyData.records[newProxy] || null, + })); - // 处理代理节点变更 - const handleProxyChange = useCallback(async (event: SelectChangeEvent) => { - if (isDirectMode) return; - - const newProxy = event.target.value; - const currentGroup = state.selection.group; - const previousProxy = state.selection.proxy; - - // 立即更新UI,优化体验 - debouncedSetState((prev: ProxyState) => ({ - ...prev, - selection: { - ...prev.selection, - proxy: newProxy - }, - displayProxy: prev.proxyData.records[newProxy] || null - })); - - // 非特殊模式下保存到本地存储 - if (!isGlobalMode && !isDirectMode) { - localStorage.setItem(STORAGE_KEY_PROXY, newProxy); - } - - try { - // 更新代理设置 - await updateProxy(currentGroup, newProxy); - - // 自动关闭连接设置 - if (verge?.auto_close_connection && previousProxy) { - getConnections().then(({ connections }) => { - connections.forEach(conn => { - if (conn.chains.includes(previousProxy)) { - deleteConnection(conn.id); - } - }); - }); + // 非特殊模式下保存到本地存储 + if (!isGlobalMode && !isDirectMode) { + localStorage.setItem(STORAGE_KEY_PROXY, newProxy); } - - // 刷新代理信息,使用较短的延迟 - setTimeout(() => { - refreshProxy(); - fetchProxyData(true); - }, 200); - } catch (error) { - console.error("更新代理失败", error); - } - }, [isDirectMode, isGlobalMode, state.proxyData.records, state.selection, verge?.auto_close_connection, refreshProxy, fetchProxyData, debouncedSetState]); + + try { + // 更新代理设置 + await updateProxy(currentGroup, newProxy); + + // 自动关闭连接设置 + if (verge?.auto_close_connection && previousProxy) { + getConnections().then(({ connections }) => { + connections.forEach((conn) => { + if (conn.chains.includes(previousProxy)) { + deleteConnection(conn.id); + } + }); + }); + } + + // 刷新代理信息,使用较短的延迟 + setTimeout(() => { + refreshProxy(); + fetchProxyData(true); + }, 200); + } catch (error) { + console.error("更新代理失败", error); + } + }, + [ + isDirectMode, + isGlobalMode, + state.proxyData.records, + state.selection, + verge?.auto_close_connection, + refreshProxy, + fetchProxyData, + debouncedSetState, + ], + ); // 导航到代理页面 const goToProxies = useCallback(() => { @@ -392,35 +423,38 @@ export const CurrentProxyCard = () => { // 获取要显示的代理节点 const proxyToDisplay = state.displayProxy || currentProxy; - + // 获取当前节点的延迟 const currentDelay = proxyToDisplay ? delayManager.getDelayFix(proxyToDisplay, state.selection.group) : -1; - + // 获取信号图标 const signalInfo = getSignalIcon(currentDelay); // 自定义渲染选择框中的值 - const renderProxyValue = useCallback((selected: string) => { - if (!selected || !state.proxyData.records[selected]) return selected; + const renderProxyValue = useCallback( + (selected: string) => { + if (!selected || !state.proxyData.records[selected]) return selected; - const delayValue = delayManager.getDelayFix( - state.proxyData.records[selected], - state.selection.group - ); + const delayValue = delayManager.getDelayFix( + state.proxyData.records[selected], + state.selection.group, + ); - return ( - - {selected} - - - ); - }, [state.proxyData.records, state.selection.group]); + return ( + + {selected} + + + ); + }, + [state.proxyData.records, state.selection.group], + ); return ( { {proxyToDisplay.name} - - + + {proxyToDisplay.type} {isGlobalMode && ( - + )} {isDirectMode && ( - + )} {/* 节点特性 */} - {proxyToDisplay.udp && } - {proxyToDisplay.tfo && } - {proxyToDisplay.xudp && } - {proxyToDisplay.mptcp && } - {proxyToDisplay.smux && } + {proxyToDisplay.udp && ( + + )} + {proxyToDisplay.tfo && ( + + )} + {proxyToDisplay.xudp && ( + + )} + {proxyToDisplay.mptcp && ( + + )} + {proxyToDisplay.smux && ( + + )} @@ -500,7 +560,12 @@ export const CurrentProxyCard = () => { )} {/* 代理组选择器 */} - + {t("Group")} diff --git a/clash-verge-rev/src/components/setting/mods/lite-mode-viewer.tsx b/clash-verge-rev/src/components/setting/mods/lite-mode-viewer.tsx index 760c240f76..a1e20a5205 100644 --- a/clash-verge-rev/src/components/setting/mods/lite-mode-viewer.tsx +++ b/clash-verge-rev/src/components/setting/mods/lite-mode-viewer.tsx @@ -12,6 +12,7 @@ import { import { useVerge } from "@/hooks/use-verge"; import { BaseDialog, DialogRef, Notice, Switch } from "@/components/base"; import { TooltipIcon } from "@/components/base/base-tooltip-icon"; +import { entry_lightweight_mode } from "@/services/cmds"; export const LiteModeViewer = forwardRef((props, ref) => { const { t } = useTranslation(); @@ -27,27 +28,18 @@ export const LiteModeViewer = forwardRef((props, ref) => { open: () => { setOpen(true); setValues({ - autoEnterLiteMode: verge?.auto_enter_lite_mode ?? false, - autoEnterLiteModeDelay: verge?.auto_enter_lite_mode_delay ?? 10, + autoEnterLiteMode: verge?.enable_auto_light_weight_mode ?? false, + autoEnterLiteModeDelay: verge?.auto_light_weight_minutes ?? 10, }); }, close: () => setOpen(false), })); - const onEnterLiteMode = useLockFn(async () => { - try { - await patchVerge({ enable_lite_mode: true }); - setOpen(false); - } catch (err: any) { - Notice.error(err.message || err.toString()); - } - }); - const onSave = useLockFn(async () => { try { await patchVerge({ - auto_enter_lite_mode: values.autoEnterLiteMode, - auto_enter_lite_mode_delay: values.autoEnterLiteModeDelay, + enable_auto_light_weight_mode: values.autoEnterLiteMode, + auto_light_weight_minutes: values.autoEnterLiteModeDelay, }); setOpen(false); } catch (err: any) { @@ -58,7 +50,7 @@ export const LiteModeViewer = forwardRef((props, ref) => { return ( ((props, ref) => { > - - + await entry_lightweight_mode()} > {t("Enable")} @@ -84,11 +76,11 @@ export const LiteModeViewer = forwardRef((props, ref) => { ((props, ref) => { {values.autoEnterLiteMode && ( <> - + ((props, ref) => { slotProps={{ input: { endAdornment: ( - {t("mins")} - ) - } + + {t("mins")} + + ), + }, }} /> - + - - {t("When closing the window, Lite Mode will be automatically activated after _n minutes", - { n: values.autoEnterLiteModeDelay })} + + {t( + "When closing the window, LightWeight Mode will be automatically activated after _n minutes", + { n: values.autoEnterLiteModeDelay }, + )} @@ -141,4 +141,4 @@ export const LiteModeViewer = forwardRef((props, ref) => { ); -}); \ No newline at end of file +}); diff --git a/clash-verge-rev/src/components/setting/setting-verge-advanced.tsx b/clash-verge-rev/src/components/setting/setting-verge-advanced.tsx index af2bbf53cc..aee48d76bd 100644 --- a/clash-verge-rev/src/components/setting/setting-verge-advanced.tsx +++ b/clash-verge-rev/src/components/setting/setting-verge-advanced.tsx @@ -107,9 +107,9 @@ const SettingVergeAdvanced = ({ onError }: Props) => { + } onClick={() => liteModeRef.current?.open()} /> diff --git a/clash-verge-rev/src/locales/ar.json b/clash-verge-rev/src/locales/ar.json index 581dfec4e3..06543dd7c2 100644 --- a/clash-verge-rev/src/locales/ar.json +++ b/clash-verge-rev/src/locales/ar.json @@ -436,8 +436,8 @@ "Global Mode": "الوضع العالمي", "Direct Mode": "الوضع المباشر", "Enable Tray Speed": "تفعيل سرعة التراي", - "Lite Mode": "وضع الأداء الخفيف", - "Lite Mode Info": "إيقاف الواجهة الرسومية والإبقاء على تشغيل النواة", + "LightWeight Mode": "وضع الأداء الخفيف", + "LightWeight Mode Info": "إيقاف الواجهة الرسومية والإبقاء على تشغيل النواة", "Config Validation Failed": "فشل التحقق من تكوين الاشتراك، يرجى فحص ملف التكوين، تم التراجع عن التغييرات، تفاصيل الخطأ:", "Boot Config Validation Failed": "فشل التحقق من التكوين عند الإقلاع، تم استخدام التكوين الافتراضي، يرجى فحص ملف التكوين، تفاصيل الخطأ:", "Core Change Config Validation Failed": "فشل التحقق من التكوين عند تغيير النواة، تم استخدام التكوين الافتراضي، يرجى فحص ملف التكوين، تفاصيل الخطأ:", diff --git a/clash-verge-rev/src/locales/en.json b/clash-verge-rev/src/locales/en.json index 369270c353..6c49e237d3 100644 --- a/clash-verge-rev/src/locales/en.json +++ b/clash-verge-rev/src/locales/en.json @@ -451,14 +451,14 @@ "Global Mode": "Global Mode", "Direct Mode": "Direct Mode", "Enable Tray Speed": "Enable Tray Speed", - "Lite Mode": "Lightweight Mode", - "Lite Mode Info": "Close the GUI and keep only the kernel running", - "Lite Mode Settings": "Lite Mode Settings", - "Enter Lite Mode Now": "Enter Lite Mode Now", - "Auto Enter Lite Mode": "Auto Enter Lite Mode", - "Auto Enter Lite Mode Info": "Enable to automatically activate Lite Mode after the window is closed for a period of time", - "Auto Enter Lite Mode Delay": "Auto Enter Lite Mode Delay", - "When closing the window, Lite Mode will be automatically activated after _n minutes": "When closing the window, Lite Mode will be automatically activated after {{n}} minutes", + "LightWeight Mode": "Lightweight Mode", + "LightWeight Mode Info": "Close the GUI and keep only the kernel running", + "LightWeight Mode Settings": "LightWeight Mode Settings", + "Enter LightWeight Mode Now": "Enter LightWeight Mode Now", + "Auto Enter LightWeight Mode": "Auto Enter LightWeight Mode", + "Auto Enter LightWeight Mode Info": "Enable to automatically activate LightWeight Mode after the window is closed for a period of time", + "Auto Enter LightWeight Mode Delay": "Auto Enter LightWeight Mode Delay", + "When closing the window, LightWeight Mode will be automatically activated after _n minutes": "When closing the window, LightWeight Mode will be automatically activated after {{n}} minutes", "Config Validation Failed": "Subscription configuration validation failed. Please check the subscription configuration file; modifications have been rolled back.", "Boot Config Validation Failed": "Boot subscription configuration validation failed. Started with the default configuration; please check the subscription configuration file.", "Core Change Config Validation Failed": "Configuration validation failed when switching the kernel. Started with the default configuration; please check the subscription configuration file.", diff --git a/clash-verge-rev/src/locales/fa.json b/clash-verge-rev/src/locales/fa.json index efc2fa1ef7..04b7d24133 100644 --- a/clash-verge-rev/src/locales/fa.json +++ b/clash-verge-rev/src/locales/fa.json @@ -433,8 +433,8 @@ "Global Mode": "حالت جهانی", "Direct Mode": "حالت مستقیم", "Enable Tray Speed": "فعال کردن سرعت ترای", - "Lite Mode": "در فارسی", - "Lite Mode Info": "رابط کاربری گرافیکی را ببندید و فقط هسته را در حال اجرا نگه دارید", + "LightWeight Mode": "در فارسی", + "LightWeight Mode Info": "رابط کاربری گرافیکی را ببندید و فقط هسته را در حال اجرا نگه دارید", "Config Validation Failed": "اعتبارسنجی پیکربندی اشتراک ناموفق بود، فایل پیکربندی را بررسی کنید، تغییرات برگشت داده شد، جزئیات خطا:", "Boot Config Validation Failed": "اعتبارسنجی پیکربندی هنگام راه‌اندازی ناموفق بود، پیکربندی پیش‌فرض استفاده شد، فایل پیکربندی را بررسی کنید، جزئیات خطا:", "Core Change Config Validation Failed": "اعتبارسنجی پیکربندی هنگام تغییر هسته ناموفق بود، پیکربندی پیش‌فرض استفاده شد، فایل پیکربندی را بررسی کنید، جزئیات خطا:", diff --git a/clash-verge-rev/src/locales/id.json b/clash-verge-rev/src/locales/id.json index 4b0400a397..747ec6ed4f 100644 --- a/clash-verge-rev/src/locales/id.json +++ b/clash-verge-rev/src/locales/id.json @@ -432,8 +432,8 @@ "Restore Success, App will restart in 1s": "Pemulihan Berhasil, Aplikasi akan dimulai ulang dalam 1 detik", "Failed to fetch backup files": "Gagal mengambil file cadangan", "Enable Tray Speed": "Aktifkan Tray Speed", - "Lite Mode": "Mode Ringan", - "Lite Mode Info": "Tutup GUI dan biarkan hanya kernel yang berjalan", + "LightWeight Mode": "Mode Ringan", + "LightWeight Mode Info": "Tutup GUI dan biarkan hanya kernel yang berjalan", "Config Validation Failed": "Validasi konfigurasi langganan gagal, periksa file konfigurasi, perubahan dibatalkan, detail kesalahan:", "Boot Config Validation Failed": "Validasi konfigurasi saat boot gagal, menggunakan konfigurasi default, periksa file konfigurasi, detail kesalahan:", "Core Change Config Validation Failed": "Validasi konfigurasi saat ganti inti gagal, menggunakan konfigurasi default, periksa file konfigurasi, detail kesalahan:", diff --git a/clash-verge-rev/src/locales/ru.json b/clash-verge-rev/src/locales/ru.json index 647773d539..672b5f9994 100644 --- a/clash-verge-rev/src/locales/ru.json +++ b/clash-verge-rev/src/locales/ru.json @@ -433,8 +433,8 @@ "Global Mode": "Глобальный режим", "Direct Mode": "Прямой режим", "Enable Tray Speed": "Включить скорость в лотке", - "Lite Mode": "Облегченный режим", - "Lite Mode Info": "Закройте графический интерфейс и оставьте работать только ядро", + "LightWeight Mode": "Облегченный режим", + "LightWeight Mode Info": "Закройте графический интерфейс и оставьте работать только ядро", "Config Validation Failed": "Ошибка проверки конфигурации подписки, проверьте файл конфигурации, изменения отменены, ошибка:", "Boot Config Validation Failed": "Ошибка проверки конфигурации при запуске, используется конфигурация по умолчанию, проверьте файл конфигурации, ошибка:", "Core Change Config Validation Failed": "Ошибка проверки конфигурации при смене ядра, используется конфигурация по умолчанию, проверьте файл конфигурации, ошибка:", diff --git a/clash-verge-rev/src/locales/tt.json b/clash-verge-rev/src/locales/tt.json index 07d0352ba1..3cd99f3665 100644 --- a/clash-verge-rev/src/locales/tt.json +++ b/clash-verge-rev/src/locales/tt.json @@ -432,8 +432,8 @@ "Global Mode": "Глобаль режим", "Direct Mode": "Туры режим", "Enable Tray Speed": "Трей скоростьне үстерү", - "Lite Mode": "Җиңел Режим", - "Lite Mode Info": "GUI-ны ябыгыз һәм бары тик төшне генә эшләтеп калдырыгыз", + "LightWeight Mode": "Җиңел Режим", + "LightWeight Mode Info": "GUI-ны ябыгыз һәм бары тик төшне генә эшләтеп калдырыгыз", "Config Validation Failed": "Язылу көйләү тикшерүе уңышсыз, көйләү файлын тикшерегез, үзгәрешләр кире кайтарылды, хата:", "Boot Config Validation Failed": "Йөкләү вакытында көйләү тикшерүе уңышсыз, стандарт көйләү кулланылды, көйләү файлын тикшерегез, хата:", "Core Change Config Validation Failed": "Ядро алыштырганда көйләү тикшерүе уңышсыз, стандарт көйләү кулланылды, көйләү файлын тикшерегез, хата:", diff --git a/clash-verge-rev/src/locales/zh.json b/clash-verge-rev/src/locales/zh.json index 3f0d53f57f..ca05ddefa5 100644 --- a/clash-verge-rev/src/locales/zh.json +++ b/clash-verge-rev/src/locales/zh.json @@ -451,14 +451,14 @@ "Global Mode": "全局模式", "Direct Mode": "直连模式", "Enable Tray Speed": "启用托盘速率", - "Lite Mode": "轻量模式", - "Lite Mode Info": "关闭GUI界面,仅保留内核运行", - "Lite Mode Settings": "轻量模式设置", - "Enter Lite Mode Now": "立即进入轻量模式", - "Auto Enter Lite Mode": "自动进入轻量模式", - "Auto Enter Lite Mode Info": "启用后,将在窗口关闭一段时间后自动激活轻量模式", - "Auto Enter Lite Mode Delay": "自动进入轻量模式延迟", - "When closing the window, Lite Mode will be automatically activated after _n minutes": "关闭窗口后,轻量模式将在 {{n}} 分钟后自动激活", + "LightWeight Mode": "轻量模式", + "LightWeight Mode Info": "关闭GUI界面,仅保留内核运行", + "LightWeight Mode Settings": "轻量模式设置", + "Enter LightWeight Mode Now": "立即进入轻量模式", + "Auto Enter LightWeight Mode": "自动进入轻量模式", + "Auto Enter LightWeight Mode Info": "启用后,将在窗口关闭一段时间后自动激活轻量模式", + "Auto Enter LightWeight Mode Delay": "自动进入轻量模式延迟", + "When closing the window, LightWeight Mode will be automatically activated after _n minutes": "关闭窗口后,轻量模式将在 {{n}} 分钟后自动激活", "Config Validation Failed": "订阅配置校验失败,请检查订阅配置文件,变更已撤销,错误详情:", "Boot Config Validation Failed": "启动订阅配置校验失败,已使用默认配置启动;请检查订阅配置文件,错误详情:", "Core Change Config Validation Failed": "切换内核时配置校验失败,已使用默认配置启动;请检查订阅配置文件,错误详情:", diff --git a/clash-verge-rev/src/pages/home.tsx b/clash-verge-rev/src/pages/home.tsx index 272abeffd9..fff5b89dea 100644 --- a/clash-verge-rev/src/pages/home.tsx +++ b/clash-verge-rev/src/pages/home.tsx @@ -37,7 +37,11 @@ import { BasePage } from "@/components/base"; import { ClashInfoCard } from "@/components/home/clash-info-card"; import { SystemInfoCard } from "@/components/home/system-info-card"; import { useLockFn } from "ahooks"; -import { openWebUrl, patchVergeConfig } from "@/services/cmds"; +import { + entry_lightweight_mode, + openWebUrl, + patchVergeConfig, +} from "@/services/cmds"; import { TestCard } from "@/components/home/test-card"; import { IpInfoCard } from "@/components/home/ip-info-card"; @@ -259,8 +263,12 @@ const HomePage = () => { contentStyle={{ padding: 2 }} header={ - - patchVergeConfig({ enable_lite_mode: true })} size="small" color="inherit"> + + await entry_lightweight_mode()} + size="small" + color="inherit" + > diff --git a/clash-verge-rev/src/services/cmds.ts b/clash-verge-rev/src/services/cmds.ts index 61618ae988..9c72561461 100644 --- a/clash-verge-rev/src/services/cmds.ts +++ b/clash-verge-rev/src/services/cmds.ts @@ -324,3 +324,11 @@ export const getAppUptime = async () => { export const installService = async () => { return invoke("install_service"); }; + +export const entry_lightweight_mode = async () => { + return invoke("entry_lightweight_mode"); +} + +export const exit_lightweight_mode = async () => { + return invoke("exit_lightweight_mode"); +} \ No newline at end of file diff --git a/clash-verge-rev/src/services/types.d.ts b/clash-verge-rev/src/services/types.d.ts index 32c8a9dcfc..44cf600ef0 100644 --- a/clash-verge-rev/src/services/types.d.ts +++ b/clash-verge-rev/src/services/types.d.ts @@ -739,9 +739,8 @@ interface IVergeConfig { tun_tray_icon?: boolean; enable_tray_speed?: boolean; enable_tun_mode?: boolean; - enable_lite_mode?: boolean; - auto_enter_lite_mode?: boolean; - auto_enter_lite_mode_delay?: number; + enable_auto_light_weight_mode?: boolean; + auto_light_weight_minutes?: number; enable_auto_launch?: boolean; enable_silent_start?: boolean; enable_system_proxy?: boolean; diff --git a/echo/internal/constant/constant.go b/echo/internal/constant/constant.go index 7f62c39fd3..235f7c8d3d 100644 --- a/echo/internal/constant/constant.go +++ b/echo/internal/constant/constant.go @@ -15,7 +15,7 @@ var ( const ( DefaultDialTimeOut = 3 * time.Second DefaultReadTimeOut = 5 * time.Second - DefaultIdleTimeOut = 10 * time.Second + DefaultIdleTimeOut = 30 * time.Second DefaultSniffTimeOut = 300 * time.Millisecond // todo,support config in relay config diff --git a/lede/package/kernel/mt76/Makefile b/lede/package/kernel/mt76/Makefile index f194abd235..339610521e 100644 --- a/lede/package/kernel/mt76/Makefile +++ b/lede/package/kernel/mt76/Makefile @@ -30,9 +30,9 @@ PKG_SOURCE_VERSION:=8f301a5c5fe3d998b50666bde1d35d412833be89 PKG_MIRROR_HASH:=49291145ccceeac2964c9a43a46bed88298e5c1da63c6f3c2c1f6b3c1902a61e PATCH_DIR:=./patches-6.x else -PKG_SOURCE_DATE:=2023-08-14 -PKG_SOURCE_VERSION:=b14c2351ddb8601c322576d84029e463d456caef -PKG_MIRROR_HASH:=62b5e157ad525424b6857e77ed373e8d39d03af71b057f8b309d8b293d6eac5f +PKG_SOURCE_DATE:=2023-09-18 +PKG_SOURCE_VERSION:=2afc7285f75dca5a0583fd917285bf33f1429cc6 +PKG_MIRROR_HASH:=2c9556b298246277ac2d65415e4449f98e6d5fdb99e0d0a92262f162df772bbc endif PKG_MAINTAINER:=Felix Fietkau diff --git a/lede/target/linux/mediatek/dts/mt7981b-konka-komi-a31.dts b/lede/target/linux/mediatek/dts/mt7981b-konka-komi-a31.dts new file mode 100644 index 0000000000..d54245eedf --- /dev/null +++ b/lede/target/linux/mediatek/dts/mt7981b-konka-komi-a31.dts @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +/dts-v1/; + +#include +#include +#include + +#include "mt7981.dtsi" + +/ { + model = "KONKA KOMI A31"; + compatible = "konka,komi-a31", "mediatek,mt7981"; + + aliases { + serial0 = &uart0; + label-mac-device = &gmac1; + led-boot = &led_status_red; + led-failsafe = &led_status_red; + led-running = &led_status_blue; + led-upgrade = &led_status_green; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + gpio-keys { + compatible = "gpio-keys"; + + button-reset { + label = "reset"; + linux,code = ; + gpios = <&pio 1 GPIO_ACTIVE_LOW>; + }; + + button-wps { + label = "wps"; + linux,code = ; + gpios = <&pio 0 GPIO_ACTIVE_LOW>; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + + led_status_green: led-0 { + color = ; + function = LED_FUNCTION_STATUS; + gpios = <&pio 8 GPIO_ACTIVE_LOW>; + }; + + led_status_blue: led-1 { + color = ; + function = LED_FUNCTION_STATUS; + gpios = <&pio 13 GPIO_ACTIVE_LOW>; + }; + + led_status_red: led-2 { + color = ; + function = LED_FUNCTION_STATUS; + gpios = <&pio 34 GPIO_ACTIVE_LOW>; + }; + }; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + nvmem-cells = <&macaddr_lan>; + nvmem-cell-names = "mac-address"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + + nvmem-cells = <&macaddr_wan>; + nvmem-cell-names = "mac-address"; + + phy-mode = "gmii"; + phy-handle = <&int_gbe_phy>; + }; +}; + +&mdio_bus { + switch: switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 39 GPIO_ACTIVE_HIGH>; + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&pio>; + interrupts = <38 IRQ_TYPE_LEVEL_HIGH>; + }; +}; + +&pio { + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + + conf-pu { + pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP"; + drive-strength = <8>; + mediatek,pull-up-adv = <0>; + }; + + conf-pd { + pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO"; + drive-strength = <8>; + mediatek,pull-up-adv = <0>; + }; + }; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + + spi_nand@0 { + compatible = "spi-nand"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + + spi-max-frequency = <52000000>; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + + mediatek,nmbm; + mediatek,bmt-max-ratio = <1>; + mediatek,bmt-max-reserved-blocks = <64>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x0000000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x0180000 0x0200000>; + read-only; + }; + + partition@380000 { + label = "FIP"; + reg = <0x0380000 0x0200000>; + read-only; + }; + + partition@580000 { + label = "ubi"; + reg = <0x0580000 0x7000000>; + }; + }; + }; +}; + +&switch { + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan1"; + }; + + port@1 { + reg = <1>; + label = "lan2"; + }; + + port@2 { + reg = <2>; + label = "lan3"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; +}; + +&uart0 { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +&wifi { + status = "okay"; + + mediatek,mtd-eeprom = <&factory 0x0>; +}; + +&factory { + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_wan: macaddr@24 { + reg = <0x24 0x6>; + }; + + macaddr_lan: macaddr@2a { + reg = <0x2a 0x6>; + }; +}; diff --git a/lede/target/linux/mediatek/filogic/base-files/etc/board.d/02_network b/lede/target/linux/mediatek/filogic/base-files/etc/board.d/02_network index b5427ad792..71e60a65b9 100644 --- a/lede/target/linux/mediatek/filogic/base-files/etc/board.d/02_network +++ b/lede/target/linux/mediatek/filogic/base-files/etc/board.d/02_network @@ -13,6 +13,7 @@ mediatek_setup_interfaces() cmcc,xr30-nand|\ h3c,magic-nx30-pro|\ imou,lc-hx3001|\ + konka,komi-a31|\ nokia,ea0326gmp) ucidef_set_interfaces_lan_wan "lan1 lan2 lan3" eth1 ;; diff --git a/lede/target/linux/mediatek/filogic/base-files/etc/hotplug.d/ieee80211/11_fix_wifi_mac b/lede/target/linux/mediatek/filogic/base-files/etc/hotplug.d/ieee80211/11_fix_wifi_mac index 9ffabafd43..137d8edd70 100644 --- a/lede/target/linux/mediatek/filogic/base-files/etc/hotplug.d/ieee80211/11_fix_wifi_mac +++ b/lede/target/linux/mediatek/filogic/base-files/etc/hotplug.d/ieee80211/11_fix_wifi_mac @@ -73,6 +73,11 @@ case "$board" in # addresses on multiple VIFs with the other radio. Use label mac to set LA bit. [ "$PHYNBR" = "1" ] && macaddr_setbit_la $(get_mac_label) > /sys${DEVPATH}/macaddress ;; + konka,komi-a31) + addr=$(mtd_get_mac_binary factory 0x2a) + [ "$PHYNBR" = "0" ] && echo "$addr" > /sys${DEVPATH}/macaddress + [ "$PHYNBR" = "1" ] && macaddr_add $addr 1 > /sys${DEVPATH}/macaddress + ;; nokia,ea0326gmp) addr=$(mtd_get_mac_binary Factory 0x28) [ "$PHYNBR" = "1" ] && macaddr_add $addr 2 > /sys${DEVPATH}/macaddress diff --git a/lede/target/linux/mediatek/image/filogic.mk b/lede/target/linux/mediatek/image/filogic.mk index 79de6c1ba5..476db82ede 100644 --- a/lede/target/linux/mediatek/image/filogic.mk +++ b/lede/target/linux/mediatek/image/filogic.mk @@ -569,6 +569,24 @@ define Device/jdcloud_re-cs-05 endef TARGET_DEVICES += jdcloud_re-cs-05 +define Device/konka_komi-a31 + DEVICE_VENDOR := KONKA + DEVICE_MODEL := KOMI A31 + DEVICE_ALT0_VENDOR := E-life + DEVICE_ALT0_MODEL := ETR631-T + DEVICE_ALT1_VENDOR := E-life + DEVICE_ALT1_MODEL := ETR635-U + DEVICE_DTS := mt7981b-konka-komi-a31 + DEVICE_DTS_DIR := ../dts + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + KERNEL_IN_UBI := 1 + DEVICE_PACKAGES := kmod-mt7981-firmware mt7981-wo-firmware + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += konka_komi-a31 + define Device/mediatek_mt7986a-rfb DEVICE_VENDOR := MediaTek DEVICE_MODEL := MTK7986 rfba AP diff --git a/mieru/Makefile b/mieru/Makefile index d03fc6c835..1a181a41f2 100644 --- a/mieru/Makefile +++ b/mieru/Makefile @@ -32,7 +32,7 @@ PROJECT_NAME=$(shell basename "${ROOT}") # - pkg/version/current.go # # Use `tools/bump_version.sh` script to change all those files at one shot. -VERSION="3.12.0" +VERSION="3.13.0" # Build binaries and installation packages. .PHONY: build diff --git a/mieru/apis/client/mieru.go b/mieru/apis/client/mieru.go index 0a2ab99b04..dca5ebbb5b 100644 --- a/mieru/apis/client/mieru.go +++ b/mieru/apis/client/mieru.go @@ -286,8 +286,5 @@ func (mc *mieruClient) dialPostHandshake(conn net.Conn, netAddrSpec model.NetAdd return nil, fmt.Errorf("server returned socks5 error code %d", resp[1]) } - if isTCP { - return conn, nil - } - return apicommon.NewPacketOverStreamTunnel(conn), nil + return conn, nil } diff --git a/mieru/build/package/mieru/amd64/debian/DEBIAN/control b/mieru/build/package/mieru/amd64/debian/DEBIAN/control index 7ae164649b..d193d5cc2d 100755 --- a/mieru/build/package/mieru/amd64/debian/DEBIAN/control +++ b/mieru/build/package/mieru/amd64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mieru -Version: 3.12.0 +Version: 3.13.0 Section: net Priority: optional Architecture: amd64 diff --git a/mieru/build/package/mieru/amd64/rpm/mieru.spec b/mieru/build/package/mieru/amd64/rpm/mieru.spec index b777c060c0..79ee5a9a85 100644 --- a/mieru/build/package/mieru/amd64/rpm/mieru.spec +++ b/mieru/build/package/mieru/amd64/rpm/mieru.spec @@ -1,5 +1,5 @@ Name: mieru -Version: 3.12.0 +Version: 3.13.0 Release: 1%{?dist} Summary: Mieru proxy client License: GPLv3+ diff --git a/mieru/build/package/mieru/arm64/debian/DEBIAN/control b/mieru/build/package/mieru/arm64/debian/DEBIAN/control index 8216eb4bb4..b6d25b8c20 100755 --- a/mieru/build/package/mieru/arm64/debian/DEBIAN/control +++ b/mieru/build/package/mieru/arm64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mieru -Version: 3.12.0 +Version: 3.13.0 Section: net Priority: optional Architecture: arm64 diff --git a/mieru/build/package/mieru/arm64/rpm/mieru.spec b/mieru/build/package/mieru/arm64/rpm/mieru.spec index b777c060c0..79ee5a9a85 100644 --- a/mieru/build/package/mieru/arm64/rpm/mieru.spec +++ b/mieru/build/package/mieru/arm64/rpm/mieru.spec @@ -1,5 +1,5 @@ Name: mieru -Version: 3.12.0 +Version: 3.13.0 Release: 1%{?dist} Summary: Mieru proxy client License: GPLv3+ diff --git a/mieru/build/package/mita/amd64/debian/DEBIAN/control b/mieru/build/package/mita/amd64/debian/DEBIAN/control index 6ccb742058..55eb7b770a 100755 --- a/mieru/build/package/mita/amd64/debian/DEBIAN/control +++ b/mieru/build/package/mita/amd64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mita -Version: 3.12.0 +Version: 3.13.0 Section: net Priority: optional Architecture: amd64 diff --git a/mieru/build/package/mita/amd64/rpm/mita.spec b/mieru/build/package/mita/amd64/rpm/mita.spec index ac38dddfa9..2ac45f7c69 100644 --- a/mieru/build/package/mita/amd64/rpm/mita.spec +++ b/mieru/build/package/mita/amd64/rpm/mita.spec @@ -1,5 +1,5 @@ Name: mita -Version: 3.12.0 +Version: 3.13.0 Release: 1%{?dist} Summary: Mieru proxy server License: GPLv3+ diff --git a/mieru/build/package/mita/arm64/debian/DEBIAN/control b/mieru/build/package/mita/arm64/debian/DEBIAN/control index cb95505747..a27fd262ad 100755 --- a/mieru/build/package/mita/arm64/debian/DEBIAN/control +++ b/mieru/build/package/mita/arm64/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: mita -Version: 3.12.0 +Version: 3.13.0 Section: net Priority: optional Architecture: arm64 diff --git a/mieru/build/package/mita/arm64/rpm/mita.spec b/mieru/build/package/mita/arm64/rpm/mita.spec index 5d264b2297..6abba83bde 100644 --- a/mieru/build/package/mita/arm64/rpm/mita.spec +++ b/mieru/build/package/mita/arm64/rpm/mita.spec @@ -1,5 +1,5 @@ Name: mita -Version: 3.12.0 +Version: 3.13.0 Release: 1%{?dist} Summary: Mieru proxy server License: GPLv3+ diff --git a/mieru/docs/operation.md b/mieru/docs/operation.md index cd435fcaa7..f44c846786 100644 --- a/mieru/docs/operation.md +++ b/mieru/docs/operation.md @@ -172,6 +172,18 @@ MIERU_CONFIG_JSON_FILE=/etc/mieru_client_config.json mieru run At this point, the logs of the mieru client will be printed directly to the terminal. Press Ctrl+C to exit. +## Disable Client Automatic Check Update + +When you start the mieru client, it will automatically check for updates every few days. If you want to turn off automatic check update, add the following client configuration: + +```json +{ + "advancedSettings": { + "noCheckUpdate": true + } +} +``` + ## Reset Server Metrics Server metrics are stored in the `/var/lib/mita/metrics.pb` file. Even if the server is restarted, you can read the accumulated metrics using the `mita get metrics` command. If you want to reset the metrics, you can run the following command: diff --git a/mieru/docs/operation.zh_CN.md b/mieru/docs/operation.zh_CN.md index e25b45c64c..032ebf96af 100644 --- a/mieru/docs/operation.zh_CN.md +++ b/mieru/docs/operation.zh_CN.md @@ -172,6 +172,18 @@ MIERU_CONFIG_JSON_FILE=/etc/mieru_client_config.json mieru run 此时,mieru 客户端的日志会直接打印至终端。按下 Ctrl+C 退出。 +## 关闭客户端自动检查更新 + +启动 mieru 客户端时,每隔几天,会自动检查更新。如果想关闭自动检查更新,请添加如下的客户端配置: + +```json +{ + "advancedSettings": { + "noCheckUpdate": true + } +} +``` + ## 重置服务器指标 服务器指标存储在文件 `/var/lib/mita/metrics.pb` 中。即便服务器重启,也可以通过 `mita get metrics` 指令读取累积的指标。如果想重置指标,可以运行下面的命令: diff --git a/mieru/docs/server-install.md b/mieru/docs/server-install.md index c7d3ef9bd6..8ad0716757 100644 --- a/mieru/docs/server-install.md +++ b/mieru/docs/server-install.md @@ -8,32 +8,32 @@ Before installation and configuration, connect to the server via SSH and then ex ```sh # Debian / Ubuntu - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.12.0/mita_3.12.0_amd64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.0/mita_3.13.0_amd64.deb # Debian / Ubuntu - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.12.0/mita_3.12.0_arm64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.0/mita_3.13.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.12.0/mita-3.12.0-1.x86_64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.0/mita-3.13.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.12.0/mita-3.12.0-1.aarch64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.0/mita-3.13.0-1.aarch64.rpm ``` ## Install mita package ```sh # Debian / Ubuntu - X86_64 -sudo dpkg -i mita_3.12.0_amd64.deb +sudo dpkg -i mita_3.13.0_amd64.deb # Debian / Ubuntu - ARM 64 -sudo dpkg -i mita_3.12.0_arm64.deb +sudo dpkg -i mita_3.13.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -sudo rpm -Uvh --force mita-3.12.0-1.x86_64.rpm +sudo rpm -Uvh --force mita-3.13.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -sudo rpm -Uvh --force mita-3.12.0-1.aarch64.rpm +sudo rpm -Uvh --force mita-3.13.0-1.aarch64.rpm ``` Those instructions can also be used to upgrade the version of mita software package. diff --git a/mieru/docs/server-install.zh_CN.md b/mieru/docs/server-install.zh_CN.md index bfed799d01..435881d188 100644 --- a/mieru/docs/server-install.zh_CN.md +++ b/mieru/docs/server-install.zh_CN.md @@ -8,32 +8,32 @@ ```sh # Debian / Ubuntu - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.12.0/mita_3.12.0_amd64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.0/mita_3.13.0_amd64.deb # Debian / Ubuntu - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.12.0/mita_3.12.0_arm64.deb +curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.0/mita_3.13.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.12.0/mita-3.12.0-1.x86_64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.0/mita-3.13.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -curl -LSO https://github.com/enfein/mieru/releases/download/v3.12.0/mita-3.12.0-1.aarch64.rpm +curl -LSO https://github.com/enfein/mieru/releases/download/v3.13.0/mita-3.13.0-1.aarch64.rpm ``` ## 安装 mita 软件包 ```sh # Debian / Ubuntu - X86_64 -sudo dpkg -i mita_3.12.0_amd64.deb +sudo dpkg -i mita_3.13.0_amd64.deb # Debian / Ubuntu - ARM 64 -sudo dpkg -i mita_3.12.0_arm64.deb +sudo dpkg -i mita_3.13.0_arm64.deb # RedHat / CentOS / Rocky Linux - X86_64 -sudo rpm -Uvh --force mita-3.12.0-1.x86_64.rpm +sudo rpm -Uvh --force mita-3.13.0-1.x86_64.rpm # RedHat / CentOS / Rocky Linux - ARM 64 -sudo rpm -Uvh --force mita-3.12.0-1.aarch64.rpm +sudo rpm -Uvh --force mita-3.13.0-1.aarch64.rpm ``` 上述指令也可以用来升级 mita 软件包的版本。 diff --git a/mieru/pkg/version/current.go b/mieru/pkg/version/current.go index 922992a50d..5b3eeae562 100644 --- a/mieru/pkg/version/current.go +++ b/mieru/pkg/version/current.go @@ -16,5 +16,5 @@ package version const ( - AppVersion = "3.12.0" + AppVersion = "3.13.0" ) diff --git a/mieru/test/cmd/exampleapiclient/exampleapiclient.go b/mieru/test/cmd/exampleapiclient/exampleapiclient.go index 8968b0a0ab..45ad0f8e87 100644 --- a/mieru/test/cmd/exampleapiclient/exampleapiclient.go +++ b/mieru/test/cmd/exampleapiclient/exampleapiclient.go @@ -205,7 +205,7 @@ func handleOneSocks5Conn(c client.Client, conn net.Conn) { panic(fmt.Sprintf("Write socks5 response failed: %v", err)) } - tunnel := proxyConn.(*apicommon.PacketOverStreamTunnel) + tunnel := apicommon.NewPacketOverStreamTunnel(proxyConn) socks5.BidiCopyUDP(udpConn, tunnel) } } diff --git a/mihomo/adapter/outbound/mieru.go b/mihomo/adapter/outbound/mieru.go index 7aab2f58d8..d2209442f8 100644 --- a/mihomo/adapter/outbound/mieru.go +++ b/mihomo/adapter/outbound/mieru.go @@ -8,11 +8,13 @@ import ( "strconv" "sync" + CN "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" C "github.com/metacubex/mihomo/constant" mieruclient "github.com/enfein/mieru/v3/apis/client" + mierucommon "github.com/enfein/mieru/v3/apis/common" mierumodel "github.com/enfein/mieru/v3/apis/model" mierupb "github.com/enfein/mieru/v3/pkg/appctl/appctlpb" "google.golang.org/protobuf/proto" @@ -32,6 +34,7 @@ type MieruOption struct { Port int `proxy:"port,omitempty"` PortRange string `proxy:"port-range,omitempty"` Transport string `proxy:"transport"` + UDP bool `proxy:"udp,omitempty"` UserName string `proxy:"username"` Password string `proxy:"password"` Multiplexing string `proxy:"multiplexing,omitempty"` @@ -50,6 +53,23 @@ func (m *Mieru) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d return NewConn(c, m), nil } +// ListenPacketContext implements C.ProxyAdapter +func (m *Mieru) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { + if err := m.ensureClientIsRunning(opts...); err != nil { + return nil, err + } + c, err := m.client.DialContext(ctx, metadata.UDPAddr()) + if err != nil { + return nil, fmt.Errorf("dial to %s failed: %w", metadata.UDPAddr(), err) + } + return newPacketConn(CN.NewRefPacketConn(CN.NewThreadSafePacketConn(mierucommon.NewUDPAssociateWrapper(mierucommon.NewPacketOverStreamTunnel(c))), m), m), nil +} + +// SupportUOT implements C.ProxyAdapter +func (m *Mieru) SupportUOT() bool { + return true +} + // ProxyInfo implements C.ProxyAdapter func (m *Mieru) ProxyInfo() C.ProxyInfo { info := m.Base.ProxyInfo() @@ -113,7 +133,7 @@ func NewMieru(option MieruOption) (*Mieru, error) { addr: addr, iface: option.Interface, tp: C.Mieru, - udp: false, + udp: option.UDP, xudp: false, rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), diff --git a/mihomo/docs/config.yaml b/mihomo/docs/config.yaml index 263d67b65c..fc11dca6eb 100644 --- a/mihomo/docs/config.yaml +++ b/mihomo/docs/config.yaml @@ -879,6 +879,7 @@ proxies: # socks5 port: 2999 # port-range: 2090-2099 #(不可同时填写 port 和 port-range) transport: TCP # 只支持 TCP + udp: true # 支持 UDP over TCP username: user password: password # 可以使用的值包括 MULTIPLEXING_OFF, MULTIPLEXING_LOW, MULTIPLEXING_MIDDLE, MULTIPLEXING_HIGH。其中 MULTIPLEXING_OFF 会关闭多路复用功能。默认值为 MULTIPLEXING_LOW。 diff --git a/mihomo/go.mod b/mihomo/go.mod index 4787606ab5..b327e196c2 100644 --- a/mihomo/go.mod +++ b/mihomo/go.mod @@ -7,7 +7,7 @@ require ( github.com/bahlo/generic-list-go v0.2.0 github.com/coreos/go-iptables v0.8.0 github.com/dlclark/regexp2 v1.11.5 - github.com/enfein/mieru/v3 v3.12.0 + github.com/enfein/mieru/v3 v3.13.0 github.com/go-chi/chi/v5 v5.2.1 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 diff --git a/mihomo/go.sum b/mihomo/go.sum index c7711bead1..9cfd6e0e1f 100644 --- a/mihomo/go.sum +++ b/mihomo/go.sum @@ -28,8 +28,8 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/enfein/mieru/v3 v3.12.0 h1:sV3moozWpRjjqwqFZJjGtMB0EacN8+D7BpjzsmacsXM= -github.com/enfein/mieru/v3 v3.12.0/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM= +github.com/enfein/mieru/v3 v3.13.0 h1:eGyxLGkb+lut9ebmx+BGwLJ5UMbEc/wGIYO0AXEKy98= +github.com/enfein/mieru/v3 v3.13.0/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= diff --git a/openwrt-packages/adguardhome/Makefile b/openwrt-packages/adguardhome/Makefile index a21c229eeb..550b02f82b 100644 --- a/openwrt-packages/adguardhome/Makefile +++ b/openwrt-packages/adguardhome/Makefile @@ -6,12 +6,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=adguardhome -PKG_VERSION:=0.107.57 +PKG_VERSION:=0.107.58 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/AdguardTeam/AdGuardHome/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=9df951486dab0e83485b596c0393f91d4ff2994de26101b43af8344efb7c1536 +PKG_HASH:=da6a52764090d8350d55bc14b957ec6afdee0bd21b34b010796e220bde5e9186 PKG_BUILD_DIR:=$(BUILD_DIR)/AdGuardHome-$(PKG_VERSION) PKG_LICENSE:=GPL-3.0-only @@ -58,7 +58,7 @@ define Download/adguardhome-frontend URL:=https://github.com/AdguardTeam/AdGuardHome/releases/download/v$(PKG_VERSION)/ URL_FILE:=AdGuardHome_frontend.tar.gz FILE:=$(FRONTEND_FILE) - HASH:=fc0b57d80dece4219bfba833b48122ffe7a140ee2026cd3cf4c7181ccdcf8c9e + HASH:=7bda3b9e33757c5a58a211b8a3d71a76a0311327fd009adf0935e6a3c4313f9f endef define Build/Prepare diff --git a/openwrt-packages/luci-lib-taskd/Makefile b/openwrt-packages/luci-lib-taskd/Makefile index cc660a7633..0e05f2b4c7 100644 --- a/openwrt-packages/luci-lib-taskd/Makefile +++ b/openwrt-packages/luci-lib-taskd/Makefile @@ -11,7 +11,7 @@ LUCI_DEPENDS:=+luci-lib-xterm +taskd LUCI_EXTRA_DEPENDS:=taskd (>=1.0.3-1) LUCI_PKGARCH:=all -PKG_VERSION:=1.0.22 +PKG_VERSION:=1.0.23 PKG_RELEASE:= PKG_MAINTAINER:=jjm2473 diff --git a/openwrt-packages/luci-lib-taskd/htdocs/luci-static/resources/tasks/tasks.js b/openwrt-packages/luci-lib-taskd/htdocs/luci-static/resources/tasks/tasks.js index 086f753322..9a5b7957d7 100644 --- a/openwrt-packages/luci-lib-taskd/htdocs/luci-static/resources/tasks/tasks.js +++ b/openwrt-packages/luci-lib-taskd/htdocs/luci-static/resources/tasks/tasks.js @@ -104,6 +104,9 @@ return false; }; const checkTask = function() { + if (!showing) { + return Promise.resolve(false); + } return getTaskDetail(task_id).then(data=>{ if (!running) { return false; @@ -163,16 +166,18 @@ } }).catch(err => { if (showing) { - if (err.target.status == 0 || err.target.status == 502) { - title_view.innerText = task_id + ' (' + $gettext("Fetch log failed, retrying...") + ')'; - setTimeout(()=>pulllog(true), 1000); - } else if (err.target.status == 403 || err.target.status == 404) { - title_view.innerText = task_id + ' (' + $gettext(err.target.status == 403?"Lost login status":"Task does not exist or has been deleted") + ')'; - container.querySelector(".dialog-icon-close").hidden = true; - container.classList.add('tasks_unknown'); - } else { - console.error(err); + console.error(err); + if (err.target) { + if (err.target.status == 0 || err.target.status == 502) { + title_view.innerText = task_id + ' (' + $gettext("Fetch log failed, retrying...") + ')'; + } else if (err.target.status == 403 || err.target.status == 404) { + title_view.innerText = task_id + ' (' + $gettext(err.target.status == 403?"Lost login status":"Task does not exist or has been deleted") + ')'; + container.querySelector(".dialog-icon-close").hidden = true; + container.classList.add('tasks_unknown'); + return + } } + setTimeout(()=>pulllog(true), 1000); } }); }; diff --git a/openwrt-passwall/.github/workflows/Auto compile with openwrt sdk.yml b/openwrt-passwall/.github/workflows/Auto compile with openwrt sdk.yml index c80e3bbfc0..583f5d5620 100644 --- a/openwrt-passwall/.github/workflows/Auto compile with openwrt sdk.yml +++ b/openwrt-passwall/.github/workflows/Auto compile with openwrt sdk.yml @@ -293,6 +293,7 @@ jobs: echo "CONFIG_PACKAGE_luci-app-passwall=m" >> .config echo "CONFIG_PACKAGE_luci-app-passwall_Iptables_Transparent_Proxy=y" >> .config echo "CONFIG_PACKAGE_luci-app-passwall_Nftables_Transparent_Proxy=y" >> .config + echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_Geoview=y" >> .config echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_Haproxy=y" >> .config echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_Hysteria=y" >> .config echo "CONFIG_PACKAGE_luci-app-passwall_INCLUDE_NaiveProxy=y" >> .config diff --git a/shadowsocks-rust/crates/shadowsocks-service/Cargo.toml b/shadowsocks-rust/crates/shadowsocks-service/Cargo.toml index 8cb207d1c5..9c14bad67c 100644 --- a/shadowsocks-rust/crates/shadowsocks-service/Cargo.toml +++ b/shadowsocks-rust/crates/shadowsocks-service/Cargo.toml @@ -166,9 +166,7 @@ http-body-util = { version = "0.1", optional = true } http = { version = "1.1", optional = true } httparse = { version = "1.9", optional = true } -hickory-resolver = { version = "0.25", optional = true, features = [ - "serde", -] } +hickory-resolver = { version = "0.25", optional = true, features = ["serde"] } idna = "1.0" ipnet = "2.10" @@ -191,6 +189,7 @@ smoltcp = { version = "0.12", optional = true, default-features = false, feature "socket-icmp", "socket-udp", "socket-tcp", + "socket-tcp-cubic", ] } serde = { version = "1.0", features = ["derive"] } diff --git a/shadowsocks-rust/crates/shadowsocks-service/src/local/tun/tcp.rs b/shadowsocks-rust/crates/shadowsocks-service/src/local/tun/tcp.rs index d5d0f0f50e..3096c4f5ee 100644 --- a/shadowsocks-rust/crates/shadowsocks-service/src/local/tun/tcp.rs +++ b/shadowsocks-rust/crates/shadowsocks-service/src/local/tun/tcp.rs @@ -19,7 +19,7 @@ use shadowsocks::{net::TcpSocketOpts, relay::socks5::Address}; use smoltcp::{ iface::{Config as InterfaceConfig, Interface, PollResult, SocketHandle, SocketSet}, phy::{DeviceCapabilities, Medium}, - socket::tcp::{Socket as TcpSocket, SocketBuffer as TcpSocketBuffer, State as TcpState}, + socket::tcp::{CongestionControl, Socket as TcpSocket, SocketBuffer as TcpSocketBuffer, State as TcpState}, storage::RingBuffer, time::{Duration as SmolDuration, Instant as SmolInstant}, wire::{HardwareAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address, TcpPacket}, @@ -516,6 +516,8 @@ impl TcpTun { socket.set_timeout(Some(SmolDuration::from_secs(7200))); // NO ACK delay // socket.set_ack_delay(None); + // Enable Cubic congestion control + socket.set_congestion_control(CongestionControl::Cubic); if let Err(err) = socket.listen(dst_addr) { return Err(io::Error::new(ErrorKind::Other, format!("listen error: {:?}", err))); diff --git a/sing-box/adapter/dns.go b/sing-box/adapter/dns.go index e0f381b82b..942f3566df 100644 --- a/sing-box/adapter/dns.go +++ b/sing-box/adapter/dns.go @@ -45,10 +45,10 @@ type RDRCStore interface { } type DNSTransport interface { + Lifecycle Type() string Tag() string Dependencies() []string - Reset() Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg, error) } diff --git a/sing-box/common/dialer/detour.go b/sing-box/common/dialer/detour.go index e4a46049f0..6a45b15ea5 100644 --- a/sing-box/common/dialer/detour.go +++ b/sing-box/common/dialer/detour.go @@ -2,6 +2,7 @@ package dialer import ( "context" + "github.com/sagernet/sing/common" "net" "sync" @@ -11,9 +12,14 @@ import ( N "github.com/sagernet/sing/common/network" ) +type DirectDialer interface { + IsEmpty() bool +} + type DetourDialer struct { outboundManager adapter.OutboundManager detour string + directResolver bool dialer N.Dialer initOnce sync.Once initErr error @@ -23,9 +29,12 @@ func NewDetour(outboundManager adapter.OutboundManager, detour string) N.Dialer return &DetourDialer{outboundManager: outboundManager, detour: detour} } -func (d *DetourDialer) Start() error { - _, err := d.Dialer() - return err +func InitializeDetour(dialer N.Dialer) error { + detourDialer, isDetour := common.Cast[*DetourDialer](dialer) + if !isDetour { + return nil + } + return common.Error(detourDialer.Dialer()) } func (d *DetourDialer) Dialer() (N.Dialer, error) { @@ -34,11 +43,18 @@ func (d *DetourDialer) Dialer() (N.Dialer, error) { } func (d *DetourDialer) init() { - var loaded bool - d.dialer, loaded = d.outboundManager.Outbound(d.detour) + dialer, loaded := d.outboundManager.Outbound(d.detour) if !loaded { d.initErr = E.New("outbound detour not found: ", d.detour) + return } + if directDialer, isDirect := dialer.(DirectDialer); isDirect { + if directDialer.IsEmpty() { + d.initErr = E.New("detour to an empty direct outbound makes no sense") + return + } + } + d.dialer = dialer } func (d *DetourDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { diff --git a/sing-box/dns/router.go b/sing-box/dns/router.go index 42cebd23a3..44edadbd3d 100644 --- a/sing-box/dns/router.go +++ b/sing-box/dns/router.go @@ -449,6 +449,6 @@ func (r *Router) LookupReverseMapping(ip netip.Addr) (string, bool) { func (r *Router) ResetNetwork() { r.ClearCache() for _, transport := range r.transport.Transports() { - transport.Reset() + transport.Close() } } diff --git a/sing-box/dns/transport/dhcp/dhcp.go b/sing-box/dns/transport/dhcp/dhcp.go index c75d736981..92dd1f8bc6 100644 --- a/sing-box/dns/transport/dhcp/dhcp.go +++ b/sing-box/dns/transport/dhcp/dhcp.go @@ -81,7 +81,7 @@ func (t *Transport) Start(stage adapter.StartStage) error { func (t *Transport) Close() error { for _, transport := range t.transports { - transport.Reset() + transport.Close() } if t.interfaceCallback != nil { t.networkManager.InterfaceMonitor().UnregisterCallback(t.interfaceCallback) @@ -89,12 +89,6 @@ func (t *Transport) Close() error { return nil } -func (t *Transport) Reset() { - for _, transport := range t.transports { - transport.Reset() - } -} - func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { err := t.fetchServers() if err != nil { @@ -252,7 +246,7 @@ func (t *Transport) recreateServers(iface *control.Interface, serverAddrs []M.So transports = append(transports, transport.NewUDPRaw(t.logger, t.TransportAdapter, serverDialer, serverAddr)) } for _, transport := range t.transports { - transport.Reset() + transport.Close() } t.transports = transports return nil diff --git a/sing-box/dns/transport/hosts/hosts.go b/sing-box/dns/transport/hosts/hosts.go index 0a1dd395bd..a5eecb4027 100644 --- a/sing-box/dns/transport/hosts/hosts.go +++ b/sing-box/dns/transport/hosts/hosts.go @@ -51,7 +51,12 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt }, nil } -func (t *Transport) Reset() { +func (t *Transport) Start(stage adapter.StartStage) error { + return nil +} + +func (t *Transport) Close() error { + return nil } func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/sing-box/dns/transport/https.go b/sing-box/dns/transport/https.go index bd150d5f31..dc36476f79 100644 --- a/sing-box/dns/transport/https.go +++ b/sing-box/dns/transport/https.go @@ -3,6 +3,7 @@ package transport import ( "bytes" "context" + "github.com/sagernet/sing-box/common/dialer" "io" "net" "net/http" @@ -149,9 +150,17 @@ func NewHTTPSRaw( } } -func (t *HTTPSTransport) Reset() { +func (t *HTTPSTransport) Start(stage adapter.StartStage) error { + if stage != adapter.StartStateStart { + return nil + } + return dialer.InitializeDetour(t.dialer) +} + +func (t *HTTPSTransport) Close() error { t.transport.CloseIdleConnections() t.transport = t.transport.Clone() + return nil } func (t *HTTPSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/sing-box/dns/transport/local/local.go b/sing-box/dns/transport/local/local.go index 4e05ff0250..f50405a53d 100644 --- a/sing-box/dns/transport/local/local.go +++ b/sing-box/dns/transport/local/local.go @@ -40,7 +40,12 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt }, nil } -func (t *Transport) Reset() { +func (t *Transport) Start(stage adapter.StartStage) error { + return nil +} + +func (t *Transport) Close() error { + return nil } func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/sing-box/dns/transport/quic/http3.go b/sing-box/dns/transport/quic/http3.go index e2a75b505d..0d871741fa 100644 --- a/sing-box/dns/transport/quic/http3.go +++ b/sing-box/dns/transport/quic/http3.go @@ -111,8 +111,12 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options }, nil } -func (t *HTTP3Transport) Reset() { - t.transport.Close() +func (t *HTTP3Transport) Start(stage adapter.StartStage) error { + return nil +} + +func (t *HTTP3Transport) Close() error { + return t.transport.Close() } func (t *HTTP3Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/sing-box/dns/transport/quic/quic.go b/sing-box/dns/transport/quic/quic.go index 4ae9ac161e..fc5101ee4f 100644 --- a/sing-box/dns/transport/quic/quic.go +++ b/sing-box/dns/transport/quic/quic.go @@ -68,13 +68,18 @@ func NewQUIC(ctx context.Context, logger log.ContextLogger, tag string, options }, nil } -func (t *Transport) Reset() { +func (t *Transport) Start(stage adapter.StartStage) error { + return nil +} + +func (t *Transport) Close() error { t.access.Lock() defer t.access.Unlock() connection := t.connection if connection != nil { connection.CloseWithError(0, "") } + return nil } func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/sing-box/dns/transport/tcp.go b/sing-box/dns/transport/tcp.go index 4abeee2fa3..fdfa384445 100644 --- a/sing-box/dns/transport/tcp.go +++ b/sing-box/dns/transport/tcp.go @@ -3,6 +3,7 @@ package transport import ( "context" "encoding/binary" + "github.com/sagernet/sing-box/common/dialer" "io" "github.com/sagernet/sing-box/adapter" @@ -46,7 +47,15 @@ func NewTCP(ctx context.Context, logger log.ContextLogger, tag string, options o }, nil } -func (t *TCPTransport) Reset() { +func (t *TCPTransport) Start(stage adapter.StartStage) error { + if stage != adapter.StartStateStart { + return nil + } + return dialer.InitializeDetour(t.dialer) +} + +func (t *TCPTransport) Close() error { + return nil } func (t *TCPTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/sing-box/dns/transport/tls.go b/sing-box/dns/transport/tls.go index ce88d42550..cd942d394e 100644 --- a/sing-box/dns/transport/tls.go +++ b/sing-box/dns/transport/tls.go @@ -2,6 +2,7 @@ package transport import ( "context" + "github.com/sagernet/sing-box/common/dialer" "sync" "github.com/sagernet/sing-box/adapter" @@ -65,13 +66,21 @@ func NewTLS(ctx context.Context, logger log.ContextLogger, tag string, options o }, nil } -func (t *TLSTransport) Reset() { +func (t *TLSTransport) Start(stage adapter.StartStage) error { + if stage != adapter.StartStateStart { + return nil + } + return dialer.InitializeDetour(t.dialer) +} + +func (t *TLSTransport) Close() error { t.access.Lock() defer t.access.Unlock() for connection := t.connections.Front(); connection != nil; connection = connection.Next() { connection.Value.Close() } t.connections.Init() + return nil } func (t *TLSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/sing-box/dns/transport/udp.go b/sing-box/dns/transport/udp.go index 8c905c4c34..71c93bb44b 100644 --- a/sing-box/dns/transport/udp.go +++ b/sing-box/dns/transport/udp.go @@ -2,6 +2,7 @@ package transport import ( "context" + "github.com/sagernet/sing-box/common/dialer" "net" "os" "sync" @@ -64,11 +65,19 @@ func NewUDPRaw(logger logger.ContextLogger, adapter dns.TransportAdapter, dialer } } -func (t *UDPTransport) Reset() { +func (t *UDPTransport) Start(stage adapter.StartStage) error { + if stage != adapter.StartStateStart { + return nil + } + return dialer.InitializeDetour(t.dialer) +} + +func (t *UDPTransport) Close() error { t.access.Lock() defer t.access.Unlock() close(t.done) t.done = make(chan struct{}) + return nil } func (t *UDPTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { diff --git a/sing-box/dns/transport_manager.go b/sing-box/dns/transport_manager.go index 8666a1b9f7..dff886df06 100644 --- a/sing-box/dns/transport_manager.go +++ b/sing-box/dns/transport_manager.go @@ -225,7 +225,7 @@ func (m *TransportManager) Remove(tag string) error { } } if started { - transport.Reset() + transport.Close() } return nil } diff --git a/sing-box/option/rule.go b/sing-box/option/rule.go index b769dab831..41bcc12604 100644 --- a/sing-box/option/rule.go +++ b/sing-box/option/rule.go @@ -125,10 +125,9 @@ func (r *DefaultRule) UnmarshalJSON(data []byte) error { return badjson.UnmarshallExcluded(data, &r.RawDefaultRule, &r.RuleAction) } -func (r *DefaultRule) IsValid() bool { +func (r DefaultRule) IsValid() bool { var defaultValue DefaultRule defaultValue.Invert = r.Invert - defaultValue.Action = r.Action return !reflect.DeepEqual(r, defaultValue) } diff --git a/sing-box/option/rule_dns.go b/sing-box/option/rule_dns.go index 9d6fb1381a..87b1501745 100644 --- a/sing-box/option/rule_dns.go +++ b/sing-box/option/rule_dns.go @@ -132,7 +132,6 @@ func (r *DefaultDNSRule) UnmarshalJSONContext(ctx context.Context, data []byte) func (r DefaultDNSRule) IsValid() bool { var defaultValue DefaultDNSRule defaultValue.Invert = r.Invert - defaultValue.DNSRuleAction = r.DNSRuleAction return !reflect.DeepEqual(r, defaultValue) } diff --git a/sing-box/protocol/direct/outbound.go b/sing-box/protocol/direct/outbound.go index 7ad756f29f..9cd1490bec 100644 --- a/sing-box/protocol/direct/outbound.go +++ b/sing-box/protocol/direct/outbound.go @@ -4,6 +4,7 @@ import ( "context" "net" "net/netip" + "reflect" "time" "github.com/sagernet/sing-box/adapter" @@ -27,6 +28,7 @@ func RegisterOutbound(registry *outbound.Registry) { var ( _ N.ParallelDialer = (*Outbound)(nil) _ dialer.ParallelNetworkDialer = (*Outbound)(nil) + _ dialer.DirectDialer = (*Outbound)(nil) ) type Outbound struct { @@ -37,6 +39,7 @@ type Outbound struct { fallbackDelay time.Duration overrideOption int overrideDestination M.Socksaddr + isEmpty bool // loopBack *loopBackDetector } @@ -56,6 +59,8 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL domainStrategy: C.DomainStrategy(options.DomainStrategy), fallbackDelay: time.Duration(options.FallbackDelay), dialer: outboundDialer.(dialer.ParallelInterfaceDialer), + //nolint:staticcheck + isEmpty: reflect.DeepEqual(options.DialerOptions, option.DialerOptions{UDPFragmentDefault: true}) && options.OverrideAddress == "" && options.OverridePort == 0, // loopBack: newLoopBackDetector(router), } //nolint:staticcheck @@ -242,6 +247,10 @@ func (h *Outbound) ListenSerialNetworkPacket(ctx context.Context, destination M. return conn, newDestination, nil } +func (h *Outbound) IsEmpty() bool { + return h.isEmpty +} + /*func (h *Outbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { if h.loopBack.CheckConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) { return E.New("reject loopback connection to ", metadata.Destination) diff --git a/small/shadowsocks-rust/Makefile b/small/shadowsocks-rust/Makefile index 5448341fc9..46645fc87f 100644 --- a/small/shadowsocks-rust/Makefile +++ b/small/shadowsocks-rust/Makefile @@ -40,10 +40,10 @@ else ifeq ($(ARCH),x86_64) PKG_HASH:=118cfd8294b233e97ad1c04c33691b10cefa72a06fea9f5d42828f1f374ecfc1 else ifeq ($(ARCH),mips) PKG_SOURCE:=$(PKG_SOURCE_HEADER).mips-$(PKG_SOURCE_BODY).$(PKG_SOURCE_FOOTER) - PKG_HASH:=cf3be6e19fde6dee456c59aef809f4a43b2ad359f74d3ead186ab06c9f3694b3 + PKG_HASH:=377ee20f9d49d1e2fc9de75a1d5f32f0c9dda0154fc80b2995b82459b60035f8 else ifeq ($(ARCH),mipsel) PKG_SOURCE:=$(PKG_SOURCE_HEADER).mipsel-$(PKG_SOURCE_BODY).$(PKG_SOURCE_FOOTER) - PKG_HASH:=10d7c8083482800c816d2a781bfc5b3fe5343d88c1eec2b635c64a9996310c06 + PKG_HASH:=992503f4d6ebe71eaa8094c90be9f8d89201e55622896ab523d3b11fc9e92caf # Set the default value to make OpenWrt Package Checker happy else PKG_SOURCE:=dummy diff --git a/v2rayn/.github/workflows/build-linux.yml b/v2rayn/.github/workflows/build-linux.yml index 66e2f4fcaa..a9fe23e2fc 100644 --- a/v2rayn/.github/workflows/build-linux.yml +++ b/v2rayn/.github/workflows/build-linux.yml @@ -45,7 +45,7 @@ jobs: dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64 - name: Upload build artifacts - uses: actions/upload-artifact@v4.6.1 + uses: actions/upload-artifact@v4.6.2 with: name: v2rayN-linux path: | diff --git a/v2rayn/.github/workflows/build-osx.yml b/v2rayn/.github/workflows/build-osx.yml index 63304149cf..78c18dc5d8 100644 --- a/v2rayn/.github/workflows/build-osx.yml +++ b/v2rayn/.github/workflows/build-osx.yml @@ -45,7 +45,7 @@ jobs: dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64 - name: Upload build artifacts - uses: actions/upload-artifact@v4.6.1 + uses: actions/upload-artifact@v4.6.2 with: name: v2rayN-macos path: | diff --git a/v2rayn/.github/workflows/build-windows-desktop.yml b/v2rayn/.github/workflows/build-windows-desktop.yml index 1bb7835b16..9afc504ddd 100644 --- a/v2rayn/.github/workflows/build-windows-desktop.yml +++ b/v2rayn/.github/workflows/build-windows-desktop.yml @@ -45,7 +45,7 @@ jobs: dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64 - name: Upload build artifacts - uses: actions/upload-artifact@v4.6.1 + uses: actions/upload-artifact@v4.6.2 with: name: v2rayN-windows-desktop path: | diff --git a/v2rayn/.github/workflows/build-windows.yml b/v2rayn/.github/workflows/build-windows.yml index 44d22eb769..1d33f85a59 100644 --- a/v2rayn/.github/workflows/build-windows.yml +++ b/v2rayn/.github/workflows/build-windows.yml @@ -46,7 +46,7 @@ jobs: - name: Upload build artifacts - uses: actions/upload-artifact@v4.6.1 + uses: actions/upload-artifact@v4.6.2 with: name: v2rayN-windows path: | diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt index 10e28b4cae..13bba44956 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt @@ -24,94 +24,6 @@ import java.net.URI object AngConfigManager { - /** - * Parses the configuration from a QR code or string. - * - * @param str The configuration string. - * @param subid The subscription ID. - * @param subItem The subscription item. - * @param removedSelectedServer The removed selected server. - * @return The result code. - */ - private fun parseConfig( - str: String?, - subid: String, - subItem: SubscriptionItem?, - removedSelectedServer: ProfileItem? - ): Int { - try { - if (str == null || TextUtils.isEmpty(str)) { - return R.string.toast_none_data - } - - val config = if (str.startsWith(EConfigType.VMESS.protocolScheme)) { - VmessFmt.parse(str) - } else if (str.startsWith(EConfigType.SHADOWSOCKS.protocolScheme)) { - ShadowsocksFmt.parse(str) - } else if (str.startsWith(EConfigType.SOCKS.protocolScheme)) { - SocksFmt.parse(str) - } else if (str.startsWith(EConfigType.TROJAN.protocolScheme)) { - TrojanFmt.parse(str) - } else if (str.startsWith(EConfigType.VLESS.protocolScheme)) { - VlessFmt.parse(str) - } else if (str.startsWith(EConfigType.WIREGUARD.protocolScheme)) { - WireguardFmt.parse(str) - } else if (str.startsWith(EConfigType.HYSTERIA2.protocolScheme) || str.startsWith(HY2)) { - Hysteria2Fmt.parse(str) - } else { - null - } - - if (config == null) { - return R.string.toast_incorrect_protocol - } - //filter - if (subItem?.filter != null && subItem.filter?.isNotEmpty() == true && config.remarks.isNotEmpty()) { - val matched = Regex(pattern = subItem.filter ?: "") - .containsMatchIn(input = config.remarks) - if (!matched) return -1 - } - - config.subscriptionId = subid - val guid = MmkvManager.encodeServerConfig("", config) - if (removedSelectedServer != null && - config.server == removedSelectedServer.server && config.serverPort == removedSelectedServer.serverPort - ) { - MmkvManager.setSelectServer(guid) - } - } catch (e: Exception) { - e.printStackTrace() - return -1 - } - return 0 - } - - /** - * Shares the configuration. - * - * @param guid The GUID of the configuration. - * @return The configuration string. - */ - private fun shareConfig(guid: String): String { - try { - val config = MmkvManager.decodeServerConfig(guid) ?: return "" - - return config.configType.protocolScheme + when (config.configType) { - EConfigType.VMESS -> VmessFmt.toUri(config) - EConfigType.CUSTOM -> "" - EConfigType.SHADOWSOCKS -> ShadowsocksFmt.toUri(config) - EConfigType.SOCKS -> SocksFmt.toUri(config) - EConfigType.HTTP -> "" - EConfigType.VLESS -> VlessFmt.toUri(config) - EConfigType.TROJAN -> TrojanFmt.toUri(config) - EConfigType.WIREGUARD -> WireguardFmt.toUri(config) - EConfigType.HYSTERIA2 -> Hysteria2Fmt.toUri(config) - } - } catch (e: Exception) { - e.printStackTrace() - return "" - } - } /** * Shares the configuration to the clipboard. @@ -214,6 +126,33 @@ object AngConfigManager { return 0 } + /** + * Shares the configuration. + * + * @param guid The GUID of the configuration. + * @return The configuration string. + */ + private fun shareConfig(guid: String): String { + try { + val config = MmkvManager.decodeServerConfig(guid) ?: return "" + + return config.configType.protocolScheme + when (config.configType) { + EConfigType.VMESS -> VmessFmt.toUri(config) + EConfigType.CUSTOM -> "" + EConfigType.SHADOWSOCKS -> ShadowsocksFmt.toUri(config) + EConfigType.SOCKS -> SocksFmt.toUri(config) + EConfigType.HTTP -> "" + EConfigType.VLESS -> VlessFmt.toUri(config) + EConfigType.TROJAN -> TrojanFmt.toUri(config) + EConfigType.WIREGUARD -> WireguardFmt.toUri(config) + EConfigType.HYSTERIA2 -> Hysteria2Fmt.toUri(config) + } + } catch (e: Exception) { + e.printStackTrace() + return "" + } + } + /** * Imports a batch of configurations. * @@ -248,7 +187,7 @@ object AngConfigManager { * @param servers The servers string. * @return The number of subscriptions parsed. */ - fun parseBatchSubscription(servers: String?): Int { + private fun parseBatchSubscription(servers: String?): Int { try { if (servers == null) { return 0 @@ -277,7 +216,7 @@ object AngConfigManager { * @param append Whether to append the configurations. * @return The number of configurations parsed. */ - fun parseBatchConfig(servers: String?, subid: String, append: Boolean): Int { + private fun parseBatchConfig(servers: String?, subid: String, append: Boolean): Int { try { if (servers == null) { return 0 @@ -324,7 +263,7 @@ object AngConfigManager { * @param subid The subscription ID. * @return The number of configurations parsed. */ - fun parseCustomConfigServer(server: String?, subid: String): Int { + private fun parseCustomConfigServer(server: String?, subid: String): Int { if (server == null) { return 0 } @@ -377,6 +316,68 @@ object AngConfigManager { } } + /** + * Parses the configuration from a QR code or string. + * + * @param str The configuration string. + * @param subid The subscription ID. + * @param subItem The subscription item. + * @param removedSelectedServer The removed selected server. + * @return The result code. + */ + private fun parseConfig( + str: String?, + subid: String, + subItem: SubscriptionItem?, + removedSelectedServer: ProfileItem? + ): Int { + try { + if (str == null || TextUtils.isEmpty(str)) { + return R.string.toast_none_data + } + + val config = if (str.startsWith(EConfigType.VMESS.protocolScheme)) { + VmessFmt.parse(str) + } else if (str.startsWith(EConfigType.SHADOWSOCKS.protocolScheme)) { + ShadowsocksFmt.parse(str) + } else if (str.startsWith(EConfigType.SOCKS.protocolScheme)) { + SocksFmt.parse(str) + } else if (str.startsWith(EConfigType.TROJAN.protocolScheme)) { + TrojanFmt.parse(str) + } else if (str.startsWith(EConfigType.VLESS.protocolScheme)) { + VlessFmt.parse(str) + } else if (str.startsWith(EConfigType.WIREGUARD.protocolScheme)) { + WireguardFmt.parse(str) + } else if (str.startsWith(EConfigType.HYSTERIA2.protocolScheme) || str.startsWith(HY2)) { + Hysteria2Fmt.parse(str) + } else { + null + } + + if (config == null) { + return R.string.toast_incorrect_protocol + } + //filter + if (subItem?.filter != null && subItem.filter?.isNotEmpty() == true && config.remarks.isNotEmpty()) { + val matched = Regex(pattern = subItem.filter ?: "") + .containsMatchIn(input = config.remarks) + if (!matched) return -1 + } + + config.subscriptionId = subid + val guid = MmkvManager.encodeServerConfig("", config) + if (removedSelectedServer != null && + config.server == removedSelectedServer.server && config.serverPort == removedSelectedServer.serverPort + ) { + MmkvManager.setSelectServer(guid) + } + } catch (e: Exception) { + e.printStackTrace() + return -1 + } + return 0 + } + /** * Updates the configuration via all subscriptions. * @@ -420,7 +421,7 @@ object AngConfigManager { var configText = try { val httpPort = SettingsManager.getHttpPort() - HttpUtil.getUrlContentWithUserAgent(url, 30000, httpPort) + HttpUtil.getUrlContentWithUserAgent(url, 15000, httpPort) } catch (e: Exception) { Log.e(AppConfig.ANG_PACKAGE, "Update subscription: proxy not ready or other error, try……") //e.printStackTrace() diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt index 6ac62c20cf..d607f0e5d8 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt @@ -135,7 +135,7 @@ object SpeedtestManager { var result: String var elapsed = -1L - val conn = HttpUtil.createProxyConnection(SettingsManager.getDelayTestUrl(), port, 30000, 30000) ?: return Pair(elapsed, "") + val conn = HttpUtil.createProxyConnection(SettingsManager.getDelayTestUrl(), port, 15000, 15000) ?: return Pair(elapsed, "") try { val start = SystemClock.elapsedRealtime() val code = conn.responseCode diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt index e0af5755e1..edbf8811c9 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt @@ -97,7 +97,6 @@ object V2rayConfigManager { } } - //取得默认配置 val assets = Utils.readTextFromAssets(context, "v2ray_config.json") if (TextUtils.isEmpty(assets)) { return result @@ -284,7 +283,7 @@ object V2rayConfigManager { ) } - // DNS inbound对象 + // DNS inbound val remoteDns = SettingsManager.getRemoteDnsServers() if (v2rayConfig.inbounds.none { e -> e.protocol == "dokodemo-door" && e.tag == "dns-in" }) { val dnsInboundSettings = V2rayConfig.InboundBean.InSettingsBean( @@ -309,7 +308,7 @@ object V2rayConfigManager { ) } - // DNS outbound对象 + // DNS outbound if (v2rayConfig.outbounds.none { e -> e.protocol == "dns" && e.tag == "dns-out" }) { v2rayConfig.outbounds.add( V2rayConfig.OutboundBean( @@ -416,7 +415,7 @@ object V2rayConfigManager { hosts[DNS_YANDEX_DOMAIN] = DNS_YANDEX_ADDRESSES - // DNS dns对象 + // DNS dns v2rayConfig.dns = V2rayConfig.DnsBean( servers = servers, hosts = hosts diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt index 8bdbc6d1fd..193489baa0 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt @@ -9,10 +9,10 @@ import com.v2ray.ang.AppConfig.MSG_MEASURE_CONFIG_SUCCESS import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.extension.serializable import com.v2ray.ang.handler.MmkvManager +import com.v2ray.ang.handler.SpeedtestManager import com.v2ray.ang.handler.V2rayConfigManager import com.v2ray.ang.util.MessageUtil import com.v2ray.ang.util.PluginUtil -import com.v2ray.ang.handler.SpeedtestManager import com.v2ray.ang.util.Utils import go.Seq import kotlinx.coroutines.CoroutineScope diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt index 2561884112..a6ca521d89 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt @@ -8,11 +8,9 @@ import android.net.Uri import android.net.VpnService import android.os.Build import android.os.Bundle -import android.text.TextUtils import android.view.KeyEvent import android.view.Menu import android.view.MenuItem -import android.widget.Toast import androidx.activity.OnBackPressedCallback import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels @@ -38,14 +36,12 @@ import com.v2ray.ang.handler.MigrateManager import com.v2ray.ang.handler.MmkvManager import com.v2ray.ang.helper.SimpleItemTouchHelperCallback import com.v2ray.ang.service.V2RayServiceManager -import com.v2ray.ang.util.HttpUtil import com.v2ray.ang.util.Utils import com.v2ray.ang.viewmodel.MainViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import me.drakeet.support.toast.ToastCompat class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener { private val binding by lazy { @@ -87,13 +83,16 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList when (pendingAction) { Action.IMPORT_QR_CODE_CONFIG -> scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java)) - Action.IMPORT_QR_CODE_URL -> - scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java)) + +// Action.IMPORT_QR_CODE_URL -> +// scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java)) + Action.READ_CONTENT_FROM_URI -> chooseFileForCustomConfig.launch(Intent.createChooser(Intent(Intent.ACTION_GET_CONTENT).apply { type = "*/*" addCategory(Intent.CATEGORY_OPENABLE) }, getString(R.string.title_file_chooser))) + Action.POST_NOTIFICATIONS -> {} else -> {} } @@ -108,7 +107,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList enum class Action { NONE, IMPORT_QR_CODE_CONFIG, - IMPORT_QR_CODE_URL, + + //IMPORT_QR_CODE_URL, READ_CONTENT_FROM_URI, POST_NOTIFICATIONS } @@ -126,11 +126,11 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } } - private val scanQRCodeForUrlToCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - if (it.resultCode == RESULT_OK) { - importConfigCustomUrl(it.data?.getStringExtra("SCAN_RESULT")) - } - } +// private val scanQRCodeForUrlToCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { +// if (it.resultCode == RESULT_OK) { +// importConfigCustomUrl(it.data?.getStringExtra("SCAN_RESULT")) +// } +// } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -326,6 +326,11 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList true } + R.id.import_local -> { + importConfigLocal() + true + } + R.id.import_manually_vmess -> { importManually(EConfigType.VMESS.value) true @@ -366,25 +371,25 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList true } - R.id.import_config_custom_clipboard -> { - importConfigCustomClipboard() - true - } - - R.id.import_config_custom_local -> { - importConfigCustomLocal() - true - } - - R.id.import_config_custom_url -> { - importConfigCustomUrlClipboard() - true - } - - R.id.import_config_custom_url_scan -> { - importQRcode(false) - true - } +// R.id.import_config_custom_clipboard -> { +// importConfigCustomClipboard() +// true +// } +// +// R.id.import_config_custom_local -> { +// importConfigCustomLocal() +// true +// } +// +// R.id.import_config_custom_url -> { +// importConfigCustomUrlClipboard() +// true +// } +// +// R.id.import_config_custom_url_scan -> { +// importQRcode(false) +// true +// } R.id.sub_update -> { importConfigViaSub() @@ -517,10 +522,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList if (forConfig) { scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java)) } else { - scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java)) + //scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java)) } } else { - pendingAction = if (forConfig) Action.IMPORT_QR_CODE_CONFIG else Action.IMPORT_QR_CODE_URL + pendingAction = Action.IMPORT_QR_CODE_CONFIG//if (forConfig) Action.IMPORT_QR_CODE_CONFIG else Action.IMPORT_QR_CODE_URL requestPermissionLauncher.launch(permission) } return true @@ -570,27 +575,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } } - - private fun importConfigCustomClipboard() - : Boolean { - try { - val configText = Utils.getClipboard(this) - if (TextUtils.isEmpty(configText)) { - toast(R.string.toast_none_data_clipboard) - return false - } - importCustomizeConfig(configText) - return true - } catch (e: Exception) { - e.printStackTrace() - return false - } - } - - /** - * import config from local config file - */ - private fun importConfigCustomLocal(): Boolean { + private fun importConfigLocal(): Boolean { try { showFileChooser() } catch (e: Exception) { @@ -600,47 +585,77 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList return true } - private fun importConfigCustomUrlClipboard() - : Boolean { - try { - val url = Utils.getClipboard(this) - if (TextUtils.isEmpty(url)) { - toast(R.string.toast_none_data_clipboard) - return false - } - return importConfigCustomUrl(url) - } catch (e: Exception) { - e.printStackTrace() - return false - } - } + +// private fun importConfigCustomClipboard() +// : Boolean { +// try { +// val configText = Utils.getClipboard(this) +// if (TextUtils.isEmpty(configText)) { +// toast(R.string.toast_none_data_clipboard) +// return false +// } +// importCustomizeConfig(configText) +// return true +// } catch (e: Exception) { +// e.printStackTrace() +// return false +// } +// } + + /** + * import config from local config file + */ +// private fun importConfigCustomLocal(): Boolean { +// try { +// showFileChooser() +// } catch (e: Exception) { +// e.printStackTrace() +// return false +// } +// return true +// } +// +// private fun importConfigCustomUrlClipboard() +// : Boolean { +// try { +// val url = Utils.getClipboard(this) +// if (TextUtils.isEmpty(url)) { +// toast(R.string.toast_none_data_clipboard) +// return false +// } +// return importConfigCustomUrl(url) +// } catch (e: Exception) { +// e.printStackTrace() +// return false +// } +// } /** * import config from url */ - private fun importConfigCustomUrl(url: String?): Boolean { - try { - if (!Utils.isValidUrl(url)) { - toast(R.string.toast_invalid_url) - return false - } - lifecycleScope.launch(Dispatchers.IO) { - val configText = try { - HttpUtil.getUrlContentWithUserAgent(url) - } catch (e: Exception) { - e.printStackTrace() - "" - } - launch(Dispatchers.Main) { - importCustomizeConfig(configText) - } - } - } catch (e: Exception) { - e.printStackTrace() - return false - } - return true - } +// private fun importConfigCustomUrl(url: String?): Boolean { +// try { +// if (!Utils.isValidUrl(url)) { +// toast(R.string.toast_invalid_url) +// return false +// } +// lifecycleScope.launch(Dispatchers.IO) { +// val configText = try { +// HttpUtil.getUrlContentWithUserAgent(url) +// } catch (e: Exception) { +// e.printStackTrace() +// "" +// } +// launch(Dispatchers.Main) { +// importCustomizeConfig(configText) +// } +// } +// } catch (e: Exception) { +// e.printStackTrace() +// return false +// } +// return true +// } /** * import config from sub @@ -699,7 +714,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) { try { contentResolver.openInputStream(uri).use { input -> - importCustomizeConfig(input?.bufferedReader()?.readText()) + importBatchConfig(input?.bufferedReader()?.readText()) } } catch (e: Exception) { e.printStackTrace() @@ -709,28 +724,28 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } } - /** - * import customize config - */ - private fun importCustomizeConfig(server: String?) { - try { - if (server == null || TextUtils.isEmpty(server)) { - toast(R.string.toast_none_data) - return - } - if (mainViewModel.appendCustomConfigServer(server)) { - mainViewModel.reloadServerList() - toast(R.string.toast_success) - } else { - toast(R.string.toast_failure) - } - //adapter.notifyItemInserted(mainViewModel.serverList.lastIndex) - } catch (e: Exception) { - ToastCompat.makeText(this, "${getString(R.string.toast_malformed_josn)} ${e.cause?.message}", Toast.LENGTH_LONG).show() - e.printStackTrace() - return - } - } +// /** +// * import customize config +// */ +// private fun importCustomizeConfig(server: String?) { +// try { +// if (server == null || TextUtils.isEmpty(server)) { +// toast(R.string.toast_none_data) +// return +// } +// if (mainViewModel.appendCustomConfigServer(server)) { +// mainViewModel.reloadServerList() +// toast(R.string.toast_success) +// } else { +// toast(R.string.toast_failure) +// } +// //adapter.notifyItemInserted(mainViewModel.serverList.lastIndex) +// } catch (e: Exception) { +// ToastCompat.makeText(this, "${getString(R.string.toast_malformed_josn)} ${e.cause?.message}", Toast.LENGTH_LONG).show() +// e.printStackTrace() +// return +// } +// } private fun setTestState(content: String?) { binding.tvTestState.text = content @@ -768,11 +783,14 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList ) } + R.id.per_app_proxy_settings -> { + startActivity(Intent(this, PerAppProxyActivity::class.java)) + } + R.id.routing_setting -> { requestSubSettingActivity.launch(Intent(this, RoutingSettingActivity::class.java)) } - R.id.promotion -> { Utils.openUri(this, "${Utils.decode(AppConfig.PromotionUrl)}?t=${System.currentTimeMillis()}") } diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt index f1b2cb8219..05036b2d45 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt @@ -83,7 +83,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter app.isSelected = if (blacklist.contains(app.packageName)) 1 else 0 @@ -69,9 +67,9 @@ class PerAppProxyActivity : BaseActivity() { appsAll = apps adapter = PerAppProxyAdapter(this@PerAppProxyActivity, apps, blacklist) binding.recyclerView.adapter = adapter - binding.pbWaiting.visibility = View.GONE + binding.pbWaiting.hide() } catch (e: Exception) { - binding.pbWaiting.visibility = View.GONE + binding.pbWaiting.hide() Log.e(ANG_PACKAGE, "Error loading apps", e) } } @@ -152,13 +150,20 @@ class PerAppProxyActivity : BaseActivity() { private fun selectProxyApp() { toast(R.string.msg_downloading_content) + binding.pbWaiting.show() + val url = AppConfig.androidpackagenamelistUrl lifecycleScope.launch(Dispatchers.IO) { - val content = HttpUtil.getUrlContent(url, 5000) + var content = HttpUtil.getUrlContent(url, 5000) + if (content.isNullOrEmpty()) { + val httpPort = SettingsManager.getHttpPort() + content = HttpUtil.getUrlContent(url, 5000, httpPort) ?: "" + } launch(Dispatchers.Main) { Log.d(ANG_PACKAGE, content) selectProxyApp(content, true) toast(R.string.toast_success) + binding.pbWaiting.hide() } } } diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyAdapter.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyAdapter.kt index deb585fea7..0f150de3ab 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyAdapter.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyAdapter.kt @@ -35,7 +35,7 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List, bl val view = View(ctx) view.layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, - ctx.resources.getDimensionPixelSize(R.dimen.bypass_list_header_height) * 0 + ctx.resources.getDimensionPixelSize(R.dimen.view_height_dp64) * 0 ) BaseViewHolder(view) } diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingActivity.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingActivity.kt index 3fe8d85a54..2957c59bb8 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingActivity.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingActivity.kt @@ -4,13 +4,11 @@ import android.content.Intent import android.os.Bundle import android.view.Menu import android.view.MenuItem -import androidx.appcompat.app.AlertDialog import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivitySubSettingBinding -import com.v2ray.ang.databinding.LayoutProgressBinding import com.v2ray.ang.dto.SubscriptionItem import com.v2ray.ang.extension.toast import com.v2ray.ang.handler.AngConfigManager @@ -59,10 +57,7 @@ class SubSettingActivity : BaseActivity() { } R.id.sub_update -> { - val dialog = AlertDialog.Builder(this) - .setView(LayoutProgressBinding.inflate(layoutInflater).root) - .setCancelable(false) - .show() + binding.pbWaiting.show() lifecycleScope.launch(Dispatchers.IO) { val count = AngConfigManager.updateConfigViaSubAll() @@ -73,7 +68,7 @@ class SubSettingActivity : BaseActivity() { } else { toast(R.string.toast_failure) } - dialog.dismiss() + binding.pbWaiting.hide() } } diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt index 8afcb8faa1..b1274f6841 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt @@ -23,7 +23,6 @@ import com.v2ray.ang.AppConfig import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivitySubSettingBinding import com.v2ray.ang.databinding.ItemRecyclerUserAssetBinding -import com.v2ray.ang.databinding.LayoutProgressBinding import com.v2ray.ang.dto.AssetUrlItem import com.v2ray.ang.extension.toTrafficString import com.v2ray.ang.extension.toast @@ -196,32 +195,31 @@ class UserAssetActivity : BaseActivity() { } private fun downloadGeoFiles() { - val dialog = AlertDialog.Builder(this) - .setView(LayoutProgressBinding.inflate(layoutInflater).root) - .setCancelable(false) - .show() + binding.pbWaiting.show() toast(R.string.msg_downloading_content) val httpPort = SettingsManager.getHttpPort() var assets = MmkvManager.decodeAssetUrls() assets = addBuiltInGeoItems(assets) - assets.forEach { - //toast(getString(R.string.msg_downloading_content) + it) - lifecycleScope.launch(Dispatchers.IO) { - var result = downloadGeo(it.second, 60000, httpPort) + var resultCount = 0 + lifecycleScope.launch(Dispatchers.IO) { + assets.forEach { + var result = downloadGeo(it.second, 15000, httpPort) if (!result) { - result = downloadGeo(it.second, 60000, 0) + result = downloadGeo(it.second, 15000, 0) } - launch(Dispatchers.Main) { - if (result) { - toast(getString(R.string.toast_success) + " " + it.second.remarks) - binding.recyclerView.adapter?.notifyDataSetChanged() - } else { - toast(getString(R.string.toast_failure) + " " + it.second.remarks) - } - dialog.dismiss() + if (result) + resultCount++ + } + withContext(Dispatchers.Main) { + if (resultCount > 0) { + toast(getString(R.string.title_update_config_count, resultCount)) + binding.recyclerView.adapter?.notifyDataSetChanged() + } else { + toast(getString(R.string.toast_failure)) } + binding.pbWaiting.hide() } } } diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt index dc0ab7dabf..135353ad76 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt @@ -26,18 +26,18 @@ object HttpUtil { * * @param url The URL to fetch content from. * @param timeout The timeout value in milliseconds. + * @param httpPort The HTTP port to use. * @return The content of the URL as a string. */ - fun getUrlContent(url: String, timeout: Int): String { - var result: String = "" - val conn = createProxyConnection(url, 0, timeout, timeout) ?: return result + fun getUrlContent(url: String, timeout: Int, httpPort: Int = 0): String? { + val conn = createProxyConnection(url, httpPort, timeout, timeout) ?: return null try { - result = conn.inputStream.bufferedReader().readText() + return conn.inputStream.bufferedReader().readText() } catch (_: Exception) { } finally { conn.disconnect() } - return result + return null } /** @@ -50,7 +50,7 @@ object HttpUtil { * @throws IOException If an I/O error occurs. */ @Throws(IOException::class) - fun getUrlContentWithUserAgent(url: String?, timeout: Int = 30000, httpPort: Int = 0): String { + fun getUrlContentWithUserAgent(url: String?, timeout: Int = 15000, httpPort: Int = 0): String { var currentUrl = url var redirects = 0 val maxRedirects = 3 @@ -88,16 +88,16 @@ object HttpUtil { * * @param urlStr The target URL address. * @param port The port of the proxy server. - * @param connectTimeout The connection timeout in milliseconds (default is 30000 ms). - * @param readTimeout The read timeout in milliseconds (default is 30000 ms). + * @param connectTimeout The connection timeout in milliseconds (default is 15000 ms). + * @param readTimeout The read timeout in milliseconds (default is 15000 ms). * @param needStream Whether the connection needs to support streaming. * @return Returns a configured HttpURLConnection object, or null if it fails. */ fun createProxyConnection( urlStr: String, port: Int, - connectTimeout: Int = 30000, - readTimeout: Int = 30000, + connectTimeout: Int = 15000, + readTimeout: Int = 15000, needStream: Boolean = false ): HttpURLConnection? { diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt index 852571cafd..290b21a489 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt @@ -19,12 +19,11 @@ import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.dto.ServersCache import com.v2ray.ang.extension.serializable import com.v2ray.ang.extension.toast -import com.v2ray.ang.fmt.CustomFmt import com.v2ray.ang.handler.AngConfigManager import com.v2ray.ang.handler.MmkvManager import com.v2ray.ang.handler.SettingsManager -import com.v2ray.ang.util.MessageUtil import com.v2ray.ang.handler.SpeedtestManager +import com.v2ray.ang.util.MessageUtil import com.v2ray.ang.util.Utils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -89,37 +88,37 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } } - /** - * Appends a custom configuration server. - * @param server The server configuration to append. - * @return True if the server was successfully appended, false otherwise. - */ - fun appendCustomConfigServer(server: String): Boolean { - if (server.contains("inbounds") - && server.contains("outbounds") - && server.contains("routing") - ) { - try { - val config = CustomFmt.parse(server) ?: return false - config.subscriptionId = subscriptionId - val key = MmkvManager.encodeServerConfig("", config) - MmkvManager.encodeServerRaw(key, server) - serverList.add(0, key) -// val profile = ProfileLiteItem( -// configType = config.configType, -// subscriptionId = config.subscriptionId, -// remarks = config.remarks, -// server = config.getProxyOutbound()?.getServerAddress(), -// serverPort = config.getProxyOutbound()?.getServerPort(), -// ) - serversCache.add(0, ServersCache(key, config)) - return true - } catch (e: Exception) { - e.printStackTrace() - } - } - return false - } +// /** +// * Appends a custom configuration server. +// * @param server The server configuration to append. +// * @return True if the server was successfully appended, false otherwise. +// */ +// fun appendCustomConfigServer(server: String): Boolean { +// if (server.contains("inbounds") +// && server.contains("outbounds") +// && server.contains("routing") +// ) { +// try { +// val config = CustomFmt.parse(server) ?: return false +// config.subscriptionId = subscriptionId +// val key = MmkvManager.encodeServerConfig("", config) +// MmkvManager.encodeServerRaw(key, server) +// serverList.add(0, key) +//// val profile = ProfileLiteItem( +//// configType = config.configType, +//// subscriptionId = config.subscriptionId, +//// remarks = config.remarks, +//// server = config.getProxyOutbound()?.getServerAddress(), +//// serverPort = config.getProxyOutbound()?.getServerPort(), +//// ) +// serversCache.add(0, ServersCache(key, config)) +// return true +// } catch (e: Exception) { +// e.printStackTrace() +// } +// } +// return false +// } /** * Swaps the positions of two servers. diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_per_apps_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_per_apps_24dp.xml new file mode 100644 index 0000000000..99d8212e7e --- /dev/null +++ b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_per_apps_24dp.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_per_apps_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_per_apps_24dp.xml new file mode 100644 index 0000000000..c45cd8c5eb --- /dev/null +++ b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_per_apps_24dp.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/activity_about.xml b/v2rayng/V2rayNG/app/src/main/res/layout/activity_about.xml index 15262fd6ac..4651bafefc 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/activity_about.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/activity_about.xml @@ -24,18 +24,18 @@ android:focusable="true" android:gravity="center|start" android:orientation="horizontal" - android:padding="@dimen/padding"> + android:padding="@dimen/padding_spacing_dp16"> + android:paddingStart="@dimen/padding_spacing_dp16"> @@ -58,23 +58,23 @@ + android:padding="@dimen/padding_spacing_dp16"> @@ -82,23 +82,23 @@ + android:padding="@dimen/padding_spacing_dp16"> @@ -109,28 +109,28 @@ android:layout_height="match_parent" android:gravity="top" android:orientation="vertical" - android:paddingTop="@dimen/padding_start"> + android:paddingTop="@dimen/padding_spacing_dp16"> + android:padding="@dimen/padding_spacing_dp16"> @@ -138,23 +138,23 @@ + android:padding="@dimen/padding_spacing_dp16"> @@ -162,23 +162,23 @@ + android:padding="@dimen/padding_spacing_dp16"> @@ -187,23 +187,23 @@ + android:padding="@dimen/padding_spacing_dp16"> @@ -211,33 +211,33 @@ + android:padding="@dimen/padding_spacing_dp16"> + android:padding="@dimen/padding_spacing_dp16"> + + + android:paddingStart="@dimen/padding_spacing_dp16" + android:paddingEnd="@dimen/padding_spacing_dp16"> @@ -71,12 +79,6 @@ - - @@ -63,7 +63,7 @@ @@ -89,7 +89,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" - android:layout_marginBottom="12dp"> + android:layout_marginBottom="@dimen/margin_spacing_dp16"> + android:padding="@dimen/margin_spacing_dp16"> @@ -143,7 +144,7 @@ @@ -162,7 +163,7 @@ @@ -182,8 +183,8 @@ diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml b/v2rayng/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml index 3032d6ce9d..17038020cc 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml @@ -20,7 +20,7 @@ @@ -39,10 +39,10 @@ + android:paddingStart="@dimen/padding_spacing_dp16"> @@ -65,7 +65,7 @@ @@ -73,7 +73,7 @@ android:id="@+id/editor" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginTop="@dimen/layout_margin_top_height" + android:layout_marginTop="@dimen/margin_spacing_dp16" android:gravity="top|start" /> diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml b/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml index a0228555b5..6ad4ffc03f 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml @@ -10,14 +10,14 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:padding="@dimen/layout_margin_top_height"> + android:padding="@dimen/margin_spacing_dp16"> @@ -36,7 +36,7 @@ @@ -55,7 +55,7 @@ @@ -74,7 +74,7 @@ @@ -93,7 +93,7 @@ @@ -112,7 +112,7 @@ @@ -133,8 +133,8 @@ diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml b/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml index 83dbfc9695..ed4466e8e4 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml @@ -10,14 +10,14 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:padding="@dimen/layout_margin_top_height"> + android:padding="@dimen/margin_spacing_dp16"> @@ -37,7 +37,7 @@ @@ -59,8 +59,8 @@ diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_socks.xml b/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_socks.xml index 42f62a3f22..3f0c20e74d 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_socks.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_socks.xml @@ -10,14 +10,14 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:padding="@dimen/layout_margin_top_height"> + android:padding="@dimen/margin_spacing_dp16"> @@ -37,7 +37,7 @@ @@ -56,8 +56,8 @@ diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml b/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml index b98753d0be..8059d8507b 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml @@ -10,14 +10,14 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:padding="@dimen/layout_margin_top_height"> + android:padding="@dimen/margin_spacing_dp16"> @@ -40,8 +40,8 @@ diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_vless.xml b/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_vless.xml index c7bf624b42..47bae4ced9 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_vless.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_vless.xml @@ -10,14 +10,14 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:padding="@dimen/layout_margin_top_height"> + android:padding="@dimen/margin_spacing_dp16"> @@ -36,7 +36,7 @@ @@ -78,8 +78,8 @@ diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_vmess.xml b/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_vmess.xml index 413127e408..b541a67d4b 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_vmess.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_vmess.xml @@ -10,14 +10,14 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:padding="@dimen/layout_margin_top_height"> + android:padding="@dimen/margin_spacing_dp16"> @@ -36,7 +36,7 @@ @@ -58,8 +58,8 @@ diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_wireguard.xml b/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_wireguard.xml index 93bc43f750..498d15a731 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_wireguard.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/activity_server_wireguard.xml @@ -10,14 +10,14 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:padding="@dimen/layout_margin_top_height"> + android:padding="@dimen/margin_spacing_dp16"> @@ -36,7 +36,7 @@ @@ -75,7 +75,7 @@ @@ -100,7 +100,7 @@ @@ -137,8 +137,8 @@ diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml b/v2rayng/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml index 504c1e076f..aecaf02ef2 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml @@ -16,7 +16,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:padding="@dimen/layout_margin_top_height"> + android:padding="@dimen/margin_spacing_dp16"> @@ -52,7 +52,7 @@ @@ -96,7 +96,7 @@ @@ -118,7 +118,7 @@ @@ -141,7 +141,7 @@ @@ -161,7 +161,7 @@ @@ -182,8 +182,8 @@ diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/activity_sub_setting.xml b/v2rayng/V2rayNG/app/src/main/res/layout/activity_sub_setting.xml index ed34fc22b6..769cf51313 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/activity_sub_setting.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/activity_sub_setting.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + xmlns:app="http://schemas.android.com/apk/res-auto" android:fitsSystemWindows="true" tools:context=".ui.SubSettingActivity"> @@ -12,6 +13,14 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + android:layout_height="@dimen/view_height_dp64"> + android:padding="@dimen/margin_spacing_dp16"> @@ -51,7 +51,7 @@ diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml b/v2rayng/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml index 1c043f4f05..4b9b97acdd 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml @@ -7,25 +7,25 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:padding="@dimen/layout_margin_top_height"> + android:padding="@dimen/margin_spacing_dp16"> + android:layout_height="@dimen/view_height_dp48" /> diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/item_qrcode.xml b/v2rayng/V2rayNG/app/src/main/res/layout/item_qrcode.xml index d5997b3f1b..d3ea9075ee 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/item_qrcode.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/item_qrcode.xml @@ -9,8 +9,8 @@ diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml b/v2rayng/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml index fee1de3018..1ca2e9debc 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml @@ -9,13 +9,13 @@ + android:layout_width="@dimen/view_height_dp72" + android:layout_height="@dimen/view_height_dp72" + android:padding="@dimen/padding_spacing_dp16" /> @@ -32,7 +32,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:maxLines="3" - android:paddingTop="@dimen/layout_margin_spacing" + android:paddingTop="@dimen/margin_spacing_dp8" android:textAppearance="@style/TextAppearance.AppCompat.Small" /> @@ -42,6 +42,6 @@ android:layout_height="wrap_content" android:clickable="false" android:focusable="false" - android:padding="@dimen/padding"/> + android:paddingEnd="@dimen/padding_spacing_dp16"/> \ No newline at end of file diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/item_recycler_footer.xml b/v2rayng/V2rayNG/app/src/main/res/layout/item_recycler_footer.xml index 6c4df973c8..e02cdc4341 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/item_recycler_footer.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/item_recycler_footer.xml @@ -8,18 +8,18 @@ + android:padding="@dimen/padding_spacing_dp16"> + android:paddingBottom="@dimen/margin_spacing_dp16"> + android:layout_margin="@dimen/margin_spacing_dp8" + android:paddingTop="@dimen/margin_spacing_dp8" + android:paddingBottom="@dimen/margin_spacing_dp8"> + android:paddingStart="@dimen/padding_spacing_dp16"> + android:paddingEnd="@dimen/padding_spacing_dp16"> + android:padding="@dimen/margin_spacing_dp8"> @@ -128,17 +128,17 @@ + android:padding="@dimen/margin_spacing_dp8"> @@ -146,17 +146,17 @@ + android:padding="@dimen/margin_spacing_dp8"> @@ -168,7 +168,7 @@ android:layout_gravity="right" android:orientation="vertical" android:paddingBottom="4dp" - android:paddingEnd="@dimen/padding_end"> + android:paddingEnd="@dimen/padding_spacing_dp16"> + android:paddingTop="@dimen/margin_spacing_dp8" + android:paddingBottom="@dimen/margin_spacing_dp8"> + android:paddingStart="@dimen/padding_spacing_dp16"> @@ -53,7 +53,7 @@ android:id="@+id/domainIp" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/layout_margin_spacing" + android:layout_marginTop="@dimen/margin_spacing_dp8" android:lines="1" android:textAppearance="@style/TextAppearance.AppCompat.Small" /> @@ -61,7 +61,7 @@ android:id="@+id/outboundTag" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/layout_margin_spacing" + android:layout_marginTop="@dimen/margin_spacing_dp8" android:lines="1" android:textAppearance="@style/TextAppearance.AppCompat.Small" /> @@ -69,11 +69,11 @@ + android:paddingStart="@dimen/padding_spacing_dp16" + android:paddingEnd="@dimen/padding_spacing_dp16"> + android:padding="@dimen/margin_spacing_dp8"> + android:paddingTop="@dimen/margin_spacing_dp8" + android:paddingBottom="@dimen/margin_spacing_dp8"> + android:paddingStart="@dimen/padding_spacing_dp16"> @@ -46,11 +46,11 @@ + android:paddingStart="@dimen/padding_spacing_dp16" + android:paddingEnd="@dimen/padding_spacing_dp16"> + android:padding="@dimen/margin_spacing_dp8"> @@ -86,11 +86,11 @@ android:gravity="center" android:nextFocusLeft="@+id/info_container" android:orientation="vertical" - android:padding="@dimen/layout_margin_spacing"> + android:padding="@dimen/margin_spacing_dp8"> @@ -99,7 +99,7 @@ + android:padding="@dimen/margin_spacing_dp8"> + android:paddingStart="@dimen/padding_spacing_dp16"> + android:padding="@dimen/margin_spacing_dp8"> @@ -76,11 +76,11 @@ android:background="?attr/selectableItemBackgroundBorderless" android:clickable="true" android:focusable="true" - android:padding="@dimen/nav_header_vertical_spacing"> + android:padding="@dimen/margin_spacing_dp8"> diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/layout_address_port.xml b/v2rayng/V2rayNG/app/src/main/res/layout/layout_address_port.xml index 66c46609c2..b34c8b5fca 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/layout_address_port.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/layout_address_port.xml @@ -19,7 +19,7 @@ @@ -38,7 +38,7 @@ @@ -57,7 +57,7 @@ diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/layout_progress.xml b/v2rayng/V2rayNG/app/src/main/res/layout/layout_progress.xml deleted file mode 100644 index 8e431cad0b..0000000000 --- a/v2rayng/V2rayNG/app/src/main/res/layout/layout_progress.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/layout_tls.xml b/v2rayng/V2rayNG/app/src/main/res/layout/layout_tls.xml index 1c0c5589f1..affb1b4018 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/layout_tls.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/layout_tls.xml @@ -2,14 +2,14 @@ @@ -21,7 +21,7 @@ @@ -31,7 +31,7 @@ android:id="@+id/lay_sni" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/activity_horizontal_margin" + android:layout_marginBottom="@dimen/margin_spacing_dp16" android:orientation="vertical"> @@ -53,7 +53,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:layout_marginBottom="@dimen/activity_horizontal_margin"> + android:layout_marginBottom="@dimen/margin_spacing_dp16"> @@ -74,7 +74,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:layout_marginBottom="@dimen/activity_horizontal_margin"> + android:layout_marginBottom="@dimen/margin_spacing_dp16"> @@ -104,7 +104,7 @@ @@ -112,7 +112,7 @@ android:id="@+id/lay_public_key" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/activity_horizontal_margin" + android:layout_marginBottom="@dimen/margin_spacing_dp16" android:orientation="vertical"> @@ -133,7 +133,7 @@ android:id="@+id/lay_short_id" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/activity_horizontal_margin" + android:layout_marginBottom="@dimen/margin_spacing_dp16" android:orientation="vertical"> @@ -154,7 +154,7 @@ android:id="@+id/lay_spider_x" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/activity_horizontal_margin" + android:layout_marginBottom="@dimen/margin_spacing_dp16" android:orientation="vertical"> diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/layout_tls_hysteria2.xml b/v2rayng/V2rayNG/app/src/main/res/layout/layout_tls_hysteria2.xml index 3180f666a9..6814af4ef0 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/layout_tls_hysteria2.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/layout_tls_hysteria2.xml @@ -2,14 +2,14 @@ @@ -21,7 +21,7 @@ @@ -31,7 +31,7 @@ android:id="@+id/lay_sni" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/activity_horizontal_margin" + android:layout_marginBottom="@dimen/margin_spacing_dp16" android:orientation="vertical"> @@ -63,14 +63,14 @@ diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/layout_transport.xml b/v2rayng/V2rayNG/app/src/main/res/layout/layout_transport.xml index bda1e11c2c..12136d59bd 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/layout_transport.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/layout_transport.xml @@ -7,7 +7,7 @@ + android:layout_height="@dimen/view_height_dp48" /> @@ -76,7 +76,7 @@ @@ -96,7 +96,7 @@ android:id="@+id/layout_extra" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/layout_margin_top_height" + android:layout_marginTop="@dimen/margin_spacing_dp16" android:orientation="vertical"> + android:padding="@dimen/margin_spacing_dp16"> diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/widget_switch.xml b/v2rayng/V2rayNG/app/src/main/res/layout/widget_switch.xml index bf0bfc71de..50776f870d 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/widget_switch.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/widget_switch.xml @@ -18,7 +18,7 @@ android:id="@+id/image_switch" android:layout_width="45dp" android:layout_height="45dp" - android:padding="@dimen/padding" + android:padding="@dimen/padding_spacing_dp16" app:srcCompat="@drawable/ic_stat_name" /> diff --git a/v2rayng/V2rayNG/app/src/main/res/menu/menu_drawer.xml b/v2rayng/V2rayNG/app/src/main/res/menu/menu_drawer.xml index 3c739e1b59..a2b17dd18e 100644 --- a/v2rayng/V2rayNG/app/src/main/res/menu/menu_drawer.xml +++ b/v2rayng/V2rayNG/app/src/main/res/menu/menu_drawer.xml @@ -12,6 +12,10 @@ android:id="@+id/settings" android:icon="@drawable/ic_settings_24dp" android:title="@string/title_settings" /> + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + حذف التكوين استيراد التكوين من رمز الاستجابة السريعة (QRcode) استيراد التكوين من الحافظة + Import config from locally الكتابة يدويًا [VMess] الكتابة يدويًا [VLESS] الكتابة يدويًا [Shadowsocks] @@ -106,6 +107,17 @@ تأكد من أن منفذ الاتصالات الواردة يتوافق مع الإعدادات تكوين مشوه مضيف (SNI) (اختياري) + الإجراء غير مسموح به + Obfs password + Port Hopping + Port Hopping Interval + pinSHA256 + Bandwidth down (units) + Bandwidth up (units) + XHTTP Mode + XHTTP Extra raw JSON, format: { XHTTPObject } + + فشل نسخ الملف، يرجى استخدام مدير الملفات إضافة أصل إضافة ملفات @@ -116,15 +128,6 @@ إضافة عنوان URL للأصل الملف غير موجود الملاحظات موجودة بالفعل - الإجراء غير مسموح به - Obfs password - Port Hopping - Port Hopping Interval - pinSHA256 - Bandwidth down (units) - Bandwidth up (units) - XHTTP Mode - XHTTP Extra raw JSON, format: { XHTTPObject } جار التحميل @@ -136,6 +139,8 @@ جارٍ تنزيل المحتوى تصدير إلى الحافظة استيراد من الحافظة + Per-app settings + Enable per-app @@ -212,6 +217,7 @@ Append HTTP Proxy to VPN HTTP proxy will be used directly from (browser/ some supported apps), without going through the virtual NIC device (Android 10+) + ملاحظات ملاحظات التحسينات أو الأخطاء إلى GitHub الانضمام إلى مجموعة Telegram @@ -273,6 +279,7 @@ بدء الخدمة تأكيد + استراتيجية النطاق إعدادات التوجيه مفصولة بفواصل (،)، تذكر الحفظ diff --git a/v2rayng/V2rayNG/app/src/main/res/values-bn/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values-bn/strings.xml index 272ee1ea82..970689e392 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values-bn/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values-bn/strings.xml @@ -27,6 +27,7 @@ কনফিগারেশন মুছুন QR কোড থেকে কনফিগারেশন আমদানি করুন ক্লিপবোর্ড থেকে কনফিগারেশন আমদানি করুন + Import config from locally ম্যানুয়ালি টাইপ করুন [VMess] ম্যানুয়ালি টাইপ করুন [VLESS] ম্যানুয়ালি টাইপ করুন [Shadowsocks] @@ -105,6 +106,17 @@ ইনবাউন্ড পোর্ট নিশ্চিত করুন সেটিংসের সাথে সামঞ্জস্যপূর্ণ কনফিগারেশন বিকৃত হোস্ট (SNI) (ঐচ্ছিক) + অ্যাকশন অনুমোদিত নয় + Obfs password + Port Hopping + Port Hopping Interval + pinSHA256 + Bandwidth down (units) + Bandwidth up (units) + XHTTP Mode + XHTTP Extra raw JSON, format: { XHTTPObject } + + ফাইল কপি ব্যর্থ, অনুগ্রহ করে ফাইল ম্যানেজার ব্যবহার করুন অ্যাসেট যোগ করুন ফাইল যোগ করুন @@ -115,15 +127,6 @@ অ্যাসেট URL যোগ করুন ফাইল খুঁজে পাওয়া যায়নি মন্তব্য ইতিমধ্যে বিদ্যমান - অ্যাকশন অনুমোদিত নয় - Obfs password - Port Hopping - Port Hopping Interval - pinSHA256 - Bandwidth down (units) - Bandwidth up (units) - XHTTP Mode - XHTTP Extra raw JSON, format: { XHTTPObject } লোড হচ্ছে @@ -135,6 +138,8 @@ বিষয়বস্তু ডাউনলোড হচ্ছে ক্লিপবোর্ডে রপ্তানি করুন ক্লিপবোর্ড থেকে আমদানি করুন + Per-app settings + Enable per-app সেটিংস @@ -212,6 +217,7 @@ Append HTTP Proxy to VPN HTTP proxy will be used directly from (browser/ some supported apps), without going through the virtual NIC device (Android 10+) + মতামত মতামত উন্নয়ন বা বাগগুলি GitHub-এ পাঠান টেলিগ্রাম গ্রুপে যোগদান করুন @@ -272,6 +278,7 @@ সার্ভিস শুরু করুন নিশ্চিত করুন + ডোমেইন কৌশল রাউটিং সেটিংস কমা (,) দ্বারা আলাদা করুন, মনে রাখবেন সেভ করতে diff --git a/v2rayng/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml index 7e31d20f85..99b0414444 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -27,6 +27,7 @@ پاک کردن کانفیگ و من ٱووردن کانفیگ ز QRcode و من ٱووردن کانفیگ ز کلیپ بورد + Import config from locally هؽل دستی[VMess] هؽل دستی[VLESS] هؽل دستی[Shadowsocks] @@ -105,6 +106,17 @@ موتمعن بۊین ک پورت وۊرۊڌی وا سامووا ی جۊر هڌ کانفیگ زبال نؽڌ هاست(SNI)(اختیاری) + ای کار ممنۊ هڌ + رزم obfs + پورت گوم (درگا سرورن ز نۊ هؽل اکونه) + فاسله پورت گوم (سانیه) + pinSHA256 + ب لم ٱووڌن پئنا باند (واهڌ) + وا روء رئڌن پئنا باند (واهڌ) + هالت XHTTP + XHTTP Extra خام JSON، قالوو: { XHTTPObject } + + لف گیری فایل ٱنجوم نوابی، ز ی برنومه دؽوۉداری فایل هیاری بگرین ازاف کردن دارایی ازاف کردن فایل @@ -115,15 +127,6 @@ آدرس اینترنتی دارایین ازاف کۊنین فایلن نجوست ائزارات ز زیتر بیڌسۉݩ - ای کار ممنۊ هڌ - رزم obfs - پورت گوم (درگا سرورن ز نۊ هؽل اکونه) - فاسله پورت گوم (سانیه) - pinSHA256 - ب لم ٱووڌن پئنا باند (واهڌ) - وا روء رئڌن پئنا باند (واهڌ) - هالت XHTTP - XHTTP Extra خام JSON، قالوو: { XHTTPObject } هون بار ونی بۊ @@ -135,6 +138,8 @@ موئتوا هونی دانلود ابۊن و در کشیڌن من کلیپ بورد و من ٱووردن ز کلیپ بورد + Per-app settings + Enable per-app @@ -212,6 +217,7 @@ پروکسی HTTP ن و VPN ازاف کۊنین پروکسی HTTP ن موسقیمن ز (مۊرۊرگر/ی قرد ز برنومیل لادراری بیڌه)، بؽ استفاڌه ز دسگا NIC مجازی (Android 10+) استفاڌه ابۊ. + فشناڌن منشڌ فشناڌن منشڌ یا داسوو موشکلا من Github ٱووڌن من جرگه تلگرام @@ -273,6 +279,7 @@ ر وندن خدمات قوۊل + نشقه دامنه سامووا تور جوستن وا کاما ز یک جوڌا ابۊن (،) پسند دامنه یا آی پی diff --git a/v2rayng/V2rayNG/app/src/main/res/values-fa/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values-fa/strings.xml index 7e7952392b..77815bffa0 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -27,6 +27,7 @@ حذف کانفیگ کانفیگ را از QRcode وارد کنید کانفیگ را از کلیپ ‌بورد وارد کنید + Import config from locally تایپ دستی[VMESS] تایپ دستی[VLESS] تایپ دستی[SHADOWSOCKS] @@ -105,11 +106,6 @@ اطمینان حاصل کنید که پورت ورودی با تنظیمات مطابقت دارد کانفیگ درست نیست هاست (SNI) (اختیاری) - کپی فایل انجام نشد، لطفا از برنامه مدیریت فایل استفاده کنید - افزودن فایل ‌ها - اسکن QRcode - URL - دانلود فایل‌ ها این عمل ممنوع است رمز عبور obfs پورت پرش (درگاه سرور را بازنویسی می کند) @@ -120,10 +116,17 @@ حالت XHTTP خام JSON XHTTP Extra، قالب: { XHTTPObject } - + + کپی فایل انجام نشد، لطفا از برنامه مدیریت فایل استفاده کنید + افزودن فایل ‌ها + اسکن QRcode + URL + دانلود فایل‌ ها آدرس اینترنتی را اضافه کنید فایل پیدا نشد نام قبلاً وجود دارد + + بارگذاری جستجو انتخاب همه @@ -133,6 +136,8 @@ در حال دانلود محتوا خروجی گرفتن در کلیپ‌ بورد وارد کردن از کلیپ‌ بورد + Per-app settings + Enable per-app تنظیمات @@ -210,6 +215,7 @@ پروکسی HTTP را به VPN اضافه کنید پروکسی HTTP مستقیماً از (مرورگر/برخی برنامه‌های پشتیبانی‌شده)، بدون استفاده از دستگاه NIC مجازی (Android 10+) استفاده می‌شود. + بازخورد بازخورد یا گزارش اشکالات در گیت‌ هاب عضویت در گروه تلگرام @@ -270,6 +276,7 @@ شروع خدمات تایید + استراتژی دامنه تنظیمات مسیریابی با کاما (،) از هم جدا شوند، ذخیره کردن فراموش نشود diff --git a/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml index 52d79a6f44..05a453fcac 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -26,6 +26,7 @@ Удалить профиль Импорт из QR-кода Импорт из буфера обмена + Import config from locally Ручной ввод VMess Ручной ввод VLESS Ручной ввод Shadowsocks @@ -104,6 +105,17 @@ Убедитесь, что входящий порт соответствует настройкам Профиль повреждён Узел (SNI) (необязательно) + Это действие запрещено + Пароль obfs + Смена портов (переопределяет порт) + Интервал смены портов + pinSHA256 + Входящая пропускная способность (единицы) + Исходящая пропускная способность (единицы) + Режим XHTTP + Необработанный JSON XHTTP Extra, формат: { XHTTPObject } + + Невозможно скопировать файл, используйте файловый менеджер Добавить ресурс Добавить файлы @@ -114,15 +126,6 @@ Добавить URL ресурса Файл не найден Название уже существует - Это действие запрещено - Пароль obfs - Смена портов (переопределяет порт) - Интервал смены портов - pinSHA256 - Входящая пропускная способность (единицы) - Исходящая пропускная способность (единицы) - Режим XHTTP - Необработанный JSON XHTTP Extra, формат: { XHTTPObject } Загрузка… @@ -134,6 +137,8 @@ Загрузка данных Экспорт в буфер обмена Импорт из буфера обмена + Per-app settings + Enable per-app @@ -211,6 +216,7 @@ Дополнительный HTTP-прокси HTTP-прокси будет использоваться напрямую (из браузера и других поддерживающих приложений), минуя виртуальный сетевой адаптер (Android 10+) + Обратная связь Предложить улучшение или сообщить об ошибке на GitHub Присоединиться к группе в Telegram @@ -272,6 +278,7 @@ Запуск службы Подтвердить + Доменная стратегия Маршрутизация Введите требуемые домены/IP через запятую diff --git a/v2rayng/V2rayNG/app/src/main/res/values-vi/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values-vi/strings.xml index f0acddf488..4f3d029441 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values-vi/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values-vi/strings.xml @@ -26,6 +26,7 @@ Xoá cấu hình Nhập cấu hình từ mã QR Nhập cấu hình từ Clipboard + Import config from locally Nhập thủ công [VMess] Nhập thủ công [VLESS] Nhập thủ công [ShadowSocks] @@ -104,11 +105,6 @@ Vui lòng đảm bảo cấu hình tùy chỉnh này không bị lỗi trước khi sử dụng! Cấu hình không hợp lệ! Host (SNI) (Không bắt buộc) - Không thể sao chép tệp tin, hãy dùng trình quản lý tệp! - Thêm tệp - Quét mã QR - URL - Tải xuống tệp tin Hành động này bị cấm! Obfs password Port Hopping @@ -119,10 +115,17 @@ XHTTP Mode XHTTP Extra raw JSON, format: { XHTTPObject } - + + Không thể sao chép tệp tin, hãy dùng trình quản lý tệp! + Thêm tệp + Quét mã QR + URL + Tải xuống tệp tin Thêm URL nội dung Không tìm thấy tập tin! Nhận xét đã tồn tại! + + Đang tải... Tìm kiếm Chọn tất cả @@ -132,6 +135,8 @@ Đang tải xuống nội dung... Xuất và Sao chép Nhập từ Clipboard + Per-app settings + Enable per-app @@ -211,6 +216,7 @@ Append HTTP Proxy to VPN HTTP proxy will be used directly from (browser/ some supported apps), without going through the virtual NIC device (Android 10+) + Phản hồi lỗi Phản hồi cải tiến hoặc lỗi lên GitHub Tham gia nhóm Telegram @@ -272,6 +278,7 @@ Khởi động v2rayNG Xác nhận + Chiến lược tên miền (DomainStrategy) Cài đặt định tuyến Phân cách bằng dấu phẩy (,). Có thể tải xuống Rules mặc định để tham khảo ở menu ba chấm. diff --git a/v2rayng/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml index 3542d89f60..6ec73453d9 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -2,7 +2,7 @@ 开关 开关 - 初次使用此功能请先用APP添加配置 + 初次使用此功能请先用 APP 添加配置 Open navigation drawer Close navigation drawer 数据迁移成功! @@ -26,32 +26,33 @@ 删除配置 扫描二维码 从剪贴板导入 - 手动输入[VMess] - 手动输入[VLESS] - 手动输入[Shadowsocks] - 手动输入[SOCKS] - 手动输入[HTTP] - 手动输入[Trojan] - 手动输入[Wireguard] - 手动输入[Hysteria2] + 从本地导入 + 手动输入 [VMess] + 手动输入 [VLESS] + 手动输入 [Shadowsocks] + 手动输入 [SOCKS] + 手动输入 [HTTP] + 手动输入 [Trojan] + 手动输入 [Wireguard] + 手动输入 [Hysteria2] 自定义配置 从剪贴板导入自定义配置 从本地导入自定义配置 - 剪贴板URL导入自定义配置 - 扫描URL导入自定义配置 + 剪贴板 URL 导入自定义配置 + 扫描 URL 导入自定义配置 确认删除? 删除前请先测试!确认删除? - 别名(remarks) - 地址(address) - 端口(port) - 用户ID(id) - 额外ID(alterId) - 加密方式(security) - 传输协议(network) - 底层传输方式(transport) - 伪装类型(type) - gRPC 传输模式(mode) - 伪装域名(host) + 别名 (remarks) + 地址 (address) + 端口 (port) + 用户 ID (id) + 额外 ID (alterId) + 加密方式 (security) + 传输协议 (network) + 底层传输方式 (transport) + 伪装类型 (type) + gRPC 传输模式 (mode) + 伪装域名 (host) http host ws host httpupgrade host @@ -62,32 +63,32 @@ path ws path httpupgrade path - xhttp path + xhttp path h2 path QUIC 加密密钥 kcp seed gRPC serviceName - 传输层安全(TLS) + 传输层安全 (TLS) Fingerprint Alpn - 跳过证书验证(allowInsecure) + 跳过证书验证 (allowInsecure) SNI 服务器地址 服务器端口 密码 加密方式 - 密码(可选) - 用户名(可选) - 加密方式(encryption) - 流控(flow) + 密码 (可选) + 用户名 (可选) + 加密方式 (encryption) + 流控 (flow) PublicKey - PreSharedKey(optional) + PreSharedKey (optional) ShortId SpiderX SecretKey - Reserved(可选,逗号隔开) - 本地地址(可选IPv4/IPv6,逗号隔开) - Mtu(可选, 默认1420) + Reserved (可选,逗号隔开) + 本地地址 (可选 IPv4/IPv6,逗号隔开) + Mtu (可选, 默认 1420) 成功 失败 没有数据 @@ -100,29 +101,31 @@ 内容 剪贴板中没有数据 无效的网址 - 请不要使用不安全的HTTP协议订阅地址 - 确保inbounds port和设置中的一致 + 请不要使用不安全的 HTTP 协议订阅地址 + 确保 inbounds port 和设置中的一致 配置格式错误 - Host(SNI)(可选) - 失败, 请使用文件管理器 - 添加文件 - 扫描 QRcode - URL - 下载文件 + Host (SNI) (可选) 禁止此项操作 混淆密码 - 跳跃端口(会覆盖服务器端口) - 端口跳跃间隔(秒) - SHA256证书指纹 + 跳跃端口 (会覆盖服务器端口) + 端口跳跃间隔 (秒) + SHA256 证书指纹 带宽下行 (单位) 带宽上行 (单位) XHTTP 模式 XHTTP Extra 原始 JSON,格式: { XHTTPObject } - + + 失败, 请使用文件管理器 + 添加文件 + 扫描 QRcode + URL + 下载文件 添加资产网址 文件未找到 备注已经存在 + + 正在加载 搜索 全选 @@ -132,13 +135,15 @@ 正在下载内容 导出至剪贴板 从剪贴板导入 + 分应用设置 + 启用分应用 设置 进阶设置 VPN 设置 - 分应用代理 - 常规:勾选的App被代理,未勾选的直连;\n绕行模式:勾选的App直连,未勾选的被代理.\n不明白者在菜单中选择自动选中需代理应用 + 分应用 + 常规: 勾选的 App 被代理, 未勾选的直连;\n绕行模式: 勾选的 App 直连, 未勾选的被代理.\n不明白者在菜单中选择自动选中需代理应用 开机时自动连接 开机时自动连接选择的服务器,可能会不成功 @@ -162,42 +167,42 @@ 启用 routeOnly 将嗅探得到的域名仅用于路由,代理目标地址仍为 IP - 启用本地DNS + 启用本地 DNS DNS 请求导入 core 由 DNS 模块处理(推荐启用 如果需要路由绕过局域网及大陆地址) - 启用虚拟DNS + 启用虚拟 DNS 本地返回虚构解析结果 (减低延时 但个别应用可能无法使用) - IPv6优先 - App优先使用IPv6地址连接服务器,同时开启VPN的IPv6路由 + IPv6 优先 + App 优先使用 IPv6 地址连接服务器, 同时开启 VPN 的 IPv6 路由 - 远程DNS (udp/tcp/https/quic)(可选) + 远程 DNS (udp/tcp/https/quic)(可选) DNS VPN DNS (仅支持 IPv4/v6) - VPN是否绕过局域网 + VPN 是否绕过局域网 - 境内DNS (可选) + 境内 DNS (可选) DNS - DNS hosts (格式: 域名:地址,…) - domain:address,… + DNS hosts (格式: 域名: 地址,…) + domain: address,… 真连接延迟测试网址 (http/https) Url 允许来自局域网的连接 - 其他设备可以使用socks/http协议通过您的IP地址连接到代理,仅在受信任的网络中启用以避免未经授权的连接 + 其他设备可以使用 socks/http 协议通过您的 IP 地址连接到代理, 仅在受信任的网络中启用以避免未经授权的连接 允许来自局域网的连接,请确保处于受信网络 - 跳过证书验证(allowInsecure) - 传输层安全选tls时,默认跳过证书验证(allowInsecure) + 跳过证书验证 (allowInsecure) + 传输层安全选 tls 时,默认跳过证书验证 (allowInsecure) 本地代理端口 本地代理端口 - 本地DNS端口 - 本地DNS端口 + 本地 DNS 端口 + 本地 DNS 端口 删除配置文件确认 删除配置文件是否需要用户二次确认 @@ -208,26 +213,27 @@ 追加 HTTP 代理至 VPN 浏览器 / 一些支持的应用 将直接使用 HTTP 代理, 而不经过虚拟网卡设备 (Android 10+) + 反馈 反馈改进或漏洞至 GitHub - 加入Telegram Group - 未找到Telegram app + 加入 Telegram Group + 未找到 Telegram app 隐私权政策 关于 源代码 Open Source licenses Telegram 频道 备份配置 - 存储位置: [%s], 卸载App或清除存储后备份将被清除 + 存储位置: [%s], 卸载 App 或清除存储后备份将被清除 还原配置 分享配置 推广 - 一些推广,点击查看详情(捐赠可去除) + 一些推广, 点击查看详情 (捐赠可去除) 自动更新订阅 在后台按一定时间间隔自动更新您的订阅。受设备影响,此功能不一定总是有效 - 自动更新间隔(分钟,最小值15) + 自动更新间隔(分钟,最小值 15) 日志级别 模式 @@ -246,7 +252,7 @@ 导出当前组配置至剪贴板 订阅分组设置 备注 - 可选地址(url) + 可选地址 (url) 别名正则过滤 启用更新 启用自动更新 @@ -254,7 +260,7 @@ 落地代理別名 请确保别名存在并唯一 更新当前组订阅 - 测试当前组配置Tcping + 测试当前组配置 Tcping 测试当前组配置真连接 Geo 资源文件 按测试结果排序 @@ -269,9 +275,10 @@ 启动服务 确定 + 域名策略 路由设置 - 用逗号(,)隔开,domain和ip二选一填写 + 用逗号(,)隔开, domain 和 ip 二选一填写 保存 清空 路由规则设置 @@ -299,8 +306,8 @@ 添加链接 分片(Fragment) 设置 分片方式 - 分片包长(最小-最大) - 分片间隔(最小-最大) + 分片包长(最小 - 最大) + 分片间隔(最小 - 最大) 启用分片(Fragment) @@ -326,10 +333,10 @@ - 绕过大陆(Whitelist) - 黑名单(Blacklist) - 全局(Global) - 伊朗(Iran) + 绕过大陆 (Whitelist) + 黑名单 (Blacklist) + 全局 (Global) + 伊朗 (Iran) diff --git a/v2rayng/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml index 05fa03b53d..7541fbf791 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -19,13 +19,14 @@ 啟動服務成功 啟動服務失敗 - + 設定檔 新增設定 儲存設定 刪除設定 從 QR Code 匯入設定 從剪貼簿匯入設定 + 從本地匯入 手動鍵入 [VMess] 手動鍵入 [VLESS] 手動鍵入 [Shadowsocks] @@ -62,7 +63,7 @@ path ws path httpupgrade path - xhttp path + xhttp path h2 path QUIC 加密金鑰 kcp seed @@ -81,13 +82,13 @@ 加密 (encryption) 流程 (flow) PublicKey - PreSharedKey(optional) + PreSharedKey (optional) ShortId SpiderX SecretKey Reserved (可選,逗號隔開) - 本機位址(可選IPv4/IPv6,逗號隔開) - MTU(可選, 預設1420) + 本機位址 (可選 IPv4/IPv6,逗號隔開) + MTU (可選, 預設 1420) 成功 失敗 無資料 @@ -100,29 +101,31 @@ 內容 剪貼簿內無資料 URL 無效 - 請不要使用不安全的HTTP協定訂閱位址 + 請不要使用不安全的 HTTP 協定訂閱位址 ​​確保 inbounds port 和設定中的一致 設定格式不正確 - Host(SNI)(可選) - 失敗,請使用檔案總管 - 新增檔案 - 掃描 QRcode - URL - 下載檔案 + Host (SNI) (可選) 禁止此項操作 混淆密碼 - 跳躍連接埠(會覆蓋伺服器連接埠) - 連接埠跳躍間隔(秒) - SHA256憑證指紋 + 跳躍連接埠 (會覆蓋伺服器連接埠) + 連接埠跳躍間隔 (秒) + SHA256 憑證指紋 頻寬下行 (單位) 頻寬上行 (單位) XHTTP 模式 XHTTP Extra 原始 JSON,格式: { XHTTPObject } - + + 失敗,請使用檔案總管 + 新增檔案 + 掃描 QRcode + URL + 下載檔案 新增資產網址 文件未找到 備註已經存在 + + 載入 搜尋 全選 @@ -132,6 +135,8 @@ 正在下載內容 匯出至剪貼簿 從剪貼簿匯入 + Per-app settings + Enable per-app @@ -173,11 +178,11 @@ IPv6 偏好 App 優先使用 IPv6 位址連線伺服器,同时開啟 VPN 的 IPv6 路由 - 遠端DNS (udp/tcp/https/quic)(可選) + 遠端 DNS (udp/tcp/https/quic)(可選) DNS VPN DNS (僅支援 IPv4/v6) - VPN是否繞過區域網 + VPN 是否繞過區域網 DNS hosts (格式: 網域:位址,…) domain:address,… @@ -207,6 +212,7 @@ 追加 HTTP 代理至 VPN 瀏覽器 / 一些支援的應用 將直接使用 HTTP 代理, 而不經過虛擬網卡設備 (Android 10+) + 意見回饋 前往 GitHub 回報錯誤 加入 Telegram 群組 @@ -226,7 +232,7 @@ 自動更新訂閱 在後台以一定時間間隔自動更新您的訂閱。受設備影響,此功能不一定總是有效 - 自動更新間隔(分鐘,最小值15) + 自動更新間隔(分鐘,最小值 15) 記錄層級 模式 @@ -268,6 +274,7 @@ 啟動服務 確定 + 網域策略 轉送設定 以半形逗號「,」分隔,domain和ip二選一填寫 @@ -325,10 +332,10 @@ - 繞過大陸(Whitelist) - 黑名單(Blacklist) - 全域(Global) - 伊朗(Iran) + 繞過大陸 (Whitelist) + 黑名單 (Blacklist) + 全域 (Global) + 伊朗 (Iran) diff --git a/v2rayng/V2rayNG/app/src/main/res/values/dimens.xml b/v2rayng/V2rayNG/app/src/main/res/values/dimens.xml index 313fa47571..4a4a33b40e 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values/dimens.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values/dimens.xml @@ -1,21 +1,14 @@ - 64dp - 16dp - 16dp - 8dp - 50dp - 24dp - 72dp - 90dp - 60dp - 16dp - 16dp - 16dp - 3dp + 8dp + 16dp + 16dp - 16dp - 16dp - 8dp - 160dp + 24dp + + 48dp + 64dp + 72dp + 90dp + 160dp diff --git a/v2rayng/V2rayNG/app/src/main/res/values/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values/strings.xml index efdee8df1a..c45f4062f9 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values/strings.xml @@ -14,7 +14,7 @@ Stop Unable to obtain the permission Unable to obtain the notification permission - click for more + Click for more Start Services Stop Services Start Services Success @@ -27,6 +27,7 @@ Delete config Import config from QRcode Import config from Clipboard + Import config from locally Type manually[VMess] Type manually[VLESS] Type manually[Shadowsocks] @@ -63,7 +64,7 @@ path ws path httpupgrade path - xhttp path + xhttp path h2 path QUIC key kcp seed @@ -105,6 +106,17 @@ Ensure inbounds port is consistent with the settings Config malformed Host(SNI)(Optional) + Action not allowed + Obfs password + Port Hopping(will override the port) + Port Hopping Interval + pinSHA256 + Bandwidth down (units) + Bandwidth up (units) + XHTTP Mode + XHTTP Extra raw JSON, format: { XHTTPObject } + + File copy failed, please use File Manager Add asset Add files @@ -115,15 +127,6 @@ Add asset URL File not found The remarks already exists - Action not allowed - Obfs password - Port Hopping(will override the port) - Port Hopping Interval - pinSHA256 - Bandwidth down (units) - Bandwidth up (units) - XHTTP Mode - XHTTP Extra raw JSON, format: { XHTTPObject } Loading @@ -135,7 +138,8 @@ Downloading content Export to Clipboard Import from Clipboard - + Per-app settings + Enable per-app Settings @@ -214,6 +218,7 @@ Append HTTP Proxy to VPN HTTP proxy will be used directly from (browser/ some supported apps), without going through the virtual NIC device (Android 10+) + Feedback Feedback enhancements or bugs to GitHub Join Telegram Group @@ -275,6 +280,7 @@ Start Service Confirm + Domain strategy Routing Settings Separated by commas(,),choose domain or ip diff --git a/xray-core/README.md b/xray-core/README.md index fd37beb13e..5090d6e82d 100644 --- a/xray-core/README.md +++ b/xray-core/README.md @@ -38,6 +38,7 @@ - [teddysun/xray](https://hub.docker.com/r/teddysun/xray) - [wulabing/xray_docker](https://github.com/wulabing/xray_docker) - Web Panel - **WARNING: Please DO NOT USE plain HTTP panels like 3X-UI**, as they are believed to be bribed by Iran GFW for supporting plain HTTP by default and refused to change (https://github.com/XTLS/Xray-core/pull/3884#issuecomment-2439595331), which has already put many users' data security in danger in the past few years. **If you are already using 3X-UI, please switch to the following panels, which are verified to support HTTPS and SSH port forwarding only:** + - [Remnawave](https://github.com/remnawave/panel) - [Marzban](https://github.com/Gozargah/Marzban) - [Xray-UI](https://github.com/qist/xray-ui) - [Hiddify](https://github.com/hiddify/Hiddify-Manager) diff --git a/xray-core/app/dns/config.pb.go b/xray-core/app/dns/config.pb.go index 05fd6099b3..33dc904bf3 100644 --- a/xray-core/app/dns/config.pb.go +++ b/xray-core/app/dns/config.pb.go @@ -128,13 +128,14 @@ type NameServer struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Address *net.Endpoint `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - ClientIp []byte `protobuf:"bytes,5,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"` - SkipFallback bool `protobuf:"varint,6,opt,name=skipFallback,proto3" json:"skipFallback,omitempty"` - PrioritizedDomain []*NameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"` - Geoip []*router.GeoIP `protobuf:"bytes,3,rep,name=geoip,proto3" json:"geoip,omitempty"` - OriginalRules []*NameServer_OriginalRule `protobuf:"bytes,4,rep,name=original_rules,json=originalRules,proto3" json:"original_rules,omitempty"` - QueryStrategy QueryStrategy `protobuf:"varint,7,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"` + Address *net.Endpoint `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + ClientIp []byte `protobuf:"bytes,5,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"` + SkipFallback bool `protobuf:"varint,6,opt,name=skipFallback,proto3" json:"skipFallback,omitempty"` + PrioritizedDomain []*NameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"` + Geoip []*router.GeoIP `protobuf:"bytes,3,rep,name=geoip,proto3" json:"geoip,omitempty"` + OriginalRules []*NameServer_OriginalRule `protobuf:"bytes,4,rep,name=original_rules,json=originalRules,proto3" json:"original_rules,omitempty"` + QueryStrategy QueryStrategy `protobuf:"varint,7,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"` + AllowUnexpectedIPs bool `protobuf:"varint,8,opt,name=allowUnexpectedIPs,proto3" json:"allowUnexpectedIPs,omitempty"` } func (x *NameServer) Reset() { @@ -216,6 +217,13 @@ func (x *NameServer) GetQueryStrategy() QueryStrategy { return QueryStrategy_USE_IP } +func (x *NameServer) GetAllowUnexpectedIPs() bool { + if x != nil { + return x.AllowUnexpectedIPs + } + return false +} + type Config struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -508,7 +516,7 @@ var file_app_dns_config_proto_rawDesc = []byte{ 0x2e, 0x64, 0x6e, 0x73, 0x1a, 0x1c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb2, 0x04, 0x0a, 0x0a, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe2, 0x04, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, @@ -534,7 +542,10 @@ var file_app_dns_config_proto_rawDesc = []byte{ 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x1a, 0x5e, 0x0a, 0x0e, 0x50, 0x72, 0x69, 0x6f, + 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x49, 0x50, 0x73, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x55, 0x6e, 0x65, 0x78, 0x70, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x49, 0x50, 0x73, 0x1a, 0x5e, 0x0a, 0x0e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, diff --git a/xray-core/app/dns/config.proto b/xray-core/app/dns/config.proto index 11e0c8debb..ea4d5e318e 100644 --- a/xray-core/app/dns/config.proto +++ b/xray-core/app/dns/config.proto @@ -28,6 +28,7 @@ message NameServer { repeated xray.app.router.GeoIP geoip = 3; repeated OriginalRule original_rules = 4; QueryStrategy query_strategy = 7; + bool allowUnexpectedIPs = 8; } enum DomainMatchingType { diff --git a/xray-core/app/dns/dns.go b/xray-core/app/dns/dns.go index db59f29269..bc544a5aea 100644 --- a/xray-core/app/dns/dns.go +++ b/xray-core/app/dns/dns.go @@ -4,6 +4,7 @@ package dns import ( "context" "fmt" + "sort" "strings" "sync" @@ -250,7 +251,11 @@ func (s *DNS) sortClients(domain string) []*Client { // Priority domain matching hasMatch := false - for _, match := range s.domainMatcher.Match(domain) { + MatchSlice := s.domainMatcher.Match(domain) + sort.Slice(MatchSlice, func(i, j int) bool { + return MatchSlice[i] < MatchSlice[j] + }) + for _, match := range MatchSlice { info := s.matcherInfos[match] client := s.clients[info.clientIdx] domainRule := client.domains[info.domainRuleIdx] diff --git a/xray-core/app/dns/nameserver.go b/xray-core/app/dns/nameserver.go index b23cb18628..e1f902bf8a 100644 --- a/xray-core/app/dns/nameserver.go +++ b/xray-core/app/dns/nameserver.go @@ -25,11 +25,12 @@ type Server interface { // Client is the interface for DNS client. type Client struct { - server Server - clientIP net.IP - skipFallback bool - domains []string - expectIPs []*router.GeoIPMatcher + server Server + clientIP net.IP + skipFallback bool + domains []string + expectIPs []*router.GeoIPMatcher + allowUnexpectedIPs bool } var errExpectedIPNonMatch = errors.New("expectIPs not match") @@ -166,6 +167,7 @@ func NewClient( client.skipFallback = ns.SkipFallback client.domains = rules client.expectIPs = matchers + client.allowUnexpectedIPs = ns.AllowUnexpectedIPs return nil }) return client, err @@ -203,6 +205,9 @@ func (c *Client) MatchExpectedIPs(domain string, ips []net.IP) ([]net.IP, error) } } if len(newIps) == 0 { + if c.allowUnexpectedIPs { + return ips, nil + } return nil, errExpectedIPNonMatch } errors.LogDebug(context.Background(), "domain ", domain, " expectIPs ", newIps, " matched at server ", c.Name()) diff --git a/xray-core/app/stats/online_map.go b/xray-core/app/stats/online_map.go index 9505b28bed..7cc6ac966c 100644 --- a/xray-core/app/stats/online_map.go +++ b/xray-core/app/stats/online_map.go @@ -40,11 +40,11 @@ func (c *OnlineMap) AddIP(ip string) { if ip == "127.0.0.1" { return } + c.access.Lock() if _, ok := list[ip]; !ok { - c.access.Lock() list[ip] = time.Now() - c.access.Unlock() } + c.access.Unlock() if time.Since(c.lastCleanup) > c.cleanupPeriod { list = c.RemoveExpiredIPs(list) c.lastCleanup = time.Now() diff --git a/xray-core/go.mod b/xray-core/go.mod index 66c86613c6..be8d7dd4f5 100644 --- a/xray-core/go.mod +++ b/xray-core/go.mod @@ -9,7 +9,7 @@ require ( github.com/golang/mock v1.7.0-rc.1 github.com/google/go-cmp v0.7.0 github.com/gorilla/websocket v1.5.3 - github.com/miekg/dns v1.1.63 + github.com/miekg/dns v1.1.64 github.com/pelletier/go-toml v1.9.5 github.com/pires/go-proxyproto v0.8.0 github.com/quic-go/quic-go v0.50.0 @@ -50,10 +50,10 @@ require ( github.com/vishvananda/netns v0.0.4 // indirect go.uber.org/mock v0.5.0 // indirect golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect - golang.org/x/mod v0.21.0 // indirect + golang.org/x/mod v0.23.0 // indirect golang.org/x/text v0.23.0 // indirect golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.26.0 // indirect + golang.org/x/tools v0.30.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/xray-core/go.sum b/xray-core/go.sum index 93200ef411..43b0b2226a 100644 --- a/xray-core/go.sum +++ b/xray-core/go.sum @@ -38,8 +38,8 @@ github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0N github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= -github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= +github.com/miekg/dns v1.1.64 h1:wuZgD9wwCE6XMT05UU/mlSko71eRSXEAm2EbjQXLKnQ= +github.com/miekg/dns v1.1.64/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck= github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= @@ -102,8 +102,8 @@ golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZv golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg= golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -134,8 +134,8 @@ golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/xray-core/infra/conf/dns.go b/xray-core/infra/conf/dns.go index 65964f3d1b..b22890264e 100644 --- a/xray-core/infra/conf/dns.go +++ b/xray-core/infra/conf/dns.go @@ -12,13 +12,14 @@ import ( ) type NameServerConfig struct { - Address *Address `json:"address"` - ClientIP *Address `json:"clientIp"` - Port uint16 `json:"port"` - SkipFallback bool `json:"skipFallback"` - Domains []string `json:"domains"` - ExpectIPs StringList `json:"expectIps"` - QueryStrategy string `json:"queryStrategy"` + Address *Address `json:"address"` + ClientIP *Address `json:"clientIp"` + Port uint16 `json:"port"` + SkipFallback bool `json:"skipFallback"` + Domains []string `json:"domains"` + ExpectIPs StringList `json:"expectIps"` + QueryStrategy string `json:"queryStrategy"` + AllowUnexpectedIPs bool `json:"allowUnexpectedIps"` } func (c *NameServerConfig) UnmarshalJSON(data []byte) error { @@ -29,13 +30,14 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error { } var advanced struct { - Address *Address `json:"address"` - ClientIP *Address `json:"clientIp"` - Port uint16 `json:"port"` - SkipFallback bool `json:"skipFallback"` - Domains []string `json:"domains"` - ExpectIPs StringList `json:"expectIps"` - QueryStrategy string `json:"queryStrategy"` + Address *Address `json:"address"` + ClientIP *Address `json:"clientIp"` + Port uint16 `json:"port"` + SkipFallback bool `json:"skipFallback"` + Domains []string `json:"domains"` + ExpectIPs StringList `json:"expectIps"` + QueryStrategy string `json:"queryStrategy"` + AllowUnexpectedIPs bool `json:"allowUnexpectedIps"` } if err := json.Unmarshal(data, &advanced); err == nil { c.Address = advanced.Address @@ -45,6 +47,7 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error { c.Domains = advanced.Domains c.ExpectIPs = advanced.ExpectIPs c.QueryStrategy = advanced.QueryStrategy + c.AllowUnexpectedIPs = advanced.AllowUnexpectedIPs return nil } @@ -111,12 +114,13 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) { Address: c.Address.Build(), Port: uint32(c.Port), }, - ClientIp: myClientIP, - SkipFallback: c.SkipFallback, - PrioritizedDomain: domains, - Geoip: geoipList, - OriginalRules: originalRules, - QueryStrategy: resolveQueryStrategy(c.QueryStrategy), + ClientIp: myClientIP, + SkipFallback: c.SkipFallback, + PrioritizedDomain: domains, + Geoip: geoipList, + OriginalRules: originalRules, + QueryStrategy: resolveQueryStrategy(c.QueryStrategy), + AllowUnexpectedIPs: c.AllowUnexpectedIPs, }, nil }