Commit
Β·
8e40013
0
Parent(s):
Pushing Frontend Code
Browse files- .dockerignore +7 -0
- .env.local.example +1 -0
- .eslintrc.json +3 -0
- .gitattributes +34 -0
- .github/workflows/sync_to_hf_spaces.yml +22 -0
- .gitignore +38 -0
- .package.json.swp +0 -0
- .prettierrc +10 -0
- Dockerfile +63 -0
- LICENSE +21 -0
- README.md +175 -0
- next.config.js +7 -0
- package-lock.json +0 -0
- package.json +44 -0
- public/failfast.svg +1 -0
- public/favicon.ico +0 -0
- src/components/base/boxes.tsx +28 -0
- src/components/base/code.tsx +19 -0
- src/components/base/example-button.tsx +41 -0
- src/components/base/options.tsx +56 -0
- src/components/base/secret.tsx +33 -0
- src/components/base/slider-with-label.tsx +22 -0
- src/components/example-components.tsx +35 -0
- src/components/footer.tsx +32 -0
- src/components/getting-started.tsx +150 -0
- src/components/huggingface/huggingface.tsx +64 -0
- src/components/huggingface/inference/summarization.tsx +190 -0
- src/components/title.tsx +62 -0
- src/components/under-construction.tsx +8 -0
- src/lib/createEmotionCache.ts +19 -0
- src/lib/theme.ts +62 -0
- src/pages/_app.tsx +29 -0
- src/pages/_document.tsx +13 -0
- src/pages/api/env.ts +11 -0
- src/pages/index.tsx +35 -0
- tsconfig.json +23 -0
.dockerignore
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Dockerfile
|
2 |
+
.dockerignore
|
3 |
+
node_modules
|
4 |
+
npm-debug.log
|
5 |
+
README.md
|
6 |
+
.next
|
7 |
+
.git
|
.env.local.example
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
HF_EXAMPLE_SECRET=thisisjustanexample
|
.eslintrc.json
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"extends": "next/core-web-vitals"
|
3 |
+
}
|
.gitattributes
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
29 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
30 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
31 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
32 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
33 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
34 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
.github/workflows/sync_to_hf_spaces.yml
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: Sync to Hugging Face Spaces
|
2 |
+
on:
|
3 |
+
push:
|
4 |
+
branches: [main]
|
5 |
+
workflow_dispatch:
|
6 |
+
|
7 |
+
env:
|
8 |
+
HF_USERNAME: failfast
|
9 |
+
HF_SPACE_NAME: nextjs-hf-spaces
|
10 |
+
|
11 |
+
jobs:
|
12 |
+
sync-to-space:
|
13 |
+
runs-on: ubuntu-latest
|
14 |
+
steps:
|
15 |
+
- uses: actions/checkout@v3
|
16 |
+
with:
|
17 |
+
fetch-depth: 0
|
18 |
+
lfs: true
|
19 |
+
- name: Push to Space
|
20 |
+
env:
|
21 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
22 |
+
run: git push --force https://$HF_USERNAME:[email protected]/spaces/$HF_USERNAME/$HF_SPACE_NAME main
|
.gitignore
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
2 |
+
|
3 |
+
# dependencies
|
4 |
+
/node_modules
|
5 |
+
/.pnp
|
6 |
+
.pnp.js
|
7 |
+
|
8 |
+
# testing
|
9 |
+
/coverage
|
10 |
+
|
11 |
+
# next.js
|
12 |
+
/.next/
|
13 |
+
/out/
|
14 |
+
|
15 |
+
# production
|
16 |
+
/build
|
17 |
+
|
18 |
+
# misc
|
19 |
+
.DS_Store
|
20 |
+
*.pem
|
21 |
+
|
22 |
+
# debug
|
23 |
+
npm-debug.log*
|
24 |
+
yarn-debug.log*
|
25 |
+
yarn-error.log*
|
26 |
+
|
27 |
+
# local env files
|
28 |
+
.env*.local
|
29 |
+
|
30 |
+
# vercel
|
31 |
+
.vercel
|
32 |
+
|
33 |
+
# typescript
|
34 |
+
*.tsbuildinfo
|
35 |
+
next-env.d.ts
|
36 |
+
|
37 |
+
# Docker secrets used for local dev
|
38 |
+
.secrets
|
.package.json.swp
ADDED
Binary file (1.02 kB). View file
|
|
.prettierrc
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"trailingComma": "es5",
|
3 |
+
"semi": true,
|
4 |
+
"singleQuote": false,
|
5 |
+
"quoteProps": "as-needed",
|
6 |
+
"jsxSingleQuote": false,
|
7 |
+
"bracketSpacing": true,
|
8 |
+
"arrowParens": "avoid",
|
9 |
+
"proseWrap": "always"
|
10 |
+
}
|
Dockerfile
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM node:18-alpine AS base
|
2 |
+
|
3 |
+
# Install dependencies only when needed
|
4 |
+
FROM base AS deps
|
5 |
+
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
6 |
+
RUN apk add --no-cache libc6-compat
|
7 |
+
WORKDIR /app
|
8 |
+
|
9 |
+
# Install dependencies based on the preferred package manager
|
10 |
+
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
|
11 |
+
RUN \
|
12 |
+
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
|
13 |
+
elif [ -f package-lock.json ]; then npm ci; \
|
14 |
+
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
|
15 |
+
else echo "Lockfile not found." && exit 1; \
|
16 |
+
fi
|
17 |
+
|
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)
|
22 |
+
|
23 |
+
# Rebuild the source code only when needed
|
24 |
+
FROM base AS builder
|
25 |
+
WORKDIR /app
|
26 |
+
COPY --from=deps /app/node_modules ./node_modules
|
27 |
+
COPY . .
|
28 |
+
|
29 |
+
# Next.js collects completely anonymous telemetry data about general usage.
|
30 |
+
# Learn more here: https://nextjs.org/telemetry
|
31 |
+
# Uncomment the following line in case you want to disable telemetry during the build.
|
32 |
+
# ENV NEXT_TELEMETRY_DISABLED 1
|
33 |
+
|
34 |
+
# RUN yarn build
|
35 |
+
|
36 |
+
# If you use yarn, comment out this line and use the line above
|
37 |
+
RUN npm run build
|
38 |
+
|
39 |
+
# Production image, copy all the files and run next
|
40 |
+
FROM base AS runner
|
41 |
+
WORKDIR /app
|
42 |
+
|
43 |
+
ENV NODE_ENV production
|
44 |
+
# Uncomment the following line in case you want to disable telemetry during runtime.
|
45 |
+
# ENV NEXT_TELEMETRY_DISABLED 1
|
46 |
+
|
47 |
+
RUN addgroup --system --gid 1001 nodejs
|
48 |
+
RUN adduser --system --uid 1001 nextjs
|
49 |
+
|
50 |
+
COPY --from=builder /app/public ./public
|
51 |
+
|
52 |
+
# Automatically leverage output traces to reduce image size
|
53 |
+
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
54 |
+
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
55 |
+
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
56 |
+
|
57 |
+
USER nextjs
|
58 |
+
|
59 |
+
EXPOSE 3000
|
60 |
+
|
61 |
+
ENV PORT 3000
|
62 |
+
|
63 |
+
CMD ["node", "server.js"]
|
LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2023 Tim Pietrusky (failfast)
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
README.md
ADDED
@@ -0,0 +1,175 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: "Next.js on \U0001F917 Spaces"
|
3 |
+
emoji: "\U0001F433\U0001F917"
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: yellow
|
6 |
+
sdk: docker
|
7 |
+
pinned: false
|
8 |
+
license: agpl-3.0
|
9 |
+
app_port: 3000
|
10 |
+
---
|
11 |
+
<h1 align="center">Next.js on π€ Spaces</h1>
|
12 |
+
|
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 |
+
---
|
20 |
+
|
21 |
+
<!-- toc -->
|
22 |
+
|
23 |
+
- [Local development](#local-development)
|
24 |
+
* [Use the Docker container locally](#use-the-docker-container-locally)
|
25 |
+
- [Secret Management](#secret-management)
|
26 |
+
* [Build-time](#build-time)
|
27 |
+
* [Runtime](#runtime)
|
28 |
+
- [Dockerize an existing project](#dockerize-an-existing-project)
|
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)
|
32 |
+
|
33 |
+
<!-- tocstop -->
|
34 |
+
|
35 |
+
---
|
36 |
+
|
37 |
+
## Local development
|
38 |
+
|
39 |
+
1. Install the dependencies: `npm i`
|
40 |
+
2. Start the local dev-server: `npm run dev`
|
41 |
+
3. Open the app via [localhost:3000](http://localhost:3000)
|
42 |
+
|
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
|
46 |
+
|
47 |
+
To make sure that everything is working out, you can run your container locally:
|
48 |
+
|
49 |
+
1. [Install Docker](https://docs.docker.com/get-docker/) on your machine
|
50 |
+
2. Go into the `nextjs-hf-spaces` folder
|
51 |
+
3. Build your Docker image: `docker build -t nextjs-hf-spaces .`
|
52 |
+
4. Run your Docker container: `docker run -p 3000:3000 nextjs-hf-spaces`.
|
53 |
+
5. Open the app via [localhost:3000](http://localhost:3000)
|
54 |
+
|
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 |
+
|
105 |
+
## Dockerize an existing project
|
106 |
+
|
107 |
+
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:
|
108 |
+
|
109 |
+
```js
|
110 |
+
// next.config.js
|
111 |
+
module.exports = {
|
112 |
+
// ... rest of the configuration.
|
113 |
+
output: "standalone",
|
114 |
+
};
|
115 |
+
```
|
116 |
+
|
117 |
+
This will build the project as a standalone app inside the Docker image.
|
118 |
+
|
119 |
+
## Sync your GitHub repository with your π€ Space
|
120 |
+
|
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.
|
122 |
+
|
123 |
+
> βΉοΈ Git-LFS is required for files bigger than 10MB
|
124 |
+
|
125 |
+
1. Create your repo on GitHub
|
126 |
+
2. Create a [Github secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository) named `HF_TOKEN` and use an [access token from Hugging Face](https://huggingface.co/settings/tokens) as its value (you must be logged in to do this)
|
127 |
+
3. Update the workflow [sync_to_hf_spaces.yml](.github/workflows/sync_to_hf_spaces.yml)
|
128 |
+
- Configure `HF_USERNAME`: Replace `failfast` with the name of your π€ user account or your π€ organization
|
129 |
+
- Configure `HF_SPACE_NAME`: Replace `nextjs-hf-spaces` with the name of your π€ space
|
130 |
+
4. Push the code into your repo on GitHub
|
131 |
+
|
132 |
+
This should force push changes in the **main** branch from GitHub into your π€ space.
|
133 |
+
|
134 |
+
For further information, you can check out the [guide on Hugging Face](https://huggingface.co/docs/hub/spaces-github-actions).
|
135 |
+
|
136 |
+
|
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 |
+
|
next.config.js
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/** @type {import('next').NextConfig} */
|
2 |
+
const nextConfig = {
|
3 |
+
output: "standalone",
|
4 |
+
reactStrictMode: true,
|
5 |
+
};
|
6 |
+
|
7 |
+
module.exports = nextConfig;
|
package-lock.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
package.json
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "nextjs-hf-spaces",
|
3 |
+
"version": "0.0.1",
|
4 |
+
"description": "Run your ML demo with ease in a Next.js environment",
|
5 |
+
"keywords": [
|
6 |
+
"nextjs",
|
7 |
+
"artificial intelligence",
|
8 |
+
"javascript",
|
9 |
+
"typescript",
|
10 |
+
"hugging face",
|
11 |
+
"machine learning"
|
12 |
+
],
|
13 |
+
"license": "MIT",
|
14 |
+
"author": {
|
15 |
+
"name": "Tim Pietrusky",
|
16 |
+
"url": "https://github.com/TimPietrusky"
|
17 |
+
},
|
18 |
+
"scripts": {
|
19 |
+
"build": "next build",
|
20 |
+
"dev": "next dev",
|
21 |
+
"lint": "next lint",
|
22 |
+
"spj": "npx sort-package-json",
|
23 |
+
"start": "next start",
|
24 |
+
"toc": "npx markdown-toc README.md -i"
|
25 |
+
},
|
26 |
+
"dependencies": {
|
27 |
+
"@emotion/cache": "11.10.7",
|
28 |
+
"@emotion/react": "11.10.6",
|
29 |
+
"@emotion/server": "11.10.0",
|
30 |
+
"@emotion/styled": "11.10.6",
|
31 |
+
"@huggingface/inference": "2.3.3",
|
32 |
+
"@mui/icons-material": "5.11.16",
|
33 |
+
"@mui/material": "5.12.0",
|
34 |
+
"@types/node": "20.1.4",
|
35 |
+
"@types/react": "18.2.6",
|
36 |
+
"@types/react-dom": "18.2.4",
|
37 |
+
"eslint": "8.40.0",
|
38 |
+
"eslint-config-next": "13.4.2",
|
39 |
+
"next": "13.4.2",
|
40 |
+
"react": "18.2.0",
|
41 |
+
"react-dom": "18.2.0",
|
42 |
+
"typescript": "5.0.4"
|
43 |
+
}
|
44 |
+
}
|
public/failfast.svg
ADDED
|
public/favicon.ico
ADDED
|
src/components/base/boxes.tsx
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Divider, DividerProps, Paper, PaperProps } from "@mui/material";
|
2 |
+
import { styled } from "@mui/material/styles";
|
3 |
+
|
4 |
+
export const SectionBox = styled(Paper)<PaperProps>(({ theme }) => ({
|
5 |
+
display: "flex",
|
6 |
+
padding: 15,
|
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%)`,
|
11 |
+
}));
|
12 |
+
|
13 |
+
export const HighlightBox = styled(Paper)<PaperProps>(({ theme }) => ({
|
14 |
+
display: "flex",
|
15 |
+
alignItems: "center",
|
16 |
+
justifyContent: "center",
|
17 |
+
padding: 10,
|
18 |
+
borderBottom: `3px solid transparent`,
|
19 |
+
borderImage: `linear-gradient(to bottom right, #b827fc 0%, #2c90fc 25%, #b8fd33 50%, #fec837 75%, #fd1892 100%)`,
|
20 |
+
borderImageSlice: 1,
|
21 |
+
}));
|
22 |
+
|
23 |
+
export const DividerBox = styled(Divider)<DividerProps>(({ theme }) => ({
|
24 |
+
marginTop: 20,
|
25 |
+
marginBottom: 20,
|
26 |
+
background: "transparent",
|
27 |
+
border: "none",
|
28 |
+
}));
|
src/components/base/code.tsx
ADDED
@@ -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 |
+
}
|
src/components/base/example-button.tsx
ADDED
@@ -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
ADDED
@@ -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 |
+
}
|
src/components/base/secret.tsx
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
IconButton,
|
3 |
+
InputAdornment,
|
4 |
+
TextField,
|
5 |
+
TextFieldProps,
|
6 |
+
} from "@mui/material";
|
7 |
+
import { useState } from "react";
|
8 |
+
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
9 |
+
|
10 |
+
export default function Secret(props: TextFieldProps) {
|
11 |
+
const { name = "secret", label = "Secret" } = props;
|
12 |
+
const [showSecret, setShowSecret] = useState(false);
|
13 |
+
|
14 |
+
const handleShowSecret = () => setShowSecret(!showSecret);
|
15 |
+
|
16 |
+
return (
|
17 |
+
<TextField
|
18 |
+
variant="filled"
|
19 |
+
label={label}
|
20 |
+
name={name}
|
21 |
+
type={showSecret ? "text" : "password"}
|
22 |
+
InputProps={{
|
23 |
+
endAdornment: (
|
24 |
+
<InputAdornment position="end">
|
25 |
+
<IconButton onClick={handleShowSecret}>
|
26 |
+
{showSecret ? <Visibility /> : <VisibilityOff />}
|
27 |
+
</IconButton>
|
28 |
+
</InputAdornment>
|
29 |
+
),
|
30 |
+
}}
|
31 |
+
/>
|
32 |
+
);
|
33 |
+
}
|
src/components/base/slider-with-label.tsx
ADDED
@@ -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 |
+
}
|
src/components/example-components.tsx
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Box, Stack, Typography } from "@mui/material";
|
2 |
+
import Huggingface from "./huggingface/huggingface";
|
3 |
+
import { DividerBox, HighlightBox, SectionBox } from "./base/boxes";
|
4 |
+
import { UnderConstruction } from "./under-construction";
|
5 |
+
|
6 |
+
export default function ExampleComponents() {
|
7 |
+
return (
|
8 |
+
<>
|
9 |
+
<SectionBox>
|
10 |
+
<Stack spacing={2}>
|
11 |
+
<Typography component="h2" variant="h3">
|
12 |
+
Components
|
13 |
+
</Typography>
|
14 |
+
|
15 |
+
<Typography variant="body1">
|
16 |
+
Unsure where to begin? Our pre-built components offer a jumpstart
|
17 |
+
for your ML demo π
|
18 |
+
</Typography>
|
19 |
+
</Stack>
|
20 |
+
</SectionBox>
|
21 |
+
|
22 |
+
<Huggingface />
|
23 |
+
|
24 |
+
<DividerBox />
|
25 |
+
|
26 |
+
<Stack spacing={4}>
|
27 |
+
<HighlightBox>
|
28 |
+
<Typography variant="h4">More comming soon!</Typography>
|
29 |
+
</HighlightBox>
|
30 |
+
|
31 |
+
<UnderConstruction />
|
32 |
+
</Stack>
|
33 |
+
</>
|
34 |
+
);
|
35 |
+
}
|
src/components/footer.tsx
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import Box from "@mui/material/Box";
|
2 |
+
import Image from "next/image";
|
3 |
+
import { Divider, Link } from "@mui/material";
|
4 |
+
|
5 |
+
const Footer = () => {
|
6 |
+
return (
|
7 |
+
<Box
|
8 |
+
component="footer"
|
9 |
+
sx={{
|
10 |
+
display: "flex",
|
11 |
+
justifyContent: "center",
|
12 |
+
gap: 1,
|
13 |
+
alignItems: "center",
|
14 |
+
mt: 8,
|
15 |
+
mb: 4,
|
16 |
+
}}
|
17 |
+
>
|
18 |
+
<Link
|
19 |
+
href="https://failfa.st"
|
20 |
+
display="flex"
|
21 |
+
alignItems="center"
|
22 |
+
rel="noopener"
|
23 |
+
target="_blank"
|
24 |
+
>
|
25 |
+
<Box sx={{ mr: 0.5 }}>Powered by</Box>{" "}
|
26 |
+
<Image src="/failfast.svg" alt="failfast Logo" width="32" height="32" />
|
27 |
+
</Link>
|
28 |
+
</Box>
|
29 |
+
);
|
30 |
+
};
|
31 |
+
|
32 |
+
export default Footer;
|
src/components/getting-started.tsx
ADDED
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
Button,
|
3 |
+
Grid,
|
4 |
+
Link,
|
5 |
+
List,
|
6 |
+
ListItem,
|
7 |
+
ListItemIcon,
|
8 |
+
ListItemText,
|
9 |
+
ListSubheader,
|
10 |
+
Paper,
|
11 |
+
Stack,
|
12 |
+
Typography,
|
13 |
+
} from "@mui/material";
|
14 |
+
|
15 |
+
import ViewQuiltIcon from "@mui/icons-material/ViewQuilt";
|
16 |
+
import SailingIcon from "@mui/icons-material/Sailing";
|
17 |
+
import LightModeIcon from "@mui/icons-material/LightMode";
|
18 |
+
import SentimentVerySatisfiedIcon from "@mui/icons-material/SentimentVerySatisfied";
|
19 |
+
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
20 |
+
import LinkIcon from "@mui/icons-material/Link";
|
21 |
+
import SyncIcon from "@mui/icons-material/Sync";
|
22 |
+
import { HighlightBox } from "./base/boxes";
|
23 |
+
|
24 |
+
export default function GettingStarted() {
|
25 |
+
return (
|
26 |
+
<>
|
27 |
+
<Grid container spacing={2} sx={{ justifyContent: "center" }}>
|
28 |
+
<Grid item sm={8} lg={6}>
|
29 |
+
<Paper sx={{ p: 2 }}>
|
30 |
+
<List disablePadding>
|
31 |
+
<ListSubheader sx={{ fontSize: "1.5em" }}>Features</ListSubheader>
|
32 |
+
|
33 |
+
<ListItem>
|
34 |
+
<ListItemIcon>
|
35 |
+
<SentimentVerySatisfiedIcon />
|
36 |
+
</ListItemIcon>
|
37 |
+
<ListItemText>
|
38 |
+
<Link
|
39 |
+
href="https://huggingface.co/docs/huggingface.js/index"
|
40 |
+
target="_blank"
|
41 |
+
rel="noopener"
|
42 |
+
>
|
43 |
+
huggingface.js
|
44 |
+
</Link>{" "}
|
45 |
+
components (WIP)
|
46 |
+
</ListItemText>
|
47 |
+
</ListItem>
|
48 |
+
|
49 |
+
<ListItem>
|
50 |
+
<ListItemIcon>
|
51 |
+
<LinkIcon />
|
52 |
+
</ListItemIcon>
|
53 |
+
<ListItemText>
|
54 |
+
<Link
|
55 |
+
href="https://js.langchain.com/docs"
|
56 |
+
target="_blank"
|
57 |
+
rel="noopener"
|
58 |
+
>
|
59 |
+
langchain.js
|
60 |
+
</Link>{" "}
|
61 |
+
and{" "}
|
62 |
+
<Link
|
63 |
+
href="https://github.com/failfa-st/hyv"
|
64 |
+
target="_blank"
|
65 |
+
rel="noopener"
|
66 |
+
>
|
67 |
+
hyv
|
68 |
+
</Link>{" "}
|
69 |
+
components (comming soon)
|
70 |
+
</ListItemText>
|
71 |
+
</ListItem>
|
72 |
+
|
73 |
+
<ListItem>
|
74 |
+
<ListItemIcon>
|
75 |
+
<ViewQuiltIcon />
|
76 |
+
</ListItemIcon>
|
77 |
+
<ListItemText>
|
78 |
+
Rapid prototyping with{" "}
|
79 |
+
<Link
|
80 |
+
href="https://mui.com/material-ui/getting-started/overview/"
|
81 |
+
target="_blank"
|
82 |
+
rel="noopener"
|
83 |
+
>
|
84 |
+
MUI
|
85 |
+
</Link>
|
86 |
+
</ListItemText>
|
87 |
+
</ListItem>
|
88 |
+
<ListItem>
|
89 |
+
<ListItemIcon>
|
90 |
+
<LightModeIcon />
|
91 |
+
</ListItemIcon>
|
92 |
+
<ListItemText>
|
93 |
+
Dark / light theme based on system preferences
|
94 |
+
</ListItemText>
|
95 |
+
</ListItem>
|
96 |
+
<ListItem>
|
97 |
+
<ListItemIcon>
|
98 |
+
<SyncIcon />
|
99 |
+
</ListItemIcon>
|
100 |
+
<ListItemText>
|
101 |
+
Sync your GitHub repository with your π€ Space
|
102 |
+
</ListItemText>
|
103 |
+
</ListItem>
|
104 |
+
<ListItem>
|
105 |
+
<ListItemIcon>
|
106 |
+
<SailingIcon />
|
107 |
+
</ListItemIcon>
|
108 |
+
<ListItemText>Runs in Docker or localhost</ListItemText>
|
109 |
+
</ListItem>
|
110 |
+
</List>
|
111 |
+
</Paper>
|
112 |
+
</Grid>
|
113 |
+
|
114 |
+
<Grid item sm={4} lg={3}>
|
115 |
+
<Stack gap={2}>
|
116 |
+
<Paper sx={{ p: 2 }}>
|
117 |
+
<Typography variant="body1">
|
118 |
+
Explore the{" "}
|
119 |
+
<Link
|
120 |
+
href="https://huggingface.co/spaces/failfast/nextjs-docker-starter/blob/main/README.md"
|
121 |
+
target="_blank"
|
122 |
+
rel="noopener"
|
123 |
+
>
|
124 |
+
README
|
125 |
+
</Link>{" "}
|
126 |
+
for a comprehensive guide on local development, Docker
|
127 |
+
utilization, secret management, and GitHub-based Space control.
|
128 |
+
</Typography>
|
129 |
+
</Paper>
|
130 |
+
|
131 |
+
<Paper sx={{ p: 2 }}>
|
132 |
+
<Typography variant="body1">
|
133 |
+
Have feedback or ideas?{" "}
|
134 |
+
<Link
|
135 |
+
href="https://huggingface.co/spaces/failfast/nextjs-docker-starter/discussions"
|
136 |
+
target="_blank"
|
137 |
+
rel="noopener"
|
138 |
+
>
|
139 |
+
We're eager to hear from you!
|
140 |
+
</Link>{" "}
|
141 |
+
As an open-source project in its early stages, your input can
|
142 |
+
significantly shape our development.
|
143 |
+
</Typography>
|
144 |
+
</Paper>
|
145 |
+
</Stack>
|
146 |
+
</Grid>
|
147 |
+
</Grid>
|
148 |
+
</>
|
149 |
+
);
|
150 |
+
}
|
src/components/huggingface/huggingface.tsx
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Alert, Link, Typography } from "@mui/material";
|
2 |
+
import { HfInference } from "@huggingface/inference";
|
3 |
+
import { useEffect } from "react";
|
4 |
+
import Summarization from "./inference/summarization";
|
5 |
+
import { HighlightBox } from "../base/boxes";
|
6 |
+
import Code from "../base/code";
|
7 |
+
|
8 |
+
export type InferenceProps = {
|
9 |
+
token?: string;
|
10 |
+
model: string;
|
11 |
+
};
|
12 |
+
|
13 |
+
export default function Huggingface() {
|
14 |
+
return (
|
15 |
+
<>
|
16 |
+
<HighlightBox>
|
17 |
+
<Typography component="h4" variant="h4">
|
18 |
+
huggingface.js
|
19 |
+
</Typography>
|
20 |
+
</HighlightBox>
|
21 |
+
|
22 |
+
<Typography variant="body1">
|
23 |
+
<Link
|
24 |
+
href="https://huggingface.co/docs/huggingface.js/index"
|
25 |
+
target="_blank"
|
26 |
+
rel="noopener"
|
27 |
+
>
|
28 |
+
huggingface.js
|
29 |
+
</Link>{" "}
|
30 |
+
is a suite of JavaScript libraries that interact with the Hugging Face
|
31 |
+
API. It enables the use of over 100,000 ML models or your own via the{" "}
|
32 |
+
<Link
|
33 |
+
href="https://huggingface.co/docs/inference-endpoints/index"
|
34 |
+
target="_blank"
|
35 |
+
rel="noopener"
|
36 |
+
>
|
37 |
+
Inference API
|
38 |
+
</Link>
|
39 |
+
, and supports managing Hugging Face repositories.
|
40 |
+
</Typography>
|
41 |
+
|
42 |
+
<Alert severity="info">
|
43 |
+
When you run into rate limits while using the components, make sure to
|
44 |
+
add your π€ access token (optained via your{" "}
|
45 |
+
<Link
|
46 |
+
href="https://huggingface.co/settings/tokens"
|
47 |
+
target="_blank"
|
48 |
+
rel="noopener"
|
49 |
+
>
|
50 |
+
account settings
|
51 |
+
</Link>
|
52 |
+
) into `HF Access Token` under "Options".
|
53 |
+
</Alert>
|
54 |
+
|
55 |
+
<Typography component="h5" variant="h5">
|
56 |
+
Summarization
|
57 |
+
</Typography>
|
58 |
+
|
59 |
+
<Code>{`<Summarization model="facebook/bart-large-cnn" maxLength={100} />`}</Code>
|
60 |
+
|
61 |
+
<Summarization model="facebook/bart-large-cnn" maxLength={100} />
|
62 |
+
</>
|
63 |
+
);
|
64 |
+
}
|
src/components/huggingface/inference/summarization.tsx
ADDED
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
Alert,
|
3 |
+
Box,
|
4 |
+
Button,
|
5 |
+
CircularProgress,
|
6 |
+
Paper,
|
7 |
+
Slider,
|
8 |
+
Stack,
|
9 |
+
TextField,
|
10 |
+
Typography,
|
11 |
+
} from "@mui/material";
|
12 |
+
import { useEffect, useRef, useState } from "react";
|
13 |
+
import { HfInference, SummarizationArgs } from "@huggingface/inference";
|
14 |
+
import { InferenceProps } from "../huggingface";
|
15 |
+
import Options from "@/components/base/options";
|
16 |
+
import SliderWithLabel from "@/components/base/slider-with-label";
|
17 |
+
import ExampleButton from "@/components/base/example-button";
|
18 |
+
import Secret from "@/components/base/secret";
|
19 |
+
|
20 |
+
type SummarizationProps = InferenceProps & {
|
21 |
+
/**
|
22 |
+
* (Default: None). Integer to define the maximum length in tokens of the output summary.
|
23 |
+
*/
|
24 |
+
maxLength?: number;
|
25 |
+
/**
|
26 |
+
* (Default: None). Float (0-120.0). The amount of time in seconds that the query should take maximum. Network can cause some overhead so it will be a soft limit.
|
27 |
+
*/
|
28 |
+
maxTime?: number;
|
29 |
+
/**
|
30 |
+
* (Default: None). Integer to define the minimum length in tokens of the output summary.
|
31 |
+
*/
|
32 |
+
minLength?: number;
|
33 |
+
/**
|
34 |
+
* (Default: None). Float (0.0-100.0). The more a token is used within generation the more it is penalized to not be picked in successive generation passes.
|
35 |
+
*/
|
36 |
+
repetitionPenalty?: number;
|
37 |
+
/**
|
38 |
+
* (Default: 1.0). Float (0.0-100.0). The temperature of the sampling operation. 1 means regular sampling, 0 means always take the highest score, 100.0 is getting closer to uniform probability.
|
39 |
+
*/
|
40 |
+
temperature?: number;
|
41 |
+
/**
|
42 |
+
* (Default: None). Integer to define the top tokens considered within the sample operation to create new text.
|
43 |
+
*/
|
44 |
+
topK?: number;
|
45 |
+
/**
|
46 |
+
* (Default: None). Float to define the tokens that are within the sample operation of text generation. Add tokens in the sample for more probable to least probable until the sum of the probabilities is greater than top_p.
|
47 |
+
*/
|
48 |
+
topP?: number;
|
49 |
+
};
|
50 |
+
|
51 |
+
export default function Summarization(props: SummarizationProps) {
|
52 |
+
const {
|
53 |
+
model,
|
54 |
+
maxLength,
|
55 |
+
maxTime,
|
56 |
+
minLength,
|
57 |
+
repetitionPenalty,
|
58 |
+
temperature,
|
59 |
+
topK,
|
60 |
+
topP,
|
61 |
+
} = props;
|
62 |
+
|
63 |
+
const [token, setToken] = useState<string>("");
|
64 |
+
const [inputText, setInputText] = useState<string>("");
|
65 |
+
const [summary, setSummary] = useState<string>("");
|
66 |
+
const [error, setError] = useState<string>("");
|
67 |
+
const [loading, setLoading] = useState(false);
|
68 |
+
|
69 |
+
const inference = useRef<HfInference | null>(null);
|
70 |
+
|
71 |
+
useEffect(() => {
|
72 |
+
inference.current = new HfInference(token);
|
73 |
+
}, [token]);
|
74 |
+
|
75 |
+
// Parse the data of the form and trigger "call"
|
76 |
+
const handleSubmit = (event: any) => {
|
77 |
+
event.preventDefault();
|
78 |
+
const data = new FormData(event.currentTarget);
|
79 |
+
|
80 |
+
setToken(data.get("token") as string);
|
81 |
+
|
82 |
+
const text = data.get("text") as string;
|
83 |
+
const max_length = Number(data.get("maxLength") as string);
|
84 |
+
|
85 |
+
call({ model, inputs: text, parameters: { max_length } });
|
86 |
+
};
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Call the inference API using args
|
90 |
+
*/
|
91 |
+
const call = async (args: SummarizationArgs) => {
|
92 |
+
const { inputs, parameters } = args;
|
93 |
+
|
94 |
+
try {
|
95 |
+
setLoading(true);
|
96 |
+
|
97 |
+
const response = await inference.current?.summarization({
|
98 |
+
model,
|
99 |
+
inputs,
|
100 |
+
parameters,
|
101 |
+
});
|
102 |
+
|
103 |
+
setSummary(response?.summary_text as string);
|
104 |
+
setError("");
|
105 |
+
} catch (error) {
|
106 |
+
if (error instanceof Error) {
|
107 |
+
setError(error.message);
|
108 |
+
} else {
|
109 |
+
setError("An unknown error occurred");
|
110 |
+
}
|
111 |
+
}
|
112 |
+
|
113 |
+
setLoading(false);
|
114 |
+
};
|
115 |
+
|
116 |
+
return (
|
117 |
+
<>
|
118 |
+
<Paper component="form" onSubmit={handleSubmit} sx={{ padding: "1em" }}>
|
119 |
+
<Stack spacing={2}>
|
120 |
+
<TextField
|
121 |
+
variant="filled"
|
122 |
+
label="Text to summarize"
|
123 |
+
multiline
|
124 |
+
required
|
125 |
+
minRows={4}
|
126 |
+
name="text"
|
127 |
+
value={inputText}
|
128 |
+
onChange={e => setInputText(e.target.value)}
|
129 |
+
/>
|
130 |
+
|
131 |
+
<Button type="submit" variant="contained" disabled={loading}>
|
132 |
+
Run{" "}
|
133 |
+
{loading && (
|
134 |
+
<CircularProgress
|
135 |
+
size={24}
|
136 |
+
sx={{
|
137 |
+
position: "absolute",
|
138 |
+
top: "50%",
|
139 |
+
left: "50%",
|
140 |
+
marginTop: "-12px",
|
141 |
+
marginLeft: "-12px",
|
142 |
+
}}
|
143 |
+
/>
|
144 |
+
)}
|
145 |
+
</Button>
|
146 |
+
|
147 |
+
{error && <Alert severity="error">{error}</Alert>}
|
148 |
+
|
149 |
+
<TextField
|
150 |
+
variant="outlined"
|
151 |
+
label="Summary"
|
152 |
+
multiline
|
153 |
+
minRows={2}
|
154 |
+
name="text"
|
155 |
+
value={summary}
|
156 |
+
/>
|
157 |
+
|
158 |
+
<Options>
|
159 |
+
<Secret name="token" label="HF Access Token" />
|
160 |
+
|
161 |
+
<SliderWithLabel
|
162 |
+
label="max_length"
|
163 |
+
name="maxLength"
|
164 |
+
aria-label="max length"
|
165 |
+
defaultValue={maxLength}
|
166 |
+
step={1}
|
167 |
+
min={56}
|
168 |
+
max={256}
|
169 |
+
/>
|
170 |
+
</Options>
|
171 |
+
|
172 |
+
<Typography variant="h6" sx={{ fontSize: "1em" }}>
|
173 |
+
Examples
|
174 |
+
</Typography>
|
175 |
+
<Stack direction="row" spacing={2}>
|
176 |
+
<ExampleButton
|
177 |
+
text="The tower is 324 metres (1,063 ft) tall, about the same height as an 81-storey building, and the tallest structure in Paris. Its base is square, measuring 125 metres (410 ft) on each side. During its construction, the Eiffel Tower surpassed the Washington Monument to become the tallest man-made structure in the world, a title it held for 41 years until the Chrysler Building in New York City was finished in 1930. It was the first structure to reach a height of 300 metres. Due to the addition of a broadcasting aerial at the top of the tower in 1957, it is now taller than the Chrysler Building by 5.2 metres (17 ft). Excluding transmitters, the Eiffel Tower is the second tallest free-standing structure in France after the Millau Viaduct."
|
178 |
+
onClick={setInputText}
|
179 |
+
/>
|
180 |
+
|
181 |
+
<ExampleButton
|
182 |
+
text="Machine learning (ML) is a field devoted to understanding and building methods that let machines 'learn' β that is, methods that leverage data to improve computer performance on some set of tasks. Machine learning algorithms build a model based on sample data, known as training data, in order to make predictions or decisions without being explicitly programmed to do so. Machine learning algorithms are used in a wide variety of applications, such as in medicine, email filtering, speech recognition, agriculture, and computer vision, where it is difficult or unfeasible to develop conventional algorithms to perform the needed tasks."
|
183 |
+
onClick={setInputText}
|
184 |
+
/>
|
185 |
+
</Stack>
|
186 |
+
</Stack>
|
187 |
+
</Paper>
|
188 |
+
</>
|
189 |
+
);
|
190 |
+
}
|
src/components/title.tsx
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Button, Link, Paper, Stack, Typography } from "@mui/material";
|
2 |
+
import { HighlightBox } from "./base/boxes";
|
3 |
+
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
4 |
+
|
5 |
+
export default function Title() {
|
6 |
+
return (
|
7 |
+
<Stack
|
8 |
+
spacing={4}
|
9 |
+
sx={{
|
10 |
+
justifyContent: "center",
|
11 |
+
alignItems: "center",
|
12 |
+
minHeight: "40vh",
|
13 |
+
p: 4,
|
14 |
+
}}
|
15 |
+
>
|
16 |
+
<Typography variant="h1" component="h1">
|
17 |
+
<Link
|
18 |
+
href="https://nextjs.org"
|
19 |
+
target="_blank"
|
20 |
+
rel="noopener noreferrer"
|
21 |
+
>
|
22 |
+
Next.js
|
23 |
+
</Link>{" "}
|
24 |
+
on π€{" "}
|
25 |
+
<Link
|
26 |
+
href="https://huggingface.co/spaces"
|
27 |
+
target="_blank"
|
28 |
+
rel="noopener noreferrer"
|
29 |
+
>
|
30 |
+
Spaces
|
31 |
+
</Link>
|
32 |
+
</Typography>
|
33 |
+
|
34 |
+
<HighlightBox>
|
35 |
+
<Typography variant="h5" component="p">
|
36 |
+
Run your ML demo with ease in a Next.js environment
|
37 |
+
</Typography>
|
38 |
+
</HighlightBox>
|
39 |
+
|
40 |
+
<Stack gap={2} direction="row">
|
41 |
+
<Button
|
42 |
+
startIcon={<ContentCopyIcon />}
|
43 |
+
variant="contained"
|
44 |
+
href="https://huggingface.co/spaces/failfast/nextjs-docker-starter?duplicate=true"
|
45 |
+
target="_blank"
|
46 |
+
rel="noopener"
|
47 |
+
color="secondary"
|
48 |
+
>
|
49 |
+
Duplicate space
|
50 |
+
</Button>
|
51 |
+
|
52 |
+
<Button
|
53 |
+
href="https://github.com/failfa-st/nextjs-docker-starter"
|
54 |
+
target="_blank"
|
55 |
+
rel="noopener"
|
56 |
+
>
|
57 |
+
Contribute on GitHub
|
58 |
+
</Button>
|
59 |
+
</Stack>
|
60 |
+
</Stack>
|
61 |
+
);
|
62 |
+
}
|
src/components/under-construction.tsx
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { styled } from "@mui/material/styles";
|
2 |
+
|
3 |
+
export const UnderConstruction = styled("div")({
|
4 |
+
width: "100%",
|
5 |
+
height: "266px",
|
6 |
+
backgroundRepeat: "round",
|
7 |
+
backgroundImage: `url("data:image/gif;base64,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")`,
|
8 |
+
});
|
src/lib/createEmotionCache.ts
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import createCache from "@emotion/cache";
|
2 |
+
|
3 |
+
const isBrowser = typeof document !== "undefined";
|
4 |
+
|
5 |
+
// On the client side, Create a meta tag at the top of the <head> and set it as insertionPoint.
|
6 |
+
// This assures that MUI styles are loaded first.
|
7 |
+
// It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
|
8 |
+
export default function createEmotionCache() {
|
9 |
+
let insertionPoint;
|
10 |
+
|
11 |
+
if (isBrowser) {
|
12 |
+
const emotionInsertionPoint = document.querySelector<HTMLMetaElement>(
|
13 |
+
'meta[name="emotion-insertion-point"]'
|
14 |
+
);
|
15 |
+
insertionPoint = emotionInsertionPoint ?? undefined;
|
16 |
+
}
|
17 |
+
|
18 |
+
return createCache({ key: "mui-style", insertionPoint });
|
19 |
+
}
|
src/lib/theme.ts
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Roboto } from "next/font/google";
|
2 |
+
import { experimental_extendTheme as extendTheme } from "@mui/material/styles";
|
3 |
+
|
4 |
+
export const roboto = Roboto({
|
5 |
+
weight: ["300", "400", "500", "700"],
|
6 |
+
subsets: ["latin"],
|
7 |
+
display: "swap",
|
8 |
+
fallback: ["Helvetica", "Arial", "sans-serif"],
|
9 |
+
});
|
10 |
+
|
11 |
+
/**
|
12 |
+
* https://mui.com/material-ui/experimental-api/css-theme-variables/customization/
|
13 |
+
*
|
14 |
+
* TL;DR
|
15 |
+
* - specify both dark and light colors at once
|
16 |
+
* - extendTheme returns a theme for CssVarsProvider, not ThemeProvider
|
17 |
+
* - CssVarsProvider has a defaultMode property, set to "system" in _app.tsx
|
18 |
+
*/
|
19 |
+
const theme = extendTheme({
|
20 |
+
colorSchemes: {
|
21 |
+
light: {
|
22 |
+
palette: {
|
23 |
+
primary: {
|
24 |
+
main: "#2c90fc",
|
25 |
+
},
|
26 |
+
secondary: {
|
27 |
+
main: "#b827fc",
|
28 |
+
},
|
29 |
+
},
|
30 |
+
},
|
31 |
+
dark: {
|
32 |
+
palette: {
|
33 |
+
primary: {
|
34 |
+
main: "#2c90fc",
|
35 |
+
},
|
36 |
+
secondary: {
|
37 |
+
main: "#b827fc",
|
38 |
+
},
|
39 |
+
},
|
40 |
+
},
|
41 |
+
},
|
42 |
+
typography: {
|
43 |
+
...roboto.style,
|
44 |
+
h1: {
|
45 |
+
fontSize: "5.25em",
|
46 |
+
},
|
47 |
+
},
|
48 |
+
components: {
|
49 |
+
MuiLink: {
|
50 |
+
styleOverrides: {
|
51 |
+
root: {
|
52 |
+
textDecoration: "none",
|
53 |
+
":hover": {
|
54 |
+
textDecoration: "underline",
|
55 |
+
},
|
56 |
+
},
|
57 |
+
},
|
58 |
+
},
|
59 |
+
},
|
60 |
+
});
|
61 |
+
|
62 |
+
export default theme;
|
src/pages/_app.tsx
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import Head from "next/head";
|
2 |
+
import { AppProps } from "next/app";
|
3 |
+
import { Experimental_CssVarsProvider as CssVarsProvider } from "@mui/material/styles";
|
4 |
+
import CssBaseline from "@mui/material/CssBaseline";
|
5 |
+
import { CacheProvider, EmotionCache } from "@emotion/react";
|
6 |
+
import theme from "@/lib/theme";
|
7 |
+
import createEmotionCache from "@/lib/createEmotionCache";
|
8 |
+
|
9 |
+
// Client-side cache, shared for the whole session of the user in the browser.
|
10 |
+
const clientSideEmotionCache = createEmotionCache();
|
11 |
+
|
12 |
+
export interface MyAppProps extends AppProps {
|
13 |
+
emotionCache?: EmotionCache;
|
14 |
+
}
|
15 |
+
|
16 |
+
export default function MyApp(props: MyAppProps) {
|
17 |
+
const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;
|
18 |
+
return (
|
19 |
+
<CacheProvider value={emotionCache}>
|
20 |
+
<Head>
|
21 |
+
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
22 |
+
</Head>
|
23 |
+
<CssVarsProvider defaultMode="system" theme={theme}>
|
24 |
+
<CssBaseline />
|
25 |
+
<Component {...pageProps} />
|
26 |
+
</CssVarsProvider>
|
27 |
+
</CacheProvider>
|
28 |
+
);
|
29 |
+
}
|
src/pages/_document.tsx
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Html, Head, Main, NextScript } from 'next/document'
|
2 |
+
|
3 |
+
export default function Document() {
|
4 |
+
return (
|
5 |
+
<Html lang="en">
|
6 |
+
<Head />
|
7 |
+
<body>
|
8 |
+
<Main />
|
9 |
+
<NextScript />
|
10 |
+
</body>
|
11 |
+
</Html>
|
12 |
+
)
|
13 |
+
}
|
src/pages/api/env.ts
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import process from "node:process";
|
2 |
+
import { NextApiRequest, NextApiResponse } from "next";
|
3 |
+
|
4 |
+
export default async function handler(
|
5 |
+
request: NextApiRequest,
|
6 |
+
response: NextApiResponse
|
7 |
+
) {
|
8 |
+
const exampleSecret = process.env.HF_EXAMPLE_SECRET;
|
9 |
+
|
10 |
+
return response.status(200).json({ HF_EXAMPLE_SECRET: exampleSecret });
|
11 |
+
}
|
src/pages/index.tsx
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import Head from "next/head";
|
2 |
+
import Container from "@mui/material/Container";
|
3 |
+
import Footer from "@/components/footer";
|
4 |
+
import Title from "@/components/title";
|
5 |
+
import Huggingface from "@/components/huggingface/huggingface";
|
6 |
+
import GettingStarted from "@/components/getting-started";
|
7 |
+
import ExampleComponents from "@/components/example-components";
|
8 |
+
import { Stack } from "@mui/material";
|
9 |
+
import { DividerBox } from "@/components/base/boxes";
|
10 |
+
|
11 |
+
export default function Home() {
|
12 |
+
return (
|
13 |
+
<>
|
14 |
+
<Head>
|
15 |
+
<title>nextjs-docker-starter</title>
|
16 |
+
<link rel="icon" href="/favicon.ico" />
|
17 |
+
<meta name="description" content="Next.js in Docker on π€ Spaces" />
|
18 |
+
</Head>
|
19 |
+
|
20 |
+
<Container component="main" sx={{ minHeight: "90vh" }}>
|
21 |
+
<Stack spacing={4} useFlexGap>
|
22 |
+
<Title />
|
23 |
+
|
24 |
+
<GettingStarted />
|
25 |
+
|
26 |
+
<DividerBox />
|
27 |
+
|
28 |
+
<ExampleComponents />
|
29 |
+
</Stack>
|
30 |
+
</Container>
|
31 |
+
|
32 |
+
<Footer />
|
33 |
+
</>
|
34 |
+
);
|
35 |
+
}
|
tsconfig.json
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"compilerOptions": {
|
3 |
+
"target": "es5",
|
4 |
+
"lib": ["dom", "dom.iterable", "esnext"],
|
5 |
+
"allowJs": true,
|
6 |
+
"skipLibCheck": true,
|
7 |
+
"strict": true,
|
8 |
+
"forceConsistentCasingInFileNames": true,
|
9 |
+
"noEmit": true,
|
10 |
+
"esModuleInterop": true,
|
11 |
+
"module": "esnext",
|
12 |
+
"moduleResolution": "node",
|
13 |
+
"resolveJsonModule": true,
|
14 |
+
"isolatedModules": true,
|
15 |
+
"jsx": "preserve",
|
16 |
+
"incremental": true,
|
17 |
+
"paths": {
|
18 |
+
"@/*": ["./src/*"]
|
19 |
+
}
|
20 |
+
},
|
21 |
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
22 |
+
"exclude": ["node_modules"]
|
23 |
+
}
|