Spaces:
Runtime error
Runtime error
Esteves Enzo
commited on
Commit
·
5240c42
1
Parent(s):
d79c631
add modal
Browse files- app/api/route.ts +1 -0
- app/page.tsx +1 -1
- components/main/collections/collection.tsx +16 -11
- components/main/collections/index.tsx +37 -28
- components/main/collections/loading.tsx +1 -1
- components/main/hooks/useInputGeneration.ts +1 -1
- components/main/index.tsx +13 -15
- components/modal/modal.tsx +82 -0
- components/modal/useCollection.ts +17 -0
- prisma/migrations/{20231027143238_init → 20231027155221_init}/migration.sql +2 -1
- prisma/schema.prisma +4 -3
- utils/index.ts +6 -0
- type/index.ts → utils/type.ts +1 -0
app/api/route.ts
CHANGED
@@ -17,6 +17,7 @@ export async function POST(
|
|
17 |
headers: {
|
18 |
Authorization: `Bearer ${process.env.NEXT_PUBLIC_APP_HF_TOKEN}`,
|
19 |
'Content-Type': 'application/json',
|
|
|
20 |
},
|
21 |
})
|
22 |
|
|
|
17 |
headers: {
|
18 |
Authorization: `Bearer ${process.env.NEXT_PUBLIC_APP_HF_TOKEN}`,
|
19 |
'Content-Type': 'application/json',
|
20 |
+
['x-use-cache']: "0"
|
21 |
},
|
22 |
})
|
23 |
|
app/page.tsx
CHANGED
@@ -4,7 +4,7 @@ import { Main } from "@/components/main";
|
|
4 |
|
5 |
export default function Home() {
|
6 |
return (
|
7 |
-
<div className="">
|
8 |
<Header />
|
9 |
<Main />
|
10 |
<Footer />
|
|
|
4 |
|
5 |
export default function Home() {
|
6 |
return (
|
7 |
+
<div className="pb-32">
|
8 |
<Header />
|
9 |
<Main />
|
10 |
<Footer />
|
components/main/collections/collection.tsx
CHANGED
@@ -1,35 +1,36 @@
|
|
1 |
import { useMemo } from "react";
|
2 |
import { motion } from "framer-motion";
|
3 |
|
4 |
-
import { Collection as CollectionType } from "@/type";
|
5 |
import { useInputGeneration } from "@/components/main/hooks/useInputGeneration";
|
|
|
6 |
|
7 |
interface Props {
|
8 |
index: number;
|
9 |
collection: CollectionType;
|
10 |
className?: string;
|
|
|
11 |
}
|
12 |
|
13 |
export const Collection: React.FC<Props> = ({
|
14 |
collection,
|
15 |
index,
|
16 |
className,
|
|
|
17 |
}) => {
|
18 |
const { setPrompt } = useInputGeneration();
|
19 |
|
20 |
-
const arrayBufferToBase64 = (buffer: ArrayBuffer) => {
|
21 |
-
let binary = "";
|
22 |
-
const bytes = [].slice.call(new Uint8Array(buffer));
|
23 |
-
bytes.forEach((b: any) => (binary += String.fromCharCode(b)));
|
24 |
-
return window.btoa(binary);
|
25 |
-
};
|
26 |
-
|
27 |
const bufferToBase64 = useMemo(() => {
|
28 |
const base64Flag = "data:image/jpeg;base64,";
|
29 |
const imageStr = arrayBufferToBase64(collection.blob.data);
|
30 |
return base64Flag + imageStr;
|
31 |
}, [collection]);
|
32 |
|
|
|
|
|
|
|
|
|
|
|
33 |
return (
|
34 |
<div className={`h-[377px] w-full relative ${className}`}>
|
35 |
<motion.div
|
@@ -37,18 +38,22 @@ export const Collection: React.FC<Props> = ({
|
|
37 |
animate={{ y: 0, opacity: 1 }}
|
38 |
transition={{ duration: 0.35, delay: index * 0.1 }}
|
39 |
className="rounded-[33px] h-[377px] cursor-pointer group overflow-hidden relative z-[1] group"
|
|
|
40 |
>
|
41 |
<div className="absolute top-0 left-0 w-full h-full translate-y-full opacity-0 transition-all duration-300 group-hover:translate-y-0 group-hover:opacity-100 flex items-end p-3">
|
42 |
<div className="bg-[#292424] backdrop-blur-sm bg-opacity-60 rounded-xl p-3 border-white/20 border w-full">
|
43 |
<p className="text-xs font-semibold text-white/60 mb-0.5">
|
44 |
-
|
45 |
</p>
|
46 |
<p className="text-lg font-medium text-white lowercase leading-snug">
|
47 |
{collection.prompt}
|
48 |
</p>
|
49 |
<p
|
50 |
className="text-white text-sm text-right font-semibold mt-2"
|
51 |
-
onClick={() =>
|
|
|
|
|
|
|
52 |
>
|
53 |
Try it now
|
54 |
</p>
|
@@ -58,7 +63,7 @@ export const Collection: React.FC<Props> = ({
|
|
58 |
style={{
|
59 |
backgroundImage: `url(${bufferToBase64})`,
|
60 |
}}
|
61 |
-
className="rounded-[33px] bg-red-400 bg-cover absolute top-0 left-0 w-full h-full z-[-1] transition-all duration-200 group-hover:scale-
|
62 |
/>
|
63 |
</motion.div>
|
64 |
</div>
|
|
|
1 |
import { useMemo } from "react";
|
2 |
import { motion } from "framer-motion";
|
3 |
|
4 |
+
import { Collection as CollectionType } from "@/utils/type";
|
5 |
import { useInputGeneration } from "@/components/main/hooks/useInputGeneration";
|
6 |
+
import { arrayBufferToBase64 } from "@/utils";
|
7 |
|
8 |
interface Props {
|
9 |
index: number;
|
10 |
collection: CollectionType;
|
11 |
className?: string;
|
12 |
+
onOpen: (id: number) => void;
|
13 |
}
|
14 |
|
15 |
export const Collection: React.FC<Props> = ({
|
16 |
collection,
|
17 |
index,
|
18 |
className,
|
19 |
+
onOpen,
|
20 |
}) => {
|
21 |
const { setPrompt } = useInputGeneration();
|
22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
const bufferToBase64 = useMemo(() => {
|
24 |
const base64Flag = "data:image/jpeg;base64,";
|
25 |
const imageStr = arrayBufferToBase64(collection.blob.data);
|
26 |
return base64Flag + imageStr;
|
27 |
}, [collection]);
|
28 |
|
29 |
+
const formatDate = useMemo(() => {
|
30 |
+
const date = new Date(collection.createdAt);
|
31 |
+
return date.toLocaleDateString();
|
32 |
+
}, [collection.createdAt]);
|
33 |
+
|
34 |
return (
|
35 |
<div className={`h-[377px] w-full relative ${className}`}>
|
36 |
<motion.div
|
|
|
38 |
animate={{ y: 0, opacity: 1 }}
|
39 |
transition={{ duration: 0.35, delay: index * 0.1 }}
|
40 |
className="rounded-[33px] h-[377px] cursor-pointer group overflow-hidden relative z-[1] group"
|
41 |
+
onClick={() => onOpen(collection.id)}
|
42 |
>
|
43 |
<div className="absolute top-0 left-0 w-full h-full translate-y-full opacity-0 transition-all duration-300 group-hover:translate-y-0 group-hover:opacity-100 flex items-end p-3">
|
44 |
<div className="bg-[#292424] backdrop-blur-sm bg-opacity-60 rounded-xl p-3 border-white/20 border w-full">
|
45 |
<p className="text-xs font-semibold text-white/60 mb-0.5">
|
46 |
+
{formatDate}
|
47 |
</p>
|
48 |
<p className="text-lg font-medium text-white lowercase leading-snug">
|
49 |
{collection.prompt}
|
50 |
</p>
|
51 |
<p
|
52 |
className="text-white text-sm text-right font-semibold mt-2"
|
53 |
+
onClick={(e) => {
|
54 |
+
e.stopPropagation();
|
55 |
+
setPrompt(collection.prompt);
|
56 |
+
}}
|
57 |
>
|
58 |
Try it now
|
59 |
</p>
|
|
|
63 |
style={{
|
64 |
backgroundImage: `url(${bufferToBase64})`,
|
65 |
}}
|
66 |
+
className="rounded-[33px] bg-red-400 bg-cover absolute top-0 left-0 w-full h-full z-[-1] transition-all duration-200 group-hover:scale-110 bg-center"
|
67 |
/>
|
68 |
</motion.div>
|
69 |
</div>
|
components/main/collections/index.tsx
CHANGED
@@ -1,45 +1,54 @@
|
|
1 |
import classNames from "classnames";
|
2 |
-
import { createBreakpoint
|
3 |
-
|
4 |
-
import { Collection as CollectionType } from "@/type";
|
5 |
|
|
|
6 |
import { useCollections } from "@/components/main/hooks/useCollections";
|
|
|
7 |
import { Collection } from "./collection";
|
8 |
import { CollectionLoading } from "./loading";
|
|
|
9 |
|
10 |
const useBreakpoint = createBreakpoint({ XL: 1280, L: 1024, S: 768, XS: 640 });
|
11 |
|
12 |
export const Collections: React.FC<{ category: string }> = ({ category }) => {
|
|
|
13 |
const { collections, loading } = useCollections(category);
|
14 |
const breakpoint = useBreakpoint();
|
15 |
|
16 |
if (loading) return null;
|
17 |
|
18 |
return (
|
19 |
-
|
20 |
-
|
21 |
-
collection
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
"
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
);
|
45 |
};
|
|
|
1 |
import classNames from "classnames";
|
2 |
+
import { createBreakpoint } from "react-use";
|
3 |
+
import { AnimatePresence } from "framer-motion";
|
|
|
4 |
|
5 |
+
import { Collection as CollectionType } from "@/utils/type";
|
6 |
import { useCollections } from "@/components/main/hooks/useCollections";
|
7 |
+
import { Modal } from "@/components/modal/modal";
|
8 |
import { Collection } from "./collection";
|
9 |
import { CollectionLoading } from "./loading";
|
10 |
+
import { useState } from "react";
|
11 |
|
12 |
const useBreakpoint = createBreakpoint({ XL: 1280, L: 1024, S: 768, XS: 640 });
|
13 |
|
14 |
export const Collections: React.FC<{ category: string }> = ({ category }) => {
|
15 |
+
const [open, setOpen] = useState<number | null>(null);
|
16 |
const { collections, loading } = useCollections(category);
|
17 |
const breakpoint = useBreakpoint();
|
18 |
|
19 |
if (loading) return null;
|
20 |
|
21 |
return (
|
22 |
+
<>
|
23 |
+
<div className="mx-auto grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-5 mt-8 lg:mt-14">
|
24 |
+
{collections?.map((collection: CollectionType, i: number) =>
|
25 |
+
collection?.id === -1 ? (
|
26 |
+
<CollectionLoading key={i} prompt={collection.prompt} />
|
27 |
+
) : (
|
28 |
+
<Collection
|
29 |
+
key={category + collection.id}
|
30 |
+
index={i}
|
31 |
+
collection={collection}
|
32 |
+
className={classNames("", {
|
33 |
+
"!translate-y-12":
|
34 |
+
breakpoint === "XL"
|
35 |
+
? i % 5 === 1 || i % 5 === 3
|
36 |
+
: breakpoint === "L"
|
37 |
+
? i % 4 === 1 || i % 4 === 3
|
38 |
+
: breakpoint === "S"
|
39 |
+
? i % 3 === 1
|
40 |
+
: breakpoint === "XS"
|
41 |
+
? i % 2 === 1
|
42 |
+
: false,
|
43 |
+
})}
|
44 |
+
onOpen={setOpen}
|
45 |
+
/>
|
46 |
+
)
|
47 |
+
)}
|
48 |
+
</div>
|
49 |
+
<AnimatePresence initial={false} mode="wait" onExitComplete={() => null}>
|
50 |
+
{open !== null && <Modal id={open} onClose={() => setOpen(null)} />}
|
51 |
+
</AnimatePresence>
|
52 |
+
</>
|
53 |
);
|
54 |
};
|
components/main/collections/loading.tsx
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
import { useMemo } from "react";
|
2 |
import { motion } from "framer-motion";
|
3 |
|
4 |
-
import { Collection as CollectionType } from "@/type";
|
5 |
|
6 |
interface Props {
|
7 |
prompt: string;
|
|
|
1 |
import { useMemo } from "react";
|
2 |
import { motion } from "framer-motion";
|
3 |
|
4 |
+
import { Collection as CollectionType } from "@/utils/type";
|
5 |
|
6 |
interface Props {
|
7 |
prompt: string;
|
components/main/hooks/useInputGeneration.ts
CHANGED
@@ -2,7 +2,7 @@ import { useState } from "react"
|
|
2 |
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
3 |
import { useLocalStorage } from 'react-use';
|
4 |
|
5 |
-
import { Collection } from "@/type"
|
6 |
import list_styles from "@/assets/list_styles.json"
|
7 |
|
8 |
export const useInputGeneration = () => {
|
|
|
2 |
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
3 |
import { useLocalStorage } from 'react-use';
|
4 |
|
5 |
+
import { Collection } from "@/utils/type"
|
6 |
import list_styles from "@/assets/list_styles.json"
|
7 |
|
8 |
export const useInputGeneration = () => {
|
components/main/index.tsx
CHANGED
@@ -33,21 +33,12 @@ export const Main = () => {
|
|
33 |
<main className="px-6 z-[2] relative max-w-[1722px] mx-auto">
|
34 |
<div className="py-2 pl-2 pr-4 bg-black bg-opacity-30 backdrop-blur-sm sticky top-6 z-10 rounded-full">
|
35 |
<div className="flex flex-col lg:flex-row items-center justify-between w-full">
|
36 |
-
<
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
/>
|
43 |
-
<p
|
44 |
-
className="text-white/70 font-medium text-sm flex items-center justify-start gap-2 hover:text-white cursor-pointer mt-2 lg:mt-0"
|
45 |
-
onClick={() => setAdvancedSettings(!advancedSettings)}
|
46 |
-
>
|
47 |
-
<HiAdjustmentsHorizontal className="w-5 h-5" />
|
48 |
-
Advanced settings
|
49 |
-
</p>
|
50 |
-
</div>
|
51 |
<div className="items-center justify-center lg:justify-end gap-5 w-full mt-6 lg:mt-0 hidden lg:flex">
|
52 |
{categories.map(({ key, label, icon }) => (
|
53 |
<Button
|
@@ -61,6 +52,13 @@ export const Main = () => {
|
|
61 |
))}
|
62 |
</div>
|
63 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
<Settings
|
65 |
open={advancedSettings}
|
66 |
style={style}
|
|
|
33 |
<main className="px-6 z-[2] relative max-w-[1722px] mx-auto">
|
34 |
<div className="py-2 pl-2 pr-4 bg-black bg-opacity-30 backdrop-blur-sm sticky top-6 z-10 rounded-full">
|
35 |
<div className="flex flex-col lg:flex-row items-center justify-between w-full">
|
36 |
+
<InputGeneration
|
37 |
+
prompt={prompt}
|
38 |
+
onChange={setPrompt}
|
39 |
+
onSubmit={submit}
|
40 |
+
loading={loading}
|
41 |
+
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
<div className="items-center justify-center lg:justify-end gap-5 w-full mt-6 lg:mt-0 hidden lg:flex">
|
43 |
{categories.map(({ key, label, icon }) => (
|
44 |
<Button
|
|
|
52 |
))}
|
53 |
</div>
|
54 |
</div>
|
55 |
+
<p
|
56 |
+
className="text-white/70 font-medium text-sm flex items-center justify-start gap-2 hover:text-white cursor-pointer mt-3"
|
57 |
+
onClick={() => setAdvancedSettings(!advancedSettings)}
|
58 |
+
>
|
59 |
+
<HiAdjustmentsHorizontal className="w-5 h-5" />
|
60 |
+
Advanced settings
|
61 |
+
</p>
|
62 |
<Settings
|
63 |
open={advancedSettings}
|
64 |
style={style}
|
components/modal/modal.tsx
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useMemo } from "react";
|
2 |
+
import { motion } from "framer-motion";
|
3 |
+
|
4 |
+
import { arrayBufferToBase64 } from "@/utils";
|
5 |
+
import { useCollection } from "./useCollection";
|
6 |
+
|
7 |
+
interface Props {
|
8 |
+
id: number;
|
9 |
+
onClose: () => void;
|
10 |
+
}
|
11 |
+
|
12 |
+
const dropIn = {
|
13 |
+
hidden: {
|
14 |
+
y: "-100vh",
|
15 |
+
opacity: 0,
|
16 |
+
},
|
17 |
+
visible: {
|
18 |
+
y: "0",
|
19 |
+
opacity: 1,
|
20 |
+
transition: {
|
21 |
+
duration: 0.1,
|
22 |
+
type: "spring",
|
23 |
+
damping: 25,
|
24 |
+
stiffness: 500,
|
25 |
+
},
|
26 |
+
},
|
27 |
+
exit: {
|
28 |
+
y: "100vh",
|
29 |
+
opacity: 0,
|
30 |
+
},
|
31 |
+
};
|
32 |
+
|
33 |
+
export const Modal: React.FC<Props> = ({ id, onClose }) => {
|
34 |
+
const collection = useCollection(id);
|
35 |
+
|
36 |
+
if (!collection) return null;
|
37 |
+
|
38 |
+
const bufferToBase64 = useMemo(() => {
|
39 |
+
if (!collection) return;
|
40 |
+
const base64Flag = "data:image/jpeg;base64,";
|
41 |
+
const imageStr = arrayBufferToBase64(collection.blob.data);
|
42 |
+
return base64Flag + imageStr;
|
43 |
+
}, [collection]);
|
44 |
+
|
45 |
+
const formatDate = useMemo(() => {
|
46 |
+
if (!collection) return;
|
47 |
+
const date = new Date(collection.createdAt);
|
48 |
+
return date.toLocaleDateString();
|
49 |
+
}, [collection?.createdAt]);
|
50 |
+
|
51 |
+
return (
|
52 |
+
<motion.div
|
53 |
+
onClick={onClose}
|
54 |
+
className="fixed top-0 w-screen h-screen left-0 bg-black/30 backdrop-blur-sm z-50 flex items-center justify-center p-6"
|
55 |
+
initial={{ opacity: 0 }}
|
56 |
+
animate={{ opacity: 1 }}
|
57 |
+
exit={{ opacity: 0 }}
|
58 |
+
>
|
59 |
+
<motion.div
|
60 |
+
onClick={(e) => e.stopPropagation()}
|
61 |
+
className="max-w-2xl h-2/3 w-full z-[1] rounded-3xl overflow-hidden flex items-center justify-center flex-col gap-4 bg-white/30 backdrop-blur-sm p-2"
|
62 |
+
variants={dropIn}
|
63 |
+
initial="hidden"
|
64 |
+
animate="visible"
|
65 |
+
exit="exit"
|
66 |
+
>
|
67 |
+
<div
|
68 |
+
className="bg-cover bg-center w-full h-full rounded-2xl"
|
69 |
+
style={{
|
70 |
+
backgroundImage: `url(${bufferToBase64})`,
|
71 |
+
}}
|
72 |
+
/>
|
73 |
+
<div className="text-left w-full px-4 pb-3 pt-2">
|
74 |
+
<p className="text-sm font-medium text-white/60 mb-1">{formatDate}</p>
|
75 |
+
<p className="text-xl font-semibold text-white lowercase leading-snug">
|
76 |
+
{collection.prompt}
|
77 |
+
</p>
|
78 |
+
</div>
|
79 |
+
</motion.div>
|
80 |
+
</motion.div>
|
81 |
+
);
|
82 |
+
};
|
components/modal/useCollection.ts
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useMemo, useState } from "react"
|
2 |
+
import { useQueryClient } from "@tanstack/react-query"
|
3 |
+
|
4 |
+
import { Collection } from "@/utils/type"
|
5 |
+
|
6 |
+
export const useCollection = (id: number) => {
|
7 |
+
const client = useQueryClient()
|
8 |
+
|
9 |
+
const collection = useMemo(() => {
|
10 |
+
const collections = client.getQueryData<Collection[]>(["collections"])
|
11 |
+
if (!collections) return null
|
12 |
+
|
13 |
+
return collections.find((collection) => collection.id === id)
|
14 |
+
}, [id])
|
15 |
+
|
16 |
+
return collection
|
17 |
+
}
|
prisma/migrations/{20231027143238_init → 20231027155221_init}/migration.sql
RENAMED
@@ -2,5 +2,6 @@
|
|
2 |
CREATE TABLE "Image" (
|
3 |
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
4 |
"prompt" TEXT NOT NULL,
|
5 |
-
"blob" BLOB NOT NULL
|
|
|
6 |
);
|
|
|
2 |
CREATE TABLE "Image" (
|
3 |
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
4 |
"prompt" TEXT NOT NULL,
|
5 |
+
"blob" BLOB NOT NULL,
|
6 |
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
7 |
);
|
prisma/schema.prisma
CHANGED
@@ -8,7 +8,8 @@ datasource db {
|
|
8 |
}
|
9 |
|
10 |
model Image {
|
11 |
-
id
|
12 |
-
prompt
|
13 |
-
blob
|
|
|
14 |
}
|
|
|
8 |
}
|
9 |
|
10 |
model Image {
|
11 |
+
id Int @id @default(autoincrement())
|
12 |
+
prompt String
|
13 |
+
blob Bytes
|
14 |
+
createdAt DateTime @default(now())
|
15 |
}
|
utils/index.ts
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const arrayBufferToBase64 = (buffer: ArrayBuffer) => {
|
2 |
+
let binary = "";
|
3 |
+
const bytes = [].slice.call(new Uint8Array(buffer));
|
4 |
+
bytes.forEach((b: any) => (binary += String.fromCharCode(b)));
|
5 |
+
return window.btoa(binary);
|
6 |
+
};
|
type/index.ts → utils/type.ts
RENAMED
@@ -5,4 +5,5 @@ export interface Collection {
|
|
5 |
data: ArrayBuffer
|
6 |
};
|
7 |
prompt: string;
|
|
|
8 |
}
|
|
|
5 |
data: ArrayBuffer
|
6 |
};
|
7 |
prompt: string;
|
8 |
+
createdAt: string;
|
9 |
}
|