Skip to content

Commit 08270bb

Browse files
Merge branch 'main' into fix/almanac-contract-api
2 parents cda6244 + fe72b75 commit 08270bb

16 files changed

+5426
-3414
lines changed

components/chat/chat-window.tsx

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
"use client";
2+
import React, { useState } from "react";
3+
import Image from "next/image";
4+
import { SendIcon } from "src/icons/shared-icons";
5+
import FetchWhite from "src/images/fetch_logo_only_white.svg";
6+
import MarkdownRenderer from "./mark-down-render";
7+
interface ChatWindowProps {
8+
onClose: () => void;
9+
}
10+
11+
const ChatWindow: React.FC<ChatWindowProps> = ({ onClose }) => {
12+
const [messages, setMessages] = useState<
13+
{ type: "user" | "ai"; text: string }[]
14+
>([
15+
{ type: "ai", text: "Hi there! 👋 Welcome to our chat." },
16+
{ type: "ai", text: "How can I assist you today?" },
17+
]);
18+
const [input, setInput] = useState("");
19+
const [isTyping, setIsTyping] = useState(false);
20+
21+
const handleSendMessage = async () => {
22+
if (!input.trim()) return;
23+
setMessages((prev) => [...prev, { type: "user", text: input }]);
24+
setInput("");
25+
setIsTyping(true);
26+
try {
27+
const response = await fetch("/docs/api/chat", {
28+
method: "POST",
29+
headers: { "Content-Type": "application/json" },
30+
body: JSON.stringify({ message: input }),
31+
});
32+
const data = await response.json();
33+
setTimeout(() => {
34+
setMessages((prev) => [...prev, { type: "ai", text: data.reply }]);
35+
setIsTyping(false);
36+
}, 1500);
37+
} catch (error) {
38+
console.log("error", error);
39+
setTimeout(() => {
40+
setMessages((prev) => [
41+
...prev,
42+
{
43+
type: "ai",
44+
text: "Sorry, something went wrong. Please try again.",
45+
},
46+
]);
47+
setIsTyping(false);
48+
}, 1500);
49+
}
50+
};
51+
52+
return (
53+
<div className="nx-fixed nx-bottom-0 md:nx-bottom-3 nx-right-0 md:nx-right-3 nx-w-full nx-max-w-md dark:nx-bg-[#242630] nx-bg-[#F3F5F8] nx-shadow-xl nx-rounded-xl nx-z-[51] nx-pb-6">
54+
<div className="nx-flex nx-justify-between nx-items-center dark:nx-bg-none nx-bg-gradient-to-b nx-py-3 nx-from-[rgba(95,56,251,0.1)] nx-to-[rgba(208,234,255,0.1)] nx-rounded-xl nx-px-6">
55+
<div className="tooltip">
56+
<div className="nx-p-1 nx-bg-[#5f38fb] nx-font-normal nx-text-sm nx-rounded-md nx-text-white">
57+
Beta
58+
</div>
59+
<div className="tooltip-text nx-p-2">
60+
Our chat agent is in training, and results will improve with use
61+
</div>
62+
</div>
63+
<button onClick={onClose} className="nx-text-[#000D3D]">
64+
&#x2715;
65+
</button>
66+
</div>
67+
<div className="nx-px-6 ">
68+
<div className="nx-space-y-2 nx-h-[calc(100vh-400px)] nx-md:!h-[464px] nx-overflow-y-auto">
69+
{messages.map((msg, idx) => (
70+
<div
71+
key={idx}
72+
className={`nx-flex ${
73+
msg.type === "user" ? "nx-justify-end" : "nx-justify-start"
74+
}`}
75+
>
76+
{msg.type === "ai" ? (
77+
<div className="nx-w-6 nx-h-6 nx-rounded-full nx-bg-white nx-mt-2 nx-mr-2 nx-flex nx-justify-center nx-items-center">
78+
<div className="nx-w-4 nx-h-4 nx-rounded-full nx-bg-[#0B1742] nx-flex nx-justify-center nx-items-center">
79+
<Image
80+
src={FetchWhite}
81+
alt="agentverse-img"
82+
width={6}
83+
height={6}
84+
className="nx-w-[6px] nx-h-[6px]"
85+
/>
86+
</div>
87+
</div>
88+
) : undefined}
89+
90+
{msg.type === "user" ? (
91+
<p
92+
className={`nx-px-4 nx-py-3 nx-rounded-lg nx-text-sm nx-my-1 nx-text-[#000D3D] dark:nx-text-white dark:nx-bg-[#363841] nx-bg-[#FCFCFD]`}
93+
>
94+
{msg.text}
95+
</p>
96+
) : (
97+
<p
98+
className={`nx-px-4 nx-py-3 nx-rounded-lg nx-text-sm nx-max-w-[350px] nx-my-1 nx-text-[#000D3D] dark:nx-text-white dark:nx-bg-[#363841] nx-bg-white`}
99+
>
100+
<MarkdownRenderer markdownContent={msg.text} />
101+
</p>
102+
)}
103+
</div>
104+
))}
105+
{isTyping && (
106+
<div className="nx-flex nx-justify-start">
107+
<div className="nx-w-6 nx-h-6 nx-rounded-full nx-bg-white nx-mt-2 nx-mr-2 nx-flex nx-justify-center nx-items-center">
108+
<div className="nx-w-4 nx-h-4 nx-rounded-full nx-bg-[#0B1742] nx-flex nx-justify-center nx-items-center">
109+
<Image
110+
src={FetchWhite}
111+
alt="agentverse-img"
112+
width={6}
113+
height={6}
114+
className="nx-w-[6px] nx-h-[6px]"
115+
/>
116+
</div>
117+
</div>
118+
<div className="nx-bg-white dark:nx-bg-[#363841] nx-text-[#000D3D] nx-px-4 nx-py-3 nx-rounded-lg nx-flex nx-items-center nx-space-x-1">
119+
<span className="dot-animation"></span>
120+
<span className="dot-animation"></span>
121+
<span className="dot-animation"></span>
122+
</div>
123+
</div>
124+
)}
125+
</div>
126+
<div className="nx-p-4 nx-h-[56px] nx-w-full nx-bg-white dark:nx-bg-[#363841] nx-rounded-lg">
127+
<div className="nx-flex nx-justify-between nx-space-x-2 nx-w-full">
128+
<input
129+
type="text"
130+
value={input}
131+
onChange={(e) => setInput(e.target.value)}
132+
placeholder="Type your message..."
133+
className="nx-w-11/12 chat-with-usInput"
134+
onKeyDown={(e) => {
135+
if (e.key === "Enter") {
136+
e.preventDefault();
137+
handleSendMessage();
138+
}
139+
}}
140+
/>
141+
<div
142+
onClick={handleSendMessage}
143+
className="nx-cursor-pointer nx-w-[14px] nx-h-[14px] nx-ml-3 nx-my-auto"
144+
>
145+
<SendIcon />
146+
</div>
147+
</div>
148+
</div>
149+
</div>
150+
</div>
151+
);
152+
};
153+
154+
export default ChatWindow;

components/chat/chat-with-us.tsx

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"use client";
2+
import React, { useState } from "react";
3+
import ChatWindow from "./chat-window";
4+
import { AgentIcon } from "src/icons/shared-icons";
5+
6+
const ChatWithUs = () => {
7+
const [isChatOpen, setIsChatOpen] = useState(false);
8+
9+
return (
10+
<>
11+
<button
12+
onClick={() => setIsChatOpen(!isChatOpen)}
13+
className="nx-fixed nx-bottom-12 nx-right-12 !nx-px-6 !nx-py-4 nx-z-40 nx-flex nx-bg-chatwithus nx-text-white nx-rounded-lg nx-font-medium nx-gap-4"
14+
>
15+
<AgentIcon />
16+
Chat with us
17+
</button>
18+
{isChatOpen && <ChatWindow onClose={() => setIsChatOpen(false)} />}
19+
</>
20+
);
21+
};
22+
23+
export default ChatWithUs;
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"use client";
2+
import { ModifiedPre } from "components/code";
3+
import React, { useState, useEffect } from "react";
4+
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
5+
6+
const CustomCodeBlock = ({
7+
children,
8+
}: {
9+
children: React.ReactNode | string;
10+
}) => {
11+
const [numberLines, setNumberLines] = useState<number>(0);
12+
13+
useEffect(() => {
14+
const text = typeof children === "string" ? children : children?.toString();
15+
const lines = text?.split("\n").filter(Boolean).length;
16+
setNumberLines(lines ?? 0);
17+
}, [children]);
18+
19+
if (numberLines === 1) {
20+
return (
21+
<div className="nx-inline-flex">
22+
<SyntaxHighlighter
23+
id="code-single"
24+
language="python"
25+
style={{
26+
margin: "0px",
27+
}}
28+
wrapLines={true}
29+
wrapLongLines={true}
30+
>
31+
{children as string}
32+
</SyntaxHighlighter>
33+
</div>
34+
);
35+
}
36+
37+
return (
38+
<div className="nx-py-4">
39+
<ModifiedPre>{children}</ModifiedPre>
40+
</div>
41+
);
42+
};
43+
export default CustomCodeBlock;

components/chat/mark-down-render.tsx

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from "react";
2+
import ReactMarkdown from "react-markdown";
3+
import rehypeSanitize from "rehype-sanitize";
4+
5+
import CustomCodeBlock from "./custome-code-blocks";
6+
7+
interface MarkdownRendererProperties {
8+
markdownContent: string;
9+
}
10+
11+
const MarkdownRenderer: React.FC<MarkdownRendererProperties> = ({
12+
markdownContent,
13+
}) => {
14+
return (
15+
<ReactMarkdown
16+
skipHtml={false}
17+
components={{ code: CustomCodeBlock }}
18+
rehypePlugins={[rehypeSanitize]}
19+
>
20+
{markdownContent}
21+
</ReactMarkdown>
22+
);
23+
};
24+
25+
export default MarkdownRenderer;

components/landing-page.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
} from "src/icons/shared-icons";
3131
import { useTheme } from "next-themes";
3232
import { ThemeMode } from "theme/fetch-ai-docs/helpers";
33+
import ChatWithUs from "./chat/chat-with-us";
3334

3435
const startingGuides = (theme) => [
3536
{
@@ -314,6 +315,7 @@ function LandingPage() {
314315
</p>
315316
<Products />
316317
</section>
318+
<ChatWithUs />
317319
</section>
318320
);
319321
}

package.json

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
"react-error-boundary": "^4.0.13",
4343
"react-icons": "^4.11.0",
4444
"react-instantsearch-dom": "^6.40.4",
45+
"react-markdown": "^9.0.1",
46+
"react-syntax-highlighter": "^15.6.1",
47+
"rehype-sanitize": "^6.0.0",
4548
"remark": "^15.0.1",
4649
"remark-html": "^16.0.1",
4750
"scroll-into-view-if-needed": "^3.0.0",

pages/api/chat.ts

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// src/pages/api/chat.ts
2+
import type { NextApiRequest, NextApiResponse } from "next";
3+
4+
export default async function handler(
5+
req: NextApiRequest,
6+
res: NextApiResponse,
7+
) {
8+
if (req.method === "POST") {
9+
const { message } = req.body;
10+
11+
try {
12+
const response = await fetch(
13+
"https://chat-with-docs-rzql.onrender.com/echo",
14+
{
15+
method: "POST",
16+
headers: { "Content-Type": "application/json" },
17+
body: JSON.stringify({ message: message }),
18+
},
19+
);
20+
const data = await response.json();
21+
return res.status(200).json({ reply: data.message });
22+
} catch (error) {
23+
console.log("error", error);
24+
return res
25+
.status(500)
26+
.json({ reply: "Something went wrong. Please try again." });
27+
}
28+
} else {
29+
res.setHeader("Allow", ["POST"]);
30+
res.status(405).end(`Method ${req.method} Not Allowed`);
31+
}
32+
}

pages/examples/agent/_meta.json

+5
Original file line numberDiff line numberDiff line change
@@ -95,5 +95,10 @@
9595
"title": "Running an Agent with Docker [Intermediate]",
9696
"tags": ["Intermediate", "Python", "Docker", "Use Cases"],
9797
"timestamp": true
98+
},
99+
"search-example": {
100+
"title": "Search Agents [Intermediate]",
101+
"tags": ["Intermediate", "Python", "Search Apis"],
102+
"timestamp": true
98103
}
99104
}

0 commit comments

Comments
 (0)