diff --git a/src/shared/versions.ts b/src/shared/versions.ts
index def4d5f..e518e69 100644
--- a/src/shared/versions.ts
+++ b/src/shared/versions.ts
@@ -1,6 +1,6 @@
export const HARBOR_VERSIONS = Object.freeze({
- macClient: '0.25.4',
- gatewayClient: '0.26.3',
+ macClient: '0.25.5',
+ gatewayClient: '0.26.4',
gatewayBackend: '0.26.4',
});
diff --git a/src/web/features/devices/DevicesPanel.tsx b/src/web/features/devices/DevicesPanel.tsx
index 3a1dc7a..cce43b6 100644
--- a/src/web/features/devices/DevicesPanel.tsx
+++ b/src/web/features/devices/DevicesPanel.tsx
@@ -337,7 +337,7 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) {
Устройства
Устройства, которые Gateway видит в локальной таблице соседей.
-
Вход — накопленные Gateway/Proxy. Выход — оценка VPN/Direct с запуска текущего учёта.
+
Вход — накопленные Gateway/Proxy. Выход — приблизительно через VPN или Direct с запуска текущего учёта.
@@ -564,8 +564,8 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) {
{trafficView === 'outbound' ? <>
VPN
- Direct ≈
- {unknownTotal > 0n && Неизв.}
+ Direct
+ {unknownTotal > 0n && Другое}
> : <>
Gateway
{hasProxyTraffic && Прокси}
diff --git a/src/web/features/devices/TrafficChart.tsx b/src/web/features/devices/TrafficChart.tsx
index 138108e..7a85226 100644
--- a/src/web/features/devices/TrafficChart.tsx
+++ b/src/web/features/devices/TrafficChart.tsx
@@ -225,12 +225,14 @@ export function TrafficChart({
{routeLabel} {formatByteString(hovered.sample.gatewayBytes)}
{byteString(hovered.sample.proxyBytes) > 0n && Proxy {formatByteString(hovered.sample.proxyBytes)}}
> : series === 'outbound' ? <>
- Учтено ≈ {formatByteString(hovered.gateway + hovered.proxy + hovered.unknown)}
+ Примерно {formatByteString(hovered.gateway + hovered.proxy + hovered.unknown)}
{hovered.gateway > 0n && VPN {formatByteString(hovered.gateway)}}
- {hovered.proxy > 0n && Direct ≈ {formatByteString(hovered.proxy)}}
- {hovered.proxy - hovered.directIpv4 > 0n && через sing-box {formatByteString(hovered.proxy - hovered.directIpv4)}}
- {hovered.directIpv4 > 0n && мимо sing-box · IPv4 {formatByteString(hovered.directIpv4)}}
- {hovered.unknown > 0n && Неизвестно · sing-box {formatByteString(hovered.unknown)}}
+ {hovered.proxy > 0n && Direct {formatByteString(hovered.proxy)}}
+ {hovered.proxy - hovered.directIpv4 > 0n && hovered.directIpv4 > 0n && <>
+ через sing-box {formatByteString(hovered.proxy - hovered.directIpv4)}
+ мимо sing-box · IPv4 {formatByteString(hovered.directIpv4)}
+ >}
+ {hovered.unknown > 0n && Маршрут не определён · sing-box {formatByteString(hovered.unknown)}}
Оценка за 15-секундный интервал
> : <>
Всего {formatByteString(hovered.gateway + hovered.proxy)}
@@ -285,9 +287,9 @@ export function TrafficChart({
{visibleSamples.length > 0 &&
{series === 'outbound' ?
- VPN
- Direct ≈
- ?
+ {visibleLines.some(({ valueKey }) => valueKey === 'gateway') && VPN}
+ {visibleLines.some(({ valueKey }) => valueKey === 'proxy') && Direct}
+ {visibleLines.some(({ valueKey }) => valueKey === 'unknown') && Другое}
: {series === 'speed' ? '↓ / ↑' : 'Вход'}}
}
diff --git a/test/web/device-inventory-contract.test.js b/test/web/device-inventory-contract.test.js
index bf1f81a..843242c 100644
--- a/test/web/device-inventory-contract.test.js
+++ b/test/web/device-inventory-contract.test.js
@@ -82,10 +82,10 @@ test('Gateway device inventory uses the existing accessible responsive drawer',
assert.match(panel, /device\.confidence === 'ambiguous'/);
assert.match(panel, /stabilizeDevicesByTraffic\(snapshot\?\.devices, sortDirection, previousIds\)/);
assert.match(panel, /Трафик временно не обновляется/);
- assert.match(panel, /Вход — накопленные Gateway\/Proxy\. Выход — оценка VPN\/Direct с запуска текущего учёта\./);
+ assert.match(panel, /Вход — накопленные Gateway\/Proxy\. Выход — приблизительно через VPN или Direct с запуска текущего учёта\./);
assert.match(panel, /const displayedTotal = trafficView === 'outbound'[\s\S]*`≈ \$\{formatByteString\(outboundTotal\.toString\(\)\)\}`[\s\S]*const trafficLabel = trafficView === 'outbound' \? 'Выход' : 'Вход'/);
assert.match(panel, /client-device-traffic-total[\s\S]*\{trafficLabel\}<\/b>VPN<\/b>[\s\S]*Direct ≈<\/b>[\s\S]*Неизв\.<\/b>/);
+ assert.match(panel, /client-device-traffic-breakdown[\s\S]*VPN<\/b>[\s\S]*Direct<\/b>[\s\S]*Другое<\/b>/);
assert.match(panel, /Gateway<\/b> 0n/);
assert.match(panel, /client-device-traffic-breakdown[\s\S]*\{hasProxyTraffic && Прокси<\/b> 0n && Proxy \{formatByteString\(hovered\.proxy\)\}<\/span>\}/);
- assert.match(chart, /Учтено ≈[\s\S]*VPN[\s\S]*Direct ≈[\s\S]*через sing-box[\s\S]*мимо sing-box · IPv4[\s\S]*Неизвестно · sing-box[\s\S]*Оценка за 15-секундный интервал/);
- assert.match(chart, /