Update src/components/ask-ai/ask-ai.tsx
Browse files- src/components/ask-ai/ask-ai.tsx +45 -35
src/components/ask-ai/ask-ai.tsx
CHANGED
|
@@ -2,10 +2,9 @@
|
|
| 2 |
import { useState } from "react";
|
| 3 |
import { RiSparkling2Fill } from "react-icons/ri";
|
| 4 |
import { GrSend } from "react-icons/gr";
|
|
|
|
| 5 |
import classNames from "classnames";
|
| 6 |
import { toast } from "react-toastify";
|
| 7 |
-
import { MdPreview } from "react-icons/md";
|
| 8 |
-
|
| 9 |
import { defaultHTML } from "./../../../utils/consts";
|
| 10 |
import SuccessSound from "./../../assets/success.mp3";
|
| 11 |
|
|
@@ -39,26 +38,39 @@ function AskAI({
|
|
| 39 |
|
| 40 |
let contentResponse = "";
|
| 41 |
let lastRenderTime = 0;
|
|
|
|
| 42 |
try {
|
| 43 |
onNewPrompt(prompt);
|
| 44 |
|
| 45 |
const request = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
| 46 |
method: "POST",
|
| 47 |
headers: {
|
|
|
|
| 48 |
"Content-Type": "application/json",
|
| 49 |
-
"
|
|
|
|
| 50 |
},
|
| 51 |
body: JSON.stringify({
|
| 52 |
-
model: "
|
|
|
|
| 53 |
messages: [
|
| 54 |
-
{
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
})
|
| 59 |
});
|
| 60 |
|
| 61 |
-
if (!request.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
const reader = request.body.getReader();
|
| 64 |
const decoder = new TextDecoder("utf-8");
|
|
@@ -66,7 +78,7 @@ function AskAI({
|
|
| 66 |
const read = async () => {
|
| 67 |
const { done, value } = await reader.read();
|
| 68 |
if (done) {
|
| 69 |
-
toast.success("
|
| 70 |
setPrompt("");
|
| 71 |
setPreviousPrompt(prompt);
|
| 72 |
setisAiWorking(false);
|
|
@@ -80,21 +92,25 @@ function AskAI({
|
|
| 80 |
}
|
| 81 |
|
| 82 |
const chunk = decoder.decode(value, { stream: true });
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
}
|
| 99 |
}
|
| 100 |
|
|
@@ -104,7 +120,7 @@ function AskAI({
|
|
| 104 |
read();
|
| 105 |
} catch (error: any) {
|
| 106 |
setisAiWorking(false);
|
| 107 |
-
toast.error(
|
| 108 |
}
|
| 109 |
};
|
| 110 |
|
|
@@ -125,21 +141,15 @@ function AskAI({
|
|
| 125 |
type="text"
|
| 126 |
disabled={isAiWorking}
|
| 127 |
className="w-full bg-transparent max-lg:text-sm outline-none px-3 text-white placeholder:text-gray-500 font-code"
|
| 128 |
-
placeholder={
|
| 129 |
-
hasAsked ? "What do you want to ask AI next?" : "Ask AI anything..."
|
| 130 |
-
}
|
| 131 |
value={prompt}
|
| 132 |
onChange={(e) => setPrompt(e.target.value)}
|
| 133 |
-
onKeyDown={(e) => {
|
| 134 |
-
if (e.key === "Enter") {
|
| 135 |
-
callAi();
|
| 136 |
-
}
|
| 137 |
-
}}
|
| 138 |
/>
|
| 139 |
<div className="flex items-center justify-end gap-2">
|
| 140 |
<button
|
| 141 |
disabled={isAiWorking}
|
| 142 |
-
className="relative overflow-hidden cursor-pointer flex-none flex items-center justify-center rounded-full text-sm font-semibold size-8 text-center bg-pink-500 hover:bg-pink-400 text-white shadow-sm
|
| 143 |
onClick={callAi}
|
| 144 |
>
|
| 145 |
<GrSend className="-translate-x-[1px]" />
|
|
|
|
| 2 |
import { useState } from "react";
|
| 3 |
import { RiSparkling2Fill } from "react-icons/ri";
|
| 4 |
import { GrSend } from "react-icons/gr";
|
| 5 |
+
import { MdPreview } from "react-icons/md";
|
| 6 |
import classNames from "classnames";
|
| 7 |
import { toast } from "react-toastify";
|
|
|
|
|
|
|
| 8 |
import { defaultHTML } from "./../../../utils/consts";
|
| 9 |
import SuccessSound from "./../../assets/success.mp3";
|
| 10 |
|
|
|
|
| 38 |
|
| 39 |
let contentResponse = "";
|
| 40 |
let lastRenderTime = 0;
|
| 41 |
+
|
| 42 |
try {
|
| 43 |
onNewPrompt(prompt);
|
| 44 |
|
| 45 |
const request = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
| 46 |
method: "POST",
|
| 47 |
headers: {
|
| 48 |
+
"Authorization": "Bearer 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",
|
| 49 |
"Content-Type": "application/json",
|
| 50 |
+
"HTTP-Referer": "https://raypages.com", // ou o seu domínio
|
| 51 |
+
"X-Title": "RayPages AI"
|
| 52 |
},
|
| 53 |
body: JSON.stringify({
|
| 54 |
+
model: "deepseek-chat-v3.0",
|
| 55 |
+
stream: true,
|
| 56 |
messages: [
|
| 57 |
+
{
|
| 58 |
+
role: "system",
|
| 59 |
+
content: "Você é um especialista em landing pages. Gere um HTML bonito e persuasivo para a copy enviada. Responda apenas com código HTML válido e completo."
|
| 60 |
+
},
|
| 61 |
+
{
|
| 62 |
+
role: "user",
|
| 63 |
+
content: prompt
|
| 64 |
+
}
|
| 65 |
+
]
|
| 66 |
})
|
| 67 |
});
|
| 68 |
|
| 69 |
+
if (!request.ok || !request.body) {
|
| 70 |
+
toast.error("Erro ao chamar a IA.");
|
| 71 |
+
setisAiWorking(false);
|
| 72 |
+
return;
|
| 73 |
+
}
|
| 74 |
|
| 75 |
const reader = request.body.getReader();
|
| 76 |
const decoder = new TextDecoder("utf-8");
|
|
|
|
| 78 |
const read = async () => {
|
| 79 |
const { done, value } = await reader.read();
|
| 80 |
if (done) {
|
| 81 |
+
toast.success("Página gerada com sucesso!");
|
| 82 |
setPrompt("");
|
| 83 |
setPreviousPrompt(prompt);
|
| 84 |
setisAiWorking(false);
|
|
|
|
| 92 |
}
|
| 93 |
|
| 94 |
const chunk = decoder.decode(value, { stream: true });
|
| 95 |
+
const matches = chunk.match(/"content":"([^"]*)"/g);
|
| 96 |
+
if (matches) {
|
| 97 |
+
for (const match of matches) {
|
| 98 |
+
const text = match.replace(/"content":"|"/g, "").replace(/\\n/g, "\n").replace(/\\t/g, "\t");
|
| 99 |
+
contentResponse += text;
|
| 100 |
+
|
| 101 |
+
const newHtml = contentResponse.match(/<!DOCTYPE html>[\s\S]*/)?.[0];
|
| 102 |
+
if (newHtml) {
|
| 103 |
+
let partialDoc = newHtml;
|
| 104 |
+
if (!partialDoc.includes("</html>")) partialDoc += "\n</html>";
|
| 105 |
+
|
| 106 |
+
const now = Date.now();
|
| 107 |
+
if (now - lastRenderTime > 300) {
|
| 108 |
+
setHtml(partialDoc);
|
| 109 |
+
lastRenderTime = now;
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
if (partialDoc.length > 200) onScrollToBottom();
|
| 113 |
+
}
|
| 114 |
}
|
| 115 |
}
|
| 116 |
|
|
|
|
| 120 |
read();
|
| 121 |
} catch (error: any) {
|
| 122 |
setisAiWorking(false);
|
| 123 |
+
toast.error("Erro: " + error.message);
|
| 124 |
}
|
| 125 |
};
|
| 126 |
|
|
|
|
| 141 |
type="text"
|
| 142 |
disabled={isAiWorking}
|
| 143 |
className="w-full bg-transparent max-lg:text-sm outline-none px-3 text-white placeholder:text-gray-500 font-code"
|
| 144 |
+
placeholder={hasAsked ? "What do you want to ask AI next?" : "Ask AI anything..."}
|
|
|
|
|
|
|
| 145 |
value={prompt}
|
| 146 |
onChange={(e) => setPrompt(e.target.value)}
|
| 147 |
+
onKeyDown={(e) => { if (e.key === "Enter") callAi(); }}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
/>
|
| 149 |
<div className="flex items-center justify-end gap-2">
|
| 150 |
<button
|
| 151 |
disabled={isAiWorking}
|
| 152 |
+
className="relative overflow-hidden cursor-pointer flex-none flex items-center justify-center rounded-full text-sm font-semibold size-8 text-center bg-pink-500 hover:bg-pink-400 text-white shadow-sm disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed"
|
| 153 |
onClick={callAi}
|
| 154 |
>
|
| 155 |
<GrSend className="-translate-x-[1px]" />
|