enzostvs HF staff commited on
Commit
f70bb71
1 Parent(s): d2a5dad

initial commit

Browse files
Dockerfile ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dockerfile
2
+ # Use an official Node.js runtime as the base image
3
+ FROM node:18
4
+
5
+ # Set the working directory in the container
6
+ WORKDIR /usr/src/app
7
+
8
+ # Copy package.json and package-lock.json to the container
9
+ COPY package.json package-lock.json ./
10
+
11
+ # Install dependencies
12
+ RUN npm install
13
+
14
+ # Copy the rest of the application files to the container
15
+ COPY . .
16
+
17
+ # Build the Next.js application for production
18
+ RUN npm run build
19
+
20
+ # Expose the application port (assuming your app runs on port 3000)
21
+ EXPOSE 3000
22
+
23
+ # Start the application
24
+ CMD ["npm", "start"]
README.md CHANGED
@@ -1,36 +1,12 @@
1
- This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2
-
3
- ## Getting Started
4
-
5
- First, run the development server:
6
-
7
- ```bash
8
- npm run dev
9
- # or
10
- yarn dev
11
- # or
12
- pnpm dev
13
- # or
14
- bun dev
15
- ```
16
-
17
- Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18
-
19
- You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20
-
21
- This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22
-
23
- ## Learn More
24
-
25
- To learn more about Next.js, take a look at the following resources:
26
-
27
- - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28
- - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29
-
30
- You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31
-
32
- ## Deploy on Vercel
33
-
34
- The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35
-
36
- Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
 
1
+ ---
2
+ title: Space Shuffler
3
+ emoji: 🔍♦️
4
+ colorFrom: blue
5
+ colorTo: pink
6
+ sdk: docker
7
+ pinned: false
8
+ short_description: Find hidden gems from all HF Spaces
9
+ license: mit
10
+ ---
11
+
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/actions.ts ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ "use server"
2
+
3
+ import { fetchSpaceRandomly } from "@/utils/network"
4
+
5
+ export const getSpace = async () => {
6
+ const space = await fetchSpaceRandomly();
7
+ return space;
8
+ }
app/layout.tsx CHANGED
@@ -1,22 +1,36 @@
1
- import type { Metadata } from "next";
2
- import { Inter } from "next/font/google";
3
- import "./globals.css";
4
 
5
- const inter = Inter({ subsets: ["latin"] });
 
6
 
7
- export const metadata: Metadata = {
8
- title: "Create Next App",
9
- description: "Generated by create next app",
10
- };
 
11
 
12
  export default function RootLayout({
13
  children,
14
- }: Readonly<{
15
  children: React.ReactNode;
16
- }>) {
17
  return (
18
- <html lang="en">
19
- <body className={inter.className}>{children}</body>
20
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  );
22
  }
 
1
+ import { Inter, Montserrat } from "next/font/google";
 
 
2
 
3
+ import { ThemeProvider } from "@/components/theme-provider";
4
+ import "@/style/globals.css";
5
 
6
+ const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
7
+ const montserrat = Montserrat({
8
+ subsets: ["latin"],
9
+ variable: "--font-montserrat",
10
+ });
11
 
12
  export default function RootLayout({
13
  children,
14
+ }: {
15
  children: React.ReactNode;
16
+ }) {
17
  return (
18
+ <>
19
+ <html lang="en" suppressHydrationWarning>
20
+ <head />
21
+ <body
22
+ className={`${inter.variable} ${montserrat.variable} bg-grid-small-white/5 font-sans`}
23
+ >
24
+ <ThemeProvider
25
+ attribute="class"
26
+ defaultTheme="system"
27
+ enableSystem
28
+ disableTransitionOnChange
29
+ >
30
+ {children}
31
+ </ThemeProvider>
32
+ </body>
33
+ </html>
34
+ </>
35
  );
36
  }
app/page.tsx CHANGED
@@ -1,112 +1,34 @@
1
  import Image from "next/image";
 
 
 
 
 
 
 
 
 
 
2
 
3
- export default function Home() {
4
  return (
5
- <main className="flex min-h-screen flex-col items-center justify-between p-24">
6
- <div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">
7
- <p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
8
- Get started by editing&nbsp;
9
- <code className="font-mono font-bold">app/page.tsx</code>
10
- </p>
11
- <div className="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:size-auto lg:bg-none">
12
- <a
13
- className="pointer-events-none flex place-items-center gap-2 p-8 lg:pointer-events-auto lg:p-0"
14
- href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
15
- target="_blank"
16
- rel="noopener noreferrer"
17
- >
18
- By{" "}
19
  <Image
20
- src="/vercel.svg"
21
- alt="Vercel Logo"
22
- className="dark:invert"
23
- width={100}
24
- height={24}
25
- priority
26
  />
27
- </a>
28
- </div>
29
- </div>
30
-
31
- <div className="relative z-[-1] flex place-items-center before:absolute before:h-[300px] before:w-full before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-full after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 sm:before:w-[480px] sm:after:w-[240px] before:lg:h-[360px]">
32
- <Image
33
- className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
34
- src="/next.svg"
35
- alt="Next.js Logo"
36
- width={180}
37
- height={37}
38
- priority
39
- />
40
- </div>
41
-
42
- <div className="mb-32 grid text-center lg:mb-0 lg:w-full lg:max-w-5xl lg:grid-cols-4 lg:text-left">
43
- <a
44
- href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
45
- className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
46
- target="_blank"
47
- rel="noopener noreferrer"
48
- >
49
- <h2 className="mb-3 text-2xl font-semibold">
50
- Docs{" "}
51
- <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
52
- -&gt;
53
- </span>
54
- </h2>
55
- <p className="m-0 max-w-[30ch] text-sm opacity-50">
56
- Find in-depth information about Next.js features and API.
57
- </p>
58
- </a>
59
-
60
- <a
61
- href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
62
- className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
63
- target="_blank"
64
- rel="noopener noreferrer"
65
- >
66
- <h2 className="mb-3 text-2xl font-semibold">
67
- Learn{" "}
68
- <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
69
- -&gt;
70
- </span>
71
- </h2>
72
- <p className="m-0 max-w-[30ch] text-sm opacity-50">
73
- Learn about Next.js in an interactive course with&nbsp;quizzes!
74
- </p>
75
- </a>
76
-
77
- <a
78
- href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
79
- className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
80
- target="_blank"
81
- rel="noopener noreferrer"
82
- >
83
- <h2 className="mb-3 text-2xl font-semibold">
84
- Templates{" "}
85
- <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
86
- -&gt;
87
- </span>
88
- </h2>
89
- <p className="m-0 max-w-[30ch] text-sm opacity-50">
90
- Explore starter templates for Next.js.
91
- </p>
92
- </a>
93
-
94
- <a
95
- href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
96
- className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
97
- target="_blank"
98
- rel="noopener noreferrer"
99
- >
100
- <h2 className="mb-3 text-2xl font-semibold">
101
- Deploy{" "}
102
- <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
103
- -&gt;
104
- </span>
105
  </h2>
106
- <p className="m-0 max-w-[30ch] text-balance text-sm opacity-50">
107
- Instantly deploy your Next.js site to a shareable URL with Vercel.
108
- </p>
109
- </a>
110
  </div>
111
  </main>
112
  );
 
1
  import Image from "next/image";
2
+ import { Shuffle } from "lucide-react";
3
+
4
+ import CardsSvg from "@/images/cards.svg";
5
+ import { Space } from "@/components/space";
6
+ import { fetchSpaceRandomly } from "@/utils/network";
7
+ import { Shuffler } from "@/components/shuffler";
8
+
9
+ export default async function Home() {
10
+ const space = await fetchSpaceRandomly();
11
+ const nextSpace = await fetchSpaceRandomly();
12
 
 
13
  return (
14
+ <main className="flex min-h-screen flex-col items-center justify-center p-8 lg:p-24">
15
+ <div className="w-full max-w-xl mx-auto grid gap-16 lg:gap-24 grid-cols-1">
16
+ <header className="grid grid-cols-1 gap-5">
17
+ <h1 className="relative font-sans text-center text-5xl lg:text-7xl font-extrabold max-w-max mx-auto text-transparent bg-clip-text bg-flashy">
18
+ Space Shuffler
 
 
 
 
 
 
 
 
 
19
  <Image
20
+ src={CardsSvg}
21
+ alt="Cards"
22
+ width={70}
23
+ height={70}
24
+ className="w-10 lg:w-14 absolute -right-9 lg:-right-12 -top-5 lg:-top-6 -rotate-6"
 
25
  />
26
+ </h1>
27
+ <h2 className="font-serif text-white/60 text-xl lg:text-2xl text-center">
28
+ Find hidden gems from 180k Spaces
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  </h2>
30
+ </header>
31
+ <Shuffler space={space} nextSpace={nextSpace} />
 
 
32
  </div>
33
  </main>
34
  );
components/animate/card.tsx ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ motion,
3
+ useMotionValue,
4
+ useTransform,
5
+ AnimatePresence,
6
+ } from "framer-motion";
7
+ import { useState } from "react";
8
+
9
+ export const Card = ({ children, drag, index, setIndex, front }: any) => {
10
+ const [exitX, setExitX] = useState(0);
11
+
12
+ const x = useMotionValue(0);
13
+ const scale = useTransform(x, [-150, 0, 150], [0.5, 1, 0.5]);
14
+ const rotate = useTransform(x, [-150, 0, 150], [-45, 0, 45], {
15
+ clamp: false,
16
+ });
17
+
18
+ const variantsFrontCard = {
19
+ animate: { scale: 1, y: 0, opacity: 1 },
20
+ exit: (custom: number) => ({
21
+ x: custom,
22
+ opacity: 0,
23
+ scale: 0.5,
24
+ transition: { duration: 0.2 },
25
+ }),
26
+ };
27
+ const variantsBackCard = {
28
+ initial: { scale: 0, y: 105, opacity: 0 },
29
+ animate: { scale: 0.75, y: 30, opacity: 0 },
30
+ };
31
+
32
+ function handleDragEnd(_: any, info: any) {
33
+ if (info.offset.x < -100) {
34
+ setExitX(-250);
35
+ setIndex(index + 1);
36
+ }
37
+ if (info.offset.x > 100) {
38
+ setExitX(250);
39
+ setIndex(index + 1);
40
+ }
41
+ }
42
+
43
+ return (
44
+ <motion.div
45
+ style={{
46
+ position: "absolute",
47
+ width: "100%",
48
+ height: "100%",
49
+ top: 0,
50
+ x,
51
+ rotate,
52
+ }}
53
+ variants={front ? variantsFrontCard : variantsBackCard}
54
+ initial="initial"
55
+ animate="animate"
56
+ exit="exit"
57
+ custom={exitX}
58
+ transition={
59
+ front
60
+ ? { type: "spring", stiffness: 300, damping: 20 }
61
+ : { scale: { duration: 0.2 }, opacity: { duration: 0.4 } }
62
+ }
63
+ >
64
+ {children}
65
+ </motion.div>
66
+ );
67
+ };
components/shuffler/button.tsx ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Shuffle } from "lucide-react";
2
+
3
+ export const ButtonShuffler = ({ onClick }: { onClick: () => void }) => {
4
+ return (
5
+ <button
6
+ className="bg-white/90 hover:bg-white transition-all duration-200 rounded-full text-xl flex items-center justify-center gap-3 text-black px-8 py-5 font-bold"
7
+ onClick={onClick}
8
+ >
9
+ <Shuffle size={28} />
10
+ Shuffle
11
+ </button>
12
+ );
13
+ };
components/shuffler/index.tsx ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { motion, AnimatePresence } from "framer-motion";
5
+
6
+ import { getSpace } from "@/app/actions";
7
+ import { Space as SpaceProps } from "@/utils/types";
8
+ import { Space } from "@/components/space";
9
+
10
+ import { ButtonShuffler } from "./button";
11
+ import { Card } from "@/components/animate/card";
12
+
13
+ export const Shuffler = ({
14
+ space: initialSpace,
15
+ nextSpace: initialNextSpace,
16
+ }: {
17
+ space: SpaceProps;
18
+ nextSpace: SpaceProps;
19
+ }) => {
20
+ const [index, setIndex] = useState(0);
21
+ const [space, setSpace] = useState<SpaceProps>(
22
+ JSON.parse(JSON.stringify(initialSpace))
23
+ );
24
+ const [nextSpace, setNextSpace] = useState<SpaceProps>(
25
+ JSON.parse(JSON.stringify(initialNextSpace))
26
+ );
27
+
28
+ return (
29
+ <motion.div className="grid grid-cols-1 gap-10 relative">
30
+ <div className="relative w-full h-[350px]">
31
+ <AnimatePresence initial={false}>
32
+ <Card key={index + 1} front={false}>
33
+ <Space space={nextSpace} />
34
+ </Card>
35
+ <Card key={index} front={true} index={index} setIndex={setIndex}>
36
+ <Space space={space} />
37
+ </Card>
38
+ </AnimatePresence>
39
+ </div>
40
+ <div className="w-4 h-[1px] bg-white/50 mx-auto" />
41
+ <footer className="flex items-center justify-center">
42
+ <ButtonShuffler
43
+ onClick={() => {
44
+ getSpace().then((newSpace) => {
45
+ setSpace(nextSpace);
46
+ setNextSpace(newSpace);
47
+ setIndex(index + 1);
48
+ });
49
+ }}
50
+ />
51
+ </footer>
52
+ </motion.div>
53
+ );
54
+ };
components/space/index.tsx ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Link from "next/link";
2
+ import { ExternalLink } from "lucide-react";
3
+
4
+ import { Space as SpaceProps } from "@/utils/types";
5
+ import { SpaceAuthor } from "./sub/author";
6
+ import { SpaceHeader } from "./sub/header";
7
+
8
+ export const Space: React.FC<{ space: SpaceProps }> = ({ space }) => {
9
+ return (
10
+ <main className="w-full rounded-xl grid grid-cols-1 gap-3">
11
+ <SpaceHeader space={space} />
12
+ <div className="flex items-start justify-between">
13
+ <p className="text-[1.95rem] leading-[2rem] text-white/80 font-light">
14
+ {space?.title}
15
+ </p>
16
+ <Link
17
+ href={`https://huggingface.co/spaces/${space.id}`}
18
+ target="_blank"
19
+ >
20
+ <ExternalLink
21
+ size={24}
22
+ className="text-white/70 hover:text-blue-500"
23
+ />
24
+ </Link>
25
+ </div>
26
+ <SpaceAuthor author={space?.authorData} />
27
+ {space?.shortDescription && (
28
+ <p className="text-xl font-serif text-white/60 flex items-center justify-start gap-1.5 line-clamp-1">
29
+ Explore thousands of community trained LoRAs.
30
+ </p>
31
+ )}
32
+ </main>
33
+ );
34
+ };
components/space/sub/author.tsx ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { SpaceAuthorData } from "@/utils/types";
2
+ import Image from "next/image";
3
+
4
+ export const SpaceAuthor = ({ author }: { author: SpaceAuthorData }) => {
5
+ return (
6
+ <p className="text-xl font-serif text-white/60 flex items-center justify-start gap-1.5">
7
+ by
8
+ <Image
9
+ src={
10
+ author.avatarUrl?.startsWith("http")
11
+ ? author.avatarUrl
12
+ : `https://huggingface.co${author.avatarUrl}`
13
+ }
14
+ width={44}
15
+ height={44}
16
+ alt={`${author.fullname}'s avatar from Hugging Face`}
17
+ className="rounded-full w-6 h-6"
18
+ />
19
+ {author.name}
20
+ </p>
21
+ );
22
+ };
components/space/sub/header.tsx ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Heart } from "lucide-react";
2
+
3
+ import { Space } from "@/utils/types";
4
+
5
+ export const SpaceHeader = ({ space }: { space: Space }) => {
6
+ return (
7
+ <figure className="relative z-[1] h-[200px] px-6 py-4 mb-4">
8
+ <div
9
+ className={`bg-gradient-to-br ${space.colorFrom} ${space.colorTo} absolute w-full top-0 left-0 h-full rounded-2xl -z-[1]`}
10
+ />
11
+ <div
12
+ className={`bg-gradient-to-br ${space.colorFrom} ${space.colorTo} -z-[1] blur-lg opacity-50 h-full w-full absolute top-0 left-0`}
13
+ />
14
+ <div className="flex items-center justify-between w-full relative">
15
+ <p>tags</p>
16
+ <p className="flex items-center justify-end gap-1 text-sm text-white">
17
+ <Heart size={14} />
18
+ {space.likes}
19
+ </p>
20
+ </div>
21
+ <p className="absolute top-0 left-0 h-full w-full flex items-center justify-center opacity-90 text-7xl text-center whitespace-nowrap truncate">
22
+ {space.emoji}
23
+ </p>
24
+ </figure>
25
+ );
26
+ };
components/theme-provider.tsx ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { ThemeProvider as NextThemesProvider } from "next-themes";
5
+ import { type ThemeProviderProps } from "next-themes/dist/types";
6
+
7
+ export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
8
+ return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
9
+ }
components/toggle-mode.tsx ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useTheme } from "next-themes";
2
+
3
+ export const ToggleMode = () => {
4
+ const { setTheme } = useTheme();
5
+
6
+ return (
7
+ <div>
8
+ <button onClick={() => setTheme("light")}>Light Mode</button>
9
+ <button onClick={() => setTheme("dark")}>Dark Mode</button>
10
+ </div>
11
+ );
12
+ };
images/cards.svg ADDED
next.config.mjs CHANGED
@@ -1,4 +1,13 @@
1
  /** @type {import('next').NextConfig} */
2
- const nextConfig = {};
 
 
 
 
 
 
 
 
 
3
 
4
  export default nextConfig;
 
1
  /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ images: {
4
+ remotePatterns: [
5
+ {
6
+ protocol: "https",
7
+ hostname: "**"
8
+ }
9
+ ],
10
+ },
11
+ };
12
 
13
  export default nextConfig;
package-lock.json CHANGED
@@ -8,7 +8,11 @@
8
  "name": "spaces-randomizer",
9
  "version": "0.1.0",
10
  "dependencies": {
 
 
 
11
  "next": "14.2.6",
 
12
  "react": "^18",
13
  "react-dom": "^18"
14
  },
@@ -2082,6 +2086,30 @@
2082
  "url": "https://github.com/sponsors/isaacs"
2083
  }
2084
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2085
  "node_modules/fs.realpath": {
2086
  "version": "1.0.0",
2087
  "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -3057,6 +3085,14 @@
3057
  "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
3058
  "dev": true
3059
  },
 
 
 
 
 
 
 
 
3060
  "node_modules/merge2": {
3061
  "version": "1.4.1",
3062
  "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -3079,6 +3115,14 @@
3079
  "node": ">=8.6"
3080
  }
3081
  },
 
 
 
 
 
 
 
 
3082
  "node_modules/minimatch": {
3083
  "version": "3.1.2",
3084
  "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -3198,6 +3242,15 @@
3198
  }
3199
  }
3200
  },
 
 
 
 
 
 
 
 
 
3201
  "node_modules/next/node_modules/postcss": {
3202
  "version": "8.4.31",
3203
  "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
 
8
  "name": "spaces-randomizer",
9
  "version": "0.1.0",
10
  "dependencies": {
11
+ "framer-motion": "^11.3.29",
12
+ "lucide-react": "^0.429.0",
13
+ "mini-svg-data-uri": "^1.4.4",
14
  "next": "14.2.6",
15
+ "next-themes": "^0.3.0",
16
  "react": "^18",
17
  "react-dom": "^18"
18
  },
 
2086
  "url": "https://github.com/sponsors/isaacs"
2087
  }
2088
  },
2089
+ "node_modules/framer-motion": {
2090
+ "version": "11.3.29",
2091
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.3.29.tgz",
2092
+ "integrity": "sha512-uyDuUOeOElJEA3kbkbyoTNEf75Jih1EUg0ouLKYMlGDdt/LaJPmO+FyOGAGxM2HwKhHcAoKFNveR5A8peb7yhw==",
2093
+ "dependencies": {
2094
+ "tslib": "^2.4.0"
2095
+ },
2096
+ "peerDependencies": {
2097
+ "@emotion/is-prop-valid": "*",
2098
+ "react": "^18.0.0",
2099
+ "react-dom": "^18.0.0"
2100
+ },
2101
+ "peerDependenciesMeta": {
2102
+ "@emotion/is-prop-valid": {
2103
+ "optional": true
2104
+ },
2105
+ "react": {
2106
+ "optional": true
2107
+ },
2108
+ "react-dom": {
2109
+ "optional": true
2110
+ }
2111
+ }
2112
+ },
2113
  "node_modules/fs.realpath": {
2114
  "version": "1.0.0",
2115
  "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
 
3085
  "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
3086
  "dev": true
3087
  },
3088
+ "node_modules/lucide-react": {
3089
+ "version": "0.429.0",
3090
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.429.0.tgz",
3091
+ "integrity": "sha512-548DahFy7Ey+0OPlOyZ6ipnbGJzewv8gq2DxDrnAzWZ4RN4+3XyIwQXhD4AQvuREFjS1EnbAgOMaYB0VQyaK1g==",
3092
+ "peerDependencies": {
3093
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
3094
+ }
3095
+ },
3096
  "node_modules/merge2": {
3097
  "version": "1.4.1",
3098
  "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
 
3115
  "node": ">=8.6"
3116
  }
3117
  },
3118
+ "node_modules/mini-svg-data-uri": {
3119
+ "version": "1.4.4",
3120
+ "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
3121
+ "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
3122
+ "bin": {
3123
+ "mini-svg-data-uri": "cli.js"
3124
+ }
3125
+ },
3126
  "node_modules/minimatch": {
3127
  "version": "3.1.2",
3128
  "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
 
3242
  }
3243
  }
3244
  },
3245
+ "node_modules/next-themes": {
3246
+ "version": "0.3.0",
3247
+ "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz",
3248
+ "integrity": "sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==",
3249
+ "peerDependencies": {
3250
+ "react": "^16.8 || ^17 || ^18",
3251
+ "react-dom": "^16.8 || ^17 || ^18"
3252
+ }
3253
+ },
3254
  "node_modules/next/node_modules/postcss": {
3255
  "version": "8.4.31",
3256
  "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
package.json CHANGED
@@ -9,18 +9,22 @@
9
  "lint": "next lint"
10
  },
11
  "dependencies": {
 
 
 
 
 
12
  "react": "^18",
13
- "react-dom": "^18",
14
- "next": "14.2.6"
15
  },
16
  "devDependencies": {
17
- "typescript": "^5",
18
  "@types/node": "^20",
19
  "@types/react": "^18",
20
  "@types/react-dom": "^18",
 
 
21
  "postcss": "^8",
22
  "tailwindcss": "^3.4.1",
23
- "eslint": "^8",
24
- "eslint-config-next": "14.2.6"
25
  }
26
  }
 
9
  "lint": "next lint"
10
  },
11
  "dependencies": {
12
+ "framer-motion": "^11.3.29",
13
+ "lucide-react": "^0.429.0",
14
+ "mini-svg-data-uri": "^1.4.4",
15
+ "next": "14.2.6",
16
+ "next-themes": "^0.3.0",
17
  "react": "^18",
18
+ "react-dom": "^18"
 
19
  },
20
  "devDependencies": {
 
21
  "@types/node": "^20",
22
  "@types/react": "^18",
23
  "@types/react-dom": "^18",
24
+ "eslint": "^8",
25
+ "eslint-config-next": "14.2.6",
26
  "postcss": "^8",
27
  "tailwindcss": "^3.4.1",
28
+ "typescript": "^5"
 
29
  }
30
  }
{app → style}/globals.css RENAMED
File without changes
tailwind.config.ts CHANGED
@@ -1,10 +1,16 @@
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}",
7
  "./app/**/*.{js,ts,jsx,tsx,mdx}",
 
8
  ],
9
  theme: {
10
  extend: {
@@ -12,9 +18,40 @@ const config: Config = {
12
  "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
13
  "gradient-conic":
14
  "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
 
15
  },
 
 
 
 
16
  },
17
  },
18
- plugins: [],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  };
 
 
 
 
 
 
 
 
 
 
 
 
20
  export default config;
 
1
  import type { Config } from "tailwindcss";
2
+ const svgToDataUri = require("mini-svg-data-uri");
3
+
4
+ const {
5
+ default: flattenColorPalette,
6
+ } = require("tailwindcss/lib/util/flattenColorPalette");
7
 
8
  const config: Config = {
9
  content: [
10
  "./pages/**/*.{js,ts,jsx,tsx,mdx}",
11
  "./components/**/*.{js,ts,jsx,tsx,mdx}",
12
  "./app/**/*.{js,ts,jsx,tsx,mdx}",
13
+ "./utils/**/*.{js,ts,jsx,tsx,mdx}",
14
  ],
15
  theme: {
16
  extend: {
 
18
  "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
19
  "gradient-conic":
20
  "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
21
+ "flashy": "linear-gradient(90deg, #2B90FF 0%, #861FFF 55.81%, #FF3270 93.33%, #FFD702 100%)",
22
  },
23
+ fontFamily: {
24
+ sans: ['var(--font-inter)'],
25
+ serif: ['var(--font-montserrat)'],
26
+ }
27
  },
28
  },
29
+ plugins: [
30
+ addVariablesForColors,
31
+ function ({ matchUtilities, theme }: any) {
32
+ matchUtilities(
33
+ {
34
+ "bg-grid-small": (value: string) => ({
35
+ backgroundImage: `url("${svgToDataUri(
36
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="44" height="44" fill="none" stroke="${value}"><path d="M0 .5H31.5V32"/></svg>`
37
+ )}")`,
38
+ }),
39
+ },
40
+ { values: flattenColorPalette(theme("backgroundColor")), type: "color" }
41
+ );
42
+ },
43
+ ],
44
  };
45
+
46
+ function addVariablesForColors({ addBase, theme }: any ) {
47
+ let allColors = flattenColorPalette(theme("colors"));
48
+ let newVars = Object.fromEntries(
49
+ Object.entries(allColors).map(([key, val]) => [`--${key}`, val])
50
+ );
51
+
52
+ addBase({
53
+ ":root": newVars,
54
+ });
55
+ }
56
+
57
  export default config;
utils/network.ts ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const COLORS = {
2
+ from: {
3
+ purple: "from-purple-500",
4
+ pink: "from-pink-500",
5
+ gray: "from-gray-700",
6
+ blue: "from-blue-500",
7
+ green: "from-green-500",
8
+ yellow: "from-yellow-500",
9
+ indigo: "from-indigo-500",
10
+ },
11
+ to: {
12
+ purple: "to-purple-500",
13
+ pink: "to-pink-500",
14
+ gray: "to-gray-700",
15
+ blue: "to-blue-500",
16
+ green: "to-green-500",
17
+ yellow: "to-yellow-500",
18
+ indigo: "to-indigo-500",
19
+ },
20
+ };
21
+
22
+ export const fetchSpaceRandomly = async () => {
23
+ const randomPage = Math.floor(Math.random() * 100) + 1;
24
+ const url = `https://huggingface.co/spaces-json?p=${randomPage}&runtime.stage=RUNNING&sort=trending`;
25
+ const response = await fetch(url);
26
+ const json = await response.json();
27
+ const spaces = json?.spaces;
28
+
29
+ if (!spaces) {
30
+ return null;
31
+ }
32
+
33
+ const randomIndex = Math.floor(Math.random() * spaces.length);
34
+
35
+ const space = {
36
+ ...spaces[randomIndex],
37
+ colorFrom: COLORS.from[spaces[randomIndex].colorFrom as keyof typeof COLORS.from] || COLORS.from.purple,
38
+ colorTo: COLORS.to[spaces[randomIndex].colorTo as keyof typeof COLORS.from] || COLORS.to.pink,
39
+ }
40
+ return space;
41
+ }
utils/types.ts ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export interface Space {
2
+ id: string;
3
+ name: string;
4
+ description: string;
5
+ likes: number;
6
+ author: string;
7
+ title: string,
8
+ emoji: string,
9
+ colorFrom: string,
10
+ colorTo: string,
11
+ pinned: boolean;
12
+ shortDescription?: string;
13
+ runtime: SpaceRuntime;
14
+ authorData: SpaceAuthorData;
15
+ }
16
+
17
+ export interface SpaceAuthorData {
18
+ avatarUrl: string;
19
+ fullname: string
20
+ name: string;
21
+ }
22
+
23
+ export interface SpaceRuntime {
24
+ stage: string;
25
+ }