legends810 commited on
Commit
7eb2fdf
·
verified ·
1 Parent(s): 8e5c46e

Update components/editor/ask-ai/index.tsx

Browse files
Files changed (1) hide show
  1. components/editor/ask-ai/index.tsx +139 -13
components/editor/ask-ai/index.tsx CHANGED
@@ -11,6 +11,7 @@ import ProModal from "@/components/pro-modal";
11
  import { Button } from "@/components/ui/button";
12
  import { MODELS } from "@/lib/providers";
13
  import { HtmlHistory, Page, Project } from "@/types";
 
14
  import { Settings } from "@/components/editor/ask-ai/settings";
15
  import { LoginModal } from "@/components/login-modal";
16
  import { ReImagine } from "@/components/editor/ask-ai/re-imagine";
@@ -34,7 +35,6 @@ export function AskAI({
34
  onScrollToBottom,
35
  isAiWorking,
36
  setisAiWorking,
37
- setAiThinking, // Add this prop
38
  isEditableModeEnabled = false,
39
  pages,
40
  htmlHistory,
@@ -58,7 +58,6 @@ export function AskAI({
58
  onNewPrompt: (prompt: string) => void;
59
  htmlHistory?: HtmlHistory[];
60
  setisAiWorking: React.Dispatch<React.SetStateAction<boolean>>;
61
- setAiThinking: React.Dispatch<React.SetStateAction<string | null>>; // Add type for the prop
62
  isNew?: boolean;
63
  onSuccess: (page: Page[], p: string, n?: number[][]) => void;
64
  isEditableModeEnabled: boolean;
@@ -81,6 +80,7 @@ export function AskAI({
81
  const [openProModal, setOpenProModal] = useState(false);
82
  const [openThink, setOpenThink] = useState(false);
83
  const [isThinking, setIsThinking] = useState(true);
 
84
  const [isFollowUp, setIsFollowUp] = useState(true);
85
  const [isUploading, setIsUploading] = useState(false);
86
  const [files, setFiles] = useState<string[]>(images ?? []);
@@ -101,7 +101,6 @@ export function AskAI({
101
  pages,
102
  isAiWorking,
103
  setisAiWorking,
104
- setAiThinking, // Pass setAiThinking to the hook
105
  });
106
 
107
  const selectedModel = useMemo(() => {
@@ -112,9 +111,8 @@ export function AskAI({
112
  if (isAiWorking) return;
113
  if (!redesignMarkdown && !prompt.trim()) return;
114
 
115
- setAiThinking(""); // Reset thinking text on new request
116
-
117
  if (isFollowUp && !redesignMarkdown && !isSameHtml) {
 
118
  const selectedElementHtml = selectedElement
119
  ? selectedElement.outerHTML
120
  : "";
@@ -160,7 +158,11 @@ export function AskAI({
160
  prompt,
161
  model,
162
  provider,
163
- redesignMarkdown
 
 
 
 
164
  );
165
 
166
  if (result?.error) {
@@ -177,6 +179,12 @@ export function AskAI({
177
  }
178
  };
179
 
 
 
 
 
 
 
180
  const handleError = (error: string, message?: string) => {
181
  switch (error) {
182
  case "login_required":
@@ -200,6 +208,12 @@ export function AskAI({
200
  }
201
  };
202
 
 
 
 
 
 
 
203
  useUpdateEffect(() => {
204
  if (!isThinking) {
205
  setOpenThink(false);
@@ -213,6 +227,43 @@ export function AskAI({
213
  return (
214
  <div className="px-3">
215
  <div className="relative bg-neutral-800 border border-neutral-700 rounded-2xl ring-[4px] focus-within:ring-neutral-500/30 focus-within:border-neutral-600 ring-transparent z-10 w-full group">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  <SelectedFiles
217
  files={selectedFiles}
218
  isAiWorking={isAiWorking}
@@ -235,7 +286,68 @@ export function AskAI({
235
  <div className="flex items-center justify-start gap-2">
236
  <Loading overlay={false} className="!size-4 opacity-50" />
237
  <p className="text-neutral-400 text-sm">
238
- {isUploading ? "Uploading images..." : "AI is working..."}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  </p>
240
  </div>
241
  {isAiWorking && (
@@ -253,20 +365,21 @@ export function AskAI({
253
  disabled={isAiWorking}
254
  className={classNames(
255
  "w-full bg-transparent text-sm outline-none text-white placeholder:text-neutral-400 p-4 resize-none",
256
- { "!pt-2.5": selectedElement && !isAiWorking }
 
 
257
  )}
258
  placeholder={
259
  selectedElement
260
- ? `Ask about ${selectedElement.tagName.toLowerCase()}...`
261
  : isFollowUp && (!isSameHtml || pages?.length > 1)
262
- ? "Ask for edits..."
263
- : "Ask anything..."
264
  }
265
  value={prompt}
266
  onChange={(e) => setPrompt(e.target.value)}
267
  onKeyDown={(e) => {
268
  if (e.key === "Enter" && !e.shiftKey) {
269
- e.preventDefault();
270
  callAi();
271
  }
272
  }}
@@ -313,10 +426,12 @@ export function AskAI({
313
  align="start"
314
  className="bg-neutral-950 text-xs text-neutral-200 py-1 px-2 rounded-md -translate-y-0.5"
315
  >
316
- Select an element to edit it directly.
 
317
  </TooltipContent>
318
  </Tooltip>
319
  )}
 
320
  </div>
321
  <div className="flex items-center justify-end gap-2">
322
  <Settings
@@ -344,6 +459,16 @@ export function AskAI({
344
  open={openProModal}
345
  onClose={() => setOpenProModal(false)}
346
  />
 
 
 
 
 
 
 
 
 
 
347
  {!isSameHtml && (
348
  <div className="absolute top-0 right-0 -translate-y-[calc(100%+8px)] select-none text-xs text-neutral-400 flex items-center justify-center gap-2 bg-neutral-800 border border-neutral-700 rounded-md p-1 pr-2.5">
349
  <label
@@ -368,6 +493,7 @@ export function AskAI({
368
  </div>
369
  <audio ref={hookAudio} id="audio" className="hidden">
370
  <source src="/success.mp3" type="audio/mpeg" />
 
371
  </audio>
372
  </div>
373
  );
 
11
  import { Button } from "@/components/ui/button";
12
  import { MODELS } from "@/lib/providers";
13
  import { HtmlHistory, Page, Project } from "@/types";
14
+ // import { InviteFriends } from "@/components/invite-friends";
15
  import { Settings } from "@/components/editor/ask-ai/settings";
16
  import { LoginModal } from "@/components/login-modal";
17
  import { ReImagine } from "@/components/editor/ask-ai/re-imagine";
 
35
  onScrollToBottom,
36
  isAiWorking,
37
  setisAiWorking,
 
38
  isEditableModeEnabled = false,
39
  pages,
40
  htmlHistory,
 
58
  onNewPrompt: (prompt: string) => void;
59
  htmlHistory?: HtmlHistory[];
60
  setisAiWorking: React.Dispatch<React.SetStateAction<boolean>>;
 
61
  isNew?: boolean;
62
  onSuccess: (page: Page[], p: string, n?: number[][]) => void;
63
  isEditableModeEnabled: boolean;
 
80
  const [openProModal, setOpenProModal] = useState(false);
81
  const [openThink, setOpenThink] = useState(false);
82
  const [isThinking, setIsThinking] = useState(true);
83
+ const [think, setThink] = useState("");
84
  const [isFollowUp, setIsFollowUp] = useState(true);
85
  const [isUploading, setIsUploading] = useState(false);
86
  const [files, setFiles] = useState<string[]>(images ?? []);
 
101
  pages,
102
  isAiWorking,
103
  setisAiWorking,
 
104
  });
105
 
106
  const selectedModel = useMemo(() => {
 
111
  if (isAiWorking) return;
112
  if (!redesignMarkdown && !prompt.trim()) return;
113
 
 
 
114
  if (isFollowUp && !redesignMarkdown && !isSameHtml) {
115
+ // Use follow-up function for existing projects
116
  const selectedElementHtml = selectedElement
117
  ? selectedElement.outerHTML
118
  : "";
 
158
  prompt,
159
  model,
160
  provider,
161
+ redesignMarkdown,
162
+ handleThink,
163
+ () => {
164
+ setIsThinking(false);
165
+ }
166
  );
167
 
168
  if (result?.error) {
 
179
  }
180
  };
181
 
182
+ const handleThink = (think: string) => {
183
+ setThink(think);
184
+ setIsThinking(true);
185
+ setOpenThink(true);
186
+ };
187
+
188
  const handleError = (error: string, message?: string) => {
189
  switch (error) {
190
  case "login_required":
 
208
  }
209
  };
210
 
211
+ useUpdateEffect(() => {
212
+ if (refThink.current) {
213
+ refThink.current.scrollTop = refThink.current.scrollHeight;
214
+ }
215
+ }, [think]);
216
+
217
  useUpdateEffect(() => {
218
  if (!isThinking) {
219
  setOpenThink(false);
 
227
  return (
228
  <div className="px-3">
229
  <div className="relative bg-neutral-800 border border-neutral-700 rounded-2xl ring-[4px] focus-within:ring-neutral-500/30 focus-within:border-neutral-600 ring-transparent z-10 w-full group">
230
+ {think && (
231
+ <div className="w-full border-b border-neutral-700 relative overflow-hidden">
232
+ <header
233
+ className="flex items-center justify-between px-5 py-2.5 group hover:bg-neutral-600/20 transition-colors duration-200 cursor-pointer"
234
+ onClick={() => {
235
+ setOpenThink(!openThink);
236
+ }}
237
+ >
238
+ <p className="text-sm font-medium text-neutral-300 group-hover:text-neutral-200 transition-colors duration-200">
239
+ {isThinking ? "DeepSite is thinking..." : "DeepSite's plan"}
240
+ </p>
241
+ <ChevronDown
242
+ className={classNames(
243
+ "size-4 text-neutral-400 group-hover:text-neutral-300 transition-all duration-200",
244
+ {
245
+ "rotate-180": openThink,
246
+ }
247
+ )}
248
+ />
249
+ </header>
250
+ <main
251
+ ref={refThink}
252
+ className={classNames(
253
+ "overflow-y-auto transition-all duration-200 ease-in-out",
254
+ {
255
+ "max-h-[0px]": !openThink,
256
+ "min-h-[250px] max-h-[250px] border-t border-neutral-700":
257
+ openThink,
258
+ }
259
+ )}
260
+ >
261
+ <p className="text-[13px] text-neutral-400 whitespace-pre-line px-5 pb-4 pt-3">
262
+ {think}
263
+ </p>
264
+ </main>
265
+ </div>
266
+ )}
267
  <SelectedFiles
268
  files={selectedFiles}
269
  isAiWorking={isAiWorking}
 
286
  <div className="flex items-center justify-start gap-2">
287
  <Loading overlay={false} className="!size-4 opacity-50" />
288
  <p className="text-neutral-400 text-sm">
289
+ {isUploading ? (
290
+ "Uploading images..."
291
+ ) : isAiWorking && !isSameHtml ? (
292
+ "AI is working..."
293
+ ) : (
294
+ <span className="inline-flex">
295
+ {[
296
+ "D",
297
+ "e",
298
+ "e",
299
+ "p",
300
+ "S",
301
+ "i",
302
+ "t",
303
+ "e",
304
+ " ",
305
+ "i",
306
+ "s",
307
+ " ",
308
+ "T",
309
+ "h",
310
+ "i",
311
+ "n",
312
+ "k",
313
+ "i",
314
+ "n",
315
+ "g",
316
+ ".",
317
+ ".",
318
+ ".",
319
+ " ",
320
+ "W",
321
+ "a",
322
+ "i",
323
+ "t",
324
+ " ",
325
+ "a",
326
+ " ",
327
+ "m",
328
+ "o",
329
+ "m",
330
+ "e",
331
+ "n",
332
+ "t",
333
+ ".",
334
+ ".",
335
+ ".",
336
+ ].map((char, index) => (
337
+ <span
338
+ key={index}
339
+ className="bg-gradient-to-r from-neutral-100 to-neutral-300 bg-clip-text text-transparent animate-pulse"
340
+ style={{
341
+ animationDelay: `${index * 0.1}s`,
342
+ animationDuration: "1.3s",
343
+ animationIterationCount: "infinite",
344
+ }}
345
+ >
346
+ {char === " " ? "\u00A0" : char}
347
+ </span>
348
+ ))}
349
+ </span>
350
+ )}
351
  </p>
352
  </div>
353
  {isAiWorking && (
 
365
  disabled={isAiWorking}
366
  className={classNames(
367
  "w-full bg-transparent text-sm outline-none text-white placeholder:text-neutral-400 p-4 resize-none",
368
+ {
369
+ "!pt-2.5": selectedElement && !isAiWorking,
370
+ }
371
  )}
372
  placeholder={
373
  selectedElement
374
+ ? `Ask DeepSite about ${selectedElement.tagName.toLowerCase()}...`
375
  : isFollowUp && (!isSameHtml || pages?.length > 1)
376
+ ? "Ask DeepSite for edits"
377
+ : "Ask DeepSite anything..."
378
  }
379
  value={prompt}
380
  onChange={(e) => setPrompt(e.target.value)}
381
  onKeyDown={(e) => {
382
  if (e.key === "Enter" && !e.shiftKey) {
 
383
  callAi();
384
  }
385
  }}
 
426
  align="start"
427
  className="bg-neutral-950 text-xs text-neutral-200 py-1 px-2 rounded-md -translate-y-0.5"
428
  >
429
+ Select an element on the page to ask DeepSite edit it
430
+ directly.
431
  </TooltipContent>
432
  </Tooltip>
433
  )}
434
+ {/* <InviteFriends /> */}
435
  </div>
436
  <div className="flex items-center justify-end gap-2">
437
  <Settings
 
459
  open={openProModal}
460
  onClose={() => setOpenProModal(false)}
461
  />
462
+ {pages.length === 1 && (
463
+ <div className="border border-sky-500/20 bg-sky-500/40 hover:bg-sky-600 transition-all duration-200 text-sky-500 pl-2 pr-4 py-1.5 text-xs rounded-full absolute top-0 -translate-y-[calc(100%+8px)] left-0 max-w-max flex items-center justify-start gap-2">
464
+ <span className="rounded-full text-[10px] font-semibold bg-white text-neutral-900 px-1.5 py-0.5">
465
+ NEW
466
+ </span>
467
+ <p className="text-sm text-neutral-100">
468
+ DeepSite can now create multiple pages at once. Try it!
469
+ </p>
470
+ </div>
471
+ )}
472
  {!isSameHtml && (
473
  <div className="absolute top-0 right-0 -translate-y-[calc(100%+8px)] select-none text-xs text-neutral-400 flex items-center justify-center gap-2 bg-neutral-800 border border-neutral-700 rounded-md p-1 pr-2.5">
474
  <label
 
493
  </div>
494
  <audio ref={hookAudio} id="audio" className="hidden">
495
  <source src="/success.mp3" type="audio/mpeg" />
496
+ Your browser does not support the audio element.
497
  </audio>
498
  </div>
499
  );