191 lines
6.1 KiB
TypeScript
191 lines
6.1 KiB
TypeScript
import React, { useContext } from "react"
|
|
|
|
import { Card } from "antd"
|
|
|
|
import { PlaygroundForm } from "./PlaygroundForm"
|
|
import { PlaygroundChat } from "./PlaygroundChat"
|
|
import { useMessageOption } from "@/hooks/useMessageOption"
|
|
import { webUIResumeLastChat } from "@/services/app"
|
|
import { PlaygroundData } from '@/components/Common/Playground/Data.tsx'
|
|
import { PlaygroundScene } from "@/components/Common/Playground/Scene.tsx"
|
|
|
|
import {
|
|
formatToChatHistory,
|
|
formatToMessage,
|
|
getPromptById,
|
|
getRecentChatFromWebUI
|
|
} from "@/db"
|
|
import { getLastUsedChatSystemPrompt } from "@/services/model-settings"
|
|
import { useStoreChatModelSettings } from "@/store/model"
|
|
import { useSmartScroll } from "@/hooks/useSmartScroll"
|
|
import { ChevronDown } from "lucide-react"
|
|
import { PlaygroundTeam } from "@/components/Common/Playground/Team.tsx"
|
|
import { PlaygroundTokenStatistics } from "@/components/Common/Playground/TokenStatistics.tsx"
|
|
import { PlaygroundHistory } from "@/components/Common/Playground/History.tsx"
|
|
import { PlaygroundIodRelevant } from "@/components/Common/Playground/IodRelevant.tsx"
|
|
import { HistoryContext } from "@/components/Layouts/Layout.tsx"
|
|
|
|
|
|
export const Playground = () => {
|
|
const drop = React.useRef<HTMLDivElement>(null)
|
|
const [dropedFile, setDropedFile] = React.useState<File | undefined>()
|
|
const {
|
|
selectedKnowledge,
|
|
messages,
|
|
setHistoryId,
|
|
setHistory,
|
|
setMessages,
|
|
setSelectedSystemPrompt,
|
|
streaming
|
|
} = useMessageOption()
|
|
const { setSystemPrompt } = useStoreChatModelSettings()
|
|
const { containerRef, isAtBottom, scrollToBottom } = useSmartScroll(
|
|
messages,
|
|
streaming
|
|
)
|
|
|
|
const [dropState, setDropState] = React.useState<
|
|
"idle" | "dragging" | "error"
|
|
>("idle")
|
|
React.useEffect(() => {
|
|
if (selectedKnowledge) {
|
|
return
|
|
}
|
|
|
|
if (!drop.current) {
|
|
return
|
|
}
|
|
const handleDragOver = (e: DragEvent) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
}
|
|
|
|
const handleDrop = (e: DragEvent) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
|
|
setDropState("idle")
|
|
|
|
const files = Array.from(e.dataTransfer?.files || [])
|
|
|
|
const isImage = files.every((file) => file.type.startsWith("image/"))
|
|
|
|
if (!isImage) {
|
|
setDropState("error")
|
|
return
|
|
}
|
|
|
|
const newFiles = Array.from(e.dataTransfer?.files || []).slice(0, 1)
|
|
if (newFiles.length > 0) {
|
|
setDropedFile(newFiles[0])
|
|
}
|
|
}
|
|
|
|
const handleDragEnter = (e: DragEvent) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
setDropState("dragging")
|
|
}
|
|
|
|
const handleDragLeave = (e: DragEvent) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
setDropState("idle")
|
|
}
|
|
|
|
drop.current.addEventListener("dragover", handleDragOver)
|
|
drop.current.addEventListener("drop", handleDrop)
|
|
drop.current.addEventListener("dragenter", handleDragEnter)
|
|
drop.current.addEventListener("dragleave", handleDragLeave)
|
|
|
|
return () => {
|
|
if (drop.current) {
|
|
drop.current.removeEventListener("dragover", handleDragOver)
|
|
drop.current.removeEventListener("drop", handleDrop)
|
|
drop.current.removeEventListener("dragenter", handleDragEnter)
|
|
drop.current.removeEventListener("dragleave", handleDragLeave)
|
|
}
|
|
}
|
|
}, [selectedKnowledge])
|
|
|
|
const setRecentMessagesOnLoad = async () => {
|
|
const isEnabled = await webUIResumeLastChat()
|
|
if (!isEnabled) {
|
|
return
|
|
}
|
|
if (messages.length === 0) {
|
|
const recentChat = await getRecentChatFromWebUI()
|
|
if (recentChat) {
|
|
setHistoryId(recentChat.history.id)
|
|
setHistory(formatToChatHistory(recentChat.messages))
|
|
setMessages(formatToMessage(recentChat.messages))
|
|
|
|
const lastUsedPrompt = await getLastUsedChatSystemPrompt(
|
|
recentChat.history.id
|
|
)
|
|
if (lastUsedPrompt) {
|
|
if (lastUsedPrompt.prompt_id) {
|
|
const prompt = await getPromptById(lastUsedPrompt.prompt_id)
|
|
if (prompt) {
|
|
setSelectedSystemPrompt(lastUsedPrompt.prompt_id)
|
|
setSystemPrompt(lastUsedPrompt.prompt_content)
|
|
}
|
|
}
|
|
setSystemPrompt(lastUsedPrompt.prompt_content)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
React.useEffect(() => {
|
|
setRecentMessagesOnLoad()
|
|
}, [])
|
|
|
|
const { show } = useContext(HistoryContext)
|
|
|
|
return (
|
|
<div
|
|
ref={drop}
|
|
className={`relative flex gap-3 h-full items-center ${
|
|
dropState === "dragging" ? "bg-gray-100 dark:bg-gray-800" : ""
|
|
} bg-white dark:bg-[#171717]`}>
|
|
<PlaygroundHistory />
|
|
<div className="relative h-full flex-1 prose-lg flex justify-center [&>*]:max-w-[848px]">
|
|
<div
|
|
ref={containerRef}
|
|
className="custom-scrollbar bg-bottom-mask-light dark:bg-bottom-mask-dark mask-bottom-fade will-change-mask flex h-full w-full flex-col items-center overflow-x-hidden overflow-y-auto px-5">
|
|
<PlaygroundChat />
|
|
</div>
|
|
<div className="absolute bottom-0 w-full">
|
|
{!isAtBottom && (
|
|
<div className="absolute bottom-36 z-20 left-0 right-0 flex justify-center">
|
|
<button
|
|
onClick={scrollToBottom}
|
|
className="bg-gray-50 shadow border border-gray-200 dark:border-none dark:bg-white/20 p-1.5 rounded-full pointer-events-auto">
|
|
<ChevronDown className="size-4 text-gray-600 dark:text-gray-300" />
|
|
</button>
|
|
</div>
|
|
)}
|
|
<PlaygroundForm dropedFile={dropedFile} />
|
|
</div>
|
|
</div>
|
|
{messages.length && (
|
|
<div
|
|
className="w-1/4 h-full grid grid-rows-[auto_530px_165px] pt-16 pr-5 pb-0 border-l border-gray-200"
|
|
style={{ paddingTop: "4rem" }}>
|
|
<div className="w-full overflow-y-auto border-gray-200 border-b p-3">
|
|
<PlaygroundIodRelevant />
|
|
</div>
|
|
<div className="w-full grid grid-cols-2 gap-3 custom-scrollbar border-gray-200 border-b p-3">
|
|
<PlaygroundData />
|
|
<PlaygroundScene />
|
|
</div>
|
|
<div className="w-full p-3 pb-0">
|
|
<PlaygroundTeam />
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|