Spaces:
Running
Running
Add device selector
Browse files- server.js +1 -1
- src/components/preview/preview.tsx +56 -6
- utils/consts.ts +1 -0
server.js
CHANGED
@@ -281,7 +281,7 @@ app.post("/api/ask-ai", async (req, res) => {
|
|
281 |
messages: [
|
282 |
{
|
283 |
role: "system",
|
284 |
-
content: `ONLY USE HTML, CSS AND JAVASCRIPT. If you want to use ICON make sure to import the library first. Try to create the best UI possible by using only HTML, CSS and JAVASCRIPT. Use as much as you can TailwindCSS for the CSS, if you can't do something with TailwindCSS, then use custom CSS (make sure to import <script src="https://cdn.tailwindcss.com"></script> in the head). Also, try to ellaborate as much as you can, to create something unique. ALWAYS GIVE THE RESPONSE INTO A SINGLE HTML FILE`,
|
285 |
},
|
286 |
...(previousPrompt
|
287 |
? [
|
|
|
281 |
messages: [
|
282 |
{
|
283 |
role: "system",
|
284 |
+
content: `ONLY USE HTML, CSS AND JAVASCRIPT. If you want to use ICON make sure to import the library first. Try to create the best UI possible by using only HTML, CSS and JAVASCRIPT. MAKE IT RESPONSIVE USING TAILWINDCSS. Use as much as you can TailwindCSS for the CSS, if you can't do something with TailwindCSS, then use custom CSS (make sure to import <script src="https://cdn.tailwindcss.com"></script> in the head). Also, try to ellaborate as much as you can, to create something unique. ALWAYS GIVE THE RESPONSE INTO A SINGLE HTML FILE`,
|
285 |
},
|
286 |
...(previousPrompt
|
287 |
? [
|
src/components/preview/preview.tsx
CHANGED
@@ -1,8 +1,10 @@
|
|
1 |
import classNames from "classnames";
|
2 |
-
import { useRef } from "react";
|
|
|
|
|
|
|
3 |
import { TbReload } from "react-icons/tb";
|
4 |
import { toast } from "react-toastify";
|
5 |
-
import { FaLaptopCode } from "react-icons/fa6";
|
6 |
import { defaultHTML } from "../../../utils/consts";
|
7 |
|
8 |
function Preview({
|
@@ -18,6 +20,7 @@ function Preview({
|
|
18 |
setView: React.Dispatch<React.SetStateAction<"editor" | "preview">>;
|
19 |
ref: React.RefObject<HTMLDivElement | null>;
|
20 |
}) {
|
|
|
21 |
const iframeRef = useRef<HTMLIFrameElement | null>(null);
|
22 |
|
23 |
const handleRefreshIframe = () => {
|
@@ -34,7 +37,12 @@ function Preview({
|
|
34 |
return (
|
35 |
<div
|
36 |
ref={ref}
|
37 |
-
className=
|
|
|
|
|
|
|
|
|
|
|
38 |
onClick={(e) => {
|
39 |
if (isAiWorking) {
|
40 |
e.preventDefault();
|
@@ -46,9 +54,15 @@ function Preview({
|
|
46 |
<iframe
|
47 |
ref={iframeRef}
|
48 |
title="output"
|
49 |
-
className={classNames(
|
50 |
-
"
|
51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
srcDoc={html}
|
53 |
/>
|
54 |
<div className="flex items-center justify-start gap-3 absolute bottom-3 lg:bottom-5 max-lg:left-3 lg:right-5">
|
@@ -68,6 +82,42 @@ function Preview({
|
|
68 |
🖼️ <span>DeepSite Gallery</span>
|
69 |
</a>
|
70 |
)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
{!isAiWorking && (
|
72 |
<button
|
73 |
className="bg-white lg:bg-gray-950 shadow-md text-gray-950 lg:text-white text-xs lg:text-sm font-medium py-2 px-3 lg:px-4 rounded-lg flex items-center gap-2 border border-gray-100 lg:border-gray-900 hover:brightness-150 transition-all duration-100 cursor-pointer"
|
|
|
1 |
import classNames from "classnames";
|
2 |
+
import { useRef, useState } from "react";
|
3 |
+
import { FaLaptopCode } from "react-icons/fa6";
|
4 |
+
import { FaMobileAlt } from "react-icons/fa";
|
5 |
+
|
6 |
import { TbReload } from "react-icons/tb";
|
7 |
import { toast } from "react-toastify";
|
|
|
8 |
import { defaultHTML } from "../../../utils/consts";
|
9 |
|
10 |
function Preview({
|
|
|
20 |
setView: React.Dispatch<React.SetStateAction<"editor" | "preview">>;
|
21 |
ref: React.RefObject<HTMLDivElement | null>;
|
22 |
}) {
|
23 |
+
const [device, setDevice] = useState<"desktop" | "mobile">("desktop");
|
24 |
const iframeRef = useRef<HTMLIFrameElement | null>(null);
|
25 |
|
26 |
const handleRefreshIframe = () => {
|
|
|
37 |
return (
|
38 |
<div
|
39 |
ref={ref}
|
40 |
+
className={classNames(
|
41 |
+
"w-full border-l border-gray-900 bg-gray-950 h-[calc(100dvh-49px)] lg:h-[calc(100dvh-53px)] relative transition-all duration-200",
|
42 |
+
{
|
43 |
+
"flex items-center justify-center": device === "mobile",
|
44 |
+
}
|
45 |
+
)}
|
46 |
onClick={(e) => {
|
47 |
if (isAiWorking) {
|
48 |
e.preventDefault();
|
|
|
54 |
<iframe
|
55 |
ref={iframeRef}
|
56 |
title="output"
|
57 |
+
className={classNames(
|
58 |
+
"w-full select-none transition-all duration-200",
|
59 |
+
{
|
60 |
+
"pointer-events-none": isResizing || isAiWorking,
|
61 |
+
"max-w-md mx-auto h-[80dvh] rounded-[86px] border-[8px] border-black":
|
62 |
+
device === "mobile",
|
63 |
+
"h-full": device === "desktop",
|
64 |
+
}
|
65 |
+
)}
|
66 |
srcDoc={html}
|
67 |
/>
|
68 |
<div className="flex items-center justify-start gap-3 absolute bottom-3 lg:bottom-5 max-lg:left-3 lg:right-5">
|
|
|
82 |
🖼️ <span>DeepSite Gallery</span>
|
83 |
</a>
|
84 |
)}
|
85 |
+
{html !== defaultHTML && !isAiWorking && (
|
86 |
+
<div className="flex items-center rounded-lg p-1 bg-gray-200 relative overflow-hidden z-0 max-lg:hidden">
|
87 |
+
<div
|
88 |
+
className={classNames(
|
89 |
+
"absolute left-1 top-1 rounded-md bg-black w-10 h-8 -z-[1] transition-all duration-200",
|
90 |
+
{
|
91 |
+
"translate-x-full": device === "mobile",
|
92 |
+
}
|
93 |
+
)}
|
94 |
+
/>
|
95 |
+
<button
|
96 |
+
className={classNames(
|
97 |
+
"rounded-md text-gray-500 w-10 h-8 flex items-center justify-center cursor-pointer",
|
98 |
+
{
|
99 |
+
"!text-white": device === "desktop",
|
100 |
+
"hover:bg-gray-300/60": device !== "desktop",
|
101 |
+
}
|
102 |
+
)}
|
103 |
+
onClick={() => setDevice("desktop")}
|
104 |
+
>
|
105 |
+
<FaLaptopCode className="text-sm" />
|
106 |
+
</button>
|
107 |
+
<button
|
108 |
+
className={classNames(
|
109 |
+
"rounded-md text-gray-500 w-10 h-8 flex items-center justify-center cursor-pointer",
|
110 |
+
{
|
111 |
+
"!text-white": device === "mobile",
|
112 |
+
"hover:bg-gray-300/60": device !== "mobile",
|
113 |
+
}
|
114 |
+
)}
|
115 |
+
onClick={() => setDevice("mobile")}
|
116 |
+
>
|
117 |
+
<FaMobileAlt className="text-sm" />
|
118 |
+
</button>
|
119 |
+
</div>
|
120 |
+
)}
|
121 |
{!isAiWorking && (
|
122 |
<button
|
123 |
className="bg-white lg:bg-gray-950 shadow-md text-gray-950 lg:text-white text-xs lg:text-sm font-medium py-2 px-3 lg:px-4 rounded-lg flex items-center gap-2 border border-gray-100 lg:border-gray-900 hover:brightness-150 transition-all duration-100 cursor-pointer"
|
utils/consts.ts
CHANGED
@@ -13,6 +13,7 @@ export const defaultHTML = `<!DOCTYPE html>
|
|
13 |
height: 100dvh;
|
14 |
font-family: "Arial", sans-serif;
|
15 |
text-align: center;
|
|
|
16 |
}
|
17 |
.arrow {
|
18 |
position: absolute;
|
|
|
13 |
height: 100dvh;
|
14 |
font-family: "Arial", sans-serif;
|
15 |
text-align: center;
|
16 |
+
background-color: #fff;
|
17 |
}
|
18 |
.arrow {
|
19 |
position: absolute;
|