diff --git a/.github/update.log b/.github/update.log
index ef359c0864..b1b05cf8ae 100644
--- a/.github/update.log
+++ b/.github/update.log
@@ -1200,3 +1200,4 @@ Update On Sat Nov 29 19:37:01 CET 2025
Update On Sun Nov 30 19:38:16 CET 2025
Update On Mon Dec 1 19:44:38 CET 2025
Update On Tue Dec 2 19:43:40 CET 2025
+Update On Wed Dec 3 19:42:45 CET 2025
diff --git a/clash-meta/adapter/outbound/sudoku.go b/clash-meta/adapter/outbound/sudoku.go
index 48d0425876..0e6c3bba68 100644
--- a/clash-meta/adapter/outbound/sudoku.go
+++ b/clash-meta/adapter/outbound/sudoku.go
@@ -5,7 +5,6 @@ import (
"crypto/sha256"
"encoding/binary"
"fmt"
- "io"
"net"
"strconv"
"strings"
@@ -14,17 +13,18 @@ import (
"github.com/saba-futai/sudoku/apis"
"github.com/saba-futai/sudoku/pkg/crypto"
"github.com/saba-futai/sudoku/pkg/obfs/httpmask"
- "github.com/saba-futai/sudoku/pkg/obfs/sudoku"
+ sudokuobfs "github.com/saba-futai/sudoku/pkg/obfs/sudoku"
N "github.com/metacubex/mihomo/common/net"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
+ "github.com/metacubex/mihomo/transport/sudoku"
)
type Sudoku struct {
*Base
option *SudokuOption
- table *sudoku.Table
+ table *sudokuobfs.Table
baseConf apis.ProtocolConfig
}
@@ -72,12 +72,45 @@ func (s *Sudoku) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Con
// ListenPacketContext implements C.ProxyAdapter
func (s *Sudoku) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {
- return nil, C.ErrNotSupport
+ if err := s.ResolveUDP(ctx, metadata); err != nil {
+ return nil, err
+ }
+
+ cfg, err := s.buildConfig(metadata)
+ if err != nil {
+ return nil, err
+ }
+
+ c, err := s.dialer.DialContext(ctx, "tcp", s.addr)
+ if err != nil {
+ return nil, fmt.Errorf("%s connect error: %w", s.addr, err)
+ }
+
+ defer func() {
+ safeConnClose(c, err)
+ }()
+
+ if ctx.Done() != nil {
+ done := N.SetupContextForConn(ctx, c)
+ defer done(&err)
+ }
+
+ c, err = s.handshakeConn(c, cfg)
+ if err != nil {
+ return nil, err
+ }
+
+ if err = sudoku.WritePreface(c); err != nil {
+ _ = c.Close()
+ return nil, fmt.Errorf("send uot preface failed: %w", err)
+ }
+
+ return newPacketConn(N.NewThreadSafePacketConn(sudoku.NewUoTPacketConn(c)), s), nil
}
// SupportUOT implements C.ProxyAdapter
func (s *Sudoku) SupportUOT() bool {
- return false // Sudoku protocol only supports TCP
+ return true
}
// ProxyInfo implements C.ProxyAdapter
@@ -101,14 +134,14 @@ func (s *Sudoku) buildConfig(metadata *C.Metadata) (*apis.ProtocolConfig, error)
return &cfg, nil
}
-func (s *Sudoku) streamConn(rawConn net.Conn, cfg *apis.ProtocolConfig) (_ net.Conn, err error) {
+func (s *Sudoku) handshakeConn(rawConn net.Conn, cfg *apis.ProtocolConfig) (_ net.Conn, err error) {
if !cfg.DisableHTTPMask {
if err = httpmask.WriteRandomRequestHeader(rawConn, cfg.ServerAddress); err != nil {
return nil, fmt.Errorf("write http mask failed: %w", err)
}
}
- obfsConn := sudoku.NewConn(rawConn, cfg.Table, cfg.PaddingMin, cfg.PaddingMax, false)
+ obfsConn := sudokuobfs.NewConn(rawConn, cfg.Table, cfg.PaddingMin, cfg.PaddingMax, false)
cConn, err := crypto.NewAEADConn(obfsConn, cfg.Key, cfg.AEADMethod)
if err != nil {
return nil, fmt.Errorf("setup crypto failed: %w", err)
@@ -120,7 +153,21 @@ func (s *Sudoku) streamConn(rawConn net.Conn, cfg *apis.ProtocolConfig) (_ net.C
return nil, fmt.Errorf("send handshake failed: %w", err)
}
- if err = writeTargetAddress(cConn, cfg.TargetAddress); err != nil {
+ return cConn, nil
+}
+
+func (s *Sudoku) streamConn(rawConn net.Conn, cfg *apis.ProtocolConfig) (_ net.Conn, err error) {
+ cConn, err := s.handshakeConn(rawConn, cfg)
+ if err != nil {
+ return nil, err
+ }
+
+ addrBuf, err := sudoku.EncodeAddress(cfg.TargetAddress)
+ if err != nil {
+ return nil, fmt.Errorf("encode target address failed: %w", err)
+ }
+
+ if _, err = cConn.Write(addrBuf); err != nil {
cConn.Close()
return nil, fmt.Errorf("send target address failed: %w", err)
}
@@ -153,7 +200,7 @@ func NewSudoku(option SudokuOption) (*Sudoku, error) {
}
start := time.Now()
- table := sudoku.NewTable(seed, tableType)
+ table := sudokuobfs.NewTable(seed, tableType)
log.Infoln("[Sudoku] Tables initialized (%s) in %v", tableType, time.Since(start))
defaultConf := apis.DefaultConfig()
@@ -191,7 +238,7 @@ func NewSudoku(option SudokuOption) (*Sudoku, error) {
name: option.Name,
addr: baseConf.ServerAddress,
tp: C.Sudoku,
- udp: false,
+ udp: true,
tfo: option.TFO,
mpTcp: option.MPTCP,
iface: option.Interface,
@@ -213,40 +260,3 @@ func buildSudokuHandshakePayload(key string) [16]byte {
copy(payload[8:], hash[:8])
return payload
}
-
-func writeTargetAddress(w io.Writer, rawAddr string) error {
- host, portStr, err := net.SplitHostPort(rawAddr)
- if err != nil {
- return err
- }
-
- portInt, err := net.LookupPort("tcp", portStr)
- if err != nil {
- return err
- }
-
- var buf []byte
- if ip := net.ParseIP(host); ip != nil {
- if ip4 := ip.To4(); ip4 != nil {
- buf = append(buf, 0x01) // IPv4
- buf = append(buf, ip4...)
- } else {
- buf = append(buf, 0x04) // IPv6
- buf = append(buf, ip...)
- }
- } else {
- if len(host) > 255 {
- return fmt.Errorf("domain too long")
- }
- buf = append(buf, 0x03) // domain
- buf = append(buf, byte(len(host)))
- buf = append(buf, host...)
- }
-
- var portBytes [2]byte
- binary.BigEndian.PutUint16(portBytes[:], uint16(portInt))
- buf = append(buf, portBytes[:]...)
-
- _, err = w.Write(buf)
- return err
-}
diff --git a/clash-meta/adapter/outboundgroup/fallback.go b/clash-meta/adapter/outboundgroup/fallback.go
index 3772107bc1..0174a7b944 100644
--- a/clash-meta/adapter/outboundgroup/fallback.go
+++ b/clash-meta/adapter/outboundgroup/fallback.go
@@ -150,6 +150,14 @@ func (f *Fallback) ForceSet(name string) {
f.selected = name
}
+func (f *Fallback) Providers() []P.ProxyProvider {
+ return f.providers
+}
+
+func (f *Fallback) Proxies() []C.Proxy {
+ return f.GetProxies(false)
+}
+
func NewFallback(option *GroupCommonOption, providers []P.ProxyProvider) *Fallback {
return &Fallback{
GroupBase: NewGroupBase(GroupBaseOption{
diff --git a/clash-meta/adapter/outboundgroup/loadbalance.go b/clash-meta/adapter/outboundgroup/loadbalance.go
index dff9b5ed9d..19ee38c7de 100644
--- a/clash-meta/adapter/outboundgroup/loadbalance.go
+++ b/clash-meta/adapter/outboundgroup/loadbalance.go
@@ -239,6 +239,18 @@ func (lb *LoadBalance) MarshalJSON() ([]byte, error) {
})
}
+func (lb *LoadBalance) Providers() []P.ProxyProvider {
+ return lb.providers
+}
+
+func (lb *LoadBalance) Proxies() []C.Proxy {
+ return lb.GetProxies(false)
+}
+
+func (lb *LoadBalance) Now() string {
+ return ""
+}
+
func NewLoadBalance(option *GroupCommonOption, providers []P.ProxyProvider, strategy string) (lb *LoadBalance, err error) {
var strategyFn strategyFn
switch strategy {
diff --git a/clash-meta/adapter/outboundgroup/patch_android.go b/clash-meta/adapter/outboundgroup/patch_android.go
deleted file mode 100644
index f0c254c2b8..0000000000
--- a/clash-meta/adapter/outboundgroup/patch_android.go
+++ /dev/null
@@ -1,52 +0,0 @@
-//go:build android && cmfa
-
-package outboundgroup
-
-import (
- C "github.com/metacubex/mihomo/constant"
- P "github.com/metacubex/mihomo/constant/provider"
-)
-
-type ProxyGroup interface {
- C.ProxyAdapter
-
- Providers() []P.ProxyProvider
- Proxies() []C.Proxy
- Now() string
-}
-
-func (f *Fallback) Providers() []P.ProxyProvider {
- return f.providers
-}
-
-func (lb *LoadBalance) Providers() []P.ProxyProvider {
- return lb.providers
-}
-
-func (f *Fallback) Proxies() []C.Proxy {
- return f.GetProxies(false)
-}
-
-func (lb *LoadBalance) Proxies() []C.Proxy {
- return lb.GetProxies(false)
-}
-
-func (lb *LoadBalance) Now() string {
- return ""
-}
-
-func (s *Selector) Providers() []P.ProxyProvider {
- return s.providers
-}
-
-func (s *Selector) Proxies() []C.Proxy {
- return s.GetProxies(false)
-}
-
-func (u *URLTest) Providers() []P.ProxyProvider {
- return u.providers
-}
-
-func (u *URLTest) Proxies() []C.Proxy {
- return u.GetProxies(false)
-}
diff --git a/clash-meta/adapter/outboundgroup/selector.go b/clash-meta/adapter/outboundgroup/selector.go
index f8975df744..7bc138fdc7 100644
--- a/clash-meta/adapter/outboundgroup/selector.go
+++ b/clash-meta/adapter/outboundgroup/selector.go
@@ -108,6 +108,14 @@ func (s *Selector) selectedProxy(touch bool) C.Proxy {
return proxies[0]
}
+func (s *Selector) Providers() []P.ProxyProvider {
+ return s.providers
+}
+
+func (s *Selector) Proxies() []C.Proxy {
+ return s.GetProxies(false)
+}
+
func NewSelector(option *GroupCommonOption, providers []P.ProxyProvider) *Selector {
return &Selector{
GroupBase: NewGroupBase(GroupBaseOption{
diff --git a/clash-meta/adapter/outboundgroup/urltest.go b/clash-meta/adapter/outboundgroup/urltest.go
index 2adb3b814e..49ea12aa0b 100644
--- a/clash-meta/adapter/outboundgroup/urltest.go
+++ b/clash-meta/adapter/outboundgroup/urltest.go
@@ -185,6 +185,14 @@ func (u *URLTest) MarshalJSON() ([]byte, error) {
})
}
+func (u *URLTest) Providers() []P.ProxyProvider {
+ return u.providers
+}
+
+func (u *URLTest) Proxies() []C.Proxy {
+ return u.GetProxies(false)
+}
+
func (u *URLTest) URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (map[string]uint16, error) {
return u.GroupBase.URLTest(ctx, u.testUrl, expectedStatus)
}
diff --git a/clash-meta/adapter/outboundgroup/util.go b/clash-meta/adapter/outboundgroup/util.go
index 66b2510c19..d35ea66f15 100644
--- a/clash-meta/adapter/outboundgroup/util.go
+++ b/clash-meta/adapter/outboundgroup/util.go
@@ -1,5 +1,29 @@
package outboundgroup
+import (
+ "context"
+
+ "github.com/metacubex/mihomo/common/utils"
+ C "github.com/metacubex/mihomo/constant"
+ P "github.com/metacubex/mihomo/constant/provider"
+)
+
+type ProxyGroup interface {
+ C.ProxyAdapter
+
+ Providers() []P.ProxyProvider
+ Proxies() []C.Proxy
+ Now() string
+ Touch()
+
+ URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (mp map[string]uint16, err error)
+}
+
+var _ ProxyGroup = (*Fallback)(nil)
+var _ ProxyGroup = (*LoadBalance)(nil)
+var _ ProxyGroup = (*URLTest)(nil)
+var _ ProxyGroup = (*Selector)(nil)
+
type SelectAble interface {
Set(string) error
ForceSet(name string)
diff --git a/clash-meta/constant/adapters.go b/clash-meta/constant/adapters.go
index c1ac3723e9..0b9098fd69 100644
--- a/clash-meta/constant/adapters.go
+++ b/clash-meta/constant/adapters.go
@@ -139,11 +139,6 @@ type ProxyAdapter interface {
Close() error
}
-type Group interface {
- URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (mp map[string]uint16, err error)
- Touch()
-}
-
type DelayHistory struct {
Time time.Time `json:"time"`
Delay uint16 `json:"delay"`
diff --git a/clash-meta/go.mod b/clash-meta/go.mod
index 35dfcbe2b1..500aad8349 100644
--- a/clash-meta/go.mod
+++ b/clash-meta/go.mod
@@ -23,7 +23,7 @@ require (
github.com/metacubex/fswatch v0.1.1
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
github.com/metacubex/kcp-go v0.0.0-20251111012849-7455698490e9
- github.com/metacubex/quic-go v0.55.1-0.20251024060151-bd465f127128
+ github.com/metacubex/quic-go v0.55.1-0.20251203073212-6940cac967c2
github.com/metacubex/randv2 v0.2.0
github.com/metacubex/restls-client-go v0.1.7
github.com/metacubex/sing v0.5.6
@@ -43,7 +43,7 @@ require (
github.com/mroth/weightedrand/v2 v2.1.0
github.com/openacid/low v0.1.21
github.com/oschwald/maxminddb-golang v1.12.0 // lastest version compatible with golang1.20
- github.com/saba-futai/sudoku v0.0.1-g
+ github.com/saba-futai/sudoku v0.0.1-i
github.com/sagernet/cors v1.2.1
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
github.com/samber/lo v1.52.0
diff --git a/clash-meta/go.sum b/clash-meta/go.sum
index 45210afb98..37cf8ad76b 100644
--- a/clash-meta/go.sum
+++ b/clash-meta/go.sum
@@ -112,8 +112,8 @@ github.com/metacubex/kcp-go v0.0.0-20251111012849-7455698490e9 h1:7m3tRPrLpKOLOv
github.com/metacubex/kcp-go v0.0.0-20251111012849-7455698490e9/go.mod h1:HIJZW4QMhbBqXuqC1ly6Hn0TEYT2SzRw58ns1yGhXTs=
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo=
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793/go.mod h1:RjRNb4G52yAgfR+Oe/kp9G4PJJ97Fnj89eY1BFO3YyA=
-github.com/metacubex/quic-go v0.55.1-0.20251024060151-bd465f127128 h1:I1uvJl206/HbkzEAZpLgGkZgUveOZb+P+6oTUj7dN+o=
-github.com/metacubex/quic-go v0.55.1-0.20251024060151-bd465f127128/go.mod h1:1lktQFtCD17FZliVypbrDHwbsFSsmz2xz2TRXydvB5c=
+github.com/metacubex/quic-go v0.55.1-0.20251203073212-6940cac967c2 h1:21KrRBqF5en0yXwwb5Vpptbeiiu3p7gD0G+RqNYvsvw=
+github.com/metacubex/quic-go v0.55.1-0.20251203073212-6940cac967c2/go.mod h1:1lktQFtCD17FZliVypbrDHwbsFSsmz2xz2TRXydvB5c=
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k=
@@ -171,8 +171,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
-github.com/saba-futai/sudoku v0.0.1-g h1:4q6OuAA6COaRW+CgoQtdim5AUPzzm0uOkvbYpJnOaBE=
-github.com/saba-futai/sudoku v0.0.1-g/go.mod h1:2ZRzRwz93cS2K/o2yOG4CPJEltcvk5y6vbvUmjftGU0=
+github.com/saba-futai/sudoku v0.0.1-i h1:t6H875LSceXaEEwho84GU9OoLa4ieoBo3v+dxpFf4wc=
+github.com/saba-futai/sudoku v0.0.1-i/go.mod h1:FNtEAA44TSMvHI94o1kri/itbjvSMm1qCrbd0e6MTZY=
github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
diff --git a/clash-meta/hub/route/groups.go b/clash-meta/hub/route/groups.go
index 873a94df51..05e8fe493e 100644
--- a/clash-meta/hub/route/groups.go
+++ b/clash-meta/hub/route/groups.go
@@ -31,7 +31,7 @@ func groupRouter() http.Handler {
func getGroups(w http.ResponseWriter, r *http.Request) {
var gs []C.Proxy
for _, p := range tunnel.Proxies() {
- if _, ok := p.Adapter().(C.Group); ok {
+ if _, ok := p.Adapter().(outboundgroup.ProxyGroup); ok {
gs = append(gs, p)
}
}
@@ -42,7 +42,7 @@ func getGroups(w http.ResponseWriter, r *http.Request) {
func getGroup(w http.ResponseWriter, r *http.Request) {
proxy := r.Context().Value(CtxKeyProxy).(C.Proxy)
- if _, ok := proxy.Adapter().(C.Group); ok {
+ if _, ok := proxy.Adapter().(outboundgroup.ProxyGroup); ok {
render.JSON(w, r, proxy)
return
}
@@ -52,7 +52,7 @@ func getGroup(w http.ResponseWriter, r *http.Request) {
func getGroupDelay(w http.ResponseWriter, r *http.Request) {
proxy := r.Context().Value(CtxKeyProxy).(C.Proxy)
- group, ok := proxy.Adapter().(C.Group)
+ group, ok := proxy.Adapter().(outboundgroup.ProxyGroup)
if !ok {
render.Status(r, http.StatusNotFound)
render.JSON(w, r, ErrNotFound)
diff --git a/clash-meta/hub/route/server.go b/clash-meta/hub/route/server.go
index f2a52d4208..fe8275304e 100644
--- a/clash-meta/hub/route/server.go
+++ b/clash-meta/hub/route/server.go
@@ -47,8 +47,10 @@ func SetEmbedMode(embed bool) {
}
type Traffic struct {
- Up int64 `json:"up"`
- Down int64 `json:"down"`
+ Up int64 `json:"up"`
+ Down int64 `json:"down"`
+ UpTotal int64 `json:"upTotal"`
+ DownTotal int64 `json:"downTotal"`
}
type Memory struct {
@@ -380,9 +382,12 @@ func traffic(w http.ResponseWriter, r *http.Request) {
for range tick.C {
buf.Reset()
up, down := t.Now()
+ upTotal, downTotal := t.Total()
if err := json.NewEncoder(buf).Encode(Traffic{
- Up: up,
- Down: down,
+ Up: up,
+ Down: down,
+ UpTotal: upTotal,
+ DownTotal: downTotal,
}); err != nil {
break
}
diff --git a/clash-meta/listener/sudoku/server.go b/clash-meta/listener/sudoku/server.go
index ce2b09afab..f3260eb341 100644
--- a/clash-meta/listener/sudoku/server.go
+++ b/clash-meta/listener/sudoku/server.go
@@ -1,6 +1,8 @@
package sudoku
import (
+ "errors"
+ "io"
"net"
"strings"
@@ -10,7 +12,9 @@ import (
"github.com/metacubex/mihomo/adapter/inbound"
C "github.com/metacubex/mihomo/constant"
LC "github.com/metacubex/mihomo/listener/config"
+ "github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/transport/socks5"
+ "github.com/metacubex/mihomo/transport/sudoku"
)
type Listener struct {
@@ -43,19 +47,74 @@ func (l *Listener) Close() error {
}
func (l *Listener) handleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {
- tunnelConn, target, err := apis.ServerHandshake(conn, &l.protoConf)
+ session, err := sudoku.ServerHandshake(conn, &l.protoConf)
if err != nil {
_ = conn.Close()
return
}
- targetAddr := socks5.ParseAddr(target)
- if targetAddr == nil {
- _ = tunnelConn.Close()
- return
+ switch session.Type {
+ case sudoku.SessionTypeUoT:
+ l.handleUoTSession(session.Conn, tunnel, additions...)
+ default:
+ targetAddr := socks5.ParseAddr(session.Target)
+ if targetAddr == nil {
+ _ = session.Conn.Close()
+ return
+ }
+ tunnel.HandleTCPConn(inbound.NewSocket(targetAddr, session.Conn, C.SUDOKU, additions...))
}
+}
- tunnel.HandleTCPConn(inbound.NewSocket(targetAddr, tunnelConn, C.SUDOKU, additions...))
+func (l *Listener) handleUoTSession(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {
+ writer := sudoku.NewUoTPacketConn(conn)
+ remoteAddr := conn.RemoteAddr()
+
+ for {
+ addrStr, payload, err := sudoku.ReadDatagram(conn)
+ if err != nil {
+ if !errors.Is(err, io.EOF) {
+ log.Debugln("[Sudoku][UoT] session closed: %v", err)
+ }
+ _ = conn.Close()
+ return
+ }
+
+ target := socks5.ParseAddr(addrStr)
+ if target == nil {
+ log.Debugln("[Sudoku][UoT] drop invalid target: %s", addrStr)
+ continue
+ }
+
+ packet := &uotPacket{
+ payload: payload,
+ writer: writer,
+ rAddr: remoteAddr,
+ }
+ tunnel.HandleUDPPacket(inbound.NewPacket(target, packet, C.SUDOKU, additions...))
+ }
+}
+
+type uotPacket struct {
+ payload []byte
+ writer *sudoku.UoTPacketConn
+ rAddr net.Addr
+}
+
+func (p *uotPacket) Data() []byte {
+ return p.payload
+}
+
+func (p *uotPacket) WriteBack(b []byte, addr net.Addr) (int, error) {
+ return p.writer.WriteTo(b, addr)
+}
+
+func (p *uotPacket) Drop() {
+ p.payload = nil
+}
+
+func (p *uotPacket) LocalAddr() net.Addr {
+ return p.rAddr
}
func New(config LC.SudokuServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
diff --git a/clash-meta/transport/sudoku/address.go b/clash-meta/transport/sudoku/address.go
new file mode 100644
index 0000000000..48b322967f
--- /dev/null
+++ b/clash-meta/transport/sudoku/address.go
@@ -0,0 +1,91 @@
+package sudoku
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+ "net"
+ "strconv"
+)
+
+func EncodeAddress(rawAddr string) ([]byte, error) {
+ host, portStr, err := net.SplitHostPort(rawAddr)
+ if err != nil {
+ return nil, err
+ }
+
+ portInt, err := strconv.ParseUint(portStr, 10, 16)
+ if err != nil {
+ return nil, err
+ }
+
+ var buf []byte
+ if ip := net.ParseIP(host); ip != nil {
+ if ip4 := ip.To4(); ip4 != nil {
+ buf = append(buf, 0x01) // IPv4
+ buf = append(buf, ip4...)
+ } else {
+ buf = append(buf, 0x04) // IPv6
+ buf = append(buf, ip...)
+ }
+ } else {
+ if len(host) > 255 {
+ return nil, fmt.Errorf("domain too long")
+ }
+ buf = append(buf, 0x03) // domain
+ buf = append(buf, byte(len(host)))
+ buf = append(buf, host...)
+ }
+
+ var portBytes [2]byte
+ binary.BigEndian.PutUint16(portBytes[:], uint16(portInt))
+ buf = append(buf, portBytes[:]...)
+ return buf, nil
+}
+
+func DecodeAddress(r io.Reader) (string, error) {
+ var atyp [1]byte
+ if _, err := io.ReadFull(r, atyp[:]); err != nil {
+ return "", err
+ }
+
+ switch atyp[0] {
+ case 0x01: // IPv4
+ var ipBuf [net.IPv4len]byte
+ if _, err := io.ReadFull(r, ipBuf[:]); err != nil {
+ return "", err
+ }
+ var portBuf [2]byte
+ if _, err := io.ReadFull(r, portBuf[:]); err != nil {
+ return "", err
+ }
+ return net.JoinHostPort(net.IP(ipBuf[:]).String(), fmt.Sprint(binary.BigEndian.Uint16(portBuf[:]))), nil
+ case 0x04: // IPv6
+ var ipBuf [net.IPv6len]byte
+ if _, err := io.ReadFull(r, ipBuf[:]); err != nil {
+ return "", err
+ }
+ var portBuf [2]byte
+ if _, err := io.ReadFull(r, portBuf[:]); err != nil {
+ return "", err
+ }
+ return net.JoinHostPort(net.IP(ipBuf[:]).String(), fmt.Sprint(binary.BigEndian.Uint16(portBuf[:]))), nil
+ case 0x03: // domain
+ var lengthBuf [1]byte
+ if _, err := io.ReadFull(r, lengthBuf[:]); err != nil {
+ return "", err
+ }
+ l := int(lengthBuf[0])
+ hostBuf := make([]byte, l)
+ if _, err := io.ReadFull(r, hostBuf); err != nil {
+ return "", err
+ }
+ var portBuf [2]byte
+ if _, err := io.ReadFull(r, portBuf[:]); err != nil {
+ return "", err
+ }
+ return net.JoinHostPort(string(hostBuf), fmt.Sprint(binary.BigEndian.Uint16(portBuf[:]))), nil
+ default:
+ return "", fmt.Errorf("unknown address type: %d", atyp[0])
+ }
+}
diff --git a/clash-meta/transport/sudoku/handshake.go b/clash-meta/transport/sudoku/handshake.go
new file mode 100644
index 0000000000..1f822707ce
--- /dev/null
+++ b/clash-meta/transport/sudoku/handshake.go
@@ -0,0 +1,147 @@
+package sudoku
+
+import (
+ "bufio"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "net"
+ "time"
+
+ "github.com/saba-futai/sudoku/apis"
+ "github.com/saba-futai/sudoku/pkg/crypto"
+ "github.com/saba-futai/sudoku/pkg/obfs/httpmask"
+ "github.com/saba-futai/sudoku/pkg/obfs/sudoku"
+
+ "github.com/metacubex/mihomo/log"
+)
+
+type SessionType int
+
+const (
+ SessionTypeTCP SessionType = iota
+ SessionTypeUoT
+)
+
+type ServerSession struct {
+ Conn net.Conn
+ Type SessionType
+ Target string
+}
+
+type bufferedConn struct {
+ net.Conn
+ r *bufio.Reader
+}
+
+func (bc *bufferedConn) Read(p []byte) (int, error) {
+ return bc.r.Read(p)
+}
+
+type preBufferedConn struct {
+ net.Conn
+ buf []byte
+}
+
+func (p *preBufferedConn) Read(b []byte) (int, error) {
+ if len(p.buf) > 0 {
+ n := copy(b, p.buf)
+ p.buf = p.buf[n:]
+ return n, nil
+ }
+ if p.Conn == nil {
+ return 0, io.EOF
+ }
+ return p.Conn.Read(b)
+}
+
+func absInt64(v int64) int64 {
+ if v < 0 {
+ return -v
+ }
+ return v
+}
+
+// ServerHandshake performs Sudoku server-side handshake and detects UoT preface.
+func ServerHandshake(rawConn net.Conn, cfg *apis.ProtocolConfig) (*ServerSession, error) {
+ if cfg == nil {
+ return nil, fmt.Errorf("config is required")
+ }
+ if err := cfg.Validate(); err != nil {
+ return nil, fmt.Errorf("invalid config: %w", err)
+ }
+
+ handshakeTimeout := time.Duration(cfg.HandshakeTimeoutSeconds) * time.Second
+ if handshakeTimeout <= 0 {
+ handshakeTimeout = 5 * time.Second
+ }
+
+ bufReader := bufio.NewReader(rawConn)
+ if !cfg.DisableHTTPMask {
+ if peek, _ := bufReader.Peek(4); len(peek) == 4 && string(peek) == "POST" {
+ if _, err := httpmask.ConsumeHeader(bufReader); err != nil {
+ return nil, fmt.Errorf("invalid http header: %w", err)
+ }
+ }
+ }
+
+ rawConn.SetReadDeadline(time.Now().Add(handshakeTimeout))
+ bConn := &bufferedConn{
+ Conn: rawConn,
+ r: bufReader,
+ }
+ sConn := sudoku.NewConn(bConn, cfg.Table, cfg.PaddingMin, cfg.PaddingMax, true)
+ cConn, err := crypto.NewAEADConn(sConn, cfg.Key, cfg.AEADMethod)
+ if err != nil {
+ return nil, fmt.Errorf("crypto setup failed: %w", err)
+ }
+
+ var handshakeBuf [16]byte
+ if _, err := io.ReadFull(cConn, handshakeBuf[:]); err != nil {
+ cConn.Close()
+ return nil, fmt.Errorf("read handshake failed: %w", err)
+ }
+
+ ts := int64(binary.BigEndian.Uint64(handshakeBuf[:8]))
+ if absInt64(time.Now().Unix()-ts) > 60 {
+ cConn.Close()
+ return nil, fmt.Errorf("timestamp skew detected")
+ }
+
+ sConn.StopRecording()
+
+ firstByte := make([]byte, 1)
+ if _, err := io.ReadFull(cConn, firstByte); err != nil {
+ cConn.Close()
+ return nil, fmt.Errorf("read first byte failed: %w", err)
+ }
+
+ if firstByte[0] == UoTMagicByte {
+ version := make([]byte, 1)
+ if _, err := io.ReadFull(cConn, version); err != nil {
+ cConn.Close()
+ return nil, fmt.Errorf("read uot version failed: %w", err)
+ }
+ if version[0] != uotVersion {
+ cConn.Close()
+ return nil, fmt.Errorf("unsupported uot version: %d", version[0])
+ }
+ rawConn.SetReadDeadline(time.Time{})
+ return &ServerSession{Conn: cConn, Type: SessionTypeUoT}, nil
+ }
+
+ prefixed := &preBufferedConn{Conn: cConn, buf: firstByte}
+ target, err := DecodeAddress(prefixed)
+ if err != nil {
+ cConn.Close()
+ return nil, fmt.Errorf("read target address failed: %w", err)
+ }
+
+ rawConn.SetReadDeadline(time.Time{})
+ log.Debugln("[Sudoku] incoming TCP session target: %s", target)
+ return &ServerSession{
+ Conn: prefixed,
+ Type: SessionTypeTCP,
+ Target: target,
+ }, nil
+}
diff --git a/clash-meta/transport/sudoku/uot.go b/clash-meta/transport/sudoku/uot.go
new file mode 100644
index 0000000000..be3fe90033
--- /dev/null
+++ b/clash-meta/transport/sudoku/uot.go
@@ -0,0 +1,158 @@
+package sudoku
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "net/netip"
+ "strconv"
+ "sync"
+ "time"
+
+ "github.com/metacubex/mihomo/log"
+)
+
+const (
+ UoTMagicByte byte = 0xEE
+ uotVersion = 0x01
+ maxUoTPayload = 64 * 1024
+)
+
+// WritePreface writes the UDP-over-TCP marker and version.
+func WritePreface(w io.Writer) error {
+ _, err := w.Write([]byte{UoTMagicByte, uotVersion})
+ return err
+}
+
+// WriteDatagram sends a single UDP datagram frame over a reliable stream.
+func WriteDatagram(w io.Writer, addr string, payload []byte) error {
+ addrBuf, err := EncodeAddress(addr)
+ if err != nil {
+ return fmt.Errorf("encode address: %w", err)
+ }
+
+ if addrLen := len(addrBuf); addrLen == 0 || addrLen > maxUoTPayload {
+ return fmt.Errorf("address too long: %d", len(addrBuf))
+ }
+ if payloadLen := len(payload); payloadLen > maxUoTPayload {
+ return fmt.Errorf("payload too large: %d", payloadLen)
+ }
+
+ var header [4]byte
+ binary.BigEndian.PutUint16(header[:2], uint16(len(addrBuf)))
+ binary.BigEndian.PutUint16(header[2:], uint16(len(payload)))
+
+ if _, err := w.Write(header[:]); err != nil {
+ return err
+ }
+ if _, err := w.Write(addrBuf); err != nil {
+ return err
+ }
+ _, err = w.Write(payload)
+ return err
+}
+
+// ReadDatagram parses a single UDP datagram frame from the reliable stream.
+func ReadDatagram(r io.Reader) (string, []byte, error) {
+ var header [4]byte
+ if _, err := io.ReadFull(r, header[:]); err != nil {
+ return "", nil, err
+ }
+
+ addrLen := int(binary.BigEndian.Uint16(header[:2]))
+ payloadLen := int(binary.BigEndian.Uint16(header[2:]))
+
+ if addrLen <= 0 || addrLen > maxUoTPayload {
+ return "", nil, fmt.Errorf("invalid address length: %d", addrLen)
+ }
+ if payloadLen < 0 || payloadLen > maxUoTPayload {
+ return "", nil, fmt.Errorf("invalid payload length: %d", payloadLen)
+ }
+
+ addrBuf := make([]byte, addrLen)
+ if _, err := io.ReadFull(r, addrBuf); err != nil {
+ return "", nil, err
+ }
+
+ addr, err := DecodeAddress(bytes.NewReader(addrBuf))
+ if err != nil {
+ return "", nil, fmt.Errorf("decode address: %w", err)
+ }
+
+ payload := make([]byte, payloadLen)
+ if _, err := io.ReadFull(r, payload); err != nil {
+ return "", nil, err
+ }
+
+ return addr, payload, nil
+}
+
+// UoTPacketConn adapts a net.Conn with the Sudoku UoT framing to net.PacketConn.
+type UoTPacketConn struct {
+ conn net.Conn
+ writeMu sync.Mutex
+}
+
+func NewUoTPacketConn(conn net.Conn) *UoTPacketConn {
+ return &UoTPacketConn{conn: conn}
+}
+
+func (c *UoTPacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
+ for {
+ addrStr, payload, err := ReadDatagram(c.conn)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ if len(payload) > len(p) {
+ return 0, nil, io.ErrShortBuffer
+ }
+
+ host, port, _ := net.SplitHostPort(addrStr)
+ portInt, _ := strconv.ParseUint(port, 10, 16)
+ ip, err := netip.ParseAddr(host)
+ if err != nil { // disallow domain addr at here, just ignore
+ log.Debugln("[Sudoku][UoT] discard datagram with invalid address %s: %v", addrStr, err)
+ continue
+ }
+ udpAddr := net.UDPAddrFromAddrPort(netip.AddrPortFrom(ip.Unmap(), uint16(portInt)))
+
+ copy(p, payload)
+ return len(payload), udpAddr, nil
+ }
+}
+
+func (c *UoTPacketConn) WriteTo(p []byte, addr net.Addr) (int, error) {
+ if addr == nil {
+ return 0, errors.New("address is nil")
+ }
+ c.writeMu.Lock()
+ defer c.writeMu.Unlock()
+ if err := WriteDatagram(c.conn, addr.String(), p); err != nil {
+ return 0, err
+ }
+ return len(p), nil
+}
+
+func (c *UoTPacketConn) Close() error {
+ return c.conn.Close()
+}
+
+func (c *UoTPacketConn) LocalAddr() net.Addr {
+ return c.conn.LocalAddr()
+}
+
+func (c *UoTPacketConn) SetDeadline(t time.Time) error {
+ return c.conn.SetDeadline(t)
+}
+
+func (c *UoTPacketConn) SetReadDeadline(t time.Time) error {
+ return c.conn.SetReadDeadline(t)
+}
+
+func (c *UoTPacketConn) SetWriteDeadline(t time.Time) error {
+ return c.conn.SetWriteDeadline(t)
+}
diff --git a/clash-meta/tunnel/statistic/manager.go b/clash-meta/tunnel/statistic/manager.go
index 9db4601e14..c69746dbcb 100644
--- a/clash-meta/tunnel/statistic/manager.go
+++ b/clash-meta/tunnel/statistic/manager.go
@@ -72,6 +72,10 @@ func (m *Manager) Now() (up int64, down int64) {
return m.uploadBlip.Load(), m.downloadBlip.Load()
}
+func (m *Manager) Total() (up, down int64) {
+ return m.uploadTotal.Load(), m.downloadTotal.Load()
+}
+
func (m *Manager) Memory() uint64 {
m.updateMemory()
return m.memory
diff --git a/clash-meta/tunnel/statistic/patch_android.go b/clash-meta/tunnel/statistic/patch_android.go
deleted file mode 100644
index f1eee346eb..0000000000
--- a/clash-meta/tunnel/statistic/patch_android.go
+++ /dev/null
@@ -1,7 +0,0 @@
-//go:build android && cmfa
-
-package statistic
-
-func (m *Manager) Total() (up, down int64) {
- return m.uploadTotal.Load(), m.downloadTotal.Load()
-}
diff --git a/clash-nyanpasu/manifest/version.json b/clash-nyanpasu/manifest/version.json
index 16462edb2f..6a5b776520 100644
--- a/clash-nyanpasu/manifest/version.json
+++ b/clash-nyanpasu/manifest/version.json
@@ -2,7 +2,7 @@
"manifest_version": 1,
"latest": {
"mihomo": "v1.19.17",
- "mihomo_alpha": "alpha-9a5e506",
+ "mihomo_alpha": "alpha-fdb7cb1",
"clash_rs": "v0.9.2",
"clash_premium": "2023-09-05-gdcc8d87",
"clash_rs_alpha": "0.9.2-alpha+sha.e1f8fbb"
@@ -69,5 +69,5 @@
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
}
},
- "updated_at": "2025-12-01T22:21:34.119Z"
+ "updated_at": "2025-12-02T22:21:02.066Z"
}
diff --git a/clash-verge-rev/.github/workflows/alpha.yml b/clash-verge-rev/.github/workflows/alpha.yml
index a7221a3673..cec7440ae7 100644
--- a/clash-verge-rev/.github/workflows/alpha.yml
+++ b/clash-verge-rev/.github/workflows/alpha.yml
@@ -35,7 +35,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Check tag and package.json version
id: check_tag
@@ -64,7 +64,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Delete Old Alpha Tags Except Latest
- uses: actions/github-script@v7
+ uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@@ -180,7 +180,7 @@ jobs:
needs: delete_old_assets
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Fetch UPDATE logs
id: fetch_update_logs
@@ -275,7 +275,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
@@ -305,9 +305,9 @@ jobs:
echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -360,7 +360,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
@@ -375,9 +375,9 @@ jobs:
save-if: false
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -491,7 +491,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
@@ -503,9 +503,9 @@ jobs:
save-if: false
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
diff --git a/clash-verge-rev/.github/workflows/autobuild-check-test.yml b/clash-verge-rev/.github/workflows/autobuild-check-test.yml
index ea827db229..4eb6da0a2c 100644
--- a/clash-verge-rev/.github/workflows/autobuild-check-test.yml
+++ b/clash-verge-rev/.github/workflows/autobuild-check-test.yml
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 2
diff --git a/clash-verge-rev/.github/workflows/autobuild.yml b/clash-verge-rev/.github/workflows/autobuild.yml
index 0f978e8a3d..fd43e28b1b 100644
--- a/clash-verge-rev/.github/workflows/autobuild.yml
+++ b/clash-verge-rev/.github/workflows/autobuild.yml
@@ -31,22 +31,22 @@ jobs:
if: ${{ needs.check_commit.outputs.should_run == 'true' }}
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Fetch UPDATE logs
id: fetch_update_logs
run: bash ./scripts/extract_update_logs.sh
shell: bash
- - uses: pnpm/action-setup@v4
+ - uses: pnpm/action-setup@v4.2.0
name: Install pnpm
with:
run_install: false
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
- name: Install dependencies
run: pnpm install --frozen-lockfile
@@ -77,20 +77,20 @@ jobs:
### Windows (不再支持Win7)
#### 正常版本(推荐)
- - [64位(常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64-setup_windows.exe) | [ARM64(不常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64-setup_windows.exe)
+ - [64位(常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64-setup.exe) | [ARM64(不常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64-setup.exe)
#### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用)
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64_fixed_webview2-setup.exe) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64_fixed_webview2-setup.exe)
### macOS
- - [Apple M芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64_darwin.dmg) | [Intel芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64_darwin.dmg)
+ - [Apple M芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.dmg) | [Intel芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64.dmg)
### Linux
#### DEB包(Debian系) 使用 apt ./路径 安装
- - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64_linux.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)
+ - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)
#### RPM包(Redhat系) 使用 dnf ./路径 安装
- - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64_linux.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm)
+ - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm)
### FAQ
- [常见问题](https://clash-verge-rev.github.io/faq/windows.html)
@@ -109,7 +109,7 @@ jobs:
body_path: release.txt
prerelease: true
token: ${{ secrets.GITHUB_TOKEN }}
- generate_release_notes: true
+ generate_release_notes: false
clean_old_assets:
name: Clean Old Release Assets
@@ -142,7 +142,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@master
@@ -156,11 +156,13 @@ jobs:
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
- workspaces: src-tauri
- cache-all-crates: true
save-if: ${{ github.ref == 'refs/heads/dev' }}
- shared-key: autobuild-${{ runner.os }}-${{ matrix.target }}
- key: ${{ runner.os }}-${{ matrix.target }}-${{ hashFiles('src-tauri/Cargo.lock') }}
+ prefix-key: "v1-rust"
+ key: "rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
+ workspaces: |
+ . -> target
+ cache-all-crates: true
+ cache-workspace-crates: true
- name: Install dependencies (ubuntu only)
if: matrix.os == 'ubuntu-22.04'
@@ -177,24 +179,24 @@ jobs:
echo "OPENSSL_LIB_DIR=$(brew --prefix openssl@3)/lib" >> $GITHUB_ENV
echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV
- - uses: pnpm/action-setup@v4
+ - uses: pnpm/action-setup@v4.2.0
name: Install pnpm
with:
run_install: false
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
cache: "pnpm"
- - name: Cache pnpm store
+ - name: Pnpm Cache
uses: actions/cache@v4
with:
path: ~/.pnpm-store
- key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
+ key: "pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
restore-keys: |
- ${{ runner.os }}-pnpm-
+ pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}
- name: Pnpm install and check
run: |
@@ -218,12 +220,12 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
- APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
- APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
- APPLE_ID: ${{ secrets.APPLE_ID }}
- APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
- APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
+ # APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
+ # APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
+ # APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
+ # APPLE_ID: ${{ secrets.APPLE_ID }}
+ # APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
+ # APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
with:
tagName: ${{ env.TAG_NAME }}
releaseName: "Clash Verge Rev ${{ env.TAG_CHANNEL }}"
@@ -251,7 +253,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@master
@@ -265,30 +267,32 @@ jobs:
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
- workspaces: src-tauri
- cache-all-crates: true
save-if: ${{ github.ref == 'refs/heads/dev' }}
- shared-key: autobuild-${{ runner.os }}-${{ matrix.target }}
- key: ${{ runner.os }}-${{ matrix.target }}-${{ hashFiles('src-tauri/Cargo.lock') }}
+ prefix-key: "v1-rust"
+ key: "rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
+ workspaces: |
+ . -> target
+ cache-all-crates: true
+ cache-workspace-crates: true
- name: Install pnpm
- uses: pnpm/action-setup@v4
+ uses: pnpm/action-setup@v4.2.0
with:
run_install: false
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
cache: "pnpm"
- - name: Cache pnpm store
+ - name: Pnpm Cache
uses: actions/cache@v4
with:
path: ~/.pnpm-store
- key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
+ key: "pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
restore-keys: |
- ${{ runner.os }}-pnpm-
+ pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}
- name: Pnpm install and check
run: |
@@ -385,8 +389,8 @@ jobs:
prerelease: true
token: ${{ secrets.GITHUB_TOKEN }}
files: |
- src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb
- src-tauri/target/${{ matrix.target }}/release/bundle/rpm/*.rpm
+ target/${{ matrix.target }}/release/bundle/deb/*.deb
+ target/${{ matrix.target }}/release/bundle/rpm/*.rpm
autobuild-x86-arm-windows_webview2:
name: Autobuild x86 and ARM Windows with WebView2
@@ -405,7 +409,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
@@ -413,30 +417,32 @@ jobs:
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
- workspaces: src-tauri
- cache-all-crates: true
save-if: ${{ github.ref == 'refs/heads/dev' }}
- shared-key: autobuild-${{ runner.os }}-${{ matrix.target }}
- key: ${{ runner.os }}-${{ matrix.target }}-${{ hashFiles('src-tauri/Cargo.lock') }}
+ prefix-key: "v1-rust"
+ key: "rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
+ workspaces: |
+ . -> target
+ cache-all-crates: true
+ cache-workspace-crates: true
- name: Install pnpm
- uses: pnpm/action-setup@v4
+ uses: pnpm/action-setup@v4.2.0
with:
run_install: false
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
cache: "pnpm"
- - name: Cache pnpm store
+ - name: Pnpm Cache
uses: actions/cache@v4
with:
path: ~/.pnpm-store
- key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
+ key: "pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
restore-keys: |
- ${{ runner.os }}-pnpm-
+ pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}
- name: Pnpm install and check
run: |
@@ -475,19 +481,19 @@ jobs:
- name: Rename
run: |
- $files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe"
+ $files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.exe$", "_fixed_webview2-setup.exe"
Rename-Item $file.FullName $newName
}
- $files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip"
+ $files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.nsis\.zip$", "_fixed_webview2-setup.nsis.zip"
Rename-Item $file.FullName $newName
}
- $files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig"
+ $files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.exe\.sig$", "_fixed_webview2-setup.exe.sig"
Rename-Item $file.FullName $newName
@@ -500,7 +506,7 @@ jobs:
name: "Clash Verge Rev ${{ env.TAG_CHANNEL }}"
prerelease: true
token: ${{ secrets.GITHUB_TOKEN }}
- files: src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*setup*
+ files: target/${{ matrix.target }}/release/bundle/nsis/*setup*
- name: Portable Bundle
run: pnpm portable-fixed-webview2 ${{ matrix.target }} --${{ env.TAG_NAME }}
@@ -519,7 +525,7 @@ jobs:
]
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Fetch UPDATE logs
id: fetch_update_logs
@@ -527,11 +533,11 @@ jobs:
shell: bash
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
- - uses: pnpm/action-setup@v4
+ - uses: pnpm/action-setup@v4.2.0
name: Install pnpm
with:
run_install: false
@@ -566,20 +572,20 @@ jobs:
### Windows (不再支持Win7)
#### 正常版本(推荐)
- - [64位(常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64-setup_windows.exe) | [ARM64(不常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64-setup_windows.exe)
+ - [64位(常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64-setup.exe) | [ARM64(不常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64-setup.exe)
#### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用)
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64_fixed_webview2-setup.exe) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64_fixed_webview2-setup.exe)
### macOS
- - [Apple M芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64_darwin.dmg) | [Intel芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64_darwin.dmg)
+ - [Apple M芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.dmg) | [Intel芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64.dmg)
### Linux
#### DEB包(Debian系) 使用 apt ./路径 安装
- - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64_linux.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)
+ - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)
#### RPM包(Redhat系) 使用 dnf ./路径 安装
- - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64_linux.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm)
+ - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm)
### FAQ
- [常见问题](https://clash-verge-rev.github.io/faq/windows.html)
diff --git a/clash-verge-rev/.github/workflows/check-commit-needs-build.yml b/clash-verge-rev/.github/workflows/check-commit-needs-build.yml
index e625319b61..f78c0aab2c 100644
--- a/clash-verge-rev/.github/workflows/check-commit-needs-build.yml
+++ b/clash-verge-rev/.github/workflows/check-commit-needs-build.yml
@@ -53,7 +53,7 @@ jobs:
autobuild_version: ${{ steps.check.outputs.autobuild_version }}
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 50
diff --git a/clash-verge-rev/.github/workflows/clean-old-assets.yml b/clash-verge-rev/.github/workflows/clean-old-assets.yml
index 4c9ad9f029..cb21eb213c 100644
--- a/clash-verge-rev/.github/workflows/clean-old-assets.yml
+++ b/clash-verge-rev/.github/workflows/clean-old-assets.yml
@@ -42,7 +42,7 @@ jobs:
autobuild_version: ${{ steps.check.outputs.autobuild_version }}
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 50
@@ -56,7 +56,7 @@ jobs:
echo "🔍 Finding last commit with Tauri-related changes..."
# Define patterns for Tauri-related files
- TAURI_PATTERNS="src/ src-tauri/src src-tauri/Cargo.toml src-tauri/Cargo.lock src-tauri/tauri.*.conf.json src-tauri/build.rs src-tauri/capabilities"
+ TAURI_PATTERNS="src/ src-tauri/src src-tauri/Cargo.toml Cargo.lock src-tauri/tauri.*.conf.json src-tauri/build.rs src-tauri/capabilities"
# Get the last commit that changed any of these patterns (excluding build artifacts)
LAST_TAURI_COMMIT=""
@@ -105,7 +105,7 @@ jobs:
needs: check_current_version
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Clean old assets from release
env:
diff --git a/clash-verge-rev/.github/workflows/cross_check.yaml b/clash-verge-rev/.github/workflows/cross_check.yaml
index 03a65bf147..06eeb91551 100644
--- a/clash-verge-rev/.github/workflows/cross_check.yaml
+++ b/clash-verge-rev/.github/workflows/cross_check.yaml
@@ -30,7 +30,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
@@ -41,9 +41,9 @@ jobs:
run: rustup target add ${{ matrix.target }}
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "20"
+ node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
diff --git a/clash-verge-rev/.github/workflows/dev.yml b/clash-verge-rev/.github/workflows/dev.yml
index e5ae26c327..74c7dd01f6 100644
--- a/clash-verge-rev/.github/workflows/dev.yml
+++ b/clash-verge-rev/.github/workflows/dev.yml
@@ -70,20 +70,22 @@ jobs:
- name: Checkout Repository
if: github.event.inputs[matrix.input] == 'true'
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Rust Stable
if: github.event.inputs[matrix.input] == 'true'
uses: dtolnay/rust-toolchain@1.91.0
- name: Rust Cache
- if: github.event.inputs[matrix.input] == 'true'
uses: Swatinem/rust-cache@v2
with:
- workspaces: src-tauri
- save-if: false
+ save-if: ${{ github.ref == 'refs/heads/dev' }}
+ prefix-key: "v1-rust"
+ key: "rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
+ workspaces: |
+ . -> target
cache-all-crates: true
- shared-key: autobuild-shared
+ cache-workspace-crates: true
- name: Install dependencies (ubuntu only)
if: matrix.os == 'ubuntu-22.04' && github.event.inputs[matrix.input] == 'true'
@@ -99,11 +101,20 @@ jobs:
- name: Install Node
if: github.event.inputs[matrix.input] == 'true'
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "20"
+ node-version: "24.11.1"
cache: "pnpm"
+ - name: Pnpm Cache
+ uses: actions/cache@v4
+ with:
+ path: ~/.pnpm-store
+ key: "pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
+ restore-keys: |
+ pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}
+ lookup-only: true
+
- name: Pnpm install and check
if: github.event.inputs[matrix.input] == 'true'
run: |
@@ -130,36 +141,36 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
- APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
- APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
- APPLE_ID: ${{ secrets.APPLE_ID }}
- APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
- APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
+ # APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
+ # APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
+ # APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
+ # APPLE_ID: ${{ secrets.APPLE_ID }}
+ # APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
+ # APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
with:
tauriScript: pnpm
args: --target ${{ matrix.target }} -b ${{ matrix.bundle }}
- name: Upload Artifacts (macOS)
if: matrix.os == 'macos-latest' && github.event.inputs[matrix.input] == 'true'
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v5
with:
name: ${{ matrix.target }}
- path: src-tauri/target/${{ matrix.target }}/release/bundle/dmg/*.dmg
+ path: target/${{ matrix.target }}/release/bundle/dmg/*.dmg
if-no-files-found: error
- name: Upload Artifacts (Windows)
if: matrix.os == 'windows-latest' && github.event.inputs[matrix.input] == 'true'
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v5
with:
name: ${{ matrix.target }}
- path: src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*.exe
+ path: target/${{ matrix.target }}/release/bundle/nsis/*.exe
if-no-files-found: error
- name: Upload Artifacts (Linux)
if: matrix.os == 'ubuntu-22.04' && github.event.inputs[matrix.input] == 'true'
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v5
with:
name: ${{ matrix.target }}
- path: src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb
+ path: target/${{ matrix.target }}/release/bundle/deb/*.deb
if-no-files-found: error
diff --git a/clash-verge-rev/.github/workflows/frontend-check.yml b/clash-verge-rev/.github/workflows/frontend-check.yml
new file mode 100644
index 0000000000..4bb8455bdf
--- /dev/null
+++ b/clash-verge-rev/.github/workflows/frontend-check.yml
@@ -0,0 +1,75 @@
+name: Frontend Check
+
+on:
+ pull_request:
+ workflow_dispatch:
+
+env:
+ HUSKY: 0
+
+jobs:
+ frontend:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Check frontend changes
+ id: check_frontend
+ uses: dorny/paths-filter@v3
+ with:
+ filters: |
+ frontend:
+ - 'src/**'
+ - '**/*.js'
+ - '**/*.ts'
+ - '**/*.tsx'
+ - '**/*.css'
+ - '**/*.scss'
+ - '**/*.json'
+ - '**/*.md'
+ - 'package.json'
+ - 'pnpm-lock.yaml'
+ - 'pnpm-workspace.yaml'
+ - 'eslint.config.ts'
+ - 'tsconfig.json'
+ - 'vite.config.*'
+
+ - name: Skip if no frontend changes
+ if: steps.check_frontend.outputs.frontend != 'true'
+ run: echo "No frontend changes, skipping frontend checks."
+
+ - name: Install pnpm
+ if: steps.check_frontend.outputs.frontend == 'true'
+ uses: pnpm/action-setup@v4
+ with:
+ run_install: false
+
+ - uses: actions/setup-node@v6
+ if: steps.check_frontend.outputs.frontend == 'true'
+ with:
+ node-version: "24.11.1"
+ cache: "pnpm"
+
+ - name: Restore pnpm cache
+ if: steps.check_frontend.outputs.frontend == 'true'
+ uses: actions/cache@v4
+ with:
+ path: ~/.pnpm-store
+ key: "pnpm-shared-stable-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}"
+ restore-keys: |
+ pnpm-shared-stable-${{ runner.os }}-
+
+ - run: pnpm install --frozen-lockfile
+ if: steps.check_frontend.outputs.frontend == 'true'
+
+ - name: Run Prettier check
+ if: steps.check_frontend.outputs.frontend == 'true'
+ run: pnpm format:check
+
+ - name: Run ESLint
+ if: steps.check_frontend.outputs.frontend == 'true'
+ run: pnpm lint
+
+ - name: Run TypeScript typecheck
+ if: steps.check_frontend.outputs.frontend == 'true'
+ run: pnpm typecheck
diff --git a/clash-verge-rev/.github/workflows/lint-clippy.yml b/clash-verge-rev/.github/workflows/lint-clippy.yml
index d7f3416ba7..ee74f9b0dd 100644
--- a/clash-verge-rev/.github/workflows/lint-clippy.yml
+++ b/clash-verge-rev/.github/workflows/lint-clippy.yml
@@ -44,7 +44,7 @@ jobs:
echo "Manual trigger detected: skipping changes check and running clippy."
- name: Checkout Repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@master
@@ -58,11 +58,13 @@ jobs:
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
- workspaces: src-tauri
+ save-if: ${{ github.ref == 'refs/heads/dev' }}
+ prefix-key: "v1-rust"
+ key: "rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
+ workspaces: |
+ . -> target
cache-all-crates: true
- save-if: false
- shared-key: autobuild-${{ runner.os }}-${{ matrix.target }}
- key: ${{ runner.os }}-${{ matrix.target }}-${{ hashFiles('src-tauri/Cargo.lock') }}
+ cache-workspace-crates: true
- name: Install dependencies (ubuntu only)
if: matrix.os == 'ubuntu-22.04'
diff --git a/clash-verge-rev/.github/workflows/release.yml b/clash-verge-rev/.github/workflows/release.yml
index 153a5f8c95..f3f2b271fc 100644
--- a/clash-verge-rev/.github/workflows/release.yml
+++ b/clash-verge-rev/.github/workflows/release.yml
@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -69,7 +69,7 @@ jobs:
needs: [release, release-for-linux-arm, release-for-fixed-webview2]
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Fetch UPDATE logs
id: fetch_update_logs
@@ -156,7 +156,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
@@ -186,9 +186,9 @@ jobs:
echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -202,7 +202,7 @@ jobs:
- name: Tauri build
# 上游 5.24 修改了 latest.json 的生成逻辑,且依赖 tauri-plugin-update 2.10.0 暂未发布,故锁定在 0.5.23 版本
- uses: tauri-apps/tauri-action@v0.5.23
+ uses: tauri-apps/tauri-action@v0.6.0
env:
NODE_OPTIONS: "--max_old_space_size=4096"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -240,7 +240,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
@@ -255,9 +255,9 @@ jobs:
save-if: false
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -364,7 +364,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
@@ -376,9 +376,9 @@ jobs:
save-if: false
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -400,7 +400,7 @@ jobs:
- name: Tauri build
id: build
# 上游 5.24 修改了 latest.json 的生成逻辑,且依赖 tauri-plugin-update 2.10.0 暂未发布,故锁定在 0.5.23 版本
- uses: tauri-apps/tauri-action@v0.5.23
+ uses: tauri-apps/tauri-action@v0.6.0
env:
NODE_OPTIONS: "--max_old_space_size=4096"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -412,19 +412,19 @@ jobs:
- name: Rename
run: |
- $files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe"
+ $files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.exe$", "_fixed_webview2-setup.exe"
Rename-Item $file.FullName $newName
}
- $files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip"
+ $files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.nsis\.zip$", "_fixed_webview2-setup.nsis.zip"
Rename-Item $file.FullName $newName
}
- $files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig"
+ $files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.exe\.sig$", "_fixed_webview2-setup.exe.sig"
Rename-Item $file.FullName $newName
@@ -452,12 +452,12 @@ jobs:
needs: [update_tag]
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -478,12 +478,12 @@ jobs:
needs: [update_tag]
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -505,7 +505,7 @@ jobs:
needs: [update_tag, release-update]
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Get Version
@@ -535,7 +535,7 @@ jobs:
]
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Fetch UPDATE logs
id: fetch_update_logs
@@ -543,9 +543,9 @@ jobs:
shell: bash
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
diff --git a/clash-verge-rev/.github/workflows/fmt.yml b/clash-verge-rev/.github/workflows/rustfmt.yml
similarity index 56%
rename from clash-verge-rev/.github/workflows/fmt.yml
rename to clash-verge-rev/.github/workflows/rustfmt.yml
index 58efc98b1c..c0f5c33c95 100644
--- a/clash-verge-rev/.github/workflows/fmt.yml
+++ b/clash-verge-rev/.github/workflows/rustfmt.yml
@@ -14,7 +14,7 @@ jobs:
rustfmt:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Check Rust changes
id: check_rust
@@ -39,42 +39,6 @@ jobs:
if: steps.check_rust.outputs.rust == 'true'
run: cargo fmt --manifest-path ./src-tauri/Cargo.toml --all -- --check
- prettier:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
-
- - name: Check Web changes
- id: check_web
- uses: dorny/paths-filter@v3
- with:
- filters: |
- web:
- - 'src/**'
- - '**/*.js'
- - '**/*.ts'
- - '**/*.tsx'
- - '**/*.css'
- - '**/*.scss'
- - '**/*.json'
- - '**/*.md'
- - '**/*.json'
-
- - name: Skip if no Web changes
- if: steps.check_web.outputs.web != 'true'
- run: echo "No web changes, skipping prettier."
-
- - uses: actions/setup-node@v4
- if: steps.check_web.outputs.web == 'true'
- with:
- node-version: "lts/*"
- - run: corepack enable
- if: steps.check_web.outputs.web == 'true'
- - run: pnpm install --frozen-lockfile
- if: steps.check_web.outputs.web == 'true'
- - run: pnpm format:check
- if: steps.check_web.outputs.web == 'true'
-
# taplo:
# name: taplo (.toml files)
# runs-on: ubuntu-latest
diff --git a/clash-verge-rev/.github/workflows/updater.yml b/clash-verge-rev/.github/workflows/updater.yml
index d482bc608b..1e182197cb 100644
--- a/clash-verge-rev/.github/workflows/updater.yml
+++ b/clash-verge-rev/.github/workflows/updater.yml
@@ -10,12 +10,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -34,12 +34,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: "22"
+ node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
diff --git a/clash-verge-rev/.husky/pre-push b/clash-verge-rev/.husky/pre-push
index 43ffed41fe..4f2a6a6b70 100644
--- a/clash-verge-rev/.husky/pre-push
+++ b/clash-verge-rev/.husky/pre-push
@@ -40,4 +40,3 @@ else
fi
echo "[pre-push] All checks passed."
-exit 0
diff --git a/clash-verge-rev/CONTRIBUTING.md b/clash-verge-rev/CONTRIBUTING.md
index e23cd352fd..833289ad41 100644
--- a/clash-verge-rev/CONTRIBUTING.md
+++ b/clash-verge-rev/CONTRIBUTING.md
@@ -1,151 +1,132 @@
# CONTRIBUTING
-Thank you for your interest in contributing to Clash Verge Rev! This document provides guidelines and instructions to help you set up your development environment and start contributing.
+Thank you for your interest in contributing to **Clash Verge Rev**! This guide provides instructions to help you set up your development environment and start contributing effectively.
## Internationalization (i18n)
-We welcome translations and improvements to existing locales. Please follow the detailed guidelines in [CONTRIBUTING_i18n.md](docs/CONTRIBUTING_i18n.md) for instructions on extracting strings, file naming conventions, testing translations, and submitting translation PRs.
+We welcome translations and improvements to existing locales. For details on contributing translations, please see [CONTRIBUTING_i18n.md](docs/CONTRIBUTING_i18n.md).
## Development Setup
-Before you start contributing to the project, you need to set up your development environment. Here are the steps you need to follow:
+Before contributing, you need to set up your development environment. Follow the steps below carefully.
### Prerequisites
-1. **Install Rust and Node.js**: Our project requires both Rust and Node.js. Please follow the instructions provided [here](https://tauri.app/start/prerequisites/) to install them on your system.
+1. **Install Rust and Node.js**
+ Our project requires both Rust and Node.js. Follow the official installation instructions [here](https://tauri.app/start/prerequisites/).
-### Setup for Windows Users
+### Windows Users
-> [!NOTE]
-> **If you are using a Windows ARM device, you additionally need to install [LLVM](https://github.com/llvm/llvm-project/releases) (including clang) and set the environment variable.**
->
-> Because the `ring` crate is compiled based on `clang` under Windows ARM.
+> [!NOTE]
+> **Windows ARM users must also install [LLVM](https://github.com/llvm/llvm-project/releases) (including clang) and set the corresponding environment variables.**
+> The `ring` crate depends on `clang` when building on Windows ARM.
-If you're a Windows user, you may need to perform some additional steps:
+Additional steps for Windows:
-- Make sure to add Rust and Node.js to your system's PATH. This is usually done during the installation process, but you can verify and manually add them if necessary.
-- The gnu `patch` tool should be installed
+- Ensure Rust and Node.js are added to your system `PATH`.
-When you setup `Rust` environment, Only use toolchain with `Windows MSVC` , to change settings follow command:
+- Install the GNU `patch` tool.
-```shell
+- Use the MSVC toolchain for Rust:
+
+```bash
rustup target add x86_64-pc-windows-msvc
rustup set default-host x86_64-pc-windows-msvc
```
-### Install Node.js Package
+### Install Node.js Package Manager
-After installing Rust and Node.js, install the necessary Node.js and Node Package Manager:
+Enable `corepack`:
-```shell
-npm install pnpm -g
+```bash
+corepack enable
```
-### Install Dependencies
+### Install Project Dependencies
-Install node packages
+Node.js dependencies:
-```shell
+```bash
pnpm install
```
-Install apt packages ONLY for Ubuntu
+Ubuntu-only system packages:
-```shell
-apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
+```bash
+sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
```
-### Download the Mihomo Core Binary
+### Download the Mihomo Core Binary (Automatic)
-You have two options for downloading the clash binary:
-
-- Automatically download it via the provided script:
-
- ```shell
- pnpm run prebuild
- # Use '--force' or '-f' to update both the Mihomo core version
- # and the Clash Verge Rev service version to the latest available.
- pnpm run prebuild --force
- ```
-
-- Manually download it from the [Mihomo release](https://github.com/MetaCubeX/mihomo/releases). After downloading, rename the binary according to the [Tauri configuration](https://tauri.app/v1/api/config#bundleconfig.externalbin).
+```bash
+pnpm run prebuild
+pnpm run prebuild --force # Re-download and overwrite Mihomo core and service binaries
+```
### Run the Development Server
-To run the development server, use the following command:
-
-```shell
-pnpm dev
-# If an app instance already exists, use a different command
-pnpm dev:diff
-# To using tauri built-in dev tool
-pnpm dev:tauri
+```bash
+pnpm dev # Standard
+pnpm dev:diff # If an app instance already exists
+pnpm dev:tauri # Run Tauri development mode
```
### Build the Project
-To build this project:
+Standard build:
-```shell
+```bash
pnpm build
```
-For a faster build, use the following command
+Fast build for testing:
-```shell
+```bash
pnpm build:fast
```
-This uses Rust's fast-release profile which significantly reduces compilation time by disabling optimization and LTO. The resulting binary will be larger and less performant than the standard build, but it's useful for testing changes quickly.
+### Clean Build
-The `Artifacts` will display in the `log` in the Terminal.
-
-### Build clean
-
-To clean rust build:
-
-```shell
+```bash
pnpm clean
```
### Portable Version (Windows Only)
-To package portable version after the build:
-
-```shell
+```bash
pnpm portable
```
## Contributing Your Changes
-#### Before commit your changes
+### Before Committing
-If you changed the rust code, it's recommanded to execute code style formatting and quailty checks.
-
-1. Code quailty checks
+**Code quality checks:**
```bash
-# For rust backend
-$ clash-verge-rev: pnpm clippy
-# For frontend (not yet).
+# Rust backend
+cargo clippy-all
+# Frontend
+pnpm lint
```
-2. Code style formatting
+**Code formatting:**
```bash
-# For rust backend
-$ clash-verge-rev: cd src-tauri
-$ clash-verge-rev/src-tauri: cargo fmt
-# For frontend
-$ clash-verge-rev: pnpm format:check
-$ clash-verge-rev: pnpm format
+# Rust backend
+cargo fmt
+# Frontend
+pnpm format
```
-Once you have made your changes:
+### Submitting Your Changes
1. Fork the repository.
-2. Create a new branch for your feature or bug fix.
-3. Commit your changes with clear and concise commit messages.
-4. Push your branch to your fork and submit a pull request to our repository.
-We appreciate your contributions and look forward to your active participation in our project!
+2. Create a new branch for your feature or bug fix.
+
+3. Commit your changes with clear messages.
+
+4. Push your branch and submit a pull request.
+
+We appreciate your contributions and look forward to your participation!
diff --git a/clash-verge-rev/src-tauri/Cargo.lock b/clash-verge-rev/Cargo.lock
similarity index 94%
rename from clash-verge-rev/src-tauri/Cargo.lock
rename to clash-verge-rev/Cargo.lock
index f763b73022..cc3352a4dc 100644
--- a/clash-verge-rev/src-tauri/Cargo.lock
+++ b/clash-verge-rev/Cargo.lock
@@ -170,6 +170,24 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+[[package]]
+name = "ashpd"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de3d60bee1a1d38c2077030f4788e1b4e31058d2e79a8cfc8f2b440bd44db290"
+dependencies = [
+ "async-fs 2.2.0",
+ "async-net 2.0.0",
+ "enumflags2",
+ "futures-channel",
+ "futures-util",
+ "rand 0.8.5",
+ "serde",
+ "serde_repr",
+ "url",
+ "zbus",
+]
+
[[package]]
name = "ashpd"
version = "0.11.0"
@@ -252,6 +270,32 @@ dependencies = [
"futures-lite 1.13.0",
]
+[[package]]
+name = "async-fs"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5"
+dependencies = [
+ "async-lock 3.4.1",
+ "blocking",
+ "futures-lite 2.6.1",
+]
+
+[[package]]
+name = "async-global-executor"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c"
+dependencies = [
+ "async-channel 2.5.0",
+ "async-executor",
+ "async-io 2.6.0",
+ "async-lock 3.4.1",
+ "blocking",
+ "futures-lite 2.6.1",
+ "once_cell",
+]
+
[[package]]
name = "async-io"
version = "1.13.0"
@@ -321,6 +365,17 @@ dependencies = [
"futures-lite 1.13.0",
]
+[[package]]
+name = "async-net"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7"
+dependencies = [
+ "async-io 2.6.0",
+ "blocking",
+ "futures-lite 2.6.1",
+]
+
[[package]]
name = "async-process"
version = "1.8.1"
@@ -364,7 +419,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -385,6 +440,32 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "async-std"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b"
+dependencies = [
+ "async-channel 1.9.0",
+ "async-global-executor",
+ "async-io 2.6.0",
+ "async-lock 3.4.1",
+ "crossbeam-utils",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-lite 2.6.1",
+ "gloo-timers",
+ "kv-log-macro",
+ "log",
+ "memchr",
+ "once_cell",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+ "wasm-bindgen-futures",
+]
+
[[package]]
name = "async-stream"
version = "0.3.6"
@@ -404,7 +485,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -421,7 +502,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -500,9 +581,9 @@ dependencies = [
[[package]]
name = "axum"
-version = "0.8.6"
+version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871"
+checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425"
dependencies = [
"axum-core 0.5.5",
"bytes",
@@ -676,7 +757,7 @@ dependencies = [
"boa_interner",
"boa_macros",
"boa_string",
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"num-bigint",
"rustc-hash",
]
@@ -706,9 +787,9 @@ dependencies = [
"futures-channel",
"futures-concurrency",
"futures-lite 2.6.1",
- "hashbrown 0.16.0",
+ "hashbrown 0.16.1",
"icu_normalizer",
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"intrusive-collections",
"itertools 0.14.0",
"num-bigint",
@@ -741,7 +822,7 @@ checksum = "f1179f690cbfcbe5364cceee5f1cb577265bb6f07b0be6f210aabe270adcf9da"
dependencies = [
"boa_macros",
"boa_string",
- "hashbrown 0.16.0",
+ "hashbrown 0.16.1",
"thin-vec",
]
@@ -753,8 +834,8 @@ checksum = "9626505d33dc63d349662437297df1d3afd9d5fc4a2b3ad34e5e1ce879a78848"
dependencies = [
"boa_gc",
"boa_macros",
- "hashbrown 0.16.0",
- "indexmap 2.12.0",
+ "hashbrown 0.16.1",
+ "indexmap 2.12.1",
"once_cell",
"phf 0.13.1",
"rustc-hash",
@@ -771,7 +852,7 @@ dependencies = [
"cow-utils",
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
"synstructure",
]
@@ -861,7 +942,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -878,9 +959,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "bytes"
-version = "1.10.1"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
+checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
dependencies = [
"serde",
]
@@ -978,9 +1059,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.2.43"
+version = "1.2.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "739eb0f94557554b3ca9a86d2d37bebd49c5e6d0c1d2bda35ba5bdac830befc2"
+checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -1080,18 +1161,18 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.5.51"
+version = "4.5.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5"
+checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
-version = "4.5.51"
+version = "4.5.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a"
+checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
dependencies = [
"anstyle",
"clap_lex",
@@ -1115,11 +1196,16 @@ dependencies = [
"base64 0.22.1",
"boa_engine",
"chrono",
+ "clash-verge-draft",
+ "clash-verge-logging",
+ "clash-verge-signal",
+ "clash-verge-types",
"clash_verge_logger",
"clash_verge_service_ipc",
"compact_str",
"console-subscriber",
"criterion",
+ "dark-light",
"deelevate",
"delay_timer",
"dunce",
@@ -1127,7 +1213,6 @@ dependencies = [
"futures",
"gethostname",
"getrandom 0.3.4",
- "libc",
"log",
"nanoid",
"network-interface",
@@ -1141,18 +1226,18 @@ dependencies = [
"reqwest_dav",
"runas",
"rust-i18n",
+ "rust_iso3166",
"scopeguard",
"serde",
"serde_json",
"serde_yaml_ng",
- "signal-hook 0.3.18",
"smartstring",
"sys-locale",
- "sysinfo",
"sysproxy",
"tauri",
"tauri-build",
"tauri-plugin-autostart",
+ "tauri-plugin-clash-verge-sysinfo",
"tauri-plugin-clipboard-manager",
"tauri-plugin-deep-link",
"tauri-plugin-devtools",
@@ -1170,15 +1255,55 @@ dependencies = [
"tokio-stream",
"warp",
"winapi",
- "windows-sys 0.61.2",
"winreg 0.55.0",
"zip 6.0.0",
]
+[[package]]
+name = "clash-verge-draft"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "criterion",
+ "parking_lot",
+ "tokio",
+]
+
+[[package]]
+name = "clash-verge-logging"
+version = "0.1.0"
+dependencies = [
+ "compact_str",
+ "flexi_logger",
+ "log",
+ "tokio",
+]
+
+[[package]]
+name = "clash-verge-signal"
+version = "0.1.0"
+dependencies = [
+ "clash-verge-logging",
+ "log",
+ "signal-hook 0.3.18",
+ "tauri",
+ "tokio",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "clash-verge-types"
+version = "0.1.0"
+dependencies = [
+ "serde",
+ "serde_yaml_ng",
+ "smartstring",
+]
+
[[package]]
name = "clash_verge_logger"
-version = "0.2.0"
-source = "git+https://github.com/clash-verge-rev/clash-verge-logger#9bb189b5b5c4c2eee35168ff4997e8fb10901c81"
+version = "0.2.1"
+source = "git+https://github.com/clash-verge-rev/clash-verge-logger#955f1b709890640ff01fd30009df0f35816bbca6"
dependencies = [
"arraydeque",
"compact_str",
@@ -1191,7 +1316,7 @@ dependencies = [
[[package]]
name = "clash_verge_service_ipc"
version = "2.0.21"
-source = "git+https://github.com/clash-verge-rev/clash-verge-service-ipc#1e34c648e48f8580208ff777686092e0a94b8025"
+source = "git+https://github.com/clash-verge-rev/clash-verge-service-ipc#da00a684c2b9723d647ed4992714eb669fcbd8a2"
dependencies = [
"anyhow",
"compact_str",
@@ -1287,7 +1412,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f76990911f2267d837d9d0ad060aa63aaad170af40904b29461734c339030d4d"
dependencies = [
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -1586,9 +1711,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "crypto-common"
-version = "0.1.6"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"rand_core 0.6.4",
@@ -1619,7 +1744,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
dependencies = [
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
+]
+
+[[package]]
+name = "csv"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938"
+dependencies = [
+ "csv-core",
+ "itoa",
+ "ryu",
+ "serde_core",
+]
+
+[[package]]
+name = "csv-core"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782"
+dependencies = [
+ "memchr",
]
[[package]]
@@ -1629,7 +1775,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
dependencies = [
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -1641,6 +1787,20 @@ dependencies = [
"cipher",
]
+[[package]]
+name = "dark-light"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18e1a09f280e29a8b00bc7e81eca5ac87dca0575639c9422a5fa25a07bb884b8"
+dependencies = [
+ "ashpd 0.10.3",
+ "async-std",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+ "web-sys",
+ "winreg 0.52.0",
+]
+
[[package]]
name = "darling"
version = "0.21.3"
@@ -1662,7 +1822,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -1673,7 +1833,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
dependencies = [
"darling_core",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -1778,7 +1938,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -1791,7 +1951,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version 0.4.1",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -1893,6 +2053,16 @@ dependencies = [
"dirs-sys 0.5.0",
]
+[[package]]
+name = "dirs-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
+dependencies = [
+ "cfg-if",
+ "dirs-sys-next",
+]
+
[[package]]
name = "dirs-sys"
version = "0.3.7"
@@ -1916,6 +2086,17 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users 0.4.6",
+ "winapi",
+]
+
[[package]]
name = "dispatch"
version = "0.2.0"
@@ -1942,7 +2123,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -1974,7 +2155,7 @@ checksum = "788160fb30de9cdd857af31c6a2675904b16ece8fc2737b2c7127ba368c9d0f4"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -2060,7 +2241,7 @@ checksum = "1ec431cd708430d5029356535259c5d645d60edd3d39c54e5eea9782d46caa7d"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -2089,6 +2270,12 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
+[[package]]
+name = "encode_unicode"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
+
[[package]]
name = "encoding_rs"
version = "0.8.35"
@@ -2122,7 +2309,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -2142,7 +2329,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -2153,9 +2340,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "erased-serde"
-version = "0.4.8"
+version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b"
+checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3"
dependencies = [
"serde",
"serde_core",
@@ -2254,7 +2441,7 @@ checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -2301,15 +2488,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.4"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
-
-[[package]]
-name = "fixedbitset"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
[[package]]
name = "fixedbitset"
@@ -2396,7 +2577,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -2474,7 +2655,7 @@ version = "7.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eb68017df91f2e477ed4bea586c59eaecaa47ed885a770d0444e21e62572cd2"
dependencies = [
- "fixedbitset 0.5.7",
+ "fixedbitset",
"futures-buffered",
"futures-core",
"futures-lite 2.6.1",
@@ -2542,7 +2723,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -2699,9 +2880,9 @@ dependencies = [
[[package]]
name = "generic-array"
-version = "0.14.9"
+version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
@@ -2831,7 +3012,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -2892,6 +3073,18 @@ dependencies = [
"walkdir",
]
+[[package]]
+name = "gloo-timers"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "js-sys",
+ "wasm-bindgen",
+]
+
[[package]]
name = "gobject-sys"
version = "0.18.0"
@@ -2952,7 +3145,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -2967,7 +3160,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http 0.2.12",
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"slab",
"tokio",
"tokio-util",
@@ -2986,7 +3179,7 @@ dependencies = [
"futures-core",
"futures-sink",
"http 1.3.1",
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"slab",
"tokio",
"tokio-util",
@@ -3043,9 +3236,9 @@ dependencies = [
[[package]]
name = "hashbrown"
-version = "0.16.0"
+version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"allocator-api2",
"equivalent",
@@ -3252,7 +3445,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
- "socket2 0.4.10",
+ "socket2 0.5.10",
"tokio",
"tower-service",
"tracing",
@@ -3261,9 +3454,9 @@ dependencies = [
[[package]]
name = "hyper"
-version = "1.7.0"
+version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
+checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
dependencies = [
"atomic-waker",
"bytes",
@@ -3289,7 +3482,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
dependencies = [
"http 1.3.1",
- "hyper 1.7.0",
+ "hyper 1.8.1",
"hyper-util",
"rustls",
"rustls-pki-types",
@@ -3317,7 +3510,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
dependencies = [
- "hyper 1.7.0",
+ "hyper 1.8.1",
"hyper-util",
"pin-project-lite",
"tokio",
@@ -3332,7 +3525,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"bytes",
"http-body-util",
- "hyper 1.7.0",
+ "hyper 1.8.1",
"hyper-util",
"native-tls",
"tokio",
@@ -3342,9 +3535,9 @@ dependencies = [
[[package]]
name = "hyper-util"
-version = "0.1.17"
+version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8"
+checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56"
dependencies = [
"base64 0.22.1",
"bytes",
@@ -3353,7 +3546,7 @@ dependencies = [
"futures-util",
"http 1.3.1",
"http-body 1.0.1",
- "hyper 1.7.0",
+ "hyper 1.8.1",
"ipnet",
"libc",
"percent-encoding",
@@ -3363,7 +3556,7 @@ dependencies = [
"tokio",
"tower-service",
"tracing",
- "windows-registry",
+ "windows-registry 0.6.1",
]
[[package]]
@@ -3534,9 +3727,9 @@ dependencies = [
[[package]]
name = "image"
-version = "0.25.8"
+version = "0.25.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7"
+checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
dependencies = [
"bytemuck",
"byteorder-lite",
@@ -3559,12 +3752,12 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.12.0"
+version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
+checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
dependencies = [
"equivalent",
- "hashbrown 0.16.0",
+ "hashbrown 0.16.1",
"serde",
"serde_core",
]
@@ -3666,9 +3859,9 @@ dependencies = [
[[package]]
name = "iri-string"
-version = "0.7.8"
+version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
+checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397"
dependencies = [
"memchr",
"serde",
@@ -3683,6 +3876,17 @@ dependencies = [
"once_cell",
]
+[[package]]
+name = "is-terminal"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
+dependencies = [
+ "hermit-abi 0.5.2",
+ "libc",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "is-wsl"
version = "0.4.0"
@@ -3869,10 +4073,19 @@ checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2"
dependencies = [
"cssparser",
"html5ever",
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"selectors",
]
+[[package]]
+name = "kv-log-macro"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
+dependencies = [
+ "log",
+]
+
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -3911,9 +4124,9 @@ checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
[[package]]
name = "libc"
-version = "0.2.177"
+version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
+checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]]
name = "libloading"
@@ -4008,9 +4221,12 @@ dependencies = [
[[package]]
name = "log"
-version = "0.4.28"
+version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+dependencies = [
+ "value-bag",
+]
[[package]]
name = "loom"
@@ -4099,7 +4315,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -4266,7 +4482,7 @@ checksum = "4568f25ccbd45ab5d5603dc34318c1ec56b117531781260002151b8530a9f931"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -4421,6 +4637,15 @@ dependencies = [
"minimal-lexical",
]
+[[package]]
+name = "nom"
+version = "8.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "normpath"
version = "1.5.0"
@@ -4527,7 +4752,7 @@ dependencies = [
"proc-macro-crate 3.4.0",
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -4857,9 +5082,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
name = "open"
-version = "5.3.2"
+version = "5.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95"
+checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc"
dependencies = [
"dunce",
"is-wsl",
@@ -4869,9 +5094,9 @@ dependencies = [
[[package]]
name = "openssl"
-version = "0.10.74"
+version = "0.10.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654"
+checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
dependencies = [
"bitflags 2.10.0",
"cfg-if",
@@ -4890,7 +5115,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -4901,9 +5126,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
-version = "0.9.110"
+version = "0.9.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2"
+checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
dependencies = [
"cc",
"libc",
@@ -5073,9 +5298,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "pest"
-version = "2.8.3"
+version = "2.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4"
+checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22"
dependencies = [
"memchr",
"ucd-trie",
@@ -5083,9 +5308,9 @@ dependencies = [
[[package]]
name = "pest_derive"
-version = "2.8.3"
+version = "2.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de"
+checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f"
dependencies = [
"pest",
"pest_generator",
@@ -5093,22 +5318,22 @@ dependencies = [
[[package]]
name = "pest_generator"
-version = "2.8.3"
+version = "2.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843"
+checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
name = "pest_meta"
-version = "2.8.3"
+version = "2.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a"
+checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82"
dependencies = [
"pest",
"sha2 0.10.9",
@@ -5116,12 +5341,13 @@ dependencies = [
[[package]]
name = "petgraph"
-version = "0.6.5"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
+checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
dependencies = [
- "fixedbitset 0.4.2",
- "indexmap 2.12.0",
+ "fixedbitset",
+ "hashbrown 0.15.5",
+ "indexmap 2.12.1",
]
[[package]]
@@ -5248,7 +5474,7 @@ dependencies = [
"phf_shared 0.11.3",
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -5261,7 +5487,7 @@ dependencies = [
"phf_shared 0.13.1",
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -5317,7 +5543,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -5356,8 +5582,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07"
dependencies = [
"base64 0.22.1",
- "indexmap 2.12.0",
- "quick-xml 0.38.3",
+ "indexmap 2.12.1",
+ "quick-xml 0.38.4",
"serde",
"time",
]
@@ -5496,9 +5722,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppmd-rust"
-version = "1.2.1"
+version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c834641d8ad1b348c9ee86dec3b9840d805acd5f24daa5f90c788951a52ff59b"
+checksum = "d558c559f0450f16f2a27a1f017ef38468c1090c9ce63c8e51366232d53717b4"
[[package]]
name = "ppv-lite86"
@@ -5515,6 +5741,20 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
+[[package]]
+name = "prettytable-rs"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a"
+dependencies = [
+ "csv",
+ "encode_unicode",
+ "is-terminal",
+ "lazy_static",
+ "term",
+ "unicode-width",
+]
+
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
@@ -5531,7 +5771,7 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8"
dependencies = [
- "toml_edit 0.20.2",
+ "toml_edit 0.20.7",
]
[[package]]
@@ -5612,7 +5852,7 @@ dependencies = [
"itertools 0.12.1",
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -5625,7 +5865,7 @@ dependencies = [
"itertools 0.14.0",
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -5669,7 +5909,7 @@ checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -5708,9 +5948,9 @@ dependencies = [
[[package]]
name = "quick-xml"
-version = "0.38.3"
+version = "0.38.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89"
+checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
dependencies = [
"memchr",
]
@@ -5772,9 +6012,9 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.41"
+version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
+checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
@@ -5984,7 +6224,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -6022,7 +6262,7 @@ version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2057b2325e68a893284d1538021ab90279adac1139957ca2a74426c6f118fb48"
dependencies = [
- "hashbrown 0.16.0",
+ "hashbrown 0.16.1",
"memchr",
]
@@ -6049,7 +6289,7 @@ dependencies = [
"http 1.3.1",
"http-body 1.0.1",
"http-body-util",
- "hyper 1.7.0",
+ "hyper 1.8.1",
"hyper-rustls",
"hyper-tls",
"hyper-util",
@@ -6107,7 +6347,7 @@ version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed"
dependencies = [
- "ashpd",
+ "ashpd 0.11.0",
"block2 0.6.2",
"dispatch2",
"glib-sys",
@@ -6172,7 +6412,7 @@ checksum = "bd83f5f173ff41e00337d97f6572e416d022ef8a19f371817259ae960324c482"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -6221,7 +6461,7 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -6257,6 +6497,18 @@ dependencies = [
"ordered-multimap",
]
+[[package]]
+name = "rust_iso3166"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df0d3a0089ee08071ea1baaba83f2265c97f7646c53c3f92b205eb2cdaab72b1"
+dependencies = [
+ "js-sys",
+ "phf 0.11.3",
+ "prettytable-rs",
+ "wasm-bindgen",
+]
+
[[package]]
name = "rustc-hash"
version = "2.1.1"
@@ -6323,9 +6575,9 @@ dependencies = [
[[package]]
name = "rustls"
-version = "0.23.34"
+version = "0.23.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a9586e9ee2b4f8fab52a0048ca7334d7024eef48e2cb9407e3497bb7cab7fa7"
+checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
dependencies = [
"once_cell",
"ring",
@@ -6421,9 +6673,9 @@ dependencies = [
[[package]]
name = "schemars"
-version = "1.0.4"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0"
+checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289"
dependencies = [
"dyn-clone",
"ref-cast",
@@ -6440,7 +6692,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -6590,7 +6842,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -6601,7 +6853,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -6625,7 +6877,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -6660,17 +6912,17 @@ dependencies = [
[[package]]
name = "serde_with"
-version = "3.15.1"
+version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa66c845eee442168b2c8134fec70ac50dc20e760769c8ba0ad1319ca1959b04"
+checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1"
dependencies = [
"base64 0.22.1",
"chrono",
"hex",
"indexmap 1.9.3",
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"schemars 0.9.0",
- "schemars 1.0.4",
+ "schemars 1.1.0",
"serde_core",
"serde_json",
"serde_with_macros",
@@ -6679,14 +6931,14 @@ dependencies = [
[[package]]
name = "serde_with_macros"
-version = "3.15.1"
+version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b91a903660542fced4e99881aa481bdbaec1634568ee02e0b8bd57c64cb38955"
+checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b"
dependencies = [
"darling",
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -6695,7 +6947,7 @@ version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"itoa",
"ryu",
"serde",
@@ -6708,7 +6960,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4db627b98b36d4203a7b458cf3573730f2bb591b28871d916dfa9efabfd41f"
dependencies = [
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"itoa",
"ryu",
"serde",
@@ -6734,7 +6986,7 @@ checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -6851,9 +7103,9 @@ dependencies = [
[[package]]
name = "signal-hook-registry"
-version = "1.4.6"
+version = "1.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
+checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad"
dependencies = [
"libc",
]
@@ -6917,10 +7169,10 @@ checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1"
dependencies = [
"async-channel 1.9.0",
"async-executor",
- "async-fs",
+ "async-fs 1.6.0",
"async-io 1.13.0",
"async-lock 2.8.0",
- "async-net",
+ "async-net 1.8.0",
"async-process 1.8.1",
"blocking",
"futures-lite 1.13.0",
@@ -6936,6 +7188,16 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "socket2"
+version = "0.5.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
[[package]]
name = "socket2"
version = "0.6.1"
@@ -7058,7 +7320,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -7091,9 +7353,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.108"
+version = "2.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
+checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
dependencies = [
"proc-macro2",
"quote",
@@ -7123,7 +7385,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -7151,13 +7413,14 @@ dependencies = [
[[package]]
name = "sysproxy"
-version = "0.3.1"
-source = "git+https://github.com/clash-verge-rev/sysproxy-rs#ea6e5b5bcef32025e1df914d663eea8558afacb2"
+version = "0.4.0"
+source = "git+https://github.com/clash-verge-rev/sysproxy-rs#0f844dd2639b0ac74da4548b1325335844947420"
dependencies = [
"interfaces",
"iptools",
"log",
"thiserror 2.0.17",
+ "tokio",
"url",
"windows 0.62.2",
"winreg 0.55.0",
@@ -7252,7 +7515,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -7280,9 +7543,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "tauri"
-version = "2.9.3"
+version = "2.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e492485dd390b35f7497401f67694f46161a2a00ffd800938d5dd3c898fb9d8"
+checksum = "15524fc7959bfcaa051ba6d0b3fb1ef18e978de2176c7c6acb977f7fd14d35c7"
dependencies = [
"anyhow",
"bytes",
@@ -7334,9 +7597,9 @@ dependencies = [
[[package]]
name = "tauri-build"
-version = "2.5.2"
+version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87d6f8cafe6a75514ce5333f115b7b1866e8e68d9672bf4ca89fc0f35697ea9d"
+checksum = "17fcb8819fd16463512a12f531d44826ce566f486d7ccd211c9c8cebdaec4e08"
dependencies = [
"anyhow",
"cargo_toml",
@@ -7356,9 +7619,9 @@ dependencies = [
[[package]]
name = "tauri-codegen"
-version = "2.5.1"
+version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7ef707148f0755110ca54377560ab891d722de4d53297595380a748026f139f"
+checksum = "9fa9844cefcf99554a16e0a278156ae73b0d8680bbc0e2ad1e4287aadd8489cf"
dependencies = [
"base64 0.22.1",
"brotli",
@@ -7372,7 +7635,7 @@ dependencies = [
"serde",
"serde_json",
"sha2 0.10.9",
- "syn 2.0.108",
+ "syn 2.0.111",
"tauri-utils",
"thiserror 2.0.17",
"time",
@@ -7383,14 +7646,14 @@ dependencies = [
[[package]]
name = "tauri-macros"
-version = "2.5.1"
+version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71664fd715ee6e382c05345ad258d6d1d50f90cf1b58c0aa726638b33c2a075d"
+checksum = "3764a12f886d8245e66b7ee9b43ccc47883399be2019a61d80cf0f4117446fde"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
"tauri-codegen",
"tauri-utils",
]
@@ -7426,6 +7689,18 @@ dependencies = [
"thiserror 2.0.17",
]
+[[package]]
+name = "tauri-plugin-clash-verge-sysinfo"
+version = "0.1.0"
+dependencies = [
+ "deelevate",
+ "libc",
+ "parking_lot",
+ "sysinfo",
+ "tauri",
+ "tauri-plugin-clipboard-manager",
+]
+
[[package]]
name = "tauri-plugin-clipboard-manager"
version = "2.3.2"
@@ -7458,7 +7733,7 @@ dependencies = [
"thiserror 2.0.17",
"tracing",
"url",
- "windows-registry",
+ "windows-registry 0.5.3",
"windows-result 0.3.4",
]
@@ -7571,7 +7846,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-mihomo"
version = "0.1.1"
-source = "git+https://github.com/clash-verge-rev/tauri-plugin-mihomo#d0f00b33cea294cc693e177441fc897426ecbc39"
+source = "git+https://github.com/clash-verge-rev/tauri-plugin-mihomo#24586eb0721314f88e65460b4ac01933b3376d3c"
dependencies = [
"base64 0.22.1",
"futures-util",
@@ -7692,9 +7967,9 @@ dependencies = [
[[package]]
name = "tauri-runtime"
-version = "2.9.1"
+version = "2.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9368f09358496f2229313fccb37682ad116b7f46fa76981efe116994a0628926"
+checksum = "87f766fe9f3d1efc4b59b17e7a891ad5ed195fa8d23582abb02e6c9a01137892"
dependencies = [
"cookie",
"dpi",
@@ -7717,9 +7992,9 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
-version = "2.9.1"
+version = "2.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "929f5df216f5c02a9e894554401bcdab6eec3e39ec6a4a7731c7067fc8688a93"
+checksum = "7950f3bde6bcca6655bc5e76d3d6ec587ceb81032851ab4ddbe1f508bdea2729"
dependencies = [
"gtk",
"http 1.3.1",
@@ -7745,9 +8020,9 @@ dependencies = [
[[package]]
name = "tauri-utils"
-version = "2.8.0"
+version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6b8bbe426abdbf52d050e52ed693130dbd68375b9ad82a3fb17efb4c8d85673"
+checksum = "76a423c51176eb3616ee9b516a9fa67fed5f0e78baaba680e44eb5dd2cc37490"
dependencies = [
"anyhow",
"brotli",
@@ -7783,10 +8058,11 @@ dependencies = [
[[package]]
name = "tauri-winres"
-version = "0.3.3"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd21509dd1fa9bd355dc29894a6ff10635880732396aa38c0066c1e6c1ab8074"
+checksum = "1087b111fe2b005e42dbdc1990fc18593234238d47453b0c99b7de1c9ab2c1e0"
dependencies = [
+ "dunce",
"embed-resource",
"toml 0.9.8",
]
@@ -7827,6 +8103,17 @@ dependencies = [
"utf-8",
]
+[[package]]
+name = "term"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
+dependencies = [
+ "dirs-next",
+ "rustversion",
+ "winapi",
+]
+
[[package]]
name = "termcolor"
version = "1.4.1"
@@ -7922,7 +8209,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -7933,7 +8220,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -8074,7 +8361,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -8122,9 +8409,9 @@ dependencies = [
[[package]]
name = "tokio-util"
-version = "0.7.16"
+version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
+checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
dependencies = [
"bytes",
"futures-core",
@@ -8151,7 +8438,7 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8"
dependencies = [
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"serde_core",
"serde_spanned 1.0.3",
"toml_datetime 0.7.3",
@@ -8184,18 +8471,18 @@ version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"toml_datetime 0.6.11",
"winnow 0.5.40",
]
[[package]]
name = "toml_edit"
-version = "0.20.2"
+version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
+checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81"
dependencies = [
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"toml_datetime 0.6.11",
"winnow 0.5.40",
]
@@ -8206,7 +8493,7 @@ version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"serde",
"serde_spanned 0.6.9",
"toml_datetime 0.6.11",
@@ -8220,7 +8507,7 @@ version = "0.23.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d"
dependencies = [
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"toml_datetime 0.7.3",
"toml_parser",
"winnow 0.7.13",
@@ -8281,14 +8568,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb7613188ce9f7df5bfe185db26c5814347d110db17920415cf2fbcad85e7203"
dependencies = [
"async-trait",
- "axum 0.8.6",
+ "axum 0.8.7",
"base64 0.22.1",
"bytes",
"h2 0.4.12",
"http 1.3.1",
"http-body 1.0.1",
"http-body-util",
- "hyper 1.7.0",
+ "hyper 1.8.1",
"hyper-timeout 0.5.2",
"hyper-util",
"percent-encoding",
@@ -8375,7 +8662,7 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
dependencies = [
"futures-core",
"futures-util",
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"pin-project-lite",
"slab",
"sync_wrapper 1.0.2",
@@ -8454,7 +8741,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -8520,13 +8807,12 @@ dependencies = [
[[package]]
name = "tree_magic_mini"
-version = "3.2.0"
+version = "3.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f943391d896cdfe8eec03a04d7110332d445be7df856db382dd96a730667562c"
+checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6"
dependencies = [
"memchr",
- "nom 7.1.3",
- "once_cell",
+ "nom 8.0.0",
"petgraph",
]
@@ -8565,7 +8851,7 @@ checksum = "ee6ff59666c9cbaec3533964505d39154dc4e0a56151fdea30a09ed0301f62e2"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
"termcolor",
]
@@ -8674,6 +8960,12 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+[[package]]
+name = "unicode-width"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
+
[[package]]
name = "universal-hash"
version = "0.5.1"
@@ -8768,6 +9060,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+[[package]]
+name = "value-bag"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0"
+
[[package]]
name = "vcpkg"
version = "0.2.15"
@@ -8776,9 +9074,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version-compare"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
+checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e"
[[package]]
name = "version_check"
@@ -8852,7 +9150,7 @@ dependencies = [
"http 1.3.1",
"http-body 1.0.1",
"http-body-util",
- "hyper 1.7.0",
+ "hyper 1.8.1",
"hyper-util",
"log",
"mime",
@@ -8935,7 +9233,7 @@ dependencies = [
"bumpalo",
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
"wasm-bindgen-shared",
]
@@ -9100,9 +9398,9 @@ dependencies = [
[[package]]
name = "webpki-roots"
-version = "1.0.3"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8"
+checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
dependencies = [
"rustls-pki-types",
]
@@ -9129,7 +9427,7 @@ checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -9145,9 +9443,9 @@ dependencies = [
[[package]]
name = "weezl"
-version = "0.1.10"
+version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3"
+checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "which"
@@ -9312,7 +9610,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -9323,7 +9621,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -9369,6 +9667,17 @@ dependencies = [
"windows-strings 0.4.2",
]
+[[package]]
+name = "windows-registry"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
+dependencies = [
+ "windows-link 0.2.1",
+ "windows-result 0.4.1",
+ "windows-strings 0.5.1",
+]
+
[[package]]
name = "windows-result"
version = "0.3.4"
@@ -9756,6 +10065,16 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "winreg"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
+dependencies = [
+ "cfg-if",
+ "windows-sys 0.48.0",
+]
+
[[package]]
name = "winreg"
version = "0.55.0"
@@ -9940,7 +10259,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
"synstructure",
]
@@ -9988,7 +10307,7 @@ dependencies = [
"proc-macro-crate 3.4.0",
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
"zbus_names",
"zvariant",
"zvariant_utils",
@@ -10008,22 +10327,22 @@ dependencies = [
[[package]]
name = "zerocopy"
-version = "0.8.27"
+version = "0.8.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
+checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
-version = "0.8.27"
+version = "0.8.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
+checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -10043,7 +10362,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
"synstructure",
]
@@ -10064,7 +10383,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -10098,7 +10417,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
]
[[package]]
@@ -10109,7 +10428,7 @@ checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1"
dependencies = [
"arbitrary",
"crc32fast",
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"memchr",
]
@@ -10128,7 +10447,7 @@ dependencies = [
"flate2",
"getrandom 0.3.4",
"hmac",
- "indexmap 2.12.0",
+ "indexmap 2.12.1",
"lzma-rust2",
"memchr",
"pbkdf2",
@@ -10225,7 +10544,7 @@ dependencies = [
"proc-macro-crate 3.4.0",
"proc-macro2",
"quote",
- "syn 2.0.108",
+ "syn 2.0.111",
"zvariant_utils",
]
@@ -10238,6 +10557,6 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
- "syn 2.0.108",
+ "syn 2.0.111",
"winnow 0.7.13",
]
diff --git a/clash-verge-rev/Cargo.toml b/clash-verge-rev/Cargo.toml
new file mode 100644
index 0000000000..dd4607e89b
--- /dev/null
+++ b/clash-verge-rev/Cargo.toml
@@ -0,0 +1,134 @@
+[workspace]
+members = [
+ "src-tauri",
+ "crates/clash-verge-draft",
+ "crates/clash-verge-logging",
+ "crates/clash-verge-signal",
+ "crates/tauri-plugin-clash-verge-sysinfo",
+ "crates/clash-verge-types",
+]
+resolver = "2"
+
+[workspace.package]
+edition = "2024"
+rust-version = "1.91"
+
+[profile.release]
+panic = "abort"
+codegen-units = 1
+lto = "thin"
+opt-level = 3
+debug = false
+strip = true
+overflow-checks = false
+rpath = false
+
+[profile.dev]
+incremental = true
+codegen-units = 64
+opt-level = 0
+debug = true
+strip = "none"
+overflow-checks = true
+lto = false
+rpath = false
+
+[profile.fast-release]
+inherits = "release"
+codegen-units = 64
+incremental = true
+lto = false
+opt-level = 0
+debug = true
+strip = false
+
+[workspace.dependencies]
+clash-verge-draft = { path = "crates/clash-verge-draft" }
+clash-verge-logging = { path = "crates/clash-verge-logging" }
+clash-verge-signal = { path = "crates/clash-verge-signal" }
+clash-verge-types = { path = "crates/clash-verge-types" }
+tauri-plugin-clash-verge-sysinfo = { path = "crates/tauri-plugin-clash-verge-sysinfo" }
+
+tauri = { version = "2.9.4" }
+tauri-plugin-clipboard-manager = "2.3.2"
+parking_lot = { version = "0.12.5", features = ["hardware-lock-elision"] }
+anyhow = "1.0.100"
+criterion = { version = "0.7.0", features = ["async_tokio"] }
+tokio = { version = "1.48.0", features = [
+ "rt-multi-thread",
+ "macros",
+ "time",
+ "sync",
+] }
+flexi_logger = "0.31.7"
+log = "0.4.29"
+
+smartstring = { version = "1.0.1" }
+compact_str = { version = "0.9.0", features = ["serde"] }
+
+serde = { version = "1.0.228" }
+serde_json = { version = "1.0.145" }
+serde_yaml_ng = { version = "0.10.0" }
+
+# *** For Windows platform only ***
+deelevate = "0.2.0"
+# *********************************
+
+[workspace.lints.clippy]
+correctness = { level = "deny", priority = -1 }
+suspicious = { level = "deny", priority = -1 }
+unwrap_used = "warn"
+expect_used = "warn"
+panic = "deny"
+unimplemented = "deny"
+todo = "warn"
+dbg_macro = "warn"
+clone_on_ref_ptr = "warn"
+rc_clone_in_vec_init = "warn"
+large_stack_arrays = "warn"
+large_const_arrays = "warn"
+async_yields_async = "deny"
+mutex_atomic = "deny"
+mutex_integer = "deny"
+rc_mutex = "deny"
+unused_async = "deny"
+await_holding_lock = "deny"
+large_futures = "deny"
+future_not_send = "deny"
+redundant_else = "deny"
+needless_continue = "deny"
+needless_raw_string_hashes = "deny"
+or_fun_call = "deny"
+cognitive_complexity = "deny"
+useless_let_if_seq = "deny"
+use_self = "deny"
+tuple_array_conversions = "deny"
+trait_duplication_in_bounds = "deny"
+suspicious_operation_groupings = "deny"
+string_lit_as_bytes = "deny"
+significant_drop_tightening = "deny"
+significant_drop_in_scrutinee = "deny"
+redundant_clone = "deny"
+# option_if_let_else = "deny" // 过于激进,暂时不开启
+needless_pass_by_ref_mut = "deny"
+needless_collect = "deny"
+missing_const_for_fn = "deny"
+iter_with_drain = "deny"
+iter_on_single_items = "deny"
+iter_on_empty_collections = "deny"
+# fallible_impl_from = "deny" // 过于激进,暂时不开启
+equatable_if_let = "deny"
+collection_is_never_read = "deny"
+branches_sharing_code = "deny"
+pathbuf_init_then_push = "deny"
+option_as_ref_cloned = "deny"
+large_types_passed_by_value = "deny"
+# implicit_clone = "deny" // 可能会造成额外开销,暂时不开启
+expl_impl_clone_on_copy = "deny"
+copy_iterator = "deny"
+cloned_instead_of_copied = "deny"
+# self_only_used_in_recursion = "deny" // Since 1.92.0
+unnecessary_self_imports = "deny"
+unused_trait_names = "deny"
+wildcard_imports = "deny"
+unnecessary_wraps = "deny"
diff --git a/clash-verge-rev/Changelog.md b/clash-verge-rev/Changelog.md
index 264ac63038..f786ab6ef2 100644
--- a/clash-verge-rev/Changelog.md
+++ b/clash-verge-rev/Changelog.md
@@ -1,29 +1,63 @@
## v2.4.4
+- **Mihomo(Meta) 内核升级至 v1.19.17**
+
+> [!WARNING]
+> Apple 公证服务故障,临时暂停 macOS 签名
+> macOS 跳过签名,终端执行 `sudo xattr -rd com.apple.quarantine /Applications/Clash\ Verge.app/`
+
### 🐞 修复问题
- Linux 无法切换 TUN 堆栈
- macOS service 启动项显示名称(试验性修改)
+- macOS 非预期 Tproxy 端口设置
+- 流量图缩放异常
+- PAC 自动代理脚本内容无法动态调整
+- 兼容从旧版服务模式升级
+- Monaco 编辑器的行数上限
+- 已删除节点在手动分组中导致配置无法加载
+- 仪表盘与托盘状态不同步
+- 修复重启或退出应用,关闭系统时无法记忆用户行为
+- 彻底修复 macOS 连接页面显示异常
+- windows 端监听关机信号失败
+- 修复代理按钮和高亮状态不同步
+- 修复侧边栏可能的未能正确跳转
+- 修复解锁测试部分地区图标编码不正确
+- 修复 IP 检测切页后强制刷新,改为仅在必要时更新
+- 修复在搜索框输入不完整正则直接崩溃
+- 修复创建窗口时在非简体中文环境或深色主题下的短暂闪烁
+- 修复更新时加载进度条异常
✨ 新增功能
-- **Mihomo(Meta) 内核升级至 v1.19.16**
- 支持连接页面各个项目的排序
- 实现可选的自动备份
- 连接页面支持查看已关闭的连接(最近最多 500 个已关闭连接)
+- 日志页面支持按时间倒序
+- 增加「重新激活订阅」的全局快捷键
🚀 优化改进
+- 网络请求改为使用 rustls,提升 TLS 兼容性
+- rustls 避免因服务器证书链配置问题或较新 TLS 要求导致订阅无法导入
- 替换前端信息编辑组件,提供更好性能
- 优化后端内存和性能表现
- 防止退出时可能的禁用 TUN 失败
- i18n 支持
- 优化备份设置布局
- 优化流量图性能表现,实现动态 FPS 和窗口失焦自动暂停
+- 性能优化系统状态获取
+- 优化托盘菜单当前订阅检测逻辑
+- 优化连接页面表格渲染
+- 优化链式代理 UI 反馈
+- 优化重启应用的资源清理逻辑
+- 优化前端数据刷新
+- 优化流量采样和数据处理
+- 优化应用重启/退出时的资源清理性能, 大幅缩短执行时间
diff --git a/clash-verge-rev/crates/clash-verge-draft/Cargo.toml b/clash-verge-rev/crates/clash-verge-draft/Cargo.toml
new file mode 100644
index 0000000000..798b9b8a87
--- /dev/null
+++ b/clash-verge-rev/crates/clash-verge-draft/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "clash-verge-draft"
+version = "0.1.0"
+edition = "2024"
+
+[[bench]]
+name = "draft_bench"
+path = "bench/benche_me.rs"
+harness = false
+
+[dependencies]
+parking_lot = { workspace = true }
+anyhow = { workspace = true }
+
+[dev-dependencies]
+criterion = { workspace = true }
+tokio = { workspace = true }
diff --git a/clash-verge-rev/src-tauri/benches/draft_benchmark.rs b/clash-verge-rev/crates/clash-verge-draft/bench/benche_me.rs
similarity index 93%
rename from clash-verge-rev/src-tauri/benches/draft_benchmark.rs
rename to clash-verge-rev/crates/clash-verge-draft/bench/benche_me.rs
index c8a07c536e..792618ad96 100644
--- a/clash-verge-rev/src-tauri/benches/draft_benchmark.rs
+++ b/clash-verge-rev/crates/clash-verge-draft/bench/benche_me.rs
@@ -3,17 +3,20 @@ use std::hint::black_box;
use std::process;
use tokio::runtime::Runtime;
-use app_lib::config::IVerge;
-use app_lib::utils::Draft as DraftNew;
+use clash_verge_draft::Draft;
-/// 创建测试数据
-fn make_draft() -> DraftNew {
+#[derive(Default, Clone, Debug)]
+struct IVerge {
+ enable_auto_launch: Option,
+ enable_tun_mode: Option,
+}
+
+fn make_draft() -> Draft {
let verge = IVerge {
enable_auto_launch: Some(true),
enable_tun_mode: Some(false),
- ..Default::default()
};
- DraftNew::new(verge)
+ Draft::new(verge)
}
pub fn bench_draft(c: &mut Criterion) {
diff --git a/clash-verge-rev/crates/clash-verge-draft/src/lib.rs b/clash-verge-rev/crates/clash-verge-draft/src/lib.rs
new file mode 100644
index 0000000000..5d548d091f
--- /dev/null
+++ b/clash-verge-rev/crates/clash-verge-draft/src/lib.rs
@@ -0,0 +1,102 @@
+use parking_lot::RwLock;
+use std::sync::Arc;
+
+pub type SharedBox = Arc>;
+type DraftInner = (SharedBox, Option>);
+
+/// Draft 管理:committed 与 optional draft 都以 Arc> 存储,
+// (committed_snapshot, optional_draft_snapshot)
+#[derive(Debug, Clone)]
+pub struct Draft {
+ inner: Arc>>,
+}
+
+impl Draft {
+ #[inline]
+ pub fn new(data: T) -> Self {
+ Self {
+ inner: Arc::new(RwLock::new((Arc::new(Box::new(data)), None))),
+ }
+ }
+ /// 以 Arc> 的形式获取当前“已提交(正式)”数据的快照(零拷贝,仅 clone Arc)
+ #[inline]
+ pub fn data_arc(&self) -> SharedBox {
+ let guard = self.inner.read();
+ Arc::clone(&guard.0)
+ }
+
+ /// 获取当前(草稿若存在则返回草稿,否则返回已提交)的快照
+ /// 这也是零拷贝:只 clone Arc,不 clone T
+ #[inline]
+ pub fn latest_arc(&self) -> SharedBox {
+ let guard = self.inner.read();
+ guard.1.clone().unwrap_or_else(|| Arc::clone(&guard.0))
+ }
+
+ /// 通过闭包以可变方式编辑草稿(在闭包中我们给出 &mut T)
+ /// - 延迟拷贝:如果只有这一个 Arc 引用,则直接修改,不会克隆 T;
+ /// - 若草稿被其他读者共享,Arc::make_mut 会做一次 T.clone(最小必要拷贝)。
+ #[inline]
+ pub fn edit_draft(&self, f: F) -> R
+ where
+ F: FnOnce(&mut T) -> R,
+ {
+ // 先获得写锁以创建或取出草稿 Arc 的可变引用位置
+ let mut guard = self.inner.write();
+ let mut draft_arc = if guard.1.is_none() {
+ Arc::clone(&guard.0)
+ } else {
+ #[allow(clippy::unwrap_used)]
+ guard.1.take().unwrap()
+ };
+ drop(guard);
+ // Arc::make_mut: 如果只有一个引用则返回可变引用;否则会克隆底层 Box(要求 T: Clone)
+ let boxed = Arc::make_mut(&mut draft_arc); // &mut Box
+ // 对 Box 解引用得到 &mut T
+ let result = f(&mut **boxed);
+ // 恢复修改后的草稿 Arc
+ self.inner.write().1 = Some(draft_arc);
+ result
+ }
+
+ /// 将草稿提交到已提交位置(替换),并清除草稿
+ #[inline]
+ pub fn apply(&self) {
+ let mut guard = self.inner.write();
+ if let Some(d) = guard.1.take() {
+ guard.0 = d;
+ }
+ }
+
+ /// 丢弃草稿(如果存在)
+ #[inline]
+ pub fn discard(&self) {
+ let mut guard = self.inner.write();
+ guard.1 = None;
+ }
+
+ /// 异步地以拥有 Box 的方式修改已提交数据:将克隆一次已提交数据到本地,
+ /// 异步闭包返回新的 Box(替换已提交数据)和业务返回值 R。
+ #[inline]
+ pub async fn with_data_modify(&self, f: F) -> Result
+ where
+ T: Send + Sync + 'static,
+ F: FnOnce(Box) -> Fut + Send,
+ Fut: std::future::Future