All checks were successful
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧪 Test (push) Successful in 1m3s
Monorepo Pipeline / 🏗️ Build (push) Successful in 3m34s
Monorepo Pipeline / 🧹 Lint (push) Successful in 3m56s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
- Remove non-existent ChatWindow.scss import causing Webpack resolution errors - Disable dynamic ChatWindowProvider injection (now statically declared in host app) - Revert build script to tsc (no SCSS copy needed)
157 lines
4.3 KiB
TypeScript
157 lines
4.3 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect } from "react";
|
|
import { useChat } from "@ai-sdk/react";
|
|
|
|
export const ChatWindowProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
children,
|
|
}) => {
|
|
return (
|
|
<>
|
|
{children}
|
|
<ChatWindow />
|
|
</>
|
|
);
|
|
};
|
|
|
|
const ChatWindow: React.FC = () => {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const [pageContext, setPageContext] = useState<any>({ url: "" });
|
|
|
|
useEffect(() => {
|
|
if (typeof window !== "undefined") {
|
|
const path = window.location.pathname;
|
|
let collectionSlug = null;
|
|
let id = null;
|
|
// Payload admin URLs are usually /admin/collections/:slug/:id
|
|
const match = path.match(/\/collections\/([^/]+)(?:\/([^/]+))?/);
|
|
if (match) {
|
|
collectionSlug = match[1];
|
|
if (match[2] && match[2] !== "create") {
|
|
id = match[2];
|
|
}
|
|
}
|
|
|
|
setPageContext({
|
|
url: window.location.href,
|
|
title: document.title,
|
|
collectionSlug,
|
|
id,
|
|
});
|
|
}
|
|
}, [isOpen]); // Refresh context when chat is opened
|
|
|
|
const { messages, input, handleInputChange, handleSubmit } = useChat({
|
|
api: "/api/mcp-chat",
|
|
initialMessages: [],
|
|
body: {
|
|
pageContext,
|
|
},
|
|
} as any) as any;
|
|
|
|
// Basic implementation to toggle chat window and submit messages
|
|
return (
|
|
<div className="payload-mcp-chat-container">
|
|
<button
|
|
className="payload-mcp-chat-toggle"
|
|
onClick={() => setIsOpen(!isOpen)}
|
|
style={{
|
|
position: "fixed",
|
|
bottom: "20px",
|
|
right: "20px",
|
|
zIndex: 9999,
|
|
padding: "12px 24px",
|
|
backgroundColor: "#000",
|
|
color: "#fff",
|
|
borderRadius: "8px",
|
|
border: "none",
|
|
cursor: "pointer",
|
|
fontWeight: "bold",
|
|
}}
|
|
>
|
|
{isOpen ? "Close AI Chat" : "Ask AI"}
|
|
</button>
|
|
|
|
{isOpen && (
|
|
<div
|
|
className="payload-mcp-chat-window"
|
|
style={{
|
|
position: "fixed",
|
|
bottom: "80px",
|
|
right: "20px",
|
|
width: "450px",
|
|
height: "650px",
|
|
backgroundColor: "#fff",
|
|
border: "1px solid #eaeaea",
|
|
borderRadius: "12px",
|
|
zIndex: 9999,
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
boxShadow: "0 10px 40px rgba(0,0,0,0.1)",
|
|
}}
|
|
>
|
|
<div
|
|
className="chat-header"
|
|
style={{
|
|
padding: "16px",
|
|
borderBottom: "1px solid #eaeaea",
|
|
backgroundColor: "#f9f9f9",
|
|
borderTopLeftRadius: "12px",
|
|
borderTopRightRadius: "12px",
|
|
}}
|
|
>
|
|
<h3 style={{ margin: 0, fontSize: "16px" }}>Payload MCP Chat</h3>
|
|
</div>
|
|
|
|
<div
|
|
className="chat-messages"
|
|
style={{ flex: 1, padding: "16px", overflowY: "auto" }}
|
|
>
|
|
{messages.map((m: any) => (
|
|
<div
|
|
key={m.id}
|
|
style={{
|
|
marginBottom: "12px",
|
|
textAlign: m.role === "user" ? "right" : "left",
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
display: "inline-block",
|
|
padding: "8px 12px",
|
|
borderRadius: "8px",
|
|
backgroundColor: m.role === "user" ? "#000" : "#f0f0f0",
|
|
color: m.role === "user" ? "#fff" : "#000",
|
|
maxWidth: "80%",
|
|
}}
|
|
>
|
|
{m.role === "user" ? "G: " : "AI: "}
|
|
{m.content}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<form
|
|
onSubmit={handleSubmit}
|
|
style={{ padding: "16px", borderTop: "1px solid #eaeaea" }}
|
|
>
|
|
<input
|
|
value={input}
|
|
placeholder="Ask me anything or use /commands..."
|
|
onChange={handleInputChange}
|
|
style={{
|
|
width: "100%",
|
|
padding: "12px",
|
|
borderRadius: "8px",
|
|
border: "1px solid #eaeaea",
|
|
boxSizing: "border-box",
|
|
}}
|
|
/>
|
|
</form>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|