diff --git a/web/src/lib/components/mihomo/mihomo-proxy-groups.svelte b/web/src/lib/components/mihomo/mihomo-proxy-groups.svelte index df207ec..c6ea47c 100644 --- a/web/src/lib/components/mihomo/mihomo-proxy-groups.svelte +++ b/web/src/lib/components/mihomo/mihomo-proxy-groups.svelte @@ -7,14 +7,24 @@ import { Skeleton } from '$lib/components/ui/skeleton/index.js'; import { cn } from '$lib/utils.js'; import RefreshCwIcon from '@lucide/svelte/icons/refresh-cw'; + import ActivityIcon from '@lucide/svelte/icons/activity'; let { alias, /** Плотная сетка мелких ячеек — для флота и списков из многих нод. */ - compact = false + compact = false, + /** Скрыть верхнюю панель «Обновить» (кнопка в Card.Action у родителя). */ + hideToolbar = false, + /** Увеличьте, чтобы принудительно перезагрузить список снаружи. */ + refreshNonce = 0, + /** Состояние загрузки для индикатора у родителя. */ + onLoadingChange }: { alias: string; compact?: boolean; + hideToolbar?: boolean; + refreshNonce?: number; + onLoadingChange?: (loading: boolean) => void; } = $props(); const MIHOMO_DELAY_DEFAULT_URL = 'https://www.gstatic.com/generate_204'; @@ -36,6 +46,7 @@ const a = alias; if (!a) return; proxiesLoading = true; + onLoadingChange?.(true); proxiesErr = null; try { proxiesData = await fetchMihomoJson(a, 'proxies'); @@ -44,11 +55,13 @@ // не затираем успешный снимок при сбое обновления — без мерцания UI } finally { proxiesLoading = false; + onLoadingChange?.(false); } } $effect(() => { const a = alias; + void refreshNonce; if (!a) return; void loadProxies(); }); @@ -136,26 +149,28 @@ } -
- -
+ + +{/if} {#snippet skeleton()} {#if compact} -
- {#each Array.from({ length: 12 }) as _, i (i)} - +
+ {#each Array.from({ length: 10 }) as _, i (i)} + {/each}
{:else} @@ -180,77 +195,87 @@ {/snippet} {#snippet children()} {#if proxiesData?.proxies} -
+
{#each selectableGroups as [gName, group] (gName)} {#if compact} -
-
-
- {gName} - - → {group.now ?? '—'} +
+
+
+ + {gName} + + + {group.now ?? '—'}
-
+
{#each group.all ?? [] as nodeName (nodeName)} {@const node = proxiesData.proxies?.[nodeName]} {@const d = node ? lastDelay(node) : null} {@const active = group.now === nodeName}
- + {#if testing === nodeName} + + {:else} +
{/each}
diff --git a/web/src/routes/mihomo/+page.svelte b/web/src/routes/mihomo/+page.svelte index 689ff55..57d85c0 100644 --- a/web/src/routes/mihomo/+page.svelte +++ b/web/src/routes/mihomo/+page.svelte @@ -17,6 +17,7 @@ import DataQueryState from '$lib/components/fleet/data-query-state.svelte'; import MihomoProxyGroups from '$lib/components/mihomo/mihomo-proxy-groups.svelte'; import { Skeleton } from '$lib/components/ui/skeleton/index.js'; + import { cn } from '$lib/utils.js'; import RefreshCwIcon from '@lucide/svelte/icons/refresh-cw'; import ServerIcon from '@lucide/svelte/icons/server'; import PlugIcon from '@lucide/svelte/icons/plug'; @@ -48,6 +49,13 @@ /** Список alias с рабочим meta; обновляется только при полной перезагрузке. */ let mihomoOkAliases: string[] = []; let pollTimer: ReturnType | null = null; + /** Принудительная перезагрузка /proxies для карточки ноды. */ + let proxyRefreshNonce = $state>({}); + let nodeProxyLoading = $state>({}); + + function bumpProxyRefresh(a: string) { + proxyRefreshNonce = { ...proxyRefreshNonce, [a]: (proxyRefreshNonce[a] ?? 0) + 1 }; + } let withMihomo = $derived(rows.filter((r) => r.metaStatus === 'ok').length); let aliveCount = $derived(rows.filter((r) => r.metaStatus === 'ok' && r.alive).length); @@ -325,8 +333,9 @@

Прокси по нодам

-

- Список групп подгружается автоматически. Клик по ячейке — выбор узла; «ping» — задержка. +

+ Автозагрузка групп. Клик по узлу — выбор; иконка справа — проверка задержки. Обновление списка — в + шапке карточки.

- - {r.alias} + + + {r.alias} + {#if r.meta?.controller_base} - + {r.meta.controller_base} {/if} + + + - - + +
+ { + nodeProxyLoading = { ...nodeProxyLoading, [r.alias]: v }; + }} + /> +
{/each}