chore(deps): bump tauri-plugin-mihomo to 0.6.0 and move home card to count stream

Locks both sides to upstream main 17c7757: the WS channel now uses a
single Json transport (connections wire shrinks ~3.4x) and count-stream
error frames are forwarded unchanged; JS/Rust API surfaces stay
identical, so upper-layer code is untouched except the card below.

The home Active Connections card now subscribes to the count-only
ws_connections_count stream instead of parsing the full connections
snapshot every second; the full stream runs only while the Connections
page is mounted.
This commit is contained in:
Tunglies
2026-09-07 07:13:27 +08:00
parent b39c5f9b6d
commit 6de0a59756
3 changed files with 173 additions and 123 deletions
Generated
+21 -21
View File
@@ -1387,7 +1387,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [
"lazy_static",
"windows-sys 0.59.0",
"windows-sys 0.48.0",
]
[[package]]
@@ -2073,7 +2073,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users 0.5.2",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -2347,7 +2347,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -2865,7 +2865,7 @@ dependencies = [
"gobject-sys 0.21.5",
"libc",
"system-deps 7.0.8",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -3488,7 +3488,7 @@ dependencies = [
"libc",
"percent-encoding",
"pin-project-lite",
"socket2 0.6.5",
"socket2 0.5.10",
"system-configuration",
"tokio",
"tower-service",
@@ -4316,7 +4316,7 @@ dependencies = [
"png 0.18.1",
"serde",
"thiserror 2.0.20",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -4485,7 +4485,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -4928,7 +4928,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.48.0",
]
[[package]]
@@ -5602,7 +5602,7 @@ dependencies = [
"quinn-udp",
"rustc-hash",
"rustls",
"socket2 0.6.5",
"socket2 0.5.10",
"thiserror 2.0.20",
"tokio",
"tracing",
@@ -5641,9 +5641,9 @@ dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2 0.6.5",
"socket2 0.5.10",
"tracing",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -6198,7 +6198,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.12.1",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -6257,7 +6257,7 @@ dependencies = [
"security-framework",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -6899,7 +6899,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -7565,8 +7565,8 @@ dependencies = [
[[package]]
name = "tauri-plugin-mihomo"
version = "0.5.6"
source = "git+https://github.com/clash-verge-rev/tauri-plugin-mihomo?branch=main#8bf567b9e2230cdd02f9b8c9774fb8eb0d71af1e"
version = "0.6.0"
source = "git+https://github.com/clash-verge-rev/tauri-plugin-mihomo?branch=main#17c7757389ca73d29882753ecf7a160de29aabbe"
dependencies = [
"arc-swap",
"clashmap",
@@ -7863,7 +7863,7 @@ dependencies = [
"getrandom 0.4.3",
"once_cell",
"rustix 1.1.4",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -8582,7 +8582,7 @@ dependencies = [
"png 0.18.1",
"serde",
"thiserror 2.0.20",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -8685,7 +8685,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
dependencies = [
"memoffset",
"tempfile",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -9270,7 +9270,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.48.0",
]
[[package]]
@@ -9874,7 +9874,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d6f32a0ff4a9f6f01231eb2059cc85479330739333e0e58cadf03b6af2cca10"
dependencies = [
"cfg-if",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
+5 -5
View File
@@ -234,7 +234,7 @@ importers:
version: 2.5.1(react@19.2.8)
tauri-plugin-mihomo-api:
specifier: github:clash-verge-rev/tauri-plugin-mihomo#main
version: https://codeload.github.com/clash-verge-rev/tauri-plugin-mihomo/tar.gz/8bf567b9e2230cdd02f9b8c9774fb8eb0d71af1e
version: https://codeload.github.com/clash-verge-rev/tauri-plugin-mihomo/tar.gz/17c7757389ca73d29882753ecf7a160de29aabbe
types-pac:
specifier: ^1.0.3
version: 1.0.3
@@ -4838,9 +4838,9 @@ packages:
resolution: {integrity: sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==}
engines: {node: '>=18'}
tauri-plugin-mihomo-api@https://codeload.github.com/clash-verge-rev/tauri-plugin-mihomo/tar.gz/8bf567b9e2230cdd02f9b8c9774fb8eb0d71af1e:
resolution: {gitHosted: true, integrity: sha512-ZUNzoqUI/328gbYuFUw9oKe0BVi/reurZZ2ut1+B8ZgEtZ6dtNgKMea7Kp8UvKTnF543WvI/8RwetH1Fzkflzw==, tarball: https://codeload.github.com/clash-verge-rev/tauri-plugin-mihomo/tar.gz/8bf567b9e2230cdd02f9b8c9774fb8eb0d71af1e}
version: 0.5.6
tauri-plugin-mihomo-api@https://codeload.github.com/clash-verge-rev/tauri-plugin-mihomo/tar.gz/17c7757389ca73d29882753ecf7a160de29aabbe:
resolution: {gitHosted: true, integrity: sha512-DoJmecNZhwYzj8Gy0tpRoJDaWBLXIM6zwOBu2ITLKgFm+TUaMyqHmJ/tWzRfeqHX8izkgMi/TFNw9BO/JWkTyQ==, tarball: https://codeload.github.com/clash-verge-rev/tauri-plugin-mihomo/tar.gz/17c7757389ca73d29882753ecf7a160de29aabbe}
version: 0.6.0
teex@1.0.1:
resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==}
@@ -10220,7 +10220,7 @@ snapshots:
minizlib: 3.1.0
yallist: 5.0.0
tauri-plugin-mihomo-api@https://codeload.github.com/clash-verge-rev/tauri-plugin-mihomo/tar.gz/8bf567b9e2230cdd02f9b8c9774fb8eb0d71af1e:
tauri-plugin-mihomo-api@https://codeload.github.com/clash-verge-rev/tauri-plugin-mihomo/tar.gz/17c7757389ca73d29882753ecf7a160de29aabbe:
dependencies:
'@tauri-apps/api': 2.11.1
+147 -97
View File
@@ -24,6 +24,10 @@ interface ConnectionMonitorData {
closedConnections: IConnectionsItem[]
}
interface ConnectionSummaryPayload {
count?: number
}
interface ConnectionSummaryData {
activeConnectionCount: number
}
@@ -34,9 +38,6 @@ const initConnSummaryData: ConnectionSummaryData = {
let connectionData: ConnectionMonitorData = initConnData
let connectionSummary: ConnectionSummaryData = initConnSummaryData
let connectionSocket: MihomoWebSocket | null = null
let connectionConnecting = false
let reconnectTimer: ReturnType<typeof setTimeout> | null = null
let flushTimer: ReturnType<typeof setTimeout> | null = null
let pendingMessageData: string | null = null
let lastFlushAt = 0
@@ -52,9 +53,6 @@ const notifySummaryListeners = () => {
summaryListeners.forEach((listener) => listener())
}
const hasConnectionSubscribers = () =>
connectionListeners.size > 0 || summaryListeners.size > 0
const sameMetadata = (left: ConnectionMetadata, right: ConnectionMetadata) =>
metadataValue(left.network) === metadataValue(right.network) &&
metadataValue(left.type) === metadataValue(right.type) &&
@@ -208,16 +206,16 @@ const mergeConnectionSnapshot = (
}
const mergeConnectionSummary = (
payload: IConnections,
payload: ConnectionSummaryPayload,
): ConnectionSummaryData => ({
activeConnectionCount: payload.connections?.length ?? 0,
activeConnectionCount: payload.count ?? 0,
})
const flushPendingMessage = () => {
flushTimer = null
const messageData = pendingMessageData
pendingMessageData = null
if (!messageData || !hasConnectionSubscribers()) return
if (!messageData || connectionListeners.size === 0) return
let payload: IConnections
try {
@@ -228,10 +226,6 @@ const flushPendingMessage = () => {
}
lastFlushAt = Date.now()
connectionSummary = mergeConnectionSummary(payload)
notifySummaryListeners()
if (connectionListeners.size === 0) return
connectionData = mergeConnectionSnapshot(payload, connectionData)
notifyConnectionListeners()
@@ -253,115 +247,171 @@ const enqueueConnectionMessage = (messageData: string) => {
)
}
const clearReconnectTimer = () => {
if (!reconnectTimer) return
window.clearTimeout(reconnectTimer)
reconnectTimer = null
}
const closeConnectionSocket = async () => {
const socket = connectionSocket
connectionSocket = null
if (!socket) return
try {
await socket.close()
} catch (err) {
console.warn('Failed to close connection websocket', err)
}
}
const scheduleReconnect = () => {
if (!hasConnectionSubscribers()) return
if (reconnectTimer) return
reconnectTimer = window.setTimeout(() => {
reconnectTimer = null
void connectConnectionSocket()
}, CONNECTION_RECONNECT_DELAY_MS)
}
async function reconnectConnectionSocket() {
if (!hasConnectionSubscribers()) return
await closeConnectionSocket()
scheduleReconnect()
}
async function connectConnectionSocket() {
if (connectionSocket || connectionConnecting) return
if (!hasConnectionSubscribers()) return
clearReconnectTimer()
connectionConnecting = true
try {
const socket = await MihomoWebSocket.connect_connections()
if (!hasConnectionSubscribers()) {
await socket.close()
return
}
connectionSocket = socket
socket.addListener((message) => {
if (connectionSocket !== socket) return
if (message.type !== 'Text') return
if (message.data.startsWith('Websocket error')) {
void reconnectConnectionSocket()
return
}
enqueueConnectionMessage(message.data)
})
} catch {
scheduleReconnect()
} finally {
connectionConnecting = false
}
}
const startConnectionMonitor = () => {
void connectConnectionSocket()
}
const stopConnectionMonitorIfIdle = () => {
if (hasConnectionSubscribers()) return
clearReconnectTimer()
const clearPendingMessage = () => {
pendingMessageData = null
if (flushTimer) {
window.clearTimeout(flushTimer)
flushTimer = null
}
void closeConnectionSocket()
}
interface SocketSupervisor {
start: () => void
stopIfIdle: () => void
reconnect: () => Promise<void>
}
const createSocketSupervisor = (options: {
listeners: Set<ConnectionListener>
connectSocket: () => Promise<MihomoWebSocket>
onText: (data: string) => void
closeLogLabel: string
onIdle?: () => void
}): SocketSupervisor => {
const { listeners, connectSocket, onText, closeLogLabel, onIdle } = options
const hasSubscribers = () => listeners.size > 0
let socket: MihomoWebSocket | null = null
let connecting = false
let reconnectTimer: ReturnType<typeof setTimeout> | null = null
const clearReconnectTimer = () => {
if (!reconnectTimer) return
window.clearTimeout(reconnectTimer)
reconnectTimer = null
}
const closeSocket = async () => {
const current = socket
socket = null
if (!current) return
try {
await current.close()
} catch (err) {
console.warn(`Failed to close ${closeLogLabel} websocket`, err)
}
}
const scheduleReconnect = () => {
if (!hasSubscribers()) return
if (reconnectTimer) return
reconnectTimer = window.setTimeout(() => {
reconnectTimer = null
void connect()
}, CONNECTION_RECONNECT_DELAY_MS)
}
const reconnect = async () => {
if (!hasSubscribers()) return
await closeSocket()
scheduleReconnect()
}
const connect = async () => {
if (socket || connecting) return
if (!hasSubscribers()) return
clearReconnectTimer()
connecting = true
try {
const connected = await connectSocket()
if (!hasSubscribers()) {
await connected.close()
return
}
socket = connected
connected.addListener((message) => {
if (socket !== connected) return
if (message.type !== 'Text') return
if (message.data.startsWith('Websocket error')) {
void reconnect()
return
}
onText(message.data)
})
} catch {
scheduleReconnect()
} finally {
connecting = false
}
}
return {
start: () => {
void connect()
},
stopIfIdle: () => {
if (hasSubscribers()) return
onIdle?.()
clearReconnectTimer()
void closeSocket()
},
reconnect,
}
}
const handleSummaryText = (messageData: string) => {
let payload: ConnectionSummaryPayload
try {
payload = JSON.parse(messageData) as ConnectionSummaryPayload
} catch (err) {
console.error(
'[Connections] Failed to parse connections count payload',
err,
)
return
}
connectionSummary = mergeConnectionSummary(payload)
notifySummaryListeners()
}
const connectionSupervisor = createSocketSupervisor({
listeners: connectionListeners,
connectSocket: () => MihomoWebSocket.connect_connections(),
onText: enqueueConnectionMessage,
closeLogLabel: 'connection',
onIdle: clearPendingMessage,
})
const summarySupervisor = createSocketSupervisor({
listeners: summaryListeners,
connectSocket: () => MihomoWebSocket.connect_connections_count(),
onText: handleSummaryText,
closeLogLabel: 'connections count',
})
const getConnectionSnapshot = () => connectionData
const getConnectionSummarySnapshot = () => connectionSummary
const subscribeConnectionData = (listener: ConnectionListener) => {
connectionListeners.add(listener)
startConnectionMonitor()
connectionSupervisor.start()
return () => {
connectionListeners.delete(listener)
stopConnectionMonitorIfIdle()
connectionSupervisor.stopIfIdle()
}
}
const subscribeConnectionSummary = (listener: ConnectionListener) => {
summaryListeners.add(listener)
startConnectionMonitor()
summarySupervisor.start()
return () => {
summaryListeners.delete(listener)
stopConnectionMonitorIfIdle()
summarySupervisor.stopIfIdle()
}
}
const refreshConnectionData = () => {
pendingMessageData = null
if (flushTimer) {
window.clearTimeout(flushTimer)
flushTimer = null
}
clearPendingMessage()
void connectionSupervisor.reconnect()
}
void reconnectConnectionSocket()
const refreshConnectionSummary = () => {
void summarySupervisor.reconnect()
}
const clearClosedConnectionData = () => {
@@ -414,7 +464,7 @@ export const useConnectionSummaryData = (options?: { enabled?: boolean }) => {
)
const response = useMemo(() => ({ data }), [data])
const refreshGetClashConnectionSummary = useCallback(() => {
refreshConnectionData()
refreshConnectionSummary()
}, [])
return {