responsive version OMG + DEEPSITE NAMING
Browse files- index.html +4 -2
- src/components/App.tsx +25 -18
- src/components/ask-ai/ask-ai.tsx +6 -4
- src/components/deploy-button/deploy-button.tsx +2 -2
- src/components/header/header.tsx +8 -4
- src/components/preview/preview.tsx +2 -2
- src/components/tabs/tabs.tsx +1 -1
- src/utils/consts.ts +1 -0
index.html
CHANGED
|
@@ -4,10 +4,12 @@
|
|
| 4 |
<meta charset="utf-8" />
|
| 5 |
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 7 |
-
<title>
|
| 8 |
<meta
|
| 9 |
name="description"
|
| 10 |
-
content="
|
|
|
|
|
|
|
| 11 |
/>
|
| 12 |
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
| 13 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
|
|
| 4 |
<meta charset="utf-8" />
|
| 5 |
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 7 |
+
<title>DeepSite | Build with AI ✨</title>
|
| 8 |
<meta
|
| 9 |
name="description"
|
| 10 |
+
content="DeepSite is a web development tool that
|
| 11 |
+
helps you build websites with AI, no code required. Let's deploy your
|
| 12 |
+
website with DeepSite and enjoy the magic of AI."
|
| 13 |
/>
|
| 14 |
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
| 15 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
src/components/App.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import { useRef, useState } from "react";
|
|
| 3 |
import Editor from "@monaco-editor/react";
|
| 4 |
import classNames from "classnames";
|
| 5 |
import { editor } from "monaco-editor";
|
| 6 |
-
import { useMount, useUnmount, useEvent } from "react-use";
|
| 7 |
import { toast } from "react-toastify";
|
| 8 |
|
| 9 |
import Header from "./header/header";
|
|
@@ -14,7 +14,14 @@ import AskAI from "./ask-ai/ask-ai";
|
|
| 14 |
import { Auth } from "../utils/types";
|
| 15 |
import Preview from "./preview/preview";
|
| 16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
function App() {
|
|
|
|
|
|
|
| 18 |
const preview = useRef<HTMLDivElement>(null);
|
| 19 |
const editor = useRef<HTMLDivElement>(null);
|
| 20 |
const resizer = useRef<HTMLDivElement>(null);
|
|
@@ -66,11 +73,14 @@ function App() {
|
|
| 66 |
useMount(() => {
|
| 67 |
fetchMe();
|
| 68 |
if (!editor.current || !preview.current) return;
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
if (!resizer.current) return;
|
| 76 |
resizer.current.addEventListener("mousedown", handleMouseDown);
|
|
@@ -91,10 +101,10 @@ function App() {
|
|
| 91 |
<Header>
|
| 92 |
<DeployButton html={html} error={error} auth={auth} />
|
| 93 |
</Header>
|
| 94 |
-
<main className="lg:flex w-full
|
| 95 |
<div
|
| 96 |
ref={editor}
|
| 97 |
-
className="w-full h-full relative"
|
| 98 |
onClick={(e) => {
|
| 99 |
if (isAiWorking) {
|
| 100 |
e.preventDefault();
|
|
@@ -107,9 +117,12 @@ function App() {
|
|
| 107 |
<Editor
|
| 108 |
language="html"
|
| 109 |
theme="vs-dark"
|
| 110 |
-
className={classNames(
|
| 111 |
-
"
|
| 112 |
-
|
|
|
|
|
|
|
|
|
|
| 113 |
value={html}
|
| 114 |
onValidate={(markers) => {
|
| 115 |
if (markers?.length > 0) {
|
|
@@ -137,7 +150,7 @@ function App() {
|
|
| 137 |
</div>
|
| 138 |
<div
|
| 139 |
ref={resizer}
|
| 140 |
-
className="bg-gray-700 hover:bg-blue-500 w-2 cursor-col-resize h-[calc(100dvh-54px)]"
|
| 141 |
/>
|
| 142 |
<Preview
|
| 143 |
html={html}
|
|
@@ -146,12 +159,6 @@ function App() {
|
|
| 146 |
ref={preview}
|
| 147 |
/>
|
| 148 |
</main>
|
| 149 |
-
<main className="lg:hidden p-5">
|
| 150 |
-
<p className="p-5 bg-red-500/10 text-red-500 rounded-md text-base text-pretty">
|
| 151 |
-
This app is not yet optimized for mobile. Please use a desktop browser
|
| 152 |
-
for the best experience.
|
| 153 |
-
</p>
|
| 154 |
-
</main>
|
| 155 |
</div>
|
| 156 |
);
|
| 157 |
}
|
|
|
|
| 3 |
import Editor from "@monaco-editor/react";
|
| 4 |
import classNames from "classnames";
|
| 5 |
import { editor } from "monaco-editor";
|
| 6 |
+
import { useMount, useUnmount, useEvent, createBreakpoint } from "react-use";
|
| 7 |
import { toast } from "react-toastify";
|
| 8 |
|
| 9 |
import Header from "./header/header";
|
|
|
|
| 14 |
import { Auth } from "../utils/types";
|
| 15 |
import Preview from "./preview/preview";
|
| 16 |
|
| 17 |
+
const useBreakpoint = createBreakpoint({
|
| 18 |
+
md: 768,
|
| 19 |
+
lg: 1024,
|
| 20 |
+
});
|
| 21 |
+
|
| 22 |
function App() {
|
| 23 |
+
const breakpoint = useBreakpoint();
|
| 24 |
+
|
| 25 |
const preview = useRef<HTMLDivElement>(null);
|
| 26 |
const editor = useRef<HTMLDivElement>(null);
|
| 27 |
const resizer = useRef<HTMLDivElement>(null);
|
|
|
|
| 73 |
useMount(() => {
|
| 74 |
fetchMe();
|
| 75 |
if (!editor.current || !preview.current) return;
|
| 76 |
+
|
| 77 |
+
if (breakpoint === "lg") {
|
| 78 |
+
// Set initial sizes
|
| 79 |
+
const initialEditorWidth = window.innerWidth / 2;
|
| 80 |
+
const initialPreviewWidth = window.innerWidth - initialEditorWidth - 4;
|
| 81 |
+
editor.current.style.width = `${initialEditorWidth}px`;
|
| 82 |
+
preview.current.style.width = `${initialPreviewWidth}px`;
|
| 83 |
+
}
|
| 84 |
|
| 85 |
if (!resizer.current) return;
|
| 86 |
resizer.current.addEventListener("mousedown", handleMouseDown);
|
|
|
|
| 101 |
<Header>
|
| 102 |
<DeployButton html={html} error={error} auth={auth} />
|
| 103 |
</Header>
|
| 104 |
+
<main className="max-lg:flex-col flex w-full">
|
| 105 |
<div
|
| 106 |
ref={editor}
|
| 107 |
+
className="w-full h-[30dvh] lg:h-full relative"
|
| 108 |
onClick={(e) => {
|
| 109 |
if (isAiWorking) {
|
| 110 |
e.preventDefault();
|
|
|
|
| 117 |
<Editor
|
| 118 |
language="html"
|
| 119 |
theme="vs-dark"
|
| 120 |
+
className={classNames(
|
| 121 |
+
"h-[calc(30dvh-41px)] lg:h-[calc(100dvh-96px)]",
|
| 122 |
+
{
|
| 123 |
+
"pointer-events-none": isAiWorking,
|
| 124 |
+
}
|
| 125 |
+
)}
|
| 126 |
value={html}
|
| 127 |
onValidate={(markers) => {
|
| 128 |
if (markers?.length > 0) {
|
|
|
|
| 150 |
</div>
|
| 151 |
<div
|
| 152 |
ref={resizer}
|
| 153 |
+
className="bg-gray-700 hover:bg-blue-500 w-2 cursor-col-resize h-[calc(100dvh-54px)] max-lg:hidden"
|
| 154 |
/>
|
| 155 |
<Preview
|
| 156 |
html={html}
|
|
|
|
| 159 |
ref={preview}
|
| 160 |
/>
|
| 161 |
</main>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
</div>
|
| 163 |
);
|
| 164 |
}
|
src/components/ask-ai/ask-ai.tsx
CHANGED
|
@@ -42,9 +42,11 @@ function AskAI({
|
|
| 42 |
if (request && request.body) {
|
| 43 |
if (!request.ok) {
|
| 44 |
const res = await request.json();
|
| 45 |
-
toast.error(res.message);
|
| 46 |
if (res.openLogin) {
|
| 47 |
setOpen(true);
|
|
|
|
|
|
|
|
|
|
| 48 |
}
|
| 49 |
setisAiWorking(false);
|
| 50 |
return;
|
|
@@ -89,16 +91,16 @@ function AskAI({
|
|
| 89 |
|
| 90 |
return (
|
| 91 |
<div
|
| 92 |
-
className={`bg-gray-950 rounded-xl py-2.5 pl-4 pr-2.5 sticky bottom-4 left-4 w-[calc(100%-2rem)] z-10 group ${
|
| 93 |
isAiWorking ? "animate-pulse" : ""
|
| 94 |
}`}
|
| 95 |
>
|
| 96 |
<div className="w-full relative flex items-center justify-between">
|
| 97 |
-
<RiSparkling2Fill className="text-xl text-gray-500 group-focus-within:text-pink-500" />
|
| 98 |
<input
|
| 99 |
type="text"
|
| 100 |
disabled={isAiWorking}
|
| 101 |
-
className="w-full bg-transparent outline-none pl-3 text-white placeholder:text-gray-500 font-code"
|
| 102 |
placeholder={
|
| 103 |
hasAsked ? "What do you want to ask AI next?" : "Ask AI anything..."
|
| 104 |
}
|
|
|
|
| 42 |
if (request && request.body) {
|
| 43 |
if (!request.ok) {
|
| 44 |
const res = await request.json();
|
|
|
|
| 45 |
if (res.openLogin) {
|
| 46 |
setOpen(true);
|
| 47 |
+
} else {
|
| 48 |
+
// don't show toast if it's a login error
|
| 49 |
+
toast.error(res.message);
|
| 50 |
}
|
| 51 |
setisAiWorking(false);
|
| 52 |
return;
|
|
|
|
| 91 |
|
| 92 |
return (
|
| 93 |
<div
|
| 94 |
+
className={`bg-gray-950 rounded-xl py-2 lg:py-2.5 pl-3.5 lg:pl-4 pr-2 lg:pr-2.5 absolute lg:sticky bottom-3 left-3 lg:bottom-4 lg:left-4 w-[calc(100%-1.5rem)] lg:w-[calc(100%-2rem)] z-10 group ${
|
| 95 |
isAiWorking ? "animate-pulse" : ""
|
| 96 |
}`}
|
| 97 |
>
|
| 98 |
<div className="w-full relative flex items-center justify-between">
|
| 99 |
+
<RiSparkling2Fill className="text-lg lg:text-xl text-gray-500 group-focus-within:text-pink-500" />
|
| 100 |
<input
|
| 101 |
type="text"
|
| 102 |
disabled={isAiWorking}
|
| 103 |
+
className="w-full bg-transparent max-lg:text-sm outline-none pl-3 text-white placeholder:text-gray-500 font-code"
|
| 104 |
placeholder={
|
| 105 |
hasAsked ? "What do you want to ask AI next?" : "Ask AI anything..."
|
| 106 |
}
|
src/components/deploy-button/deploy-button.tsx
CHANGED
|
@@ -58,7 +58,7 @@ function DeployButton({
|
|
| 58 |
};
|
| 59 |
|
| 60 |
return (
|
| 61 |
-
<div className="relative
|
| 62 |
{auth && (
|
| 63 |
<p className="mr-3 text-sm text-gray-300">
|
| 64 |
Connected as{" "}
|
|
@@ -73,7 +73,7 @@ function DeployButton({
|
|
| 73 |
)}
|
| 74 |
<button
|
| 75 |
className={classNames(
|
| 76 |
-
"relative cursor-pointer flex-none flex items-center justify-center rounded-md text-sm font-semibold leading-6 py-1.5 px-5 hover:bg-pink-400 text-white shadow-sm dark:shadow-highlight/20",
|
| 77 |
{
|
| 78 |
"bg-pink-400": open,
|
| 79 |
"bg-pink-500": !open,
|
|
|
|
| 58 |
};
|
| 59 |
|
| 60 |
return (
|
| 61 |
+
<div className="relative flex items-center justify-end">
|
| 62 |
{auth && (
|
| 63 |
<p className="mr-3 text-sm text-gray-300">
|
| 64 |
Connected as{" "}
|
|
|
|
| 73 |
)}
|
| 74 |
<button
|
| 75 |
className={classNames(
|
| 76 |
+
"relative cursor-pointer flex-none flex items-center justify-center rounded-md text-xs lg:text-sm font-semibold leading-5 lg:leading-6 py-1.5 px-5 hover:bg-pink-400 text-white shadow-sm dark:shadow-highlight/20",
|
| 77 |
{
|
| 78 |
"bg-pink-400": open,
|
| 79 |
"bg-pink-500": !open,
|
src/components/header/header.tsx
CHANGED
|
@@ -3,11 +3,15 @@ import { ReactNode } from "react";
|
|
| 3 |
|
| 4 |
function Header({ children }: { children?: ReactNode }) {
|
| 5 |
return (
|
| 6 |
-
<header className="border-b border-gray-900 px-6 py-2 flex justify-
|
| 7 |
<div className="flex items-center justify-start gap-3">
|
| 8 |
-
<h1 className="text-white text-xl font-bold flex items-center justify-start">
|
| 9 |
-
<img
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
</h1>
|
| 12 |
<p className="text-gray-700 max-md:hidden">|</p>
|
| 13 |
<p className="text-gray-500 text-sm max-md:hidden">
|
|
|
|
| 3 |
|
| 4 |
function Header({ children }: { children?: ReactNode }) {
|
| 5 |
return (
|
| 6 |
+
<header className="border-b border-gray-900 px-3 lg:px-6 py-2 flex justify-between items-center">
|
| 7 |
<div className="flex items-center justify-start gap-3">
|
| 8 |
+
<h1 className="text-white text-lg lg:text-xl font-bold flex items-center justify-start">
|
| 9 |
+
<img
|
| 10 |
+
src={SpaceIcon}
|
| 11 |
+
alt="Space Icon"
|
| 12 |
+
className="size-6 lg:size-8 mr-2"
|
| 13 |
+
/>
|
| 14 |
+
DeepSite
|
| 15 |
</h1>
|
| 16 |
<p className="text-gray-700 max-md:hidden">|</p>
|
| 17 |
<p className="text-gray-500 text-sm max-md:hidden">
|
src/components/preview/preview.tsx
CHANGED
|
@@ -29,7 +29,7 @@ function Preview({
|
|
| 29 |
return (
|
| 30 |
<div
|
| 31 |
ref={ref}
|
| 32 |
-
className="w-full border-l border-gray-900 bg-white h-[calc(100dvh-54px)] relative"
|
| 33 |
>
|
| 34 |
<iframe
|
| 35 |
ref={iframeRef}
|
|
@@ -40,7 +40,7 @@ function Preview({
|
|
| 40 |
srcDoc={html}
|
| 41 |
/>
|
| 42 |
<button
|
| 43 |
-
className="bg-gray-
|
| 44 |
onClick={handleRefreshIframe}
|
| 45 |
>
|
| 46 |
<TbReload />
|
|
|
|
| 29 |
return (
|
| 30 |
<div
|
| 31 |
ref={ref}
|
| 32 |
+
className="w-full border-l border-gray-900 bg-white h-[calc(70dvh-53px)] lg:h-[calc(100dvh-54px)] relative"
|
| 33 |
>
|
| 34 |
<iframe
|
| 35 |
ref={iframeRef}
|
|
|
|
| 40 |
srcDoc={html}
|
| 41 |
/>
|
| 42 |
<button
|
| 43 |
+
className="bg-gray-950 shadow-md text-white text-xs lg:text-sm font-medium absolute bottom-3 lg:bottom-5 right-3 lg:right-5 py-2 px-3 lg:px-4 rounded-lg flex items-center gap-2 border border-gray-900 hover:brightness-150 transition-all duration-100 cursor-pointer"
|
| 44 |
onClick={handleRefreshIframe}
|
| 45 |
>
|
| 46 |
<TbReload />
|
src/components/tabs/tabs.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import Deepseek from "./../../assets/deepseek-color.svg";
|
|
| 2 |
|
| 3 |
function Tabs({ children }: { children?: React.ReactNode }) {
|
| 4 |
return (
|
| 5 |
-
<div className="border-b border-gray-800 pl-7 pr-3 flex items-center justify-between">
|
| 6 |
<div
|
| 7 |
className="
|
| 8 |
space-x-6"
|
|
|
|
| 2 |
|
| 3 |
function Tabs({ children }: { children?: React.ReactNode }) {
|
| 4 |
return (
|
| 5 |
+
<div className="border-b border-gray-800 pl-4 lg:pl-7 pr-3 flex items-center justify-between">
|
| 6 |
<div
|
| 7 |
className="
|
| 8 |
space-x-6"
|
src/utils/consts.ts
CHANGED
|
@@ -11,6 +11,7 @@ export const defaultHTML = `<!DOCTYPE html>
|
|
| 11 |
align-items: center;
|
| 12 |
height: 100dvh;
|
| 13 |
font-family: "Arial", sans-serif;
|
|
|
|
| 14 |
}
|
| 15 |
</style>
|
| 16 |
</head>
|
|
|
|
| 11 |
align-items: center;
|
| 12 |
height: 100dvh;
|
| 13 |
font-family: "Arial", sans-serif;
|
| 14 |
+
text-align: center;
|
| 15 |
}
|
| 16 |
</style>
|
| 17 |
</head>
|