Spaces:
Running
Running
Commit
β’
b93a813
1
Parent(s):
2ccf6f7
add support for Docker
Browse files- README.md +1 -1
- public/index.html +8 -1
- public/placeholder.html +1 -1
- src/createSpace.mts +3 -1
- src/generateFiles.mts +5 -1
- src/getReactApp.mts +61 -0
- src/getWebApp.mts +14 -7
- src/isReactAppPrompt.mts +9 -0
README.md
CHANGED
@@ -24,7 +24,7 @@ http://localhost:7860/?prompt=A%20simple%20page%20to%20compute%20the%20BMI%20(us
|
|
24 |
```bash
|
25 |
nvm use
|
26 |
npm i
|
27 |
-
|
28 |
```
|
29 |
|
30 |
## Building and running with Docker
|
|
|
24 |
```bash
|
25 |
nvm use
|
26 |
npm i
|
27 |
+
npm run start
|
28 |
```
|
29 |
|
30 |
## Building and running with Docker
|
public/index.html
CHANGED
@@ -53,7 +53,7 @@
|
|
53 |
</p>
|
54 |
<button
|
55 |
class="btn disabled:text-stone-400"
|
56 |
-
@click="open = true, prompt = promptDraft, state = state === 'stopped' ? 'loading' : 'stopped', state === 'streaming' ? stopGeneration() : true"
|
57 |
:class="promptDraft.length < minPromptSize ? 'btn-neutral' : state === 'stopped' ? 'btn-accent' : 'btn-warning'"
|
58 |
:disabled="promptDraft.length < minPromptSize"
|
59 |
>
|
@@ -141,16 +141,23 @@
|
|
141 |
}
|
142 |
|
143 |
function app() {
|
|
|
|
|
|
|
144 |
return {
|
145 |
open: false,
|
146 |
promptDraft:
|
147 |
new URLSearchParams(window.location.search).get("prompt") || '',
|
148 |
prompt: "",
|
|
|
149 |
size: 0,
|
150 |
minPromptSize: 16, // if you change this, you will need to also change in src/index.mts
|
151 |
timeoutInSec: 15, // time before we determine something went wrong
|
152 |
state: "stopped",
|
153 |
lastTokenAt: +new Date(),
|
|
|
|
|
|
|
154 |
init() {
|
155 |
setInterval(() => {
|
156 |
if (this.state === "stopped") {
|
|
|
53 |
</p>
|
54 |
<button
|
55 |
class="btn disabled:text-stone-400"
|
56 |
+
@click="open = true, saveToken(token), prompt = promptDraft, state = state === 'stopped' ? 'loading' : 'stopped', state === 'streaming' ? stopGeneration() : true"
|
57 |
:class="promptDraft.length < minPromptSize ? 'btn-neutral' : state === 'stopped' ? 'btn-accent' : 'btn-warning'"
|
58 |
:disabled="promptDraft.length < minPromptSize"
|
59 |
>
|
|
|
141 |
}
|
142 |
|
143 |
function app() {
|
144 |
+
|
145 |
+
const storageKey = "SPACE_FACTORY_ACCESS_TOKEN"
|
146 |
+
|
147 |
return {
|
148 |
open: false,
|
149 |
promptDraft:
|
150 |
new URLSearchParams(window.location.search).get("prompt") || '',
|
151 |
prompt: "",
|
152 |
+
token: localStorage.getItem(storageKey) || "",
|
153 |
size: 0,
|
154 |
minPromptSize: 16, // if you change this, you will need to also change in src/index.mts
|
155 |
timeoutInSec: 15, // time before we determine something went wrong
|
156 |
state: "stopped",
|
157 |
lastTokenAt: +new Date(),
|
158 |
+
saveToken(token) {
|
159 |
+
localStorage.setItem(storageKey, token)
|
160 |
+
},
|
161 |
init() {
|
162 |
setInterval(() => {
|
163 |
if (this.state === "stopped") {
|
public/placeholder.html
CHANGED
@@ -8,7 +8,7 @@
|
|
8 |
<div class="hero min-h-screen bg-stone-100">
|
9 |
<div class="hero-content text-center">
|
10 |
<div class="flex flex-col max-w-xl space-y-6">
|
11 |
-
<h1 class="font-bold text-stone-600 mb-4">
|
12 |
</div>
|
13 |
</div>
|
14 |
</div>
|
|
|
8 |
<div class="hero min-h-screen bg-stone-100">
|
9 |
<div class="hero-content text-center">
|
10 |
<div class="flex flex-col max-w-xl space-y-6">
|
11 |
+
<h1 class="font-bold text-stone-600 mb-4">Waiting for content..</h1>
|
12 |
</div>
|
13 |
</div>
|
14 |
</div>
|
src/createSpace.mts
CHANGED
@@ -35,7 +35,9 @@ export const createSpace = async (files: RepoFile[], token: string) => {
|
|
35 |
credentials,
|
36 |
license: "mit",
|
37 |
sdk:
|
38 |
-
files.some(file => file.path.includes("
|
|
|
|
|
39 |
? "streamlit"
|
40 |
: "static" // "streamlit" | "gradio" | "docker" | "static";
|
41 |
});
|
|
|
35 |
credentials,
|
36 |
license: "mit",
|
37 |
sdk:
|
38 |
+
files.some(file => file.path.includes("Dockerfile"))
|
39 |
+
? "docker"
|
40 |
+
: files.some(file => file.path.includes("app.py"))
|
41 |
? "streamlit"
|
42 |
: "static" // "streamlit" | "gradio" | "docker" | "static";
|
43 |
});
|
src/generateFiles.mts
CHANGED
@@ -4,7 +4,9 @@ import { createLlamaPrompt } from './createLlamaPrompt.mts'
|
|
4 |
import { parseTutorial } from './parseTutorial.mts'
|
5 |
import { getPythonApp} from './getPythonApp.mts'
|
6 |
import { getWebApp } from './getWebApp.mts'
|
|
|
7 |
import { isPythonAppPrompt } from './isPythonAppPrompt.mts'
|
|
|
8 |
|
9 |
export const generateFiles = async (prompt: string, token: string) => {
|
10 |
if (`${prompt}`.length < 2) {
|
@@ -14,6 +16,8 @@ export const generateFiles = async (prompt: string, token: string) => {
|
|
14 |
const { prefix, instructions } =
|
15 |
isPythonAppPrompt(prompt)
|
16 |
? getPythonApp(prompt)
|
|
|
|
|
17 |
: getWebApp(prompt)
|
18 |
|
19 |
const inputs = createLlamaPrompt(instructions) + "\nSure! Here are the source files:\n" + prefix
|
@@ -60,7 +64,7 @@ let tutorial = prefix
|
|
60 |
|
61 |
console.log("analyzing the generated instructions..")
|
62 |
const files = parseTutorial(tutorial).map(({ filename, content }) => ({
|
63 |
-
path: `${filename || ""}`.trim(),
|
64 |
content: `${content || ""}`
|
65 |
} as RepoFile))
|
66 |
.filter(res => res.path.length && res.content.length)
|
|
|
4 |
import { parseTutorial } from './parseTutorial.mts'
|
5 |
import { getPythonApp} from './getPythonApp.mts'
|
6 |
import { getWebApp } from './getWebApp.mts'
|
7 |
+
import { getReactApp } from './getReactApp.mts'
|
8 |
import { isPythonAppPrompt } from './isPythonAppPrompt.mts'
|
9 |
+
import { isReactAppPrompt } from './isReactAppPrompt.mts'
|
10 |
|
11 |
export const generateFiles = async (prompt: string, token: string) => {
|
12 |
if (`${prompt}`.length < 2) {
|
|
|
16 |
const { prefix, instructions } =
|
17 |
isPythonAppPrompt(prompt)
|
18 |
? getPythonApp(prompt)
|
19 |
+
: isReactAppPrompt(prompt)
|
20 |
+
? getReactApp(prompt)
|
21 |
: getWebApp(prompt)
|
22 |
|
23 |
const inputs = createLlamaPrompt(instructions) + "\nSure! Here are the source files:\n" + prefix
|
|
|
64 |
|
65 |
console.log("analyzing the generated instructions..")
|
66 |
const files = parseTutorial(tutorial).map(({ filename, content }) => ({
|
67 |
+
path: `${filename || ""}`.trim().replace(" ", ""),
|
68 |
content: `${content || ""}`
|
69 |
} as RepoFile))
|
70 |
.filter(res => res.path.length && res.content.length)
|
src/getReactApp.mts
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { alpine } from "./alpine.mts"
|
2 |
+
import { daisy } from "./daisy.mts"
|
3 |
+
|
4 |
+
export function getReactApp(prompt: string) {
|
5 |
+
const prefix = `# In src/main.tsx:\n\`\`\``
|
6 |
+
const instructions = [
|
7 |
+
{
|
8 |
+
role: "system",
|
9 |
+
content: [
|
10 |
+
`You are a TypeScript developer, expert at crafting NextJS and React applications, using TailwindCSS utility classes.`,
|
11 |
+
].filter(item => item).join("\n")
|
12 |
+
},
|
13 |
+
{
|
14 |
+
role: "user",
|
15 |
+
content: `Please write, file by file, the source code for a Next 12 application.
|
16 |
+
|
17 |
+
The app should be buildable when we call:
|
18 |
+
|
19 |
+
\`\`\`
|
20 |
+
npm install
|
21 |
+
npm run start
|
22 |
+
\`\`\`
|
23 |
+
|
24 |
+
And installable using a Dockerfile. Here is an example:
|
25 |
+
|
26 |
+
\`\`\`
|
27 |
+
FROM node:18
|
28 |
+
RUN useradd -o -u 1000 user
|
29 |
+
USER user
|
30 |
+
ENV HOME=/home/user \
|
31 |
+
PATH=/home/user/.local/bin:$PATH
|
32 |
+
WORKDIR $HOME/app
|
33 |
+
COPY --chown=user package*.json $HOME/app
|
34 |
+
RUN npm install
|
35 |
+
COPY --chown=user . $HOME/app
|
36 |
+
EXPOSE 7860
|
37 |
+
CMD [ "npm", "run", "start" ]
|
38 |
+
\`\`\`
|
39 |
+
|
40 |
+
Don't forget to write a valid package.json file!
|
41 |
+
|
42 |
+
Don't forget to write a README.md with the following header:
|
43 |
+
\`\`\`
|
44 |
+
---
|
45 |
+
license: apache-2.0
|
46 |
+
title: <APPNAME>
|
47 |
+
sdk: docker
|
48 |
+
emoji: π¨βπ»
|
49 |
+
colorFrom: yellow
|
50 |
+
colorTo: green
|
51 |
+
---
|
52 |
+
\`\`\`
|
53 |
+
|
54 |
+
Of course, you MUST replace <APPNAME> with a good app name!
|
55 |
+
|
56 |
+
The app is about: ${prompt}`,
|
57 |
+
}
|
58 |
+
]
|
59 |
+
|
60 |
+
return { prefix, instructions }
|
61 |
+
}
|
src/getWebApp.mts
CHANGED
@@ -1,30 +1,37 @@
|
|
1 |
import { alpine } from "./alpine.mts"
|
|
|
2 |
|
3 |
export function getWebApp(prompt: string) {
|
4 |
-
const prefix =
|
|
|
5 |
|
6 |
const instructions = [
|
7 |
{
|
8 |
role: "system",
|
9 |
content: [
|
10 |
`You are a JavaScript developer, expert at crafting applications using AlpineJS, DaisyUI and Tailwind.`,
|
11 |
-
`Here is an extract from the
|
12 |
-
alpine
|
|
|
|
|
13 |
].filter(item => item).join("\n")
|
14 |
},
|
15 |
{
|
16 |
role: "user",
|
17 |
content: `Please write, file by file, the source code for a HTML JS app.
|
18 |
|
19 |
-
|
20 |
- AlpineJS (use "https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js")
|
21 |
- DaisyUI (use "https://cdn.jsdelivr.net/npm/[email protected]/dist/full.css")
|
22 |
- Tailwind (use "https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio")
|
23 |
-
|
24 |
-
But you can optionally also load those:
|
25 |
- Three.js (use "https://cdnjs.cloudflare.com/ajax/libs/three.js/0.156.1/three.min.js")
|
26 |
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
28 |
|
29 |
Don't forget to write a README.md with the following header:
|
30 |
\`\`\`
|
|
|
1 |
import { alpine } from "./alpine.mts"
|
2 |
+
import { daisy } from "./daisy.mts"
|
3 |
|
4 |
export function getWebApp(prompt: string) {
|
5 |
+
const prefix = `# In index.html:\n\`\`\`
|
6 |
+
<html><head><link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/full.css" rel="stylesheet" type="text/css" /><script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script><script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio"></script><script defer src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.156.1/three.min.js"></script><script type="module" src="main.js"></script><title>`
|
7 |
|
8 |
const instructions = [
|
9 |
{
|
10 |
role: "system",
|
11 |
content: [
|
12 |
`You are a JavaScript developer, expert at crafting applications using AlpineJS, DaisyUI and Tailwind.`,
|
13 |
+
`Here is an extract from the AlpineJS documentation:`,
|
14 |
+
alpine,
|
15 |
+
`Here is an extract from the DaisyUI documentation:`,
|
16 |
+
daisy
|
17 |
].filter(item => item).join("\n")
|
18 |
},
|
19 |
{
|
20 |
role: "user",
|
21 |
content: `Please write, file by file, the source code for a HTML JS app.
|
22 |
|
23 |
+
Here are some recommended librairies:
|
24 |
- AlpineJS (use "https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js")
|
25 |
- DaisyUI (use "https://cdn.jsdelivr.net/npm/[email protected]/dist/full.css")
|
26 |
- Tailwind (use "https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio")
|
|
|
|
|
27 |
- Three.js (use "https://cdnjs.cloudflare.com/ajax/libs/three.js/0.156.1/three.min.js")
|
28 |
|
29 |
+
Those library will be globally exposed thanks to the <script> dependencies, so you do not need to write "import ... from ..".
|
30 |
+
|
31 |
+
Some remarks:
|
32 |
+
- DO NOT USE VUE.JS
|
33 |
+
|
34 |
+
Rember, you need to write the index.html but also the app.js and/or the style.css files!
|
35 |
|
36 |
Don't forget to write a README.md with the following header:
|
37 |
\`\`\`
|
src/isReactAppPrompt.mts
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export function isReactAppPrompt(prompt: string) {
|
2 |
+
const lowerCasePrompt = prompt.toLocaleLowerCase()
|
3 |
+
return lowerCasePrompt.includes("react")
|
4 |
+
|| lowerCasePrompt.includes("create react app")
|
5 |
+
|| lowerCasePrompt.includes("reactjs")
|
6 |
+
|| lowerCasePrompt.includes("next app")
|
7 |
+
|| lowerCasePrompt.includes("nextjs app")
|
8 |
+
|| lowerCasePrompt.includes("nextjs")
|
9 |
+
}
|