enzostvs HF Staff commited on
Commit
b573489
·
1 Parent(s): 6692511

mobile version improvement

Browse files
src/components/App.tsx CHANGED
@@ -26,6 +26,9 @@ function App() {
26
  const [html, setHtml] = useState((htmlStorage as string) ?? defaultHTML);
27
  const [isAiWorking, setisAiWorking] = useState(false);
28
  const [auth, setAuth] = useState<Auth | undefined>(undefined);
 
 
 
29
 
30
  const fetchMe = async () => {
31
  const res = await fetch("/api/@me");
@@ -157,7 +160,15 @@ function App() {
157
  <DeployButton html={html} error={error} auth={auth} />
158
  </Header>
159
  <main className="max-lg:flex-col flex w-full">
160
- <div ref={editor} className="w-full h-[30dvh] lg:h-full relative">
 
 
 
 
 
 
 
 
161
  <Tabs />
162
  <div
163
  onClick={(e) => {
@@ -172,9 +183,9 @@ function App() {
172
  language="html"
173
  theme="vs-dark"
174
  className={classNames(
175
- "h-[calc(30dvh-41px)] lg:h-[calc(100dvh-96px)]",
176
  {
177
- "pointer-events-none": !isAiWorking,
178
  }
179
  )}
180
  value={html}
@@ -196,6 +207,7 @@ function App() {
196
  setHtml={setHtml}
197
  isAiWorking={isAiWorking}
198
  setisAiWorking={setisAiWorking}
 
199
  onScrollToBottom={() => {
200
  editorRef.current?.revealLine(
201
  editorRef.current?.getModel()?.getLineCount() ?? 0
@@ -205,13 +217,14 @@ function App() {
205
  </div>
206
  <div
207
  ref={resizer}
208
- className="bg-gray-700 hover:bg-blue-500 w-2 cursor-col-resize h-[calc(100dvh-54px)] max-lg:hidden"
209
  />
210
  <Preview
211
  html={html}
212
  isResizing={isResizing}
213
  isAiWorking={isAiWorking}
214
  ref={preview}
 
215
  />
216
  </main>
217
  </div>
 
26
  const [html, setHtml] = useState((htmlStorage as string) ?? defaultHTML);
27
  const [isAiWorking, setisAiWorking] = useState(false);
28
  const [auth, setAuth] = useState<Auth | undefined>(undefined);
29
+ const [currentView, setCurrentView] = useState<"editor" | "preview">(
30
+ "editor"
31
+ );
32
 
33
  const fetchMe = async () => {
34
  const res = await fetch("/api/@me");
 
160
  <DeployButton html={html} error={error} auth={auth} />
161
  </Header>
162
  <main className="max-lg:flex-col flex w-full">
163
+ <div
164
+ ref={editor}
165
+ className={classNames(
166
+ "w-full h-[calc(100dvh-49px)] lg:h-[calc(100dvh-54px)] relative overflow-hidden transition-all duration-200",
167
+ {
168
+ "max-lg:h-0": currentView === "preview",
169
+ }
170
+ )}
171
+ >
172
  <Tabs />
173
  <div
174
  onClick={(e) => {
 
183
  language="html"
184
  theme="vs-dark"
185
  className={classNames(
186
+ "h-[calc(100dvh-90px)] lg:h-[calc(100dvh-96px)]",
187
  {
188
+ "pointer-events-none": isAiWorking,
189
  }
190
  )}
191
  value={html}
 
207
  setHtml={setHtml}
208
  isAiWorking={isAiWorking}
209
  setisAiWorking={setisAiWorking}
210
+ setView={setCurrentView}
211
  onScrollToBottom={() => {
212
  editorRef.current?.revealLine(
213
  editorRef.current?.getModel()?.getLineCount() ?? 0
 
217
  </div>
218
  <div
219
  ref={resizer}
220
+ className="bg-gray-700 hover:bg-blue-500 w-2 cursor-col-resize h-[calc(100dvh-53px)] max-lg:hidden"
221
  />
222
  <Preview
223
  html={html}
224
  isResizing={isResizing}
225
  isAiWorking={isAiWorking}
226
  ref={preview}
227
+ setView={setCurrentView}
228
  />
229
  </main>
230
  </div>
src/components/ask-ai/ask-ai.tsx CHANGED
@@ -3,12 +3,13 @@ import { RiSparkling2Fill } from "react-icons/ri";
3
  import { GrSend } from "react-icons/gr";
4
  import classNames from "classnames";
5
  import { toast } from "react-toastify";
 
 
6
 
7
  import Login from "../login/login";
8
  import { defaultHTML } from "./../../../utils/consts";
9
  import SuccessSound from "./../../assets/success.mp3";
10
  import Settings from "../settings/settings";
11
- import { useLocalStorage } from "react-use";
12
 
13
  function AskAI({
14
  html,
@@ -16,11 +17,13 @@ function AskAI({
16
  onScrollToBottom,
17
  isAiWorking,
18
  setisAiWorking,
 
19
  }: {
20
  html: string;
21
  setHtml: (html: string) => void;
22
  onScrollToBottom: () => void;
23
  isAiWorking: boolean;
 
24
  setisAiWorking: React.Dispatch<React.SetStateAction<boolean>>;
25
  }) {
26
  const [open, setOpen] = useState(false);
@@ -80,6 +83,7 @@ function AskAI({
80
  setisAiWorking(false);
81
  setHasAsked(true);
82
  audio.play();
 
83
 
84
  // Now we have the complete HTML including </html>, so set it to be sure
85
  const finalDoc = contentResponse.match(
@@ -135,6 +139,15 @@ function AskAI({
135
  isAiWorking ? "animate-pulse" : ""
136
  }`}
137
  >
 
 
 
 
 
 
 
 
 
138
  <div className="w-full relative flex items-center justify-between">
139
  <RiSparkling2Fill className="text-lg lg:text-xl text-gray-500 group-focus-within:text-pink-500" />
140
  <input
 
3
  import { GrSend } from "react-icons/gr";
4
  import classNames from "classnames";
5
  import { toast } from "react-toastify";
6
+ import { useLocalStorage } from "react-use";
7
+ import { MdPreview } from "react-icons/md";
8
 
9
  import Login from "../login/login";
10
  import { defaultHTML } from "./../../../utils/consts";
11
  import SuccessSound from "./../../assets/success.mp3";
12
  import Settings from "../settings/settings";
 
13
 
14
  function AskAI({
15
  html,
 
17
  onScrollToBottom,
18
  isAiWorking,
19
  setisAiWorking,
20
+ setView,
21
  }: {
22
  html: string;
23
  setHtml: (html: string) => void;
24
  onScrollToBottom: () => void;
25
  isAiWorking: boolean;
26
+ setView: React.Dispatch<React.SetStateAction<"editor" | "preview">>;
27
  setisAiWorking: React.Dispatch<React.SetStateAction<boolean>>;
28
  }) {
29
  const [open, setOpen] = useState(false);
 
83
  setisAiWorking(false);
84
  setHasAsked(true);
85
  audio.play();
86
+ setView("preview");
87
 
88
  // Now we have the complete HTML including </html>, so set it to be sure
89
  const finalDoc = contentResponse.match(
 
139
  isAiWorking ? "animate-pulse" : ""
140
  }`}
141
  >
142
+ {defaultHTML !== html && (
143
+ <button
144
+ className="bg-white lg:hidden -translate-y-[calc(100%+8px)] absolute left-0 top-0 shadow-md text-gray-950 text-xs font-medium py-2 px-3 lg:px-4 rounded-lg flex items-center gap-2 border border-gray-100 hover:brightness-150 transition-all duration-100 cursor-pointer"
145
+ onClick={() => setView("preview")}
146
+ >
147
+ <MdPreview />
148
+ Back to Preview
149
+ </button>
150
+ )}
151
  <div className="w-full relative flex items-center justify-between">
152
  <RiSparkling2Fill className="text-lg lg:text-xl text-gray-500 group-focus-within:text-pink-500" />
153
  <input
src/components/deploy-button/deploy-button.tsx CHANGED
@@ -76,8 +76,8 @@ function DeployButton({
76
  return (
77
  <div className="relative flex items-center justify-end">
78
  {auth && (
79
- <p className="mr-3 text-sm text-gray-300">
80
- Connected as{" "}
81
  <a
82
  href={`https://huggingface.co/${auth.preferred_username}`}
83
  target="_blank"
 
76
  return (
77
  <div className="relative flex items-center justify-end">
78
  {auth && (
79
+ <p className="mr-3 text-xs lg:text-sm text-gray-300">
80
+ <span className="max-lg:hidden">Connected as </span>
81
  <a
82
  href={`https://huggingface.co/${auth.preferred_username}`}
83
  target="_blank"
src/components/header/header.tsx CHANGED
@@ -11,7 +11,7 @@ function Header({
11
  children?: ReactNode;
12
  }) {
13
  return (
14
- <header className="border-b border-gray-900 px-3 lg:px-6 py-2 flex justify-between items-center">
15
  <div className="flex items-center justify-start gap-3">
16
  <h1 className="text-white text-lg lg:text-xl font-bold flex items-center justify-start">
17
  <img
 
11
  children?: ReactNode;
12
  }) {
13
  return (
14
+ <header className="border-b border-gray-900 bg-gray-950 px-3 lg:px-6 py-2 flex justify-between items-center sticky top-0 max-lg:z-20">
15
  <div className="flex items-center justify-start gap-3">
16
  <h1 className="text-white text-lg lg:text-xl font-bold flex items-center justify-start">
17
  <img
src/components/preview/preview.tsx CHANGED
@@ -2,16 +2,19 @@ import classNames from "classnames";
2
  import { useRef } from "react";
3
  import { TbReload } from "react-icons/tb";
4
  import { toast } from "react-toastify";
 
5
 
6
  function Preview({
7
  html,
8
  isResizing,
9
  isAiWorking,
 
10
  ref,
11
  }: {
12
  html: string;
13
  isResizing: boolean;
14
  isAiWorking: boolean;
 
15
  ref: React.RefObject<HTMLDivElement | null>;
16
  }) {
17
  const iframeRef = useRef<HTMLIFrameElement | null>(null);
@@ -30,7 +33,7 @@ function Preview({
30
  return (
31
  <div
32
  ref={ref}
33
- className="w-full border-l border-gray-900 bg-white h-[calc(70dvh-53px)] lg:h-[calc(100dvh-54px)] relative"
34
  onClick={(e) => {
35
  if (isAiWorking) {
36
  e.preventDefault();
@@ -47,13 +50,22 @@ function Preview({
47
  })}
48
  srcDoc={html}
49
  />
50
- <button
51
- 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"
52
- onClick={handleRefreshIframe}
53
- >
54
- <TbReload />
55
- Refresh Preview
56
- </button>
 
 
 
 
 
 
 
 
 
57
  </div>
58
  );
59
  }
 
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
 
7
  function Preview({
8
  html,
9
  isResizing,
10
  isAiWorking,
11
+ setView,
12
  ref,
13
  }: {
14
  html: string;
15
  isResizing: boolean;
16
  isAiWorking: boolean;
17
+ setView: React.Dispatch<React.SetStateAction<"editor" | "preview">>;
18
  ref: React.RefObject<HTMLDivElement | null>;
19
  }) {
20
  const iframeRef = useRef<HTMLIFrameElement | null>(null);
 
33
  return (
34
  <div
35
  ref={ref}
36
+ className="w-full border-l border-gray-900 bg-white h-[calc(100dvh-49px)] lg:h-[calc(100dvh-53px)] relative"
37
  onClick={(e) => {
38
  if (isAiWorking) {
39
  e.preventDefault();
 
50
  })}
51
  srcDoc={html}
52
  />
53
+ <div className="flex items-center justify-start gap-3 absolute bottom-3 lg:bottom-5 max-lg:left-3 lg:right-5">
54
+ <button
55
+ className="lg:hidden bg-gray-950 shadow-md 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-900 hover:brightness-150 transition-all duration-100 cursor-pointer"
56
+ onClick={() => setView("editor")}
57
+ >
58
+ <FaLaptopCode />
59
+ Back to Editor
60
+ </button>
61
+ <button
62
+ 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"
63
+ onClick={handleRefreshIframe}
64
+ >
65
+ <TbReload />
66
+ Refresh Preview
67
+ </button>
68
+ </div>
69
  </div>
70
  );
71
  }
utils/consts.ts CHANGED
@@ -29,9 +29,7 @@ export const defaultHTML = `<!DOCTYPE html>
29
  }
30
  @media screen and (max-width: 640px) {
31
  .arrow {
32
- top: 12px;
33
- left: 56px;
34
- transform: rotate(180deg);
35
  }
36
  }
37
  </style>
 
29
  }
30
  @media screen and (max-width: 640px) {
31
  .arrow {
32
+ display: none;
 
 
33
  }
34
  }
35
  </style>