"use client"; import { type ReactNode, createContext, useContext, useState } from "react"; import { faker } from "@faker-js/faker"; import { cn } from "@udecode/cn"; import { CopilotPlugin } from "@udecode/plate-ai/react"; import { useEditorPlugin } from "@udecode/plate-common/react"; import { useChat as useBaseChat } from "ai/react"; import { ArrowUpRight, Check, ChevronsUpDown, Eye, EyeOff, Settings } from "lucide-react"; import { Button } from "~/components/plate-ui/button"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "~/components/plate-ui/command"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "~/components/plate-ui/dialog"; import { Input } from "~/components/plate-ui/input"; import { Popover, PopoverContent, PopoverTrigger } from "~/components/plate-ui/popover"; export const useChat = (props?: Parameters[0]) => { return useBaseChat({ id: "editor", api: "/api/ai/command", ...props, }); }; // Used for testing. Remove it after implementing useChat api. const fakeStreamText = ({ chunkCount = 10, streamProtocol = "data", }: { chunkCount?: number; streamProtocol?: "data" | "text"; } = {}) => { const chunks = Array.from({ length: chunkCount }, () => ({ delay: faker.number.int({ max: 150, min: 50 }), texts: faker.lorem.words({ max: 3, min: 1 }) + " ", })); const encoder = new TextEncoder(); return new ReadableStream({ async start(controller) { for (const chunk of chunks) { await new Promise((resolve) => setTimeout(resolve, chunk.delay)); if (streamProtocol === "text") { controller.enqueue(encoder.encode(chunk.texts)); } else { controller.enqueue(encoder.encode(`0:${JSON.stringify(chunk.texts)}\n`)); } } if (streamProtocol === "data") { controller.enqueue( `d:{"finishReason":"stop","usage":{"promptTokens":0,"completionTokens":${chunks.length}}}\n`, ); } controller.close(); }, }); }; interface Model { label: string; value: string; } interface OpenAIContextType { apiKey: string; model: Model; setApiKey: (key: string) => void; setModel: (model: Model) => void; } export const models: Model[] = [ { label: "gpt-4o-mini", value: "gpt-4o-mini" }, { label: "gpt-4o", value: "gpt-4o" }, { label: "gpt-4-turbo", value: "gpt-4-turbo" }, { label: "gpt-4", value: "gpt-4" }, { label: "gpt-3.5-turbo", value: "gpt-3.5-turbo" }, { label: "gpt-3.5-turbo-instruct", value: "gpt-3.5-turbo-instruct" }, ]; const OpenAIContext = createContext(undefined); export function OpenAIProvider({ children }: { children: ReactNode }) { const [apiKey, setApiKey] = useState(""); const [model, setModel] = useState(models[0]); return ( {children} ); } export function useOpenAI() { const context = useContext(OpenAIContext); return ( context ?? ({ apiKey: "", model: models[0], setApiKey: () => {}, setModel: () => {}, } as OpenAIContextType) ); } export function SettingsDialog() { const { apiKey, model, setApiKey, setModel } = useOpenAI(); const [tempKey, setTempKey] = useState(apiKey); const [showKey, setShowKey] = useState(false); const [open, setOpen] = useState(false); const [openModel, setOpenModel] = useState(false); const { getOptions, setOption } = useEditorPlugin(CopilotPlugin); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); setApiKey(tempKey); setOpen(false); const completeOptions = getOptions().completeOptions ?? {}; setOption("completeOptions", { ...completeOptions, body: { ...completeOptions.body, apiKey: tempKey, model: model.value, }, }); }; return ( AI Settings Enter your{" "} OpenAI API key {" "} to use AI features.
setTempKey(e.target.value)} placeholder="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" data-1p-ignore type={showKey ? "text" : "password"} />
No model found. {models.map((m) => ( { setModel(m); setOpenModel(false); }} > {m.label} ))}

Not stored anywhere. Used only for current session requests.

); }