feat(ui): add useP2PMode hook and embedded prop to Nodes and P2P pages

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Ettore Di Giacinto
2026-06-01 23:03:42 +00:00
committed by localai-org-maint-bot
parent c6e9cf30cf
commit 0390a54fe2
3 changed files with 62 additions and 27 deletions

View File

@@ -0,0 +1,31 @@
import { useState, useEffect, useCallback } from 'react'
import { p2pApi } from '../utils/api'
// useP2PMode reports whether p2p / swarm mode is available, mirroring
// useDistributedMode. Availability is "a network token exists" (the same signal
// the standalone P2P page used). One-shot probe on mount plus a manual refetch.
//
// Returns:
// enabled — true when a non-empty network token is present
// loading — true until the first probe completes
// refetch — manual trigger to re-run the probe
export function useP2PMode() {
const [enabled, setEnabled] = useState(false)
const [loading, setLoading] = useState(true)
const probe = useCallback(async () => {
setLoading(true)
try {
const token = await p2pApi.getToken()
setEnabled(!!(token && String(token).trim()))
} catch {
setEnabled(false)
} finally {
setLoading(false)
}
}, [])
useEffect(() => { probe() }, [probe])
return { enabled, loading, refetch: probe }
}

View File

@@ -89,7 +89,7 @@ function WorkerHintCard({ addToast, activeTab, hasWorkers }) {
)
}
export default function Nodes() {
export default function Nodes({ embedded = false }) {
const { addToast } = useOutletContext()
const { t } = useTranslation('admin')
const [nodesList, setNodesList] = useState([])
@@ -282,16 +282,18 @@ export default function Nodes() {
: activeTab === 'agent' ? agentNodes : backendNodes
return (
<div className="page page--wide">
<PageHeader
title={
<>
<i className="fas fa-network-wired icon-before" />
{t('nodes.title')}
</>
}
supporting={t('nodes.subtitle')}
/>
<div className={embedded ? '' : 'page page--wide'}>
{!embedded && (
<PageHeader
title={
<>
<i className="fas fa-network-wired icon-before" />
{t('nodes.title')}
</>
}
supporting={t('nodes.subtitle')}
/>
)}
<ClusterPulse nodes={nodesList} />
<AttentionCallout nodes={nodesList} onApprove={handleApprove} />

View File

@@ -69,7 +69,7 @@ function NoNodes({ icon, title, hint }) {
)
}
export default function P2P() {
export default function P2P({ embedded = false }) {
const { addToast } = useOutletContext()
const { t } = useTranslation('admin')
const [workers, setWorkers] = useState([])
@@ -145,7 +145,7 @@ export default function P2P() {
if (loading) {
return (
<div className="page page--narrow loading-center">
<div className={embedded ? 'loading-center' : 'page page--narrow loading-center'}>
<LoadingSpinner size="lg" />
</div>
)
@@ -154,7 +154,7 @@ export default function P2P() {
// ── P2P Disabled ──
if (!enabled) {
return (
<div className="page page--narrow">
<div className={embedded ? '' : 'page page--narrow'}>
<div className="p2p-hero">
<i className="fas fa-network-wired" />
<h1>P2P distribution not enabled</h1>
@@ -243,19 +243,21 @@ export default function P2P() {
const mlxTotal = stats.mlx_workers?.total ?? 0
return (
<div className="page page--narrow">
<PageHeader
title={<><i className="fas fa-circle-nodes fa-circle-info" /> {t('p2p.title')}</>}
supporting={
<>
{t('p2p.subtitle')}
{' '}
<a href="https://localai.io/features/distribute/" target="_blank" rel="noopener noreferrer">
<i className="fas fa-up-right-from-square" aria-hidden="true" />
</a>
</>
}
/>
<div className={embedded ? '' : 'page page--narrow'}>
{!embedded && (
<PageHeader
title={<><i className="fas fa-circle-nodes fa-circle-info" /> {t('p2p.title')}</>}
supporting={
<>
{t('p2p.subtitle')}
{' '}
<a href="https://localai.io/features/distribute/" target="_blank" rel="noopener noreferrer">
<i className="fas fa-up-right-from-square" aria-hidden="true" />
</a>
</>
}
/>
)}
<section className="p2p-token">
<div className="hstack hstack--nowrap tone-warning">