Skip to content

Commit d378496

Browse files
committed
feat: added base components, updated the readme, extended summarization
1 parent 41d4a1f commit d378496

20 files changed

+496
-67
lines changed

.env.local.example

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
HF_EXAMPLE_SECRET=thisisjustanexample

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,7 @@ yarn-error.log*
3232

3333
# typescript
3434
*.tsbuildinfo
35-
next-env.d.ts
35+
next-env.d.ts
36+
37+
# Docker secrets used for local dev
38+
.secrets

Dockerfile

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ RUN \
1515
else echo "Lockfile not found." && exit 1; \
1616
fi
1717

18+
# Uncomment the following lines if you want to use a secret at buildtime,
19+
# for example to access your private npm packages
20+
# RUN --mount=type=secret,id=HF_EXAMPLE_SECRET,mode=0444,required=true \
21+
# $(cat /run/secrets/HF_EXAMPLE_SECRET)
1822

1923
# Rebuild the source code only when needed
2024
FROM base AS builder

README.md

+110-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: "Next.js on \U0001F917 Spaces"
3-
emoji: "🐳🤗"
3+
emoji: "\U0001F433\U0001F917"
44
colorFrom: blue
55
colorTo: yellow
66
sdk: docker
@@ -10,14 +10,25 @@ app_port: 3000
1010
---
1111
<h1 align="center">Next.js on 🤗 Spaces</h1>
1212

13-
This starter can be used to run [Next.js](https://nextjs.org/) using [Docker](https://huggingface.co/docs/hub/spaces-sdks-docker) on 🤗 [Spaces](https://huggingface.co/spaces).
13+
<p align="center">
14+
Run your ML demo with ease in a <a href="https://nextjs.org">Next.js</a> environment
15+
</p>
16+
17+
At failfast, we're passionate about crafting demos with TypeScript, Next.js, and MUI. Inspired by the ease-of-use of Gradio and Streamlit within Hugging Face Spaces, we aim to deliver a similar developer experience to JavaScript enthusiasts. Our toolkit includes predefined MUI components, empowering you to build intuitive UIs for your ML demos.
18+
19+
---
1420

1521
<!-- toc -->
1622

1723
- [Local development](#local-development)
18-
- [Use the Docker container locally](#use-the-docker-container-locally)
24+
* [Use the Docker container locally](#use-the-docker-container-locally)
25+
- [Secret Management](#secret-management)
26+
* [Build-time](#build-time)
27+
* [Runtime](#runtime)
1928
- [Dockerize an existing project](#dockerize-an-existing-project)
20-
- [Manage your 🤗 Space via GitHub](#manage-your-%F0%9F%A4%97-space-via-github)
29+
- [Sync your GitHub repository with your 🤗 Space](#sync-your-github-repository-with-your-%F0%9F%A4%97-space)
30+
- [Cleanup your 🤗 Space](#cleanup-your-%F0%9F%A4%97-space)
31+
- [Development Roadmap](#development-roadmap)
2132

2233
<!-- tocstop -->
2334

@@ -29,16 +40,68 @@ This starter can be used to run [Next.js](https://nextjs.org/) using [Docker](ht
2940
2. Start the local dev-server: `npm run dev`
3041
3. Open the app via [localhost:3000](http://localhost:3000)
3142

32-
## Use the Docker container locally
43+
### Use the Docker container locally
44+
45+
> ℹ️ In order for the commands to work, you need at least Docker >= 20.10, as we use env-variables as secrets
3346
3447
To make sure that everything is working out, you can run your container locally:
3548

3649
1. [Install Docker](https://docs.docker.com/get-docker/) on your machine
3750
2. Go into the `nextjs-hf-spaces` folder
38-
3. Build your Docker image: `docker build -t nextjs-hf-spaces .`.
51+
3. Build your Docker image: `docker build -t nextjs-hf-spaces .`
3952
4. Run your Docker container: `docker run -p 3000:3000 nextjs-hf-spaces`.
4053
5. Open the app via [localhost:3000](http://localhost:3000)
4154

55+
If you also have a secret that needs to be passed into the container, you can do this:
56+
57+
1. Create a copy of `.env.local.example` and rename it to `.env.local` (it contains the secret `HF_EXAMPLE_SECRET`)
58+
2. Run your Docker container and specify the env-file: `docker run -p 3000:3000 --env-file .env.local nextjs-hf-spaces`
59+
3. Open the example API via [localhost:3000/api/env](http://localhost:3000/api/env) and see that the value of our secret `HF_EXAMPLE_SECRET` is shown
60+
61+
## Secret Management
62+
63+
To not expose your secrets to end users, you can add them directly in **Settings** of your 🤗 Space.
64+
65+
1. Open your space and navigate to the **Settings**
66+
2. Find **Repository secrets** & click on **New secret**
67+
68+
That's it, you can now access your secret.
69+
70+
### Build-time
71+
72+
If you need to have a secret during build-time (e.g. you want to install private npm packages), then you can add this directly into the `Dockerfile`:
73+
74+
```dockerfile
75+
# Uncomment the following lines if you want to use a secret at buildtime,
76+
# for example to access your private npm packages
77+
RUN --mount=type=secret,id=HF_EXAMPLE_SECRET,mode=0444,required=true \
78+
$(cat /run/secrets/HF_EXAMPLE_SECRET)
79+
```
80+
81+
In this case, we mount the secret `HF_EXAMPLE_SECRET` (using [Docker secrets](https://docs.docker.com/engine/swarm/secrets/)) inside and can use it.
82+
83+
### Runtime
84+
85+
When your 🤗 Space is running and you want to use a secret (e.g. access an API that requires authentication) without exposing it to the user, you can use it as an environment variable via `process.env`.
86+
87+
```typescript
88+
import process from "node:process";
89+
import { NextApiRequest, NextApiResponse } from "next";
90+
91+
export default async function handler(
92+
request: NextApiRequest,
93+
response: NextApiResponse
94+
) {
95+
const exampleSecret = process.env.HF_EXAMPLE_SECRET;
96+
97+
// Your logic to access an API that requires authentication
98+
99+
return response.status(200).json("We have access to an external API");
100+
}
101+
```
102+
103+
A simple example can be found at [nextjs-hf-spaces/api/env](https://huggingface.co/spaces/failfast/nextjs-hf-spaces/api/env). This will return the secret to see that it's working, but you wouldn't do this in your space, as you don't want to expose the secret to an end user.
104+
42105
## Dockerize an existing project
43106

44107
To add support for Docker to an existing project, just copy the `Dockerfile` into the root of the project and add the following to the `next.config.js` file:
@@ -53,9 +116,9 @@ module.exports = {
53116

54117
This will build the project as a standalone app inside the Docker image.
55118

56-
## Manage your 🤗 Space via GitHub
119+
## Sync your GitHub repository with your 🤗 Space
57120

58-
If you want to use all the features for collaborative development on GitHub, but keep your demo on Spaces, then you can set up a GitHub action that will automatically push changes from GitHub into Spaces.
121+
If you want to use all the features for collaborative development on GitHub, but keep your demo on 🤗 Spaces, then you can set up a GitHub action that will automatically push changes from GitHub into Spaces.
59122

60123
> ℹ️ Git-LFS is required for files bigger than 10MB
61124
@@ -71,3 +134,42 @@ This should force push changes in the **main** branch from GitHub into your 🤗
71134
For further information, you can check out the [guide on Hugging Face](https://huggingface.co/docs/hub/spaces-github-actions).
72135

73136

137+
## Cleanup your 🤗 Space
138+
139+
You don't need all the demo content and examples? Then you can delete these resources to get a clean 🤗 Space:
140+
141+
* `src/pages/api/env.ts`
142+
* `src/components/example-components.tsx`
143+
* `src/components/getting-started.tsx`
144+
* `src/components/under-construction.tsx`
145+
* `src/components/title.tsx`
146+
* `src/components/huggingface/huggingface.tsx`
147+
148+
Update the `src/components/index.tsx` and remove:
149+
150+
```jsx
151+
<Title />
152+
153+
<GettingStarted />
154+
155+
<DividerBox />
156+
157+
<ExampleComponents />
158+
```
159+
160+
> i Got an idea how this could be better? Please let us know!
161+
162+
## Development Roadmap
163+
164+
The next milestones in no particular order are:
165+
166+
* Components for all [`@huggingface/inference`](https://huggingface.co/docs/huggingface.js/inference/README) methods (WIP)
167+
* Components to use [langchain.js](https://js.langchain.com/docs)
168+
* Components to use [hyv](https://github.com/failfa-st/hyv)
169+
* Publish components on npm to make them usable outside of [nextjs-hf-spaces](https://github.com/failfa-st/nextjs-hf-spaces)
170+
* Provide templates for different use-cases, that are too complex for single components
171+
* Docs on how to use the components with all available options
172+
173+
> i Anything missing? Please let us know!
174+
175+

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
{
2-
"name": "nextjs-docker-starter",
3-
"version": "1.0.0",
4-
"description": "Run your ML demo app using Next.js on 🤗 Spaces",
2+
"name": "nextjs-hf-spaces",
3+
"version": "0.0.1",
4+
"description": "Run your ML demo with ease in a Next.js environment",
55
"keywords": [
66
"nextjs",
77
"artificial intelligence",
88
"javascript",
9-
"hugging face spaces",
9+
"typescript",
10+
"hugging face",
1011
"machine learning"
1112
],
1213
"license": "MIT",
@@ -15,11 +16,11 @@
1516
"url": "https://github.com/TimPietrusky"
1617
},
1718
"scripts": {
18-
"dev": "next dev",
1919
"build": "next build",
20-
"start": "next start",
20+
"dev": "next dev",
2121
"lint": "next lint",
2222
"spj": "npx sort-package-json",
23+
"start": "next start",
2324
"toc": "npx markdown-toc README.md -i"
2425
},
2526
"dependencies": {

src/components/boxes.tsx src/components/base/boxes.tsx

+4-5
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ import { styled } from "@mui/material/styles";
33

44
export const SectionBox = styled(Paper)<PaperProps>(({ theme }) => ({
55
display: "flex",
6-
alignItems: "center",
7-
justifyContent: "center",
86
padding: 15,
9-
border: `5px solid transparent`,
10-
borderImage: `linear-gradient(to bottom right, #b827fc 0%, #2c90fc 25%, #b8fd33 50%, #fec837 75%, #fd1892 100%)`,
11-
borderImageSlice: 1,
7+
paddingTop: 30,
8+
paddingBottom: 30,
9+
marginBottom: 20,
10+
background: `linear-gradient(to bottom right, ${theme.palette.primary.main} 0%, ${theme.palette.secondary.main} 100%)`,
1211
}));
1312

1413
export const HighlightBox = styled(Paper)<PaperProps>(({ theme }) => ({

src/components/base/code.tsx

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { styled } from "@mui/material/styles";
2+
import { Paper, PaperProps } from "@mui/material";
3+
4+
type CodeProps = {
5+
children: string;
6+
};
7+
8+
const CodeBox = styled(Paper)<PaperProps>(({ theme }) => ({
9+
fontFamily: "monospace",
10+
padding: 8,
11+
borderTop: `2px solid ${theme.palette.secondary.dark}`,
12+
borderBottom: `2px solid ${theme.palette.secondary.dark}`,
13+
}));
14+
15+
export default function Code(props: CodeProps) {
16+
const { children } = props;
17+
18+
return <CodeBox>{children}</CodeBox>;
19+
}
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Button, Typography } from "@mui/material";
2+
import { MouseEventHandler } from "react";
3+
4+
interface ExampleButtonProps {
5+
text: string;
6+
displayLength?: number;
7+
onClick?: (text: string) => void;
8+
}
9+
10+
/**
11+
*
12+
* A button that hosts an example "text" that can be used as the input
13+
* to anything to get an inspiration on how to get started.
14+
*
15+
* @param props ExampleButtonProps
16+
* @returns
17+
*/
18+
export default function ExampleButton(props: ExampleButtonProps) {
19+
const { text, displayLength = 50, onClick } = props;
20+
21+
const displayText =
22+
text.slice(0, displayLength) + (text.length > displayLength ? "..." : "");
23+
24+
const handleClick: MouseEventHandler = event => {
25+
event.preventDefault();
26+
27+
if (onClick) {
28+
onClick(text);
29+
}
30+
};
31+
32+
return (
33+
<Button
34+
onClick={handleClick}
35+
sx={{ textTransform: "none" }}
36+
variant="outlined"
37+
>
38+
<Typography>{displayText}</Typography>
39+
</Button>
40+
);
41+
}

src/components/base/options.tsx

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { KeyboardArrowDown, KeyboardArrowUp } from "@mui/icons-material";
2+
import {
3+
Card,
4+
CardContent,
5+
CardHeader,
6+
Collapse,
7+
IconButton,
8+
Stack,
9+
} from "@mui/material";
10+
import { ReactElement, useState } from "react";
11+
12+
type OptionsProps = {
13+
children: ReactElement | ReactElement[];
14+
opened?: boolean;
15+
};
16+
17+
/**
18+
* Define options that are hidden by default
19+
*
20+
* @param props OptionsProps
21+
* @param props.opened boolean - Are the options visible or not (default)
22+
*
23+
* @returns Options
24+
*/
25+
export default function Options(props: OptionsProps) {
26+
const { children, opened = false } = props;
27+
28+
const [showOptions, setShowOptions] = useState(opened);
29+
30+
const handleShowOptions = () => setShowOptions(!showOptions);
31+
32+
return (
33+
<>
34+
<Card>
35+
<CardHeader
36+
title="Options"
37+
onClick={handleShowOptions}
38+
action={
39+
<IconButton aria-label="expand" size="small">
40+
{showOptions ? <KeyboardArrowUp /> : <KeyboardArrowDown />}
41+
</IconButton>
42+
}
43+
sx={{
44+
cursor: "pointer",
45+
}}
46+
titleTypographyProps={{ variant: "h6", sx: { fontSize: "1em" } }}
47+
/>
48+
<Collapse in={showOptions}>
49+
<CardContent>
50+
<Stack spacing={2}>{children}</Stack>
51+
</CardContent>
52+
</Collapse>
53+
</Card>
54+
</>
55+
);
56+
}
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import {
2+
Box,
3+
FormControlLabel,
4+
Slider,
5+
SliderProps,
6+
Typography,
7+
} from "@mui/material";
8+
9+
type SliderWithLabelProps = SliderProps & {
10+
label?: string;
11+
};
12+
13+
export default function SliderWithLabel(props: SliderWithLabelProps) {
14+
const { label = "", valueLabelDisplay = "auto" } = props;
15+
16+
return (
17+
<Box>
18+
<Typography variant="subtitle1">{label}</Typography>
19+
<Slider {...props} valueLabelDisplay={valueLabelDisplay} />
20+
</Box>
21+
);
22+
}

0 commit comments

Comments
 (0)