AUXteam's picture
Upload folder using huggingface_hub
6a7089a verified
import { useMemo, useCallback } from "react";
import {
AreaChart,
Area,
XAxis,
YAxis,
Tooltip,
CartesianGrid,
ResponsiveContainer,
} from "recharts";
import type {
TabDataPoint,
MemoryDataPoint,
ServerDataPoint,
} from "../../stores/useAppStore";
interface Props {
data: TabDataPoint[];
memoryData?: MemoryDataPoint[];
serverData?: ServerDataPoint[];
instances: { id: string; profileName: string }[];
selectedInstanceId: string | null;
onSelectInstance: (id: string) => void;
}
const COLORS = [
"#6366f1",
"#8b5cf6",
"#06b6d4",
"#10b981",
"#f59e0b",
"#ef4444",
"#ec4899",
"#14b8a6",
];
function formatTime(timestamp: number): string {
return new Date(timestamp).toLocaleTimeString("en-GB", {
hour: "2-digit",
minute: "2-digit",
});
}
function formatMetricValue(value: number, maximumFractionDigits = 0): string {
if (!Number.isFinite(value)) return "0";
return new Intl.NumberFormat("en-US", { maximumFractionDigits }).format(
value,
);
}
/* ── Glassmorphism Tooltip ── */
function GlassTooltip({
active,
payload,
label,
instances,
}: {
active?: boolean;
payload?: readonly { dataKey: string; value: number; color: string }[];
label?: string | number;
instances: { id: string; profileName: string }[];
}) {
if (!active || !payload?.length) return null;
return (
<div
style={{
background: "rgba(15, 17, 23, 0.75)",
backdropFilter: "blur(16px)",
WebkitBackdropFilter: "blur(16px)",
border: "1px solid rgba(255,255,255,0.12)",
borderRadius: "14px",
padding: "10px 14px",
boxShadow:
"0 20px 40px rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.06)",
minWidth: "140px",
}}
>
<div
style={{
fontSize: "10px",
color: "#94a3b8",
marginBottom: "6px",
fontWeight: 600,
letterSpacing: "0.05em",
textTransform: "uppercase" as const,
}}
>
{formatTime(label as number)}
</div>
{payload.map((entry) => {
const nameStr = String(entry.dataKey);
const isMemory = nameStr.endsWith("_mem");
const isHeap = nameStr === "goHeapMB";
const instId = isMemory ? nameStr.replace("_mem", "") : nameStr;
const inst = instances.find((i) => i.id === instId);
const displayName = isHeap
? "Server Heap"
: inst?.profileName || instId;
const suffix = isMemory ? " (mem)" : isHeap ? "" : " (tabs)";
const formattedVal =
isMemory || isHeap
? `${formatMetricValue(Number(entry.value), 1)}MB`
: formatMetricValue(Number(entry.value));
return (
<div
key={entry.dataKey}
style={{
display: "flex",
alignItems: "center",
gap: "8px",
padding: "2px 0",
fontSize: "12px",
}}
>
<span
style={{
width: "8px",
height: "8px",
borderRadius: "50%",
background: entry.color,
boxShadow: `0 0 6px ${entry.color}80`,
flexShrink: 0,
}}
/>
<span style={{ color: "#cbd5e1", flex: 1 }}>
{displayName}
{suffix}
</span>
<span
style={{
color: "#f1f5f9",
fontWeight: 600,
fontFamily: "var(--font-mono, monospace)",
}}
>
{formattedVal}
</span>
</div>
);
})}
</div>
);
}
/* ── Animated Cursor ── */
function AnimatedCursor({
points,
height,
}: {
points?: Array<{ x: number; y: number }>;
width?: number;
height?: number;
}) {
if (!points?.length || !height) return null;
const x = points[0].x;
return (
<line
x1={x}
y1={0}
x2={x}
y2={height}
stroke="rgba(99,102,241,0.4)"
strokeWidth={1}
strokeDasharray="4 3"
style={{ transition: "x1 80ms ease, x2 80ms ease" }}
/>
);
}
/* ── Loading Dots ── */
function LoadingDots({ text }: { text: string }) {
return (
<div className="dashboard-panel flex h-56 flex-col items-center justify-center gap-3">
<div className="flex gap-1.5">
{[0, 1, 2].map((i) => (
<span
key={i}
className="inline-block h-2 w-2 rounded-full bg-indigo-500/70"
style={{
animation: "pulse 1.4s ease-in-out infinite",
animationDelay: `${i * 0.2}s`,
}}
/>
))}
</div>
<span className="text-sm text-text-muted">{text}</span>
</div>
);
}
/* ── Main Component ── */
export default function TabsChart({
data,
memoryData,
serverData,
instances,
selectedInstanceId,
onSelectInstance,
}: Props) {
const instanceColors = useMemo(() => {
const colors: Record<string, string> = {};
instances.forEach((inst, i) => {
colors[inst.id] = COLORS[i % COLORS.length];
});
return colors;
}, [instances]);
const mergedData = useMemo(() => {
const memByTime = new Map((memoryData || []).map((m) => [m.timestamp, m]));
const serverByTime = new Map(
(serverData || []).map((s) => [s.timestamp, s]),
);
const baseData =
data.length > 0
? data
: (serverData || []).map((s) => ({ timestamp: s.timestamp }));
return baseData.map((d) => {
const merged: Record<string, number> = { timestamp: d.timestamp };
for (const [key, val] of Object.entries(d)) {
if (key !== "timestamp") merged[key] = val as number;
}
const mem = memByTime.get(d.timestamp);
if (mem) {
for (const [key, val] of Object.entries(mem)) {
if (key !== "timestamp") merged[`${key}_mem`] = val;
}
}
const srv = serverByTime.get(d.timestamp);
if (srv) merged.goHeapMB = srv.goHeapMB;
return merged;
});
}, [data, memoryData, serverData]);
// Current values for header badges
const currentValues = useMemo(() => {
if (mergedData.length === 0) return null;
const latest = mergedData[mergedData.length - 1];
const vals: Array<{ label: string; value: string; color: string }> = [];
instances.forEach((inst) => {
const v = latest[inst.id];
if (v !== undefined) {
vals.push({
label: inst.profileName,
value: `${formatMetricValue(Number(v))} tabs`,
color: instanceColors[inst.id] || COLORS[0],
});
}
const memV = latest[`${inst.id}_mem`];
if (memV !== undefined) {
vals.push({
label: `${inst.profileName} mem`,
value: `${formatMetricValue(Number(memV), 1)}MB`,
color: instanceColors[inst.id] || COLORS[0],
});
}
});
if (latest.goHeapMB !== undefined) {
vals.push({
label: "Heap",
value: `${formatMetricValue(Number(latest.goHeapMB), 1)}MB`,
color: "#94a3b8",
});
}
return vals;
}, [mergedData, instances, instanceColors]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const tooltipContent = useCallback(
(props: any) => <GlassTooltip {...props} instances={instances} />,
[instances],
);
if (mergedData.length < 2) {
return (
<LoadingDots
text={
mergedData.length === 0
? "Collecting data..."
: "Waiting for more data..."
}
/>
);
}
const hasMemory = memoryData && memoryData.length > 0;
const hasServer = serverData && serverData.length > 0;
return (
<div className="dashboard-panel overflow-hidden">
<div className="flex items-center justify-between border-b border-border-subtle px-4 py-3">
<div className="flex items-center gap-2.5">
<div>
<div className="dashboard-section-label">Monitoring</div>
<div className="mt-1 flex items-center gap-2 text-sm font-semibold text-text-primary">
Live telemetry
{/* Pulse indicator */}
<span className="relative flex h-2.5 w-2.5">
<span
className="absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"
style={{
animation: "ping 1.5s cubic-bezier(0,0,0.2,1) infinite",
}}
/>
<span className="relative inline-flex h-2.5 w-2.5 rounded-full bg-emerald-400" />
</span>
</div>
</div>
</div>
<div className="flex flex-wrap items-center gap-1.5">
{/* Current value badges */}
{currentValues?.map((cv) => (
<span
key={cv.label}
className="flex items-center gap-1.5 rounded-md border border-white/[0.08] bg-white/[0.04] px-2 py-1 text-[0.65rem] font-medium text-text-secondary"
>
<span
className="h-1.5 w-1.5 rounded-full"
style={{
background: cv.color,
boxShadow: `0 0 4px ${cv.color}60`,
}}
/>
{cv.value}
</span>
))}
{/* Metric type badges */}
<span className="rounded-sm border border-border-subtle bg-white/[0.03] px-2 py-1 text-[0.68rem] font-semibold uppercase tracking-[0.08em] text-text-secondary">
Tabs
</span>
{hasMemory && (
<span className="rounded-sm border border-info/35 bg-info/10 px-2 py-1 text-[0.68rem] font-semibold uppercase tracking-[0.08em] text-info">
Memory
</span>
)}
{hasServer && (
<span className="rounded-sm border border-primary/35 bg-primary/10 px-2 py-1 text-[0.68rem] font-semibold uppercase tracking-[0.08em] text-primary">
Heap
</span>
)}
</div>
</div>
<div className="px-2 py-3">
<ResponsiveContainer width="100%" height={220}>
<AreaChart
data={mergedData}
margin={{
top: 16,
right: hasMemory || hasServer ? 50 : 16,
bottom: 8,
left: 8,
}}
>
<defs>
{/* Gradients for each instance */}
{instances.map((inst) => {
const color = instanceColors[inst.id] || COLORS[0];
return (
<linearGradient
key={`grad-${inst.id}`}
id={`grad-${inst.id}`}
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop offset="0%" stopColor={color} stopOpacity={0.35} />
<stop offset="95%" stopColor={color} stopOpacity={0.02} />
</linearGradient>
);
})}
{/* Gradient for server heap */}
<linearGradient id="grad-heap" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#94a3b8" stopOpacity={0.2} />
<stop offset="95%" stopColor="#94a3b8" stopOpacity={0.01} />
</linearGradient>
</defs>
<CartesianGrid
strokeDasharray="3 3"
stroke="rgba(255,255,255,0.04)"
vertical={false}
/>
<XAxis
dataKey="timestamp"
tickFormatter={formatTime}
stroke="#64748b"
fontSize={11}
tickLine={false}
axisLine={false}
/>
<YAxis
yAxisId="tabs"
stroke="#64748b"
fontSize={11}
allowDecimals={false}
domain={[0, "auto"]}
tickLine={false}
axisLine={false}
width={30}
tickFormatter={(value) => formatMetricValue(Number(value))}
/>
{(hasMemory || hasServer) && (
<YAxis
yAxisId="memory"
orientation="right"
stroke="#94a3b8"
fontSize={11}
allowDecimals={false}
domain={[0, "auto"]}
tickLine={false}
axisLine={false}
width={40}
tickFormatter={(value) =>
`${formatMetricValue(Number(value), 1)}MB`
}
/>
)}
<Tooltip content={tooltipContent} cursor={<AnimatedCursor />} />
{/* Tab count areas (solid gradient fill) */}
{instances.map((inst) => (
<Area
key={inst.id}
yAxisId="tabs"
type="monotone"
dataKey={inst.id}
name={inst.id}
stroke={instanceColors[inst.id]}
strokeWidth={selectedInstanceId === inst.id ? 2.5 : 1.5}
strokeLinecap="round"
strokeOpacity={
selectedInstanceId && selectedInstanceId !== inst.id ? 0.3 : 1
}
fill={`url(#grad-${inst.id})`}
fillOpacity={
selectedInstanceId && selectedInstanceId !== inst.id
? 0.15
: 1
}
dot={false}
activeDot={{
r: 5,
strokeWidth: 2,
stroke: instanceColors[inst.id],
fill: "#0f1117",
onClick: () => onSelectInstance(inst.id),
style: {
cursor: "pointer",
filter: `drop-shadow(0 0 4px ${instanceColors[inst.id]}80)`,
},
}}
animationDuration={800}
animationEasing="ease-in-out"
/>
))}
{/* Memory areas (dashed, lighter fill) */}
{hasMemory &&
instances.map((inst) => (
<Area
key={`${inst.id}_mem`}
yAxisId="memory"
type="monotone"
dataKey={`${inst.id}_mem`}
name={`${inst.id}_mem`}
stroke={instanceColors[inst.id]}
strokeWidth={selectedInstanceId === inst.id ? 2 : 1}
strokeLinecap="round"
strokeOpacity={
selectedInstanceId && selectedInstanceId !== inst.id
? 0.2
: 0.6
}
strokeDasharray="4 2"
fill="transparent"
dot={false}
animationDuration={800}
animationEasing="ease-in-out"
/>
))}
{/* Server heap area (dotted, gray) */}
{hasServer && (
<Area
yAxisId="memory"
type="monotone"
dataKey="goHeapMB"
name="goHeapMB"
stroke="#94a3b8"
strokeWidth={1.5}
strokeLinecap="round"
strokeDasharray="2 2"
fill="url(#grad-heap)"
fillOpacity={0.5}
dot={false}
animationDuration={800}
animationEasing="ease-in-out"
/>
)}
</AreaChart>
</ResponsiveContainer>
</div>
</div>
);
}