"use client"; import type React from "react"; import { useState, useEffect, useRef } from "react"; import { ArrowUp, Loader2, MessageSquare } from "lucide-react"; import { useEditorStore } from "@/lib/store"; import { useIsMobile } from "@/lib/use-mobile"; import type { ObjectType, TransformMode, SceneObject, Material, Physics, Damage, } from "@/lib/types"; export function InGamePrompt() { const [isOpen, setIsOpen] = useState(false); const [prompt, setPrompt] = useState(""); const [isProcessing, setIsProcessing] = useState(false); const [previousPrompts, setPreviousPrompts] = useState([]); const textareaRef = useRef(null); const isMobile = useIsMobile(); const { addObject, scenes, activeSceneId, updateObjectTransform, updateObjectMaterial, updateObjectPhysics, updateDamage, selectObject, setTransformMode, createCustomObject, isPlaying, setIsPromptOpen, } = useEditorStore(); const activeScene = scenes.find((scene) => scene.id === activeSceneId) || scenes[0]; const objects = activeScene ? activeScene.objects : []; const processAction = (action: { function: string; args: unknown[] }) => { try { switch (action.function) { case "addObject": addObject(action.args[0] as ObjectType); break; case "createCustomObject": createCustomObject( action.args[0] as ObjectType, action.args[1] as Partial, ); break; case "updateObjectTransform": updateObjectTransform( action.args[0] as string, action.args[1] as Partial< Pick >, ); break; case "updateObjectMaterial": updateObjectMaterial( action.args[0] as string, action.args[1] as Partial, ); break; case "updateObjectPhysics": updateObjectPhysics( action.args[0] as string, action.args[1] as Partial, ); break; case "updateDamage": updateDamage( action.args[0] as string, action.args[1] as Partial, ); break; case "selectObject": selectObject(action.args[0] as string); break; case "setTransformMode": setTransformMode(action.args[0] as TransformMode); break; } } catch (error) { console.error("Error processing action:", error, action); } }; const handleSubmit = async (e?: React.FormEvent) => { if (e) e.preventDefault(); if (!prompt.trim() || isProcessing) return; setIsProcessing(true); const currentPrompt = prompt; setPreviousPrompts((prev) => [...prev.slice(-4), currentPrompt]); setIsOpen(false); setIsPromptOpen(false); try { const response = await fetch("/api/ai-game", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ prompt: currentPrompt, objects, previousPrompts, }), }); if (!response.ok || !response.body) { throw new Error("Failed to get AI response"); } const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ""; while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value, { stream: true }); buffer += chunk; let startIndex = 0; while (true) { const openBrace = buffer.indexOf("{", startIndex); if (openBrace === -1) break; let depth = 0; let closeBrace = -1; for (let i = openBrace; i < buffer.length; i++) { if (buffer[i] === "{") depth++; else if (buffer[i] === "}") { depth--; if (depth === 0) { closeBrace = i; break; } } } if (closeBrace === -1) { startIndex = openBrace + 1; break; } const jsonStr = buffer.substring(openBrace, closeBrace + 1); try { const jsonObj = JSON.parse(jsonStr); if ( jsonObj && typeof jsonObj.function === "string" && Array.isArray(jsonObj.args) ) { processAction(jsonObj); } buffer = buffer.substring(0, openBrace) + buffer.substring(closeBrace + 1); startIndex = 0; } catch { startIndex = openBrace + 1; } } } } catch (error) { console.error("Error processing AI prompt:", error); } finally { setIsProcessing(false); setPrompt(""); } }; useEffect(() => { if (!isPlaying) { setIsOpen(false); setIsPromptOpen(false); return; } if (isMobile) return; const handleKeyDown = (e: KeyboardEvent) => { if ((e.metaKey || e.ctrlKey) && e.key === "/") { e.preventDefault(); const newIsOpen = !isOpen; setIsOpen(newIsOpen); setIsPromptOpen(newIsOpen); } }; window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, [isPlaying, isOpen, setIsPromptOpen, isMobile]); useEffect(() => { if (isOpen && textareaRef.current) { setTimeout(() => textareaRef.current?.focus(), 100); } }, [isOpen]); const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); handleSubmit(); } }; const handleTextareaChange = (e: React.ChangeEvent) => { setPrompt(e.target.value); e.target.style.height = "auto"; e.target.style.height = `${e.target.scrollHeight}px`; }; if (!isPlaying) return null; if (!isOpen) { if (isMobile) { return ( ); } return null; } return (