jbilcke-hf HF staff commited on
Commit
f42fa3f
β€’
1 Parent(s): 888548a

exhumation of this old project 🧟

Browse files
.env CHANGED
@@ -1,4 +1,6 @@
1
 
 
 
2
  # ----------- CENSORSHIP -------
3
  # Due to abuse by users, I've had to add a censorship/fingerprinting mechanism
4
  ENABLE_CENSORSHIP="false"
 
1
 
2
+ HF_API_KEY=""
3
+
4
  # ----------- CENSORSHIP -------
5
  # Due to abuse by users, I've had to add a censorship/fingerprinting mechanism
6
  ENABLE_CENSORSHIP="false"
.nvmrc CHANGED
@@ -1 +1 @@
1
- v20.9.0
 
1
+ v20.15.1
next.config.js CHANGED
@@ -1,11 +1,6 @@
1
  /** @type {import('next').NextConfig} */
2
  const nextConfig = {
3
  output: 'standalone',
4
-
5
- experimental: {
6
- serverActions: true,
7
- serverActionsBodySizeLimit: '8mb',
8
- },
9
  }
10
 
11
  module.exports = nextConfig
 
1
  /** @type {import('next').NextConfig} */
2
  const nextConfig = {
3
  output: 'standalone',
 
 
 
 
 
4
  }
5
 
6
  module.exports = nextConfig
package-lock.json CHANGED
The diff for this file is too large to render. See raw diff
 
package.json CHANGED
@@ -9,7 +9,8 @@
9
  "lint": "next lint"
10
  },
11
  "dependencies": {
12
- "@gradio/client": "1.2.1",
 
13
  "@photo-sphere-viewer/core": "5.1.7",
14
  "@photo-sphere-viewer/markers-plugin": "5.1.7",
15
  "@photo-sphere-viewer/video-plugin": "5.1.7",
@@ -31,46 +32,39 @@
31
  "@radix-ui/react-switch": "1.0.3",
32
  "@radix-ui/react-toast": "1.1.4",
33
  "@radix-ui/react-tooltip": "1.0.6",
34
- "@react-pdf/renderer": "3.1.12",
35
  "@types/node": "20.4.2",
36
  "@types/react": "18.2.15",
37
  "@types/react-dom": "18.2.7",
38
  "@types/uuid": "9.0.2",
39
- "autoprefixer": "10.4.14",
40
- "class-variance-authority": "0.6.1",
41
- "clsx": "2.0.0",
42
- "cmdk": "0.2.0",
43
  "cookies-next": "2.1.2",
44
- "date-fns": "2.30.0",
45
- "eslint": "8.45.0",
46
- "eslint-config-next": "13.4.10",
47
- "html2canvas": "1.4.1",
48
- "lucide-react": "0.260.0",
49
- "next": "13.4.10",
50
  "photo-sphere-viewer-lensflare-plugin": "1.1.1",
51
  "pick": "0.0.1",
52
- "postcss": "8.4.26",
53
  "react": "18.2.0",
54
  "react-circular-progressbar": "2.1.0",
55
  "react-dom": "18.2.0",
56
  "react-photo-sphere-viewer": "3.3.5-psv5.1.4",
57
- "react-virtualized-auto-sizer": "1.0.20",
58
- "sbd": "1.0.19",
59
- "sharp": "0.32.5",
60
- "styled-components": "6.0.7",
61
- "tailwind-merge": "1.13.2",
62
- "tailwindcss": "3.3.3",
63
- "tailwindcss-animate": "1.0.6",
64
- "tesseract.js": "4.1.2",
65
- "ts-node": "10.9.1",
66
- "typescript": "5.1.6",
67
  "usehooks-ts": ".9.1",
68
  "uuid": "9.0.0",
69
- "zustand": "4.4.1"
70
  },
71
  "devDependencies": {
72
  "@types/qs": "6.9.7",
73
- "@types/react-virtualized": "9.21.22",
74
- "@types/sbd": "1.0.3"
75
  }
76
  }
 
9
  "lint": "next lint"
10
  },
11
  "dependencies": {
12
+ "@gradio/client": "1.5.0",
13
+ "@huggingface/inference": "^2.8.0",
14
  "@photo-sphere-viewer/core": "5.1.7",
15
  "@photo-sphere-viewer/markers-plugin": "5.1.7",
16
  "@photo-sphere-viewer/video-plugin": "5.1.7",
 
32
  "@radix-ui/react-switch": "1.0.3",
33
  "@radix-ui/react-toast": "1.1.4",
34
  "@radix-ui/react-tooltip": "1.0.6",
 
35
  "@types/node": "20.4.2",
36
  "@types/react": "18.2.15",
37
  "@types/react-dom": "18.2.7",
38
  "@types/uuid": "9.0.2",
39
+ "autoprefixer": "10.4.20",
40
+ "class-variance-authority": "0.7.0",
41
+ "clsx": "2.1.1",
42
+ "cmdk": "^1.0.0",
43
  "cookies-next": "2.1.2",
44
+ "eslint": "^8.57.0",
45
+ "eslint-config-next": "^14.2.5",
46
+ "lucide-react": "0.427.0",
47
+ "next": "14.2.5",
 
 
48
  "photo-sphere-viewer-lensflare-plugin": "1.1.1",
49
  "pick": "0.0.1",
50
+ "postcss": "8.4.41",
51
  "react": "18.2.0",
52
  "react-circular-progressbar": "2.1.0",
53
  "react-dom": "18.2.0",
54
  "react-photo-sphere-viewer": "3.3.5-psv5.1.4",
55
+ "sharp": "0.33.4",
56
+ "styled-components": "6.1.12",
57
+ "tailwind-merge": "2.5.2",
58
+ "tailwindcss": "3.4.9",
59
+ "tailwindcss-animate": "1.0.7",
60
+ "ts-node": "^10.9.2",
61
+ "typescript": "5.5.4",
 
 
 
62
  "usehooks-ts": ".9.1",
63
  "uuid": "9.0.0",
64
+ "zustand": "4.5.4"
65
  },
66
  "devDependencies": {
67
  "@types/qs": "6.9.7",
68
+ "@types/react-virtualized": "9.21.22"
 
69
  }
70
  }
src/app/engine/getPanoramaFlux.ts ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use server"
2
+
3
+ import { HfInference, HfInferenceEndpoint } from '@huggingface/inference'
4
+
5
+ import { filterOutBadWords } from "./censorship"
6
+
7
+ export async function getPanoramaFlux({
8
+ prompt,
9
+ }: {
10
+ prompt: string
11
+ }): Promise<string> {
12
+ if (!prompt) {
13
+ console.error(`cannot call the rendering API without a prompt, aborting..`)
14
+ throw new Error(`cannot call the rendering API without a prompt, aborting..`)
15
+ }
16
+
17
+ prompt = [
18
+ `hdri view`,
19
+ `highly detailed`,
20
+ `intricate details`,
21
+ filterOutBadWords(prompt)
22
+ ].join(', ')
23
+
24
+
25
+ console.log(`calling API with prompt: ${prompt}`)
26
+
27
+ const hf: HfInferenceEndpoint = new HfInference(
28
+ `${process.env.HF_API_KEY}`
29
+ )
30
+
31
+ const blob: Blob = await hf.textToImage({
32
+ model: "<put a 360Β° flux model here>",
33
+ inputs: prompt,
34
+ parameters: {
35
+ height: 1024,
36
+ width: 2048,
37
+
38
+ // this triggers the following exception:
39
+ // Error: __call__() got an unexpected keyword argument 'negative_prompt'
40
+ // negative_prompt: request.prompts.image.negative || '',
41
+
42
+ /**
43
+ * The number of denoising steps. More denoising steps usually lead to a higher quality image at the expense of slower inference.
44
+ */
45
+ // num_inference_steps?: number;
46
+ /**
47
+ * Guidance scale: Higher guidance scale encourages to generate images that are closely linked to the text `prompt`, usually at the expense of lower image quality.
48
+ */
49
+ // guidance_scale?: number;
50
+ },
51
+ })
52
+
53
+ // console.log('output from Hugging Face Inference API:', blob)
54
+
55
+ const buffer = Buffer.from(await blob.arrayBuffer())
56
+
57
+ return `data:${blob.type || 'image/jpeg'};base64,${buffer.toString('base64')}`
58
+ }
src/app/engine/getPanoramaSDXL.ts ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use server"
2
+
3
+ import { Client } from "@gradio/client"
4
+
5
+ export async function getPanoramaSDXL({
6
+ prompt
7
+ }: {
8
+ prompt: string
9
+ }): Promise<string> {
10
+ const app = await Client.connect("jbilcke-hf/panorama-api")
11
+
12
+ const result = await app.predict("/predict", [
13
+ process.env.MICRO_SERVICE_SECRET_TOKEN || "",
14
+ prompt,
15
+ undefined
16
+ ])
17
+ console.log(`result:`, result)
18
+
19
+ return ""
20
+ }
src/app/engine/render.ts DELETED
@@ -1,245 +0,0 @@
1
- "use server"
2
-
3
-
4
- import { RenderRequest, RenderedScene } from "@/types"
5
- import { generateSeed } from "@/lib/generateSeed"
6
- import { sleep } from "@/lib/sleep"
7
- import { filterOutBadWords } from "./censorship"
8
-
9
- export async function newRender({
10
- prompt,
11
- clearCache,
12
- }: {
13
- prompt: string
14
- clearCache: boolean
15
- }) {
16
- if (!prompt) {
17
- console.error(`cannot call the rendering API without a prompt, aborting..`)
18
- throw new Error(`cannot call the rendering API without a prompt, aborting..`)
19
- }
20
-
21
- prompt = [
22
- `hdri view`,
23
- `highly detailed`,
24
- `intricate details`,
25
- filterOutBadWords(prompt)
26
- ].join(', ')
27
-
28
- // return await Gorgon.get(cacheKey, async () => {
29
-
30
- let defaulResult: RenderedScene = {
31
- renderId: "",
32
- status: "error",
33
- assetUrl: "",
34
- alt: prompt || "",
35
- maskUrl: "",
36
- error: "failed to fetch the data",
37
- segments: []
38
- }
39
-
40
- try {
41
- console.log(`calling Gradio space with prompt: ${prompt}`)
42
-
43
- const request = {
44
- prompt,
45
- nbFrames: 1, // when nbFrames is 1, we will only generate static images
46
- nbSteps: 35, // 20 = fast, 30 = better, 50 = best
47
- actionnables: [],
48
- segmentation: "disabled", // one day we will remove this param, to make it automatic
49
- width: 1024,
50
- height: 768,
51
-
52
- // on VideoQuest we use an aggressive setting: 4X upscaling
53
- // this generates images that can be slow to load, but that's
54
- // not too much of an issue since we use async loading
55
- upscalingFactor: 1,
56
-
57
- // note that we never disable the cache completely for VideoQuest
58
- // that's because in the feedbacks people prefer speed to avoid frustration
59
- cache: clearCache ? "renew" : "use",
60
-
61
- } as Partial<RenderRequest>
62
-
63
- console.table(request)
64
-
65
- if (renderingEngine === "REPLICATE") {
66
- if (!replicateToken) {
67
- throw new Error(`you need to configure your REPLICATE_API_TOKEN in order to use the REPLICATE rendering engine`)
68
- }
69
- if (!replicateModel) {
70
- throw new Error(`you need to configure your REPLICATE_API_MODEL in order to use the REPLICATE rendering engine`)
71
- }
72
- if (!replicateModelVersion) {
73
- throw new Error(`you need to configure your REPLICATE_API_MODEL_VERSION in order to use the REPLICATE rendering engine`)
74
- }
75
- const replicate = new Replicate({ auth: replicateToken })
76
-
77
- // console.log("Calling replicate..")
78
- const seed = generateSeed()
79
- const prediction = await replicate.predictions.create({
80
- version: replicateModelVersion,
81
- input: { prompt, seed }
82
- })
83
-
84
- // console.log("prediction:", prediction)
85
-
86
- // no need to reply straight away: good things take time
87
- // also our friends at Replicate won't like it if we spam them with requests
88
- await sleep(12000)
89
-
90
- return {
91
- renderId: prediction.id,
92
- status: "pending",
93
- assetUrl: "",
94
- alt: prompt,
95
- error: prediction.error,
96
- maskUrl: "",
97
- segments: []
98
- } as RenderedScene
99
- } else {
100
-
101
- const res = await fetch(`${apiUrl}/render`, {
102
- method: "POST",
103
- headers: {
104
- Accept: "application/json",
105
- "Content-Type": "application/json",
106
- // Authorization: `Bearer ${process.env.VC_SECRET_ACCESS_TOKEN}`,
107
- },
108
- body: JSON.stringify(request),
109
- cache: 'no-store',
110
- // we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
111
- // next: { revalidate: 1 }
112
- })
113
-
114
- // console.log("res:", res)
115
- // The return value is *not* serialized
116
- // You can return Date, Map, Set, etc.
117
-
118
- // Recommendation: handle errors
119
- if (res.status !== 200) {
120
- // This will activate the closest `error.js` Error Boundary
121
- throw new Error('Failed to fetch data')
122
- }
123
-
124
- const response = (await res.json()) as RenderedScene
125
- // console.log("response:", response)
126
- return response
127
- }
128
- } catch (err) {
129
- console.log("request failed:", err)
130
- console.error(err)
131
- // Gorgon.clear(cacheKey)
132
- return defaulResult
133
- }
134
-
135
- // }, cacheDurationInSec * 1000)
136
- }
137
-
138
- export async function getRender(renderId: string) {
139
- if (!renderId) {
140
- console.error(`cannot call the rendering API without a renderId, aborting..`)
141
- throw new Error(`cannot call the rendering API without a renderId, aborting..`)
142
- }
143
-
144
- let defaulResult: RenderedScene = {
145
- renderId: "",
146
- status: "pending",
147
- assetUrl: "",
148
- alt: "",
149
- maskUrl: "",
150
- error: "",
151
- segments: []
152
- }
153
-
154
- try {
155
-
156
-
157
- if (renderingEngine === "REPLICATE") {
158
- if (!replicateToken) {
159
- throw new Error(`you need to configure your REPLICATE_API_TOKEN in order to use the REPLICATE rendering engine`)
160
- }
161
- if (!replicateModel) {
162
- throw new Error(`you need to configure your REPLICATE_API_MODEL in order to use the REPLICATE rendering engine`)
163
- }
164
-
165
- // const replicate = new Replicate({ auth: replicateToken })
166
-
167
- // console.log("Calling replicate..")
168
- // const prediction = await replicate.predictions.get(renderId)
169
- // console.log("Prediction:", prediction)
170
-
171
- // console.log(`calling GET https://api.replicate.com/v1/predictions/${renderId}`)
172
- const res = await fetch(`https://api.replicate.com/v1/predictions/${renderId}`, {
173
- method: "GET",
174
- headers: {
175
- // Accept: "application/json",
176
- // "Content-Type": "application/json",
177
- Authorization: `Token ${replicateToken}`,
178
- },
179
- cache: 'no-store',
180
- // we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
181
- // next: { revalidate: 1 }
182
- })
183
-
184
- // console.log("res:", res)
185
- // The return value is *not* serialized
186
- // You can return Date, Map, Set, etc.
187
-
188
- // Recommendation: handle errors
189
- if (res.status !== 200) {
190
- // This will activate the closest `error.js` Error Boundary
191
- throw new Error('Failed to fetch data')
192
- }
193
-
194
- const response = (await res.json()) as any
195
- // console.log("response:", response)
196
-
197
- return {
198
- renderId,
199
- status: response?.error ? "error" : response?.status === "succeeded" ? "completed" : "pending",
200
- assetUrl: `${response?.output || ""}`,
201
- alt: `${response?.input?.prompt || ""}`,
202
- error: `${response?.error || ""}`,
203
- maskUrl: "",
204
- segments: []
205
- } as RenderedScene
206
- } else {
207
-
208
- // console.log(`calling GET ${apiUrl}/render with renderId: ${renderId}`)
209
- const res = await fetch(`${apiUrl}/render/${renderId}`, {
210
- method: "GET",
211
- headers: {
212
- Accept: "application/json",
213
- "Content-Type": "application/json",
214
- Authorization: `Bearer ${process.env.VC_SECRET_ACCESS_TOKEN}`,
215
- },
216
- cache: 'no-store',
217
- // we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
218
- // next: { revalidate: 1 }
219
- })
220
-
221
- // console.log("res:", res)
222
- // The return value is *not* serialized
223
- // You can return Date, Map, Set, etc.
224
-
225
- // Recommendation: handle errors
226
- if (res.status !== 200) {
227
- // This will activate the closest `error.js` Error Boundary
228
- throw new Error('Failed to fetch data')
229
- }
230
-
231
- const response = (await res.json()) as RenderedScene
232
- // console.log("response:", response)
233
-
234
- return response
235
- }
236
- } catch (err) {
237
- console.error(err)
238
- defaulResult.status = "error"
239
- defaulResult.error = `${err}`
240
- // Gorgon.clear(cacheKey)
241
- return defaulResult
242
- }
243
-
244
- // }, cacheDurationInSec * 1000)
245
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/app/engine/upscaleImage.ts ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use server"
2
+
3
+ import { Client } from "@gradio/client"
4
+
5
+ export async function upscalePanorama({
6
+ image,
7
+ prompt
8
+ }: {
9
+ image: string
10
+ prompt: string
11
+ }): Promise<string> {
12
+ const app = await Client.connect("jbilcke-hf/clarity-upscaler-api")
13
+
14
+ // const dataUri = await fetch(`data:image/jpeg;base64,${base64Data}`)
15
+ const dataUri = await fetch(image)
16
+
17
+ const imageBlob = await dataUri.blob()
18
+
19
+ const result = await app.predict("/predict", {
20
+ "Secret Token": process.env.MICRO_SERVICE_SECRET_TOKEN || "",
21
+
22
+ // convert the base64 image to blob
23
+ "Image": imageBlob,
24
+
25
+ Prompt: `360Β° HDRI panorama photo, ${prompt}`,
26
+
27
+ "Negative Prompt": "blurry, cropped",
28
+
29
+ "Scalue Factor": 2,
30
+
31
+ "Dynamic": 6,
32
+
33
+ "Creativity": 0.35,
34
+
35
+ "Resemblance": 0.6,
36
+
37
+ "tiling_width": 112,
38
+
39
+ "tiling_height": 144,
40
+
41
+ // epicrealism_naturalSinRC1VAE.safetensors [84d76a0328]', 'juggernaut_reborn.safetensors [338b85bc4f]', 'flat2DAnimerge_v45Sharp.safetensors'],
42
+ "sd_model": "juggernaut_reborn.safetensors [338b85bc4f]",
43
+ "scheduler": "DPM++ 3M SDE Karras",
44
+ })
45
+ /*
46
+
47
+ inputs.append(gr.Slider(
48
+ label="Num Inference Steps", info='''Number of denoising steps''', value=18,
49
+ minimum=1, maximum=100, step=1,
50
+ ))
51
+
52
+ inputs.append(gr.Number(
53
+ label="Seed", info='''Random seed. Leave blank to randomize the seed''', value=1337
54
+ ))
55
+
56
+ inputs.append(gr.Checkbox(
57
+ label="Downscaling", info='''Downscale the image before upscaling. Can improve quality and speed for images with high resolution but lower quality''', value=False
58
+ ))
59
+
60
+ inputs.append(gr.Number(
61
+ label="Downscaling Resolution", info='''Downscaling resolution''', value=768
62
+ ))
63
+
64
+ inputs.append(gr.Textbox(
65
+ label="Lora Links", info='''Link to a lora file you want to use in your upscaling. Multiple links possible, seperated by comma'''
66
+ ))
67
+
68
+ inputs.append(gr.Textbox(
69
+ label="Custom Sd Model", info='''Link to a custom safetensors checkpoint file you want to use in your upscaling. Will overwrite sd_model checkpoint.'''
70
+ ))
71
+
72
+ ])
73
+ */
74
+ console.log(`result:`, result)
75
+
76
+ return ""
77
+ }
src/app/firehose/{page.tsx β†’ page.txt} RENAMED
@@ -1,6 +1,6 @@
1
  "use client"
2
 
3
- import { useEffect, useState, useTransition } from "react"
4
 
5
  import { Post } from "@/types"
6
  import { cn } from "@/lib/utils"
@@ -13,7 +13,7 @@ import { Delete } from "./delete"
13
  import Link from "next/link"
14
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
15
 
16
- export default function FirehosePage() {
17
  const searchParams = useSearchParams()
18
  const [_isPending, startTransition] = useTransition()
19
  const [posts, setPosts] = useState<Post[]>([])
@@ -35,12 +35,7 @@ export default function FirehosePage() {
35
  }
36
 
37
  return (
38
- <TooltipProvider delayDuration={100}>
39
- <div className={cn(
40
- `light fixed w-full h-full flex flex-col items-center bg-slate-300 text-slate-800`,
41
- ``,
42
- actionman.className
43
- )}>
44
  <div className="w-full flex flex-col items-center overflow-y-scroll">
45
  <div className="flex flex-col space-y-2 pt-18 mb-6">
46
  <h1 className="text-4xl md:text-6xl lg:text-[70px] xl:text-[100px] text-cyan-700">🌐 Text-to-panorama</h1>
@@ -96,6 +91,20 @@ export default function FirehosePage() {
96
  </div>
97
  </div>
98
  <Delete post={toDelete} moderationKey={moderationKey} onDelete={handleOnDelete} />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  </div>
100
  </TooltipProvider>
101
  )
 
1
  "use client"
2
 
3
+ import { Suspense, useEffect, useState, useTransition } from "react"
4
 
5
  import { Post } from "@/types"
6
  import { cn } from "@/lib/utils"
 
13
  import Link from "next/link"
14
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
15
 
16
+ function PageContent() {
17
  const searchParams = useSearchParams()
18
  const [_isPending, startTransition] = useTransition()
19
  const [posts, setPosts] = useState<Post[]>([])
 
35
  }
36
 
37
  return (
38
+ <>
 
 
 
 
 
39
  <div className="w-full flex flex-col items-center overflow-y-scroll">
40
  <div className="flex flex-col space-y-2 pt-18 mb-6">
41
  <h1 className="text-4xl md:text-6xl lg:text-[70px] xl:text-[100px] text-cyan-700">🌐 Text-to-panorama</h1>
 
91
  </div>
92
  </div>
93
  <Delete post={toDelete} moderationKey={moderationKey} onDelete={handleOnDelete} />
94
+ </>
95
+ )
96
+ }
97
+
98
+
99
+ export default function FirehosePage() {
100
+ return (
101
+ <TooltipProvider delayDuration={100}>
102
+ <div className={cn(
103
+ `light fixed w-full h-full flex flex-col items-center bg-slate-300 text-slate-800`,
104
+ ``,
105
+ actionman.className
106
+ )}>
107
+ <Suspense><PageContent /></Suspense>
108
  </div>
109
  </TooltipProvider>
110
  )
src/app/generate/page.tsx CHANGED
@@ -1,6 +1,6 @@
1
  "use client"
2
 
3
- import { useEffect, useRef, useTransition } from "react"
4
 
5
  import { cn } from "@/lib/utils"
6
  import { TopMenu } from "../interface/top-menu"
@@ -9,31 +9,22 @@ import { fonts } from "@/lib/fonts"
9
  import { useStore } from "../store"
10
  import { BottomBar } from "../interface/bottom-bar"
11
  import { SphericalImage } from "../interface/spherical-image"
12
- import { getRender, newRender } from "../engine/render"
13
- import { RenderedScene } from "@/types"
14
- import { getPost, postToCommunity } from "../engine/community"
15
  import { useSearchParams } from "next/navigation"
16
 
17
- export default function GeneratePage() {
18
  const searchParams = useSearchParams()
19
  const [_isPending, startTransition] = useTransition()
20
  const postId = (searchParams.get("postId") as string) || ""
21
 
22
  const prompt = useStore(s => s.prompt)
23
  const setPrompt = useStore(s => s.setPrompt)
24
- const setRendered = useStore(s => s.setRendered)
25
- const renderedScene = useStore(s => s.renderedScene)
26
  const isLoading = useStore(s => s.isLoading)
27
  const setLoading = useStore(s => s.setLoading)
28
 
29
- // keep a ref in sync
30
- const renderedRef = useRef<RenderedScene>()
31
- const renderedKey = JSON.stringify(renderedScene)
32
- useEffect(() => { renderedRef.current = renderedScene }, [renderedKey])
33
-
34
- const timeoutRef = useRef<any>(null)
35
-
36
- const delay = 3000
37
 
38
  // react to prompt changes
39
  useEffect(() => {
@@ -44,89 +35,21 @@ export default function GeneratePage() {
44
  // if (isLoading) { return }
45
 
46
  startTransition(async () => {
47
-
48
  try {
49
- const rendered = await newRender({ prompt, clearCache: true })
50
- setRendered(rendered)
51
- } catch (err) {
52
- console.error(err)
53
- } finally {
54
- }
55
- })
56
- }, [prompt]) // important: we need to react to preset changes too
57
-
58
-
59
- const checkStatus = () => {
60
- startTransition(async () => {
61
- clearTimeout(timeoutRef.current)
62
-
63
- if (renderedRef.current?.status === "completed") {
64
- console.log("rendering job is already completed")
65
- return
66
- }
67
-
68
- if (!renderedRef.current?.renderId || renderedRef.current?.status !== "pending") {
69
- timeoutRef.current = setTimeout(checkStatus, delay)
70
- return
71
- }
72
-
73
- try {
74
- // console.log(`Checking job status API for job ${renderedRef.current?.renderId}`)
75
- const newRendered = await getRender(renderedRef.current.renderId)
76
- if (!newRendered) {
77
- throw new Error(`getRender failed`)
78
- }
79
- // console.log("got a response!", newRendered)
80
-
81
- if (JSON.stringify(renderedRef.current) !== JSON.stringify(newRendered)) {
82
- // console.log("updated panel:", newRendered)
83
- setRendered(renderedRef.current = newRendered)
84
- }
85
- // console.log("status:", newRendered.status)
86
-
87
- if (newRendered.status === "pending") {
88
- console.log("job not finished")
89
- timeoutRef.current = setTimeout(checkStatus, delay)
90
- } else if (newRendered.status === "error" ||
91
- (newRendered.status === "completed" && !newRendered.assetUrl?.length)) {
92
- console.log(`panorama got an error and/or an empty asset url :/ "${newRendered.error}", but let's try to recover..`)
93
  setLoading(false)
94
  } else {
95
- console.log("panorama finished:", newRendered)
96
- /*
97
- let's disable the community for now
98
-
99
- try {
100
- await postToCommunity({
101
- prompt,
102
- model: "jbilcke-hf/sdxl-panorama",
103
- assetUrl: newRendered.assetUrl,
104
- })
105
- } catch (err) {
106
- console.log("failed to post to community, but it's no big deal")
107
- }
108
- */
109
- setRendered(newRendered)
110
- setLoading(false)
111
  }
112
  } catch (err) {
113
  console.error(err)
114
- timeoutRef.current = setTimeout(checkStatus, delay)
 
115
  }
116
  })
117
- }
118
-
119
- useEffect(() => {
120
- // console.log("starting timeout")
121
- clearTimeout(timeoutRef.current)
122
-
123
- // normally it should reply in < 1sec, but we could also use an interval
124
- timeoutRef.current = setTimeout(checkStatus, delay)
125
-
126
- return () => {
127
- clearTimeout(timeoutRef.current)
128
- }
129
- }, [prompt])
130
 
131
  useEffect(() => {
132
  if (!postId) {
@@ -144,15 +67,7 @@ export default function GeneratePage() {
144
  // because we are set the app to "is loading"
145
  // setPrompt(post.prompt)
146
 
147
- setRendered({
148
- renderId: postId,
149
- status: "completed",
150
- assetUrl: post.assetUrl,
151
- alt: post.prompt,
152
- error: "",
153
- maskUrl: "",
154
- segments: []
155
- })
156
  setLoading(false)
157
  } catch (err) {
158
  console.error("failed to get post: ", err)
@@ -162,19 +77,16 @@ export default function GeneratePage() {
162
  }, [postId])
163
 
164
  return (
165
- <div className="">
166
- <TopMenu />
167
  <div className={cn(
168
  `fixed inset-0 w-screen h-screen overflow-y-scroll`,
169
  fonts.actionman.className
170
  )}>
171
- {renderedScene.assetUrl ? <SphericalImage
172
- rendered={renderedScene}
173
- onEvent={(() => {}) as any}
174
  debug={true}
175
  /> : null}
176
  </div>
177
- <BottomBar />
178
  <div className={cn(
179
  `print:hidden`,
180
  `z-20 fixed inset-0`,
@@ -193,6 +105,17 @@ export default function GeneratePage() {
193
  {isLoading ? 'Generating metaverse location in the latent space..' : ''}
194
  </div>
195
  </div>
 
 
 
 
 
 
 
 
 
 
 
196
  </div>
197
  )
198
  }
 
1
  "use client"
2
 
3
+ import { Suspense, useEffect, useTransition } from "react"
4
 
5
  import { cn } from "@/lib/utils"
6
  import { TopMenu } from "../interface/top-menu"
 
9
  import { useStore } from "../store"
10
  import { BottomBar } from "../interface/bottom-bar"
11
  import { SphericalImage } from "../interface/spherical-image"
12
+ import { getPanoramaSDXL } from "../engine/getPanoramaSDXL"
13
+ import { getPost } from "../engine/community"
 
14
  import { useSearchParams } from "next/navigation"
15
 
16
+ function PageContent() {
17
  const searchParams = useSearchParams()
18
  const [_isPending, startTransition] = useTransition()
19
  const postId = (searchParams.get("postId") as string) || ""
20
 
21
  const prompt = useStore(s => s.prompt)
22
  const setPrompt = useStore(s => s.setPrompt)
23
+ const assetUrl = useStore(s => s.assetUrl)
24
+ const setAssetUrl = useStore(s => s.setAssetUrl)
25
  const isLoading = useStore(s => s.isLoading)
26
  const setLoading = useStore(s => s.setLoading)
27
 
 
 
 
 
 
 
 
 
28
 
29
  // react to prompt changes
30
  useEffect(() => {
 
35
  // if (isLoading) { return }
36
 
37
  startTransition(async () => {
 
38
  try {
39
+ const assetUrl = await getPanoramaSDXL({ prompt })
40
+ if (assetUrl) {
41
+ setAssetUrl(assetUrl)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  setLoading(false)
43
  } else {
44
+ console.log(`panorama got an error and/or an empty asset url`)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  }
46
  } catch (err) {
47
  console.error(err)
48
+ } finally {
49
+ setLoading(false)
50
  }
51
  })
52
+ }, [prompt]) // important: we need to react to preset changes too
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
  useEffect(() => {
55
  if (!postId) {
 
67
  // because we are set the app to "is loading"
68
  // setPrompt(post.prompt)
69
 
70
+ setAssetUrl(post.assetUrl)
 
 
 
 
 
 
 
 
71
  setLoading(false)
72
  } catch (err) {
73
  console.error("failed to get post: ", err)
 
77
  }, [postId])
78
 
79
  return (
80
+ <>
 
81
  <div className={cn(
82
  `fixed inset-0 w-screen h-screen overflow-y-scroll`,
83
  fonts.actionman.className
84
  )}>
85
+ {assetUrl ? <SphericalImage
86
+ assetUrl={assetUrl}
 
87
  debug={true}
88
  /> : null}
89
  </div>
 
90
  <div className={cn(
91
  `print:hidden`,
92
  `z-20 fixed inset-0`,
 
105
  {isLoading ? 'Generating metaverse location in the latent space..' : ''}
106
  </div>
107
  </div>
108
+ </>
109
+ )
110
+ }
111
+
112
+
113
+ export default function GeneratePage() {
114
+ return (
115
+ <div className="">
116
+ <Suspense><TopMenu /></Suspense>
117
+ <Suspense><PageContent /></Suspense>
118
+ <BottomBar />
119
  </div>
120
  )
121
  }
src/app/interface/display/index.tsx CHANGED
@@ -1,10 +1,9 @@
1
- import { RenderedScene } from "@/types"
2
 
3
- export function Display ({ rendered }: { rendered: RenderedScene }) {
4
  return (
5
  <>
6
  <img
7
- src={rendered.assetUrl || undefined}
8
  className="fixed w-screen top-0 left-0 right-0"
9
  />
10
  </>
 
 
1
 
2
+ export function Display ({ assetUrl }: { assetUrl: string }) {
3
  return (
4
  <>
5
  <img
6
+ src={assetUrl || undefined}
7
  className="fixed w-screen top-0 left-0 right-0"
8
  />
9
  </>
src/app/interface/spherical-image/index.tsx CHANGED
@@ -1,33 +1,27 @@
1
  "use client"
2
 
3
  import { useEffect, useRef, useState } from "react"
4
- import { PanoramaPosition, PluginConstructor, Point, Position, SphericalPosition, Viewer } from "@photo-sphere-viewer/core"
5
- import { LensflarePlugin, ReactPhotoSphereViewer } from "react-photo-sphere-viewer"
6
-
7
- import { MouseEventHandler, RenderedScene } from "@/types"
8
 
9
  import { useImageDimension } from "@/lib/useImageDimension"
10
- import { lightSourceNames } from "@/lib/lightSourceNames"
11
 
12
  type PhotoSpherePlugin = (PluginConstructor | [PluginConstructor, any])
13
 
14
  export function SphericalImage({
15
- rendered,
16
- onEvent,
17
  className,
18
  debug,
19
  }: {
20
- rendered: RenderedScene
21
- onEvent: MouseEventHandler
22
  className?: string
23
  debug?: boolean
24
  }) {
25
 
26
 
27
- const imageDimension = useImageDimension(rendered.assetUrl)
28
- const maskDimension = useImageDimension(rendered.maskUrl)
29
 
30
- const sceneConfig = JSON.stringify({ rendered, debug, imageDimension, maskDimension })
31
  const [lastSceneConfig, setLastSceneConfig] = useState<string>(sceneConfig)
32
  const rootContainerRef = useRef<HTMLDivElement>(null)
33
  const viewerContainerRef = useRef<HTMLElement>()
@@ -39,7 +33,7 @@ export function SphericalImage({
39
  const options = {
40
  defaultZoomLvl,
41
  fisheye: false, // ..no!
42
- overlay: rendered.maskUrl || undefined,
43
  overlayOpacity: debug ? 0.5 : 0,
44
  /*
45
  panoData: {
@@ -56,47 +50,6 @@ export function SphericalImage({
56
  */
57
  }
58
 
59
-
60
- const cacheRef = useRef("")
61
- useEffect(() => {
62
- const listener = (e: DragEvent) => {
63
- if (!rootContainerRef.current) { return }
64
-
65
- // TODO: check if we are currently dragging an object
66
- // if yes, then we should check if clientX and clientY are matching the
67
- const boundingRect = rootContainerRef.current.getBoundingClientRect()
68
-
69
- // abort if we are not currently dragging over our display area
70
- if (e.clientX < boundingRect.left) { return }
71
- if (e.clientX > (boundingRect.left + boundingRect.width)) { return }
72
- if (e.clientY < boundingRect.top) { return }
73
- if (e.clientY > (boundingRect.top + boundingRect.height)) { return }
74
-
75
- const containerX = e.clientX - boundingRect.left
76
- const containerY = e.clientY - boundingRect.top
77
-
78
- const relativeX = containerX / boundingRect.width
79
- const relativeY = containerY / boundingRect.height
80
-
81
- const key = `${relativeX},${relativeY}`
82
-
83
- // to avoid use
84
- if (cacheRef.current === key) {
85
- return
86
- }
87
- // console.log(`DRAG: calling onEvent("hover", ${relativeX}, ${relativeY})`)
88
-
89
- cacheRef.current = key
90
- onEvent("hover", relativeX, relativeY)
91
- }
92
-
93
- document.addEventListener('drag', listener)
94
-
95
- return () => {
96
- document.removeEventListener('drag', listener)
97
- }
98
- }, [onEvent])
99
-
100
  useEffect(() => {
101
  const task = async () => {
102
  // console.log("SphericalImage: useEffect")
@@ -114,50 +67,7 @@ export function SphericalImage({
114
  ...options,
115
  }
116
 
117
- const lensflares: { id: string; position: SphericalPosition; type: number }[] = []
118
-
119
- if (maskDimension.width && imageDimension.width) {
120
-
121
- // console.log("rendered.segments:", rendered.segments)
122
-
123
- rendered.segments
124
- .filter(segment => lightSourceNames.includes(segment.label))
125
- .forEach(light => {
126
- // console.log("light detected", light)
127
- const [x1, y1, x2, y2] = light.box
128
- const [centerX, centerY] = [(x1 + x2) / 2, (y1 + y2) / 2]
129
- // console.log("center:", { centerX, centerY })
130
- const [relativeX, relativeY] = [centerX / maskDimension.width, centerY/ maskDimension.height]
131
- // console.log("relative:", { relativeX, relativeY})
132
-
133
- const panoramaPosition: PanoramaPosition = {
134
- textureX: relativeX * imageDimension.width,
135
- textureY: relativeY * imageDimension.height
136
- }
137
- // console.log("panoramaPosition:", panoramaPosition)
138
-
139
- const position = viewer.dataHelper.textureCoordsToSphericalCoords(panoramaPosition)
140
- // console.log("sphericalPosition:", position)
141
- if ( // make sure coordinates are valid
142
- !isNaN(position.pitch) && isFinite(position.pitch) &&
143
- !isNaN(position.yaw) && isFinite(position.yaw)) {
144
- lensflares.push({
145
- id: `flare_${lensflares.length}`,
146
- position,
147
- type: 0,
148
- })
149
- }
150
- })
151
- }
152
-
153
- // console.log("lensflares:", lensflares)
154
- const lensFlarePlugin = viewer.getPlugin<LensflarePlugin>("lensflare")
155
- lensFlarePlugin.setLensflares(lensflares)
156
-
157
- // console.log("SphericalImage: calling setOptions")
158
- // console.log("SphericalImage: changing the panorama to: " + rendered.assetUrl.slice(0, 120))
159
-
160
- await viewer.setPanorama(rendered.assetUrl, {
161
  ...newOptions,
162
  showLoader: false,
163
  })
@@ -173,86 +83,24 @@ export function SphericalImage({
173
  }
174
  }
175
  task()
176
- }, [sceneConfig, rendered.assetUrl, viewerRef.current, maskDimension.width, imageDimension])
177
-
178
- const handleEvent = async (event: React.MouseEvent<HTMLDivElement, MouseEvent>, isClick: boolean) => {
179
- const rootContainer = rootContainerRef.current
180
- const viewer = viewerRef.current
181
- const viewerContainer = viewerContainerRef.current
182
-
183
- /*
184
- if (isClick) console.log(`handleEvent(${isClick})`, {
185
- " imageDimension.width": imageDimension.width,
186
- "rendered.maskUrl": rendered.maskUrl
187
- })
188
- */
189
-
190
- if (!viewer || !rootContainer || !viewerContainer || !imageDimension.width || !rendered.maskUrl) {
191
- return
192
- }
193
-
194
- const containerRect = viewerContainer.getBoundingClientRect()
195
- // if (isClick) console.log("containerRect:", containerRect)
196
-
197
- const containerY = event.clientY - containerRect.top
198
- // console.log("containerY:", containerY)
199
-
200
- const position: Position = viewer.getPosition()
201
-
202
- const viewerPosition: Point = viewer.dataHelper.sphericalCoordsToViewerCoords(position)
203
- // if (isClick) console.log("viewerPosition:", viewerPosition)
204
 
205
- // we want to ignore events that are happening in the toolbar
206
- // note that we will probably hide this toolbar at some point,
207
- // to implement our own UI
208
- if (isClick && containerY > (containerRect.height - 40)) {
209
- // console.log("we are in the toolbar.. ignoring the click")
210
- return
211
- }
212
-
213
- const panoramaPosition: PanoramaPosition = viewer.dataHelper.sphericalCoordsToTextureCoords(position)
214
-
215
- if (typeof panoramaPosition.textureX !== "number" || typeof panoramaPosition.textureY !== "number") {
216
- return
217
- }
218
-
219
- const relativeX = panoramaPosition.textureX / imageDimension.width
220
- const relativeY = panoramaPosition.textureY / imageDimension.height
221
 
222
- onEvent(isClick ? "click" : "hover", relativeX, relativeY)
223
- }
224
-
225
- if (!rendered.assetUrl) {
226
  return null
227
  }
228
 
229
  return (
230
  <div
231
  ref={rootContainerRef}
232
- onMouseMove={(event) => {
233
- handleEvent(event, false)
234
- setMouseMoved(true)
235
- }}
236
- onMouseUp={(event) => {
237
- if (!mouseMoved) {
238
- handleEvent(event, true)
239
- }
240
- setMouseMoved(false)
241
- }}
242
- onMouseDown={() => {
243
- setMouseMoved(false)
244
- }}
245
  >
246
  <ReactPhotoSphereViewer
247
- src={rendered.assetUrl}
248
  container=""
249
  containerClass={className}
250
 
251
  height="100vh"
252
  width="100%"
253
-
254
- // to access a plugin we must use viewer.getPlugin()
255
- plugins={[[LensflarePlugin, { lensflares: [] }]]}
256
 
257
  {...options}
258
 
@@ -263,24 +111,7 @@ export function SphericalImage({
263
  }}
264
 
265
  onReady={(instance) => {
266
- viewerRef.current = instance
267
  viewerContainerRef.current = instance.container
268
-
269
- /*
270
- const markersPlugs = instance.getPlugin(MarkersPlugin);
271
- if (!markersPlugs)
272
- return;
273
- markersPlugs.addMarker({
274
- id: "imageLayer2",
275
- imageLayer: "drone.png",
276
- size: { width: 220, height: 220 },
277
- position: { yaw: '130.5deg', pitch: '-0.1deg' },
278
- tooltip: "Image embedded in the scene"
279
- });
280
- markersPlugs.addEventListener("select-marker", () => {
281
- console.log("asd");
282
- });
283
- */
284
  }}
285
 
286
  />
 
1
  "use client"
2
 
3
  import { useEffect, useRef, useState } from "react"
4
+ import { PluginConstructor, Viewer } from "@photo-sphere-viewer/core"
5
+ import { ReactPhotoSphereViewer } from "react-photo-sphere-viewer"
 
 
6
 
7
  import { useImageDimension } from "@/lib/useImageDimension"
 
8
 
9
  type PhotoSpherePlugin = (PluginConstructor | [PluginConstructor, any])
10
 
11
  export function SphericalImage({
12
+ assetUrl,
 
13
  className,
14
  debug,
15
  }: {
16
+ assetUrl: string
 
17
  className?: string
18
  debug?: boolean
19
  }) {
20
 
21
 
22
+ const imageDimension = useImageDimension(assetUrl)
 
23
 
24
+ const sceneConfig = JSON.stringify({ assetUrl, debug, imageDimension })
25
  const [lastSceneConfig, setLastSceneConfig] = useState<string>(sceneConfig)
26
  const rootContainerRef = useRef<HTMLDivElement>(null)
27
  const viewerContainerRef = useRef<HTMLElement>()
 
33
  const options = {
34
  defaultZoomLvl,
35
  fisheye: false, // ..no!
36
+ overlay: undefined,
37
  overlayOpacity: debug ? 0.5 : 0,
38
  /*
39
  panoData: {
 
50
  */
51
  }
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  useEffect(() => {
54
  const task = async () => {
55
  // console.log("SphericalImage: useEffect")
 
67
  ...options,
68
  }
69
 
70
+ await viewer.setPanorama(assetUrl, {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  ...newOptions,
72
  showLoader: false,
73
  })
 
83
  }
84
  }
85
  task()
86
+ }, [sceneConfig, assetUrl, viewerRef.current, imageDimension])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
+ if (!assetUrl) {
 
 
 
90
  return null
91
  }
92
 
93
  return (
94
  <div
95
  ref={rootContainerRef}
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  >
97
  <ReactPhotoSphereViewer
98
+ src={assetUrl}
99
  container=""
100
  containerClass={className}
101
 
102
  height="100vh"
103
  width="100%"
 
 
 
104
 
105
  {...options}
106
 
 
111
  }}
112
 
113
  onReady={(instance) => {
 
114
  viewerContainerRef.current = instance.container
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  }}
116
 
117
  />
src/app/interface/top-menu/index.tsx CHANGED
@@ -11,7 +11,7 @@ export function TopMenu() {
11
  const prompt = useStore(s => s.prompt)
12
 
13
  const setPrompt = useStore(s => s.setPrompt)
14
- const setRendered = useStore(s => s.setRendered)
15
 
16
  const isLoading = useStore(s => s.isLoading)
17
  const setLoading = useStore(s => s.setLoading)
@@ -26,15 +26,7 @@ export function TopMenu() {
26
  const promptChanged = draftPrompt.trim() !== prompt.trim()
27
  if (!isLoading && (promptChanged)) {
28
  // important: we reset!
29
- setRendered({
30
- renderId: "",
31
- status: "pending",
32
- assetUrl: "",
33
- alt: "",
34
- error: "",
35
- maskUrl: "",
36
- segments: []
37
- })
38
  setPrompt(draftPrompt)
39
  }
40
  }
 
11
  const prompt = useStore(s => s.prompt)
12
 
13
  const setPrompt = useStore(s => s.setPrompt)
14
+ const setAssetUrl = useStore(s => s.setAssetUrl)
15
 
16
  const isLoading = useStore(s => s.isLoading)
17
  const setLoading = useStore(s => s.setLoading)
 
26
  const promptChanged = draftPrompt.trim() !== prompt.trim()
27
  if (!isLoading && (promptChanged)) {
28
  // important: we reset!
29
+ setAssetUrl("")
 
 
 
 
 
 
 
 
30
  setPrompt(draftPrompt)
31
  }
32
  }
src/app/page.tsx CHANGED
@@ -1,10 +1,12 @@
1
  "use server"
2
 
3
  import Head from "next/head"
 
4
 
5
  // import Firehose from "./firehose/page"
6
  import Generate from "./generate/page"
7
 
 
8
  // https://nextjs.org/docs/pages/building-your-application/optimizing/fonts
9
 
10
  export default async function Page() {
@@ -18,7 +20,7 @@ export default async function Page() {
18
  <main className={
19
  `light bg-zinc-50 text-stone-900
20
  `}>
21
- <Generate />
22
  </main>
23
  </>
24
  )
 
1
  "use server"
2
 
3
  import Head from "next/head"
4
+ import { Suspense } from "react"
5
 
6
  // import Firehose from "./firehose/page"
7
  import Generate from "./generate/page"
8
 
9
+
10
  // https://nextjs.org/docs/pages/building-your-application/optimizing/fonts
11
 
12
  export default async function Page() {
 
20
  <main className={
21
  `light bg-zinc-50 text-stone-900
22
  `}>
23
+ <Suspense><Generate /></Suspense>
24
  </main>
25
  </>
26
  )
src/app/store/index.ts CHANGED
@@ -2,33 +2,23 @@
2
 
3
  import { create } from "zustand"
4
 
5
- import { RenderedScene } from "@/types"
6
-
7
  export const useStore = create<{
8
  prompt: string
9
- renderedScene: RenderedScene
10
  isLoading: boolean
11
  setLoading: (isLoading: boolean) => void
12
- setRendered: (renderedScene: RenderedScene) => void
13
  setPrompt: (prompt: string) => void
14
  }>((set, get) => ({
15
  prompt: "",
16
- renderedScene: {
17
- renderId: "",
18
- status: "pending",
19
- assetUrl: "",
20
- alt: "",
21
- error: "",
22
- maskUrl: "",
23
- segments: []
24
- },
25
  isLoading: false,
26
  setLoading: (isLoading: boolean) => {
27
  set({ isLoading })
28
  },
29
- setRendered: (renderedScene: RenderedScene) => {
30
  set({
31
- renderedScene
32
  })
33
  },
34
  setPrompt: (prompt: string) => {
 
2
 
3
  import { create } from "zustand"
4
 
 
 
5
  export const useStore = create<{
6
  prompt: string
7
+ assetUrl: string
8
  isLoading: boolean
9
  setLoading: (isLoading: boolean) => void
10
+ setAssetUrl: (assetUrl: string) => void
11
  setPrompt: (prompt: string) => void
12
  }>((set, get) => ({
13
  prompt: "",
14
+ assetUrl: "",
 
 
 
 
 
 
 
 
15
  isLoading: false,
16
  setLoading: (isLoading: boolean) => {
17
  set({ isLoading })
18
  },
19
+ setAssetUrl: (assetUrl: string) => {
20
  set({
21
+ assetUrl
22
  })
23
  },
24
  setPrompt: (prompt: string) => {
src/lib/getInitialRenderedScene.ts DELETED
@@ -1,11 +0,0 @@
1
- import { RenderedScene } from "@/types"
2
-
3
- export const getInitialRenderedScene = (): RenderedScene => ({
4
- renderId: "",
5
- status: "pending",
6
- assetUrl: "",
7
- alt: "",
8
- error: "",
9
- maskUrl: "",
10
- segments: []
11
- })
 
 
 
 
 
 
 
 
 
 
 
 
src/types.ts CHANGED
@@ -1,92 +1,4 @@
1
- export type ProjectionMode = 'cartesian' | 'spherical'
2
 
3
- export type MouseEventType = "hover" | "click"
4
-
5
- export type MouseEventHandler = (type: MouseEventType, x: number, y: number) => Promise<void>
6
-
7
- export type CacheMode = "use" | "renew" | "ignore"
8
-
9
- export interface RenderRequest {
10
- prompt: string
11
-
12
- // whether to use video segmentation
13
- // disabled (default)
14
- // firstframe: we only analyze the first frame
15
- // allframes: we analyze all the frames
16
- segmentation: 'disabled' | 'firstframe' | 'allframes'
17
-
18
- // segmentation will only be executed if we have a non-empty list of actionnables
19
- // actionnables are names of things like "chest", "key", "tree", "chair" etc
20
- actionnables: string[]
21
-
22
- // note: this is the number of frames for Zeroscope,
23
- // which is currently configured to only output 3 seconds, so:
24
- // nbFrames=8 -> 1 sec
25
- // nbFrames=16 -> 2 sec
26
- // nbFrames=24 -> 3 sec
27
- nbFrames: number // min: 1, max: 24
28
-
29
- nbSteps: number // min: 1, max: 50
30
-
31
- seed: number
32
-
33
- width: number // fixed at 1024 for now
34
- height: number // fixed at 512 for now
35
-
36
- // upscaling factor
37
- // 0: no upscaling
38
- // 1: no upscaling
39
- // 2: 2x larger
40
- // 3: 3x larger
41
- // 4x: 4x larger, up to 4096x4096 (warning: a PNG of this size can be 50 Mb!)
42
- upscalingFactor: number
43
-
44
- projection: ProjectionMode
45
-
46
- cache: CacheMode
47
-
48
- wait: boolean // wait until the job is completed
49
-
50
- analyze: boolean // analyze the image to generate a caption (optional)
51
- }
52
-
53
- export interface ImageSegment {
54
- id: number
55
- box: number[]
56
- color: number[]
57
- label: string
58
- score: number
59
- }
60
-
61
- export type RenderedSceneStatus =
62
- | "pending"
63
- | "completed"
64
- | "error"
65
-
66
- export interface RenderedScene {
67
- renderId: string
68
- status: RenderedSceneStatus
69
- assetUrl: string
70
- alt: string
71
- error: string
72
- maskUrl: string
73
- segments: ImageSegment[]
74
- }
75
-
76
- export interface ImageAnalysisRequest {
77
- image: string // in base64
78
- prompt: string
79
- }
80
-
81
- export interface ImageAnalysisResponse {
82
- result: string
83
- error?: string
84
- }
85
-
86
- export type RenderingEngine =
87
- | "VIDEOCHAIN"
88
- | "OPENAI"
89
- | "REPLICATE"
90
 
91
  export type PostVisibility =
92
  | "featured" // featured by admins
 
 
1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  export type PostVisibility =
4
  | "featured" // featured by admins