Spaces:
Running
Running
add details gpu
Browse files- components/spaces/details.tsx +61 -0
- components/spaces/index.tsx +16 -3
- components/spaces/progress.tsx +19 -0
- components/spaces/space.tsx +11 -3
components/spaces/details.tsx
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use client";
|
2 |
+
import { useEffect, useState } from "react";
|
3 |
+
|
4 |
+
import { SpaceProps } from "@/utils/type";
|
5 |
+
import { GpuIcon } from "../gpu_icon";
|
6 |
+
import { Progress } from "./progress";
|
7 |
+
|
8 |
+
export const Details: React.FC<{ space: SpaceProps }> = ({ space }) => {
|
9 |
+
const [metric, setMetric] = useState<any>(null);
|
10 |
+
const [gpuNumber, setGpuNumber] = useState<number>(0);
|
11 |
+
|
12 |
+
useEffect(() => {
|
13 |
+
const esMetric = new EventSource(
|
14 |
+
`https://api.hf.space/v1/${space.id}/live-metrics/sse`
|
15 |
+
);
|
16 |
+
const esGpu = new EventSource(`https://api.hf.space/v1/${space.id}/sse`);
|
17 |
+
|
18 |
+
esGpu.addEventListener("zero-gpu-count", (event) => {
|
19 |
+
setGpuNumber(parseInt(event.data));
|
20 |
+
});
|
21 |
+
esMetric.addEventListener("metric", (event) => {
|
22 |
+
setMetric(JSON.parse(event.data));
|
23 |
+
});
|
24 |
+
|
25 |
+
return () => {
|
26 |
+
esMetric.close();
|
27 |
+
esGpu.close();
|
28 |
+
};
|
29 |
+
}, [space]);
|
30 |
+
|
31 |
+
return (
|
32 |
+
<div className="border border-gray-200 shadow-md bg-white p-4 rounded-lg absolute -bottom-2 translate-y-full left-0 z-[99999]">
|
33 |
+
<p className="text-sm text-gray-600 flex items-center justify-start gap-1 font-medium">
|
34 |
+
This Space is currently using{" "}
|
35 |
+
<span className="border border-emerald-200 bg-emerald-50 font-medium text-emerald-500 flex items-center justify-center gap-1 max-w-max rounded-md px-1 py-0.5 text-xs">
|
36 |
+
{gpuNumber} <GpuIcon />
|
37 |
+
</span>{" "}
|
38 |
+
GPU{gpuNumber > 1 && "s"}.
|
39 |
+
</p>
|
40 |
+
<div className="mt-3 space-y-1">
|
41 |
+
<div className="flex items-center justify-between font-mono text-[.8rem]">
|
42 |
+
<div className="flex flex-col">
|
43 |
+
<p className="font-semibold uppercase">CPU</p>{" "}
|
44 |
+
<p className="text-gray-400">{metric?.cpu_usage_pct}%</p>
|
45 |
+
</div>{" "}
|
46 |
+
<Progress usage={metric?.cpu_usage_pct} max={100} />
|
47 |
+
</div>
|
48 |
+
<div className="flex items-center justify-between font-mono text-[.8rem]">
|
49 |
+
<div className="flex flex-col">
|
50 |
+
<p className="font-semibold uppercase">RAM</p>{" "}
|
51 |
+
<p className="text-gray-400">{metric?.cpu_usage_pct}%</p>
|
52 |
+
</div>{" "}
|
53 |
+
<Progress
|
54 |
+
usage={Math.ceil(metric?.memory_used_bytes / 1_000_000_000)}
|
55 |
+
max={Math.ceil(metric?.memory_total_bytes / 1_000_000_000)}
|
56 |
+
/>
|
57 |
+
</div>
|
58 |
+
</div>
|
59 |
+
</div>
|
60 |
+
);
|
61 |
+
};
|
components/spaces/index.tsx
CHANGED
@@ -1,9 +1,8 @@
|
|
1 |
"use client";
|
2 |
-
import {
|
3 |
|
4 |
import { SpaceProps } from "@/utils/type";
|
5 |
import { SpaceIcon } from "@/components/space_icon";
|
6 |
-
import { fetchAllPages, sortByCreatedAt, sortByLikes } from "@/utils";
|
7 |
|
8 |
import { Space } from "@/components/spaces/space";
|
9 |
import { Sort } from "@/components/sort";
|
@@ -11,6 +10,7 @@ import { useUpdateEffect } from "react-use";
|
|
11 |
import { useRouter } from "next/navigation";
|
12 |
|
13 |
export const Spaces: React.FC<{ spaces: SpaceProps[] }> = ({ spaces }) => {
|
|
|
14 |
const router = useRouter();
|
15 |
const [sort, setSort] = useState<"likes" | "createdAt">("likes");
|
16 |
|
@@ -18,6 +18,14 @@ export const Spaces: React.FC<{ spaces: SpaceProps[] }> = ({ spaces }) => {
|
|
18 |
router.push("/?sort=" + sort);
|
19 |
}, [sort]);
|
20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
return (
|
22 |
<>
|
23 |
<header className="max-w-5xl mx-auto w-full max-lg:flex-col items-start flex lg:items-end lg:justify-between gap-4">
|
@@ -37,7 +45,12 @@ export const Spaces: React.FC<{ spaces: SpaceProps[] }> = ({ spaces }) => {
|
|
37 |
</header>
|
38 |
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
|
39 |
{spaces?.map((space: SpaceProps) => (
|
40 |
-
<Space
|
|
|
|
|
|
|
|
|
|
|
41 |
))}
|
42 |
</div>
|
43 |
</>
|
|
|
1 |
"use client";
|
2 |
+
import { useState } from "react";
|
3 |
|
4 |
import { SpaceProps } from "@/utils/type";
|
5 |
import { SpaceIcon } from "@/components/space_icon";
|
|
|
6 |
|
7 |
import { Space } from "@/components/spaces/space";
|
8 |
import { Sort } from "@/components/sort";
|
|
|
10 |
import { useRouter } from "next/navigation";
|
11 |
|
12 |
export const Spaces: React.FC<{ spaces: SpaceProps[] }> = ({ spaces }) => {
|
13 |
+
const [selectedGpu, setSelectedGpu] = useState<string | undefined>(undefined);
|
14 |
const router = useRouter();
|
15 |
const [sort, setSort] = useState<"likes" | "createdAt">("likes");
|
16 |
|
|
|
18 |
router.push("/?sort=" + sort);
|
19 |
}, [sort]);
|
20 |
|
21 |
+
const handleSelectGpu = (gpu: string) => {
|
22 |
+
if (selectedGpu === gpu) {
|
23 |
+
setSelectedGpu(undefined);
|
24 |
+
return;
|
25 |
+
}
|
26 |
+
setSelectedGpu(gpu);
|
27 |
+
};
|
28 |
+
|
29 |
return (
|
30 |
<>
|
31 |
<header className="max-w-5xl mx-auto w-full max-lg:flex-col items-start flex lg:items-end lg:justify-between gap-4">
|
|
|
45 |
</header>
|
46 |
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
|
47 |
{spaces?.map((space: SpaceProps) => (
|
48 |
+
<Space
|
49 |
+
key={space.id}
|
50 |
+
space={space}
|
51 |
+
selected={selectedGpu === space.id}
|
52 |
+
onSelect={handleSelectGpu}
|
53 |
+
/>
|
54 |
))}
|
55 |
</div>
|
56 |
</>
|
components/spaces/progress.tsx
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useMemo } from "react";
|
2 |
+
|
3 |
+
export const Progress: React.FC<{ usage: number; max: number }> = ({
|
4 |
+
usage,
|
5 |
+
max,
|
6 |
+
}) => {
|
7 |
+
const percentage = useMemo(() => {
|
8 |
+
return (100 * usage) / max;
|
9 |
+
}, [usage, max]);
|
10 |
+
|
11 |
+
return (
|
12 |
+
<div className="flex h-8 w-3 flex-col-reverse overflow-hidden rounded-[.1rem] bg-gray-200">
|
13 |
+
<div
|
14 |
+
className="border-green-600 bg-green-600/40 shadow-[inset_0_2px_0_rgb(5,150,105)] transition-[height] dark:bg-green-800"
|
15 |
+
style={{ height: `${percentage.toFixed(0)}%` }}
|
16 |
+
></div>
|
17 |
+
</div>
|
18 |
+
);
|
19 |
+
};
|
components/spaces/space.tsx
CHANGED
@@ -1,19 +1,26 @@
|
|
|
|
1 |
import Image from "next/image";
|
2 |
import { TiHeartFullOutline } from "react-icons/ti";
|
|
|
|
|
3 |
|
4 |
import { SpaceProps } from "@/utils/type";
|
5 |
-
import Link from "next/link";
|
6 |
import { GpuIcon } from "../gpu_icon";
|
|
|
7 |
|
8 |
interface Props {
|
9 |
space: SpaceProps;
|
|
|
|
|
10 |
}
|
11 |
-
export const Space: React.FC<Props> = ({ space }) => {
|
12 |
return (
|
13 |
<Link
|
14 |
href={`https://huggingface.co/spaces/${space.id}`}
|
15 |
target="_blank"
|
16 |
-
className="bg-gray-50 border border-gray-200 px-6 py-4 rounded-xl transition-all duration-300 hover:-translate-y-1.5 hover:border-amber-400 hover:shadow-xl hover:shadow-black/5 group"
|
|
|
|
|
17 |
>
|
18 |
<header className="flex items-center justify-between gap-5">
|
19 |
<div>
|
@@ -68,6 +75,7 @@ export const Space: React.FC<Props> = ({ space }) => {
|
|
68 |
</div>
|
69 |
</div>
|
70 |
</header>
|
|
|
71 |
</Link>
|
72 |
);
|
73 |
};
|
|
|
1 |
+
import Link from "next/link";
|
2 |
import Image from "next/image";
|
3 |
import { TiHeartFullOutline } from "react-icons/ti";
|
4 |
+
import { FiChevronRight } from "react-icons/fi";
|
5 |
+
import classNames from "classnames";
|
6 |
|
7 |
import { SpaceProps } from "@/utils/type";
|
|
|
8 |
import { GpuIcon } from "../gpu_icon";
|
9 |
+
import { Details } from "./details";
|
10 |
|
11 |
interface Props {
|
12 |
space: SpaceProps;
|
13 |
+
selected?: boolean;
|
14 |
+
onSelect?: (gpu: string) => void;
|
15 |
}
|
16 |
+
export const Space: React.FC<Props> = ({ space, selected, onSelect }) => {
|
17 |
return (
|
18 |
<Link
|
19 |
href={`https://huggingface.co/spaces/${space.id}`}
|
20 |
target="_blank"
|
21 |
+
className="relative hover:z-40 bg-gray-50 border border-gray-200 px-6 py-4 rounded-xl transition-all duration-300 hover:-translate-y-1.5 hover:border-amber-400 hover:shadow-xl hover:shadow-black/5 group"
|
22 |
+
onMouseEnter={() => onSelect?.(space.id)}
|
23 |
+
onMouseLeave={() => onSelect?.(space.id)}
|
24 |
>
|
25 |
<header className="flex items-center justify-between gap-5">
|
26 |
<div>
|
|
|
75 |
</div>
|
76 |
</div>
|
77 |
</header>
|
78 |
+
{selected && <Details space={space} />}
|
79 |
</Link>
|
80 |
);
|
81 |
};
|