Spaces:
Running
Running
add dark theme
Browse files- app/layout.tsx +4 -1
- app/page.tsx +0 -1
- assets/globals.css +1 -1
- components/sort.tsx +7 -5
- components/spaces/details.tsx +2 -2
- components/spaces/index.tsx +26 -8
- components/spaces/space.tsx +7 -7
- package-lock.json +10 -0
- package.json +1 -0
- tailwind.config.ts +1 -0
app/layout.tsx
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
import type { Metadata } from "next";
|
2 |
import { Inter } from "next/font/google";
|
|
|
3 |
import "@/assets/globals.css";
|
4 |
|
5 |
const inter = Inter({ subsets: ["latin"] });
|
@@ -15,7 +16,9 @@ export default function RootLayout({
|
|
15 |
}>) {
|
16 |
return (
|
17 |
<html lang="en">
|
18 |
-
<body className={inter.className}>
|
|
|
|
|
19 |
</html>
|
20 |
);
|
21 |
}
|
|
|
1 |
import type { Metadata } from "next";
|
2 |
import { Inter } from "next/font/google";
|
3 |
+
import { ThemeProvider } from "next-themes";
|
4 |
import "@/assets/globals.css";
|
5 |
|
6 |
const inter = Inter({ subsets: ["latin"] });
|
|
|
16 |
}>) {
|
17 |
return (
|
18 |
<html lang="en">
|
19 |
+
<body className={inter.className}>
|
20 |
+
<ThemeProvider attribute="class">{children}</ThemeProvider>
|
21 |
+
</body>
|
22 |
</html>
|
23 |
);
|
24 |
}
|
app/page.tsx
CHANGED
@@ -1,7 +1,6 @@
|
|
1 |
import { fetchAllPages } from "@/utils";
|
2 |
import { SpaceProps } from "@/utils/type";
|
3 |
import { Spaces } from "@/components/spaces";
|
4 |
-
import { Suspense } from "react";
|
5 |
|
6 |
export const revalidate = 120;
|
7 |
|
|
|
1 |
import { fetchAllPages } from "@/utils";
|
2 |
import { SpaceProps } from "@/utils/type";
|
3 |
import { Spaces } from "@/components/spaces";
|
|
|
4 |
|
5 |
export const revalidate = 120;
|
6 |
|
assets/globals.css
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
@tailwind utilities;
|
4 |
|
5 |
body {
|
6 |
-
@apply bg-gray-100 overflow-hidden;
|
7 |
}
|
8 |
|
9 |
@layer utilities {
|
|
|
3 |
@tailwind utilities;
|
4 |
|
5 |
body {
|
6 |
+
@apply bg-gray-100 overflow-hidden dark:bg-slate-950;
|
7 |
}
|
8 |
|
9 |
@layer utilities {
|
components/sort.tsx
CHANGED
@@ -8,12 +8,13 @@ interface Props {
|
|
8 |
}
|
9 |
export const Sort = ({ value, onChange }: Props) => {
|
10 |
return (
|
11 |
-
<div className="flex items-center justify-center border-[3px] rounded-full border-gray-50 drop-shadow-sm bg-gray-100 ring-[1px] ring-gray-200 gap-1">
|
12 |
<button
|
13 |
className={classNames(
|
14 |
-
"rounded-full pl-3 pr-4 py-2.5 transition-all duration-200 font-semibold text-xs hover:bg-gray-200/70 flex items-center justify-center gap-2",
|
15 |
{
|
16 |
-
"bg-black hover:!bg-black text-white":
|
|
|
17 |
}
|
18 |
)}
|
19 |
onClick={() => onChange("likes")}
|
@@ -23,9 +24,10 @@ export const Sort = ({ value, onChange }: Props) => {
|
|
23 |
</button>
|
24 |
<button
|
25 |
className={classNames(
|
26 |
-
"rounded-full pl-3 pr-4 py-2.5 transition-all duration-200 font-semibold text-xs hover:bg-gray-200/70 flex items-center justify-center gap-2",
|
27 |
{
|
28 |
-
"bg-black hover:!bg-black text-white":
|
|
|
29 |
}
|
30 |
)}
|
31 |
onClick={() => onChange("createdAt")}
|
|
|
8 |
}
|
9 |
export const Sort = ({ value, onChange }: Props) => {
|
10 |
return (
|
11 |
+
<div className="flex items-center justify-center border-[3px] rounded-full border-gray-50 drop-shadow-sm bg-gray-100 dark:bg-slate-900 dark:border-slate-800 ring-[1px] ring-gray-200 dark:ring-slate-700 gap-1">
|
12 |
<button
|
13 |
className={classNames(
|
14 |
+
"rounded-full pl-3 pr-4 py-2.5 transition-all duration-200 font-semibold text-xs hover:bg-gray-200/70 dark:hover:bg-gray-700/70 flex items-center justify-center gap-2",
|
15 |
{
|
16 |
+
"bg-black hover:!bg-black dark:bg-slate-950 dark:hover:!bg-slate-950 text-white":
|
17 |
+
value === "likes",
|
18 |
}
|
19 |
)}
|
20 |
onClick={() => onChange("likes")}
|
|
|
24 |
</button>
|
25 |
<button
|
26 |
className={classNames(
|
27 |
+
"rounded-full pl-3 pr-4 py-2.5 transition-all duration-200 font-semibold text-xs hover:bg-gray-200/70 dark:hover:bg-gray-700/70 flex items-center justify-center gap-2",
|
28 |
{
|
29 |
+
"bg-black hover:!bg-black dark:bg-slate-950 dark:hover:!bg-slate-950 text-white":
|
30 |
+
value === "createdAt",
|
31 |
}
|
32 |
)}
|
33 |
onClick={() => onChange("createdAt")}
|
components/spaces/details.tsx
CHANGED
@@ -29,8 +29,8 @@ export const Details: React.FC<{ space: SpaceProps }> = ({ space }) => {
|
|
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 />
|
|
|
29 |
}, [space]);
|
30 |
|
31 |
return (
|
32 |
+
<div className="border border-gray-200 shadow-md bg-white dark:bg-slate-900 dark:border-slate-800 p-4 rounded-lg absolute -bottom-2 translate-y-full left-0 z-[99999]">
|
33 |
+
<p className="text-sm text-gray-600 dark:text-slate-500 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 />
|
components/spaces/index.tsx
CHANGED
@@ -1,7 +1,9 @@
|
|
1 |
"use client";
|
2 |
-
import { useState } from "react";
|
3 |
-
import { useUpdateEffect } from "react-use";
|
4 |
import { useRouter } from "next/navigation";
|
|
|
|
|
5 |
|
6 |
import { SpaceProps } from "@/utils/type";
|
7 |
import { SpaceIcon } from "@/components/space_icon";
|
@@ -10,10 +12,12 @@ import { Space } from "@/components/spaces/space";
|
|
10 |
import { Sort } from "@/components/sort";
|
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 |
-
const [elementsInView, setElementsInView] = useState<string[]>([]);
|
17 |
|
18 |
useUpdateEffect(() => {
|
19 |
router.push("/?sort=" + sort);
|
@@ -32,14 +36,28 @@ export const Spaces: React.FC<{ spaces: SpaceProps[] }> = ({ spaces }) => {
|
|
32 |
<div className="w-full container px-6 py-10 lg:py-20 mx-auto space-y-10 lg:space-y-14">
|
33 |
<header className="max-w-5xl mx-auto w-full max-lg:flex-col items-start flex lg:items-end lg:justify-between gap-4">
|
34 |
<div>
|
35 |
-
<div className="mb-6
|
36 |
-
<
|
37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
</div>
|
39 |
-
<h1 className="font-extrabold text-3xl text-black">
|
40 |
Zero GPU Spaces
|
41 |
</h1>
|
42 |
-
<p className="text-neutral-500 font-regular text-base">
|
43 |
Discover spaces with zero GPU usage on 🤗 Hugging Face Spaces.
|
44 |
</p>
|
45 |
</div>
|
|
|
1 |
"use client";
|
2 |
+
import { useEffect, useState } from "react";
|
3 |
+
import { useLocalStorage, useUpdateEffect } from "react-use";
|
4 |
import { useRouter } from "next/navigation";
|
5 |
+
import { useTheme } from "next-themes";
|
6 |
+
import { MdDarkMode, MdLightMode } from "react-icons/md";
|
7 |
|
8 |
import { SpaceProps } from "@/utils/type";
|
9 |
import { SpaceIcon } from "@/components/space_icon";
|
|
|
12 |
import { Sort } from "@/components/sort";
|
13 |
|
14 |
export const Spaces: React.FC<{ spaces: SpaceProps[] }> = ({ spaces }) => {
|
15 |
+
const { systemTheme, theme, setTheme } = useTheme();
|
16 |
+
const currentTheme = theme === "system" ? systemTheme : theme;
|
17 |
+
|
18 |
const [selectedGpu, setSelectedGpu] = useState<string | undefined>(undefined);
|
19 |
const router = useRouter();
|
20 |
const [sort, setSort] = useState<"likes" | "createdAt">("likes");
|
|
|
21 |
|
22 |
useUpdateEffect(() => {
|
23 |
router.push("/?sort=" + sort);
|
|
|
36 |
<div className="w-full container px-6 py-10 lg:py-20 mx-auto space-y-10 lg:space-y-14">
|
37 |
<header className="max-w-5xl mx-auto w-full max-lg:flex-col items-start flex lg:items-end lg:justify-between gap-4">
|
38 |
<div>
|
39 |
+
<div className="mb-6 flex items-center justify-start gap-2.5">
|
40 |
+
<button
|
41 |
+
className="flex items-center justify-center w-9 h-9 rounded-full border border-gray-200 bg-gray-50 dark:bg-slate-900 dark:border-slate-800 transition-all duration-200 hover:brightness-125"
|
42 |
+
onClick={() =>
|
43 |
+
theme == "dark" ? setTheme("light") : setTheme("dark")
|
44 |
+
}
|
45 |
+
>
|
46 |
+
{theme === "light" ? (
|
47 |
+
<MdDarkMode className="w-6 h-6 text-purple-600" />
|
48 |
+
) : (
|
49 |
+
<MdLightMode className="w-6 h-6 text-amber-500" />
|
50 |
+
)}
|
51 |
+
</button>
|
52 |
+
<div className="font-regular text-xs text-center max-w-max rounded-full border-gray-200 bg-gray-50 border text-gray-700 dark:text-slate-400 dark:bg-slate-900 dark:border-slate-800 px-3 py-2 transition-all duration-300">
|
53 |
+
<SpaceIcon className="inline-block w-4 h-4 mr-2 drop-shadow-lg" />
|
54 |
+
Browse {spaces.length} spaces
|
55 |
+
</div>
|
56 |
</div>
|
57 |
+
<h1 className="font-extrabold text-3xl text-black dark:text-slate-100">
|
58 |
Zero GPU Spaces
|
59 |
</h1>
|
60 |
+
<p className="text-neutral-500 font-regular text-base dark:text-slate-300">
|
61 |
Discover spaces with zero GPU usage on 🤗 Hugging Face Spaces.
|
62 |
</p>
|
63 |
</div>
|
components/spaces/space.tsx
CHANGED
@@ -1,8 +1,6 @@
|
|
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";
|
@@ -18,16 +16,18 @@ export const Space: React.FC<Props> = ({ space, selected, onSelect }) => {
|
|
18 |
<Link
|
19 |
href={`https://huggingface.co/spaces/${space.id}`}
|
20 |
target="_blank"
|
21 |
-
className="space 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 |
data-space-id={space.id}
|
23 |
onMouseEnter={() => onSelect?.(space.id)}
|
24 |
onMouseLeave={() => onSelect?.(space.id)}
|
25 |
>
|
26 |
<header className="flex items-center justify-between gap-5">
|
27 |
<div>
|
28 |
-
<h2 className="font-semibold text-black">
|
|
|
|
|
29 |
{space.shortDescription && (
|
30 |
-
<p className="text-gray-500 text-xs mt-0.5 line-clamp-1">
|
31 |
{space.shortDescription}
|
32 |
</p>
|
33 |
)}
|
@@ -48,7 +48,7 @@ export const Space: React.FC<Props> = ({ space, selected, onSelect }) => {
|
|
48 |
className="rounded-full w-4 h-4"
|
49 |
/>
|
50 |
<div>
|
51 |
-
<p className="text-gray-500 text-xs font-regular">
|
52 |
{space.authorData.name}
|
53 |
</p>
|
54 |
</div>
|
@@ -56,7 +56,7 @@ export const Space: React.FC<Props> = ({ space, selected, onSelect }) => {
|
|
56 |
</div>
|
57 |
<div className="space-y-3 flex flex-col items-end">
|
58 |
<div
|
59 |
-
className="rounded-full truncate min-w-[2.5rem] min-h-[2.5rem] max-w-[2.5rem] max-h-[2.5rem] overflow-hidden flex items-center justify-center bg-gradient-to-br from-blue-500 to-red-500 border-[2px] border-white ring-1 ring-gray-200"
|
60 |
style={{
|
61 |
// @ts-ignore
|
62 |
"--tw-gradient-from": `${space.colorFrom} var(--tw-gradient-to-position)`,
|
|
|
1 |
import Link from "next/link";
|
2 |
import Image from "next/image";
|
3 |
import { TiHeartFullOutline } from "react-icons/ti";
|
|
|
|
|
4 |
|
5 |
import { SpaceProps } from "@/utils/type";
|
6 |
import { GpuIcon } from "../gpu_icon";
|
|
|
16 |
<Link
|
17 |
href={`https://huggingface.co/spaces/${space.id}`}
|
18 |
target="_blank"
|
19 |
+
className="space relative hover:z-40 bg-gray-50 border border-gray-200 dark:bg-slate-900 dark:border-slate-800 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"
|
20 |
data-space-id={space.id}
|
21 |
onMouseEnter={() => onSelect?.(space.id)}
|
22 |
onMouseLeave={() => onSelect?.(space.id)}
|
23 |
>
|
24 |
<header className="flex items-center justify-between gap-5">
|
25 |
<div>
|
26 |
+
<h2 className="font-semibold text-black dark:text-slate-100">
|
27 |
+
{space?.title}
|
28 |
+
</h2>
|
29 |
{space.shortDescription && (
|
30 |
+
<p className="text-gray-500 dark:text-slate-400 text-xs mt-0.5 line-clamp-1">
|
31 |
{space.shortDescription}
|
32 |
</p>
|
33 |
)}
|
|
|
48 |
className="rounded-full w-4 h-4"
|
49 |
/>
|
50 |
<div>
|
51 |
+
<p className="text-gray-500 dark:text-slate-400 text-xs font-regular">
|
52 |
{space.authorData.name}
|
53 |
</p>
|
54 |
</div>
|
|
|
56 |
</div>
|
57 |
<div className="space-y-3 flex flex-col items-end">
|
58 |
<div
|
59 |
+
className="rounded-full truncate min-w-[2.5rem] min-h-[2.5rem] max-w-[2.5rem] max-h-[2.5rem] overflow-hidden flex items-center justify-center bg-gradient-to-br from-blue-500 to-red-500 border-[2px] border-white ring-1 ring-gray-200 dark:border-slate-900 dark:ring-slate-700"
|
60 |
style={{
|
61 |
// @ts-ignore
|
62 |
"--tw-gradient-from": `${space.colorFrom} var(--tw-gradient-to-position)`,
|
package-lock.json
CHANGED
@@ -10,6 +10,7 @@
|
|
10 |
"dependencies": {
|
11 |
"classnames": "^2.5.1",
|
12 |
"next": "14.2.3",
|
|
|
13 |
"react": "^18",
|
14 |
"react-dom": "^18",
|
15 |
"react-icons": "^5.1.0",
|
@@ -3264,6 +3265,15 @@
|
|
3264 |
}
|
3265 |
}
|
3266 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3267 |
"node_modules/next/node_modules/postcss": {
|
3268 |
"version": "8.4.31",
|
3269 |
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
|
|
10 |
"dependencies": {
|
11 |
"classnames": "^2.5.1",
|
12 |
"next": "14.2.3",
|
13 |
+
"next-themes": "^0.3.0",
|
14 |
"react": "^18",
|
15 |
"react-dom": "^18",
|
16 |
"react-icons": "^5.1.0",
|
|
|
3265 |
}
|
3266 |
}
|
3267 |
},
|
3268 |
+
"node_modules/next-themes": {
|
3269 |
+
"version": "0.3.0",
|
3270 |
+
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz",
|
3271 |
+
"integrity": "sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==",
|
3272 |
+
"peerDependencies": {
|
3273 |
+
"react": "^16.8 || ^17 || ^18",
|
3274 |
+
"react-dom": "^16.8 || ^17 || ^18"
|
3275 |
+
}
|
3276 |
+
},
|
3277 |
"node_modules/next/node_modules/postcss": {
|
3278 |
"version": "8.4.31",
|
3279 |
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
package.json
CHANGED
@@ -11,6 +11,7 @@
|
|
11 |
"dependencies": {
|
12 |
"classnames": "^2.5.1",
|
13 |
"next": "14.2.3",
|
|
|
14 |
"react": "^18",
|
15 |
"react-dom": "^18",
|
16 |
"react-icons": "^5.1.0",
|
|
|
11 |
"dependencies": {
|
12 |
"classnames": "^2.5.1",
|
13 |
"next": "14.2.3",
|
14 |
+
"next-themes": "^0.3.0",
|
15 |
"react": "^18",
|
16 |
"react-dom": "^18",
|
17 |
"react-icons": "^5.1.0",
|
tailwind.config.ts
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
import type { Config } from "tailwindcss";
|
2 |
|
3 |
const config: Config = {
|
|
|
4 |
content: [
|
5 |
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
6 |
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
|
|
1 |
import type { Config } from "tailwindcss";
|
2 |
|
3 |
const config: Config = {
|
4 |
+
darkMode: "class",
|
5 |
content: [
|
6 |
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
7 |
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|