groove-dev 0.27.64 → 0.27.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/api.js +103 -31
- package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +0 -1
- package/node_modules/@groove-dev/gui/dist/assets/{index-DiiEKVEo.js → index-BvvSZvQz.js} +1735 -1735
- package/node_modules/@groove-dev/gui/dist/assets/index-DFp5IOnd.css +1 -0
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/components/network/activity-chart.jsx +10 -14
- package/node_modules/@groove-dev/gui/src/components/network/compute-header.jsx +67 -200
- package/node_modules/@groove-dev/gui/src/components/network/earnings-card.jsx +30 -0
- package/node_modules/@groove-dev/gui/src/components/network/fleet-table.jsx +114 -72
- package/node_modules/@groove-dev/gui/src/components/network/identity-bar.jsx +94 -0
- package/node_modules/@groove-dev/gui/src/components/network/node-card.jsx +88 -0
- package/node_modules/@groove-dev/gui/src/components/network/wallet-view.jsx +77 -0
- package/node_modules/@groove-dev/gui/src/components/onboarding/setup-wizard.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/stores/groove.js +13 -0
- package/node_modules/@groove-dev/gui/src/views/network.jsx +59 -18
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +103 -31
- package/packages/daemon/src/providers/claude-code.js +0 -1
- package/packages/gui/dist/assets/{index-DiiEKVEo.js → index-BvvSZvQz.js} +1735 -1735
- package/packages/gui/dist/assets/index-DFp5IOnd.css +1 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/network/activity-chart.jsx +10 -14
- package/packages/gui/src/components/network/compute-header.jsx +67 -200
- package/packages/gui/src/components/network/earnings-card.jsx +30 -0
- package/packages/gui/src/components/network/fleet-table.jsx +114 -72
- package/packages/gui/src/components/network/identity-bar.jsx +94 -0
- package/packages/gui/src/components/network/node-card.jsx +88 -0
- package/packages/gui/src/components/network/wallet-view.jsx +77 -0
- package/packages/gui/src/components/onboarding/setup-wizard.jsx +1 -1
- package/packages/gui/src/stores/groove.js +13 -0
- package/packages/gui/src/views/network.jsx +59 -18
- package/node_modules/@groove-dev/gui/dist/assets/index-B3AqeyS4.css +0 -1
- package/packages/gui/dist/assets/index-B3AqeyS4.css +0 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
import { memo, useState, useEffect, useCallback } from 'react';
|
|
3
|
+
import { useGrooveStore } from '../../stores/groove';
|
|
4
|
+
import { cn } from '../../lib/cn';
|
|
5
|
+
import { StatusDot } from '../ui/status-dot';
|
|
6
|
+
import { Button } from '../ui/button';
|
|
7
|
+
import { Tooltip } from '../ui/tooltip';
|
|
8
|
+
import { fmtUptime } from '../../lib/format';
|
|
9
|
+
import { Copy, Check, Wallet } from 'lucide-react';
|
|
10
|
+
|
|
11
|
+
function shortAddr(addr) {
|
|
12
|
+
if (!addr || typeof addr !== 'string') return '—';
|
|
13
|
+
if (addr.length < 14) return addr;
|
|
14
|
+
return `${addr.slice(0, 6)}…${addr.slice(-4)}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function nodeStatusMap(node) {
|
|
18
|
+
if (node.active && node.status === 'connected') return 'running';
|
|
19
|
+
if (node.status === 'connecting') return 'starting';
|
|
20
|
+
return 'stopped';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function statusColor(status) {
|
|
24
|
+
if (status === 'connected') return 'text-success';
|
|
25
|
+
if (status === 'connecting') return 'text-warning';
|
|
26
|
+
return 'text-danger';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const IdentityBar = memo(function IdentityBar() {
|
|
30
|
+
const node = useGrooveStore((s) => s.networkNode);
|
|
31
|
+
const wallet = useGrooveStore((s) => s.networkWallet);
|
|
32
|
+
const [copied, setCopied] = useState(false);
|
|
33
|
+
const [uptime, setUptime] = useState(0);
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (!node.startedAt) return;
|
|
37
|
+
const tick = () => setUptime(Math.floor((Date.now() - node.startedAt) / 1000));
|
|
38
|
+
tick();
|
|
39
|
+
const id = setInterval(tick, 1000);
|
|
40
|
+
return () => clearInterval(id);
|
|
41
|
+
}, [node.startedAt]);
|
|
42
|
+
|
|
43
|
+
const handleCopy = useCallback(async () => {
|
|
44
|
+
if (!node.nodeId) return;
|
|
45
|
+
try {
|
|
46
|
+
await navigator.clipboard.writeText(node.nodeId);
|
|
47
|
+
setCopied(true);
|
|
48
|
+
setTimeout(() => setCopied(false), 1500);
|
|
49
|
+
} catch { /* clipboard blocked */ }
|
|
50
|
+
}, [node.nodeId]);
|
|
51
|
+
|
|
52
|
+
const connStatus = node.status || 'disconnected';
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div className="flex items-center h-11 px-4 bg-surface-0 border-b border-border-subtle gap-3">
|
|
56
|
+
{/* Left: node identity */}
|
|
57
|
+
<div className="flex items-center gap-2">
|
|
58
|
+
<StatusDot status={nodeStatusMap(node)} size="sm" />
|
|
59
|
+
<code className="text-xs font-mono text-text-0">{shortAddr(node.nodeId)}</code>
|
|
60
|
+
<Tooltip content={copied ? 'Copied' : 'Copy address'} side="bottom">
|
|
61
|
+
<button
|
|
62
|
+
onClick={handleCopy}
|
|
63
|
+
className="h-6 w-6 inline-flex items-center justify-center rounded border border-border-subtle text-text-3 hover:text-accent hover:border-accent/40 cursor-pointer transition-colors"
|
|
64
|
+
>
|
|
65
|
+
{copied ? <Check size={10} /> : <Copy size={10} />}
|
|
66
|
+
</button>
|
|
67
|
+
</Tooltip>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
{/* Center: status + uptime */}
|
|
71
|
+
<div className="flex-1 flex items-center justify-center gap-3">
|
|
72
|
+
<span className={cn('text-2xs uppercase tracking-widest font-sans', statusColor(connStatus))}>
|
|
73
|
+
{connStatus === 'connected' ? 'Connected' : connStatus === 'connecting' ? 'Connecting' : 'Disconnected'}
|
|
74
|
+
</span>
|
|
75
|
+
<span className="w-1 h-1 rounded-full bg-text-4" />
|
|
76
|
+
<span className="text-2xs font-mono text-text-3 tabular-nums">
|
|
77
|
+
{fmtUptime(uptime)}
|
|
78
|
+
</span>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
{/* Right: token balance + wallet */}
|
|
82
|
+
<div className="flex items-center gap-3">
|
|
83
|
+
<span className="text-xs font-mono text-text-2">
|
|
84
|
+
<span className="text-text-0">{wallet.balance}</span>
|
|
85
|
+
<span className="text-text-3"> GROOVE</span>
|
|
86
|
+
</span>
|
|
87
|
+
<Button variant="outline" size="sm" className="text-2xs gap-1.5" disabled>
|
|
88
|
+
<Wallet size={12} />
|
|
89
|
+
Connect Wallet
|
|
90
|
+
</Button>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
import { useGrooveStore } from '../../stores/groove';
|
|
4
|
+
import { cn } from '../../lib/cn';
|
|
5
|
+
import { StatusDot } from '../ui/status-dot';
|
|
6
|
+
|
|
7
|
+
function shortAddr(addr) {
|
|
8
|
+
if (!addr || typeof addr !== 'string') return '—';
|
|
9
|
+
if (addr.length < 14) return addr;
|
|
10
|
+
return `${addr.slice(0, 6)}…${addr.slice(-4)}`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function nodeStatusMap(node) {
|
|
14
|
+
if (node.active && node.status === 'connected') return 'running';
|
|
15
|
+
if (node.status === 'connecting') return 'starting';
|
|
16
|
+
return 'stopped';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function modelShort(model) {
|
|
20
|
+
if (!model) return '—';
|
|
21
|
+
const parts = model.split('/');
|
|
22
|
+
return parts[parts.length - 1];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const NodeCard = memo(function NodeCard() {
|
|
26
|
+
const node = useGrooveStore((s) => s.networkNode);
|
|
27
|
+
const hardware = node.hardware || {};
|
|
28
|
+
const memPct = Number.isFinite(node.memoryPct) ? node.memoryPct : null;
|
|
29
|
+
const layersLabel = Array.isArray(node.layers) ? `${node.layers[0]}-${node.layers[1]}` : '—';
|
|
30
|
+
|
|
31
|
+
const metrics = [
|
|
32
|
+
{ label: 'DEVICE', value: hardware.device || 'auto' },
|
|
33
|
+
{ label: 'GPU', value: hardware.gpu || 'None' },
|
|
34
|
+
{ label: 'LAYERS', value: layersLabel },
|
|
35
|
+
{ label: 'MODEL', value: modelShort(node.model) },
|
|
36
|
+
{ label: 'SESSIONS', value: node.sessions ?? 0 },
|
|
37
|
+
{ label: 'VRAM', value: hardware.memory || '—' },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className="flex flex-col h-full">
|
|
42
|
+
<div className="px-3 pt-2.5 pb-1">
|
|
43
|
+
<span className="text-2xs font-mono text-text-3 uppercase tracking-widest">Your Node</span>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div className="px-3 py-2 space-y-2">
|
|
47
|
+
<div>
|
|
48
|
+
<div className="text-2xs text-text-4 uppercase tracking-wider">ADDRESS</div>
|
|
49
|
+
<div className="text-xs font-mono text-accent truncate">{shortAddr(node.nodeId)}</div>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div className="flex items-center gap-1.5">
|
|
53
|
+
<StatusDot status={nodeStatusMap(node)} size="sm" />
|
|
54
|
+
<span className="text-2xs capitalize text-text-2">{node.status || 'disconnected'}</span>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div className="border-t border-border-subtle my-1" />
|
|
58
|
+
|
|
59
|
+
<div className="grid grid-cols-2 gap-x-3 gap-y-1.5">
|
|
60
|
+
{metrics.map((m) => (
|
|
61
|
+
<div key={m.label}>
|
|
62
|
+
<div className="text-2xs text-text-4 uppercase tracking-wider">{m.label}</div>
|
|
63
|
+
<div className="text-xs font-mono text-text-1 tabular-nums truncate">{m.value}</div>
|
|
64
|
+
</div>
|
|
65
|
+
))}
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
{memPct != null && (
|
|
69
|
+
<div className="mt-1">
|
|
70
|
+
<div className="flex items-center justify-between mb-0.5">
|
|
71
|
+
<span className="text-2xs text-text-4 uppercase tracking-wider">MEM</span>
|
|
72
|
+
<span className="text-2xs font-mono text-text-3 tabular-nums">{Math.round(memPct)}%</span>
|
|
73
|
+
</div>
|
|
74
|
+
<div className="h-1 w-full rounded-full bg-surface-3 overflow-hidden">
|
|
75
|
+
<div
|
|
76
|
+
className={cn(
|
|
77
|
+
'h-full rounded-full transition-all',
|
|
78
|
+
memPct > 90 ? 'bg-danger' : memPct > 70 ? 'bg-warning' : 'bg-accent',
|
|
79
|
+
)}
|
|
80
|
+
style={{ width: `${Math.min(100, Math.max(0, memPct))}%` }}
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
)}
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
import { Badge } from '../ui/badge';
|
|
4
|
+
import { Button } from '../ui/button';
|
|
5
|
+
import { Wallet, Cpu, Activity, Clock, Zap } from 'lucide-react';
|
|
6
|
+
|
|
7
|
+
const REWARD_CARDS = [
|
|
8
|
+
{ icon: Cpu, label: 'Compute Hours', value: '—' },
|
|
9
|
+
{ icon: Activity, label: 'Sessions Served', value: '—' },
|
|
10
|
+
{ icon: Clock, label: 'Uptime Score', value: '—' },
|
|
11
|
+
{ icon: Zap, label: 'Network Score', value: '—' },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const PAYOUT_COLS = ['Date', 'Amount', 'Status', 'TX'];
|
|
15
|
+
|
|
16
|
+
export const WalletView = memo(function WalletView() {
|
|
17
|
+
return (
|
|
18
|
+
<div className="flex flex-col h-full overflow-auto">
|
|
19
|
+
{/* Hero */}
|
|
20
|
+
<div className="px-6 py-8 text-center border-b border-border-subtle bg-surface-0">
|
|
21
|
+
<div className="text-3xl font-mono font-semibold text-text-0 tabular-nums">0.00</div>
|
|
22
|
+
<div className="text-sm font-mono text-text-3 mt-1">GROOVE</div>
|
|
23
|
+
<Badge variant="purple" className="mt-3">Base L2</Badge>
|
|
24
|
+
<div className="mt-4">
|
|
25
|
+
<Button variant="primary" size="md" className="gap-2" disabled>
|
|
26
|
+
<Wallet size={14} />
|
|
27
|
+
Connect Wallet
|
|
28
|
+
</Button>
|
|
29
|
+
</div>
|
|
30
|
+
<div className="text-2xs text-text-4 mt-2">Connect your Base wallet to claim rewards</div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
{/* Reward Metrics */}
|
|
34
|
+
<div className="px-4 py-4">
|
|
35
|
+
<div className="text-2xs font-mono text-text-3 uppercase tracking-widest mb-3">EARNING POTENTIAL</div>
|
|
36
|
+
<div className="grid grid-cols-4 gap-2">
|
|
37
|
+
{REWARD_CARDS.map((card) => (
|
|
38
|
+
<div key={card.label} className="rounded-md border border-border-subtle bg-surface-1 px-3 py-3 text-center">
|
|
39
|
+
<card.icon size={16} className="text-text-3 mx-auto mb-1.5" />
|
|
40
|
+
<div className="text-lg font-mono text-text-0 tabular-nums">{card.value}</div>
|
|
41
|
+
<div className="text-2xs text-text-4 mt-0.5">{card.label}</div>
|
|
42
|
+
</div>
|
|
43
|
+
))}
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
{/* Earnings Timeline */}
|
|
48
|
+
<div className="px-4 py-4 border-t border-border-subtle">
|
|
49
|
+
<div className="text-2xs font-mono text-text-3 uppercase tracking-widest mb-3">EARNINGS HISTORY</div>
|
|
50
|
+
<div className="h-40 rounded-md border border-border-subtle bg-surface-0 flex items-center justify-center">
|
|
51
|
+
<span className="text-xs font-mono text-text-4">Earnings data will appear here</span>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
{/* Payouts Table */}
|
|
56
|
+
<div className="px-4 py-4 border-t border-border-subtle">
|
|
57
|
+
<div className="text-2xs font-mono text-text-3 uppercase tracking-widest mb-3">PAYOUT HISTORY</div>
|
|
58
|
+
<div className="grid grid-cols-4 px-3 py-1.5 text-2xs font-mono text-text-4 uppercase tracking-wider">
|
|
59
|
+
{PAYOUT_COLS.map((col) => (
|
|
60
|
+
<span key={col}>{col}</span>
|
|
61
|
+
))}
|
|
62
|
+
</div>
|
|
63
|
+
<div className="px-3 py-6 text-center text-xs font-mono text-text-4">No payouts yet</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
{/* Banner */}
|
|
67
|
+
<div className="px-4 pb-6">
|
|
68
|
+
<div className="rounded-md border border-purple/20 bg-purple/5 px-4 py-3 flex items-center gap-3">
|
|
69
|
+
<Zap size={16} className="text-purple flex-shrink-0" />
|
|
70
|
+
<span className="text-xs font-sans text-text-2">
|
|
71
|
+
Rewards go live when the GROOVE token launches on Base. Keep your node running to accumulate compute credits.
|
|
72
|
+
</span>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
});
|
|
@@ -19,7 +19,7 @@ const PROVIDERS = [
|
|
|
19
19
|
id: 'claude-code',
|
|
20
20
|
name: 'Claude Code',
|
|
21
21
|
subtitle: 'by Anthropic',
|
|
22
|
-
models: ['Opus 4.
|
|
22
|
+
models: ['Opus 4.6', 'Sonnet 4.6', 'Haiku 4.5'],
|
|
23
23
|
authType: 'Subscription or API key',
|
|
24
24
|
authModes: ['subscription', 'apikey'],
|
|
25
25
|
recommended: true,
|
|
@@ -105,6 +105,8 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
105
105
|
networkUpdateProgress: { updating: false, step: null, message: null, percent: 0, error: null },
|
|
106
106
|
networkCompute: { totalRamMb: 0, totalVramMb: 0, totalCpuCores: 0, totalBandwidthMbps: 0, activeNodes: 0, totalNodes: 0, avgLoad: 0 },
|
|
107
107
|
networkSnapshots: [],
|
|
108
|
+
networkWallet: { connected: false, address: null, balance: '0.00', token: 'GROOVE', chain: 'base-l2' },
|
|
109
|
+
networkEarnings: { today: 0, thisWeek: 0, allTime: 0, history: [] },
|
|
108
110
|
|
|
109
111
|
// ── Marketplace Auth ───────────────────────────────────────
|
|
110
112
|
marketplaceUser: null, // { id, displayName, avatar, ... } or null
|
|
@@ -732,6 +734,8 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
732
734
|
nodeCount: wsActive.length,
|
|
733
735
|
avgLoad: wsActive.length > 0 ? wsActive.reduce((s, n) => s + (n.load || 0), 0) / wsActive.length : 0,
|
|
734
736
|
myLoad: wsOwn?.load ?? 0,
|
|
737
|
+
totalVramMb: wsNodes.reduce((s, n) => s + (n.vram_mb || 0), 0),
|
|
738
|
+
totalRamMb: wsNodes.reduce((s, n) => s + (n.ram_mb || 0), 0),
|
|
735
739
|
};
|
|
736
740
|
let wsSnapshots = [...get().networkSnapshots, wsSnap];
|
|
737
741
|
if (wsSnapshots.length > 100) wsSnapshots = wsSnapshots.slice(-100);
|
|
@@ -2179,6 +2183,8 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
2179
2183
|
nodeCount: activeNodes.length,
|
|
2180
2184
|
avgLoad: activeNodes.length > 0 ? activeNodes.reduce((s, n) => s + (n.load || 0), 0) / activeNodes.length : 0,
|
|
2181
2185
|
myLoad: ownNode?.load ?? 0,
|
|
2186
|
+
totalVramMb: nodes.reduce((s, n) => s + (n.vram_mb || 0), 0),
|
|
2187
|
+
totalRamMb: nodes.reduce((s, n) => s + (n.ram_mb || 0), 0),
|
|
2182
2188
|
};
|
|
2183
2189
|
let snapshots = [...get().networkSnapshots, snap];
|
|
2184
2190
|
if (snapshots.length > 100) snapshots = snapshots.slice(-100);
|
|
@@ -2258,6 +2264,13 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
2258
2264
|
}
|
|
2259
2265
|
},
|
|
2260
2266
|
|
|
2267
|
+
async fetchNetworkWallet() {
|
|
2268
|
+
return get().networkWallet;
|
|
2269
|
+
},
|
|
2270
|
+
async fetchNetworkEarnings() {
|
|
2271
|
+
return get().networkEarnings;
|
|
2272
|
+
},
|
|
2273
|
+
|
|
2261
2274
|
async renameFile(oldPath, newPath) {
|
|
2262
2275
|
try {
|
|
2263
2276
|
await api.post('/files/rename', { oldPath, newPath });
|
|
@@ -6,12 +6,17 @@ import { Badge } from '../components/ui/badge';
|
|
|
6
6
|
import { Button } from '../components/ui/button';
|
|
7
7
|
import { Dialog, DialogContent, DialogTrigger } from '../components/ui/dialog';
|
|
8
8
|
import { ScrollArea } from '../components/ui/scroll-area';
|
|
9
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from '../components/ui/tabs';
|
|
9
10
|
import { cn } from '../lib/cn';
|
|
10
|
-
import {
|
|
11
|
+
import { IdentityBar } from '../components/network/identity-bar';
|
|
11
12
|
import { ComputeHeader } from '../components/network/compute-header';
|
|
12
13
|
import { ActivityChart } from '../components/network/activity-chart';
|
|
13
14
|
import { ActivityStream } from '../components/network/activity-stream';
|
|
14
15
|
import { NetworkHealth } from '../components/network/network-health';
|
|
16
|
+
import { NodeCard } from '../components/network/node-card';
|
|
17
|
+
import { EarningsCard } from '../components/network/earnings-card';
|
|
18
|
+
import { FleetTable } from '../components/network/fleet-table';
|
|
19
|
+
import { WalletView } from '../components/network/wallet-view';
|
|
15
20
|
import { HEX, hexAlpha } from '../lib/theme-hex';
|
|
16
21
|
import { Globe, Download, Check, AlertCircle, Loader2, Trash2, ArrowUpCircle, Zap } from 'lucide-react';
|
|
17
22
|
|
|
@@ -405,12 +410,44 @@ function IdleHero() {
|
|
|
405
410
|
);
|
|
406
411
|
}
|
|
407
412
|
|
|
413
|
+
function NetworkOverview() {
|
|
414
|
+
return (
|
|
415
|
+
<div className="flex flex-col h-full">
|
|
416
|
+
<ComputeHeader />
|
|
417
|
+
<div className="flex-1 min-h-0 grid" style={{ gridTemplateColumns: '2.5fr 1fr', gap: '1px', background: HEX.surface3 }}>
|
|
418
|
+
{/* Left column */}
|
|
419
|
+
<div className="flex flex-col min-w-0 min-h-0" style={{ gap: '1px' }}>
|
|
420
|
+
<div className="flex-1 min-h-0 overflow-hidden bg-surface-1">
|
|
421
|
+
<ActivityChart />
|
|
422
|
+
</div>
|
|
423
|
+
<div className="flex-[0.5] min-h-0 overflow-hidden bg-surface-1">
|
|
424
|
+
<ActivityStream />
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
427
|
+
{/* Right column */}
|
|
428
|
+
<div className="flex flex-col min-w-0 min-h-0" style={{ gap: '1px' }}>
|
|
429
|
+
<div className="min-h-0 overflow-hidden bg-surface-1">
|
|
430
|
+
<NodeCard />
|
|
431
|
+
</div>
|
|
432
|
+
<div className="flex-1 min-h-0 overflow-hidden bg-surface-1">
|
|
433
|
+
<NetworkHealth />
|
|
434
|
+
</div>
|
|
435
|
+
<div className="min-h-0 overflow-hidden bg-surface-1">
|
|
436
|
+
<EarningsCard />
|
|
437
|
+
</div>
|
|
438
|
+
</div>
|
|
439
|
+
</div>
|
|
440
|
+
</div>
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
|
|
408
444
|
export default function NetworkView() {
|
|
409
445
|
const fetchNetworkNodeStatus = useGrooveStore((s) => s.fetchNetworkNodeStatus);
|
|
410
446
|
const fetchNetworkStatus = useGrooveStore((s) => s.fetchNetworkStatus);
|
|
411
447
|
const checkNetworkUpdate = useGrooveStore((s) => s.checkNetworkUpdate);
|
|
412
448
|
const installed = useGrooveStore((s) => s.networkInstalled);
|
|
413
449
|
const nodeActive = useGrooveStore((s) => s.networkNode.active);
|
|
450
|
+
const [activeTab, setActiveTab] = useState('overview');
|
|
414
451
|
|
|
415
452
|
useEffect(() => {
|
|
416
453
|
fetchNetworkNodeStatus();
|
|
@@ -440,23 +477,27 @@ export default function NetworkView() {
|
|
|
440
477
|
return (
|
|
441
478
|
<div className="flex flex-col h-full">
|
|
442
479
|
<NetworkHeader />
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
<
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
</
|
|
455
|
-
|
|
456
|
-
<
|
|
457
|
-
<
|
|
458
|
-
</
|
|
459
|
-
|
|
480
|
+
<IdentityBar />
|
|
481
|
+
|
|
482
|
+
<Tabs value={activeTab} onValueChange={setActiveTab} className="flex-1 min-h-0 flex flex-col">
|
|
483
|
+
<TabsList className="bg-surface-0 border-b border-border-subtle px-4 flex-shrink-0">
|
|
484
|
+
<TabsTrigger value="overview" className="text-xs">Overview</TabsTrigger>
|
|
485
|
+
<TabsTrigger value="fleet" className="text-xs">Fleet</TabsTrigger>
|
|
486
|
+
<TabsTrigger value="wallet" className="text-xs">Wallet</TabsTrigger>
|
|
487
|
+
</TabsList>
|
|
488
|
+
|
|
489
|
+
<TabsContent value="overview" className="flex-1 min-h-0 overflow-hidden">
|
|
490
|
+
<NetworkOverview />
|
|
491
|
+
</TabsContent>
|
|
492
|
+
|
|
493
|
+
<TabsContent value="fleet" className="flex-1 min-h-0 overflow-hidden bg-surface-1">
|
|
494
|
+
<FleetTable />
|
|
495
|
+
</TabsContent>
|
|
496
|
+
|
|
497
|
+
<TabsContent value="wallet" className="flex-1 min-h-0 overflow-hidden">
|
|
498
|
+
<WalletView />
|
|
499
|
+
</TabsContent>
|
|
500
|
+
</Tabs>
|
|
460
501
|
</div>
|
|
461
502
|
);
|
|
462
503
|
}
|