diff --git a/app/(user)/page.tsx b/app/(user)/page.tsx index 106727f..d729fae 100644 --- a/app/(user)/page.tsx +++ b/app/(user)/page.tsx @@ -1,21 +1,85 @@ 'use client'; -// import { MutableRefObject, useRef, useState } from 'react'; -// import axios from 'axios'; -import { ImageGenerator } from '../../components/ImageGenerator'; +import { useState, useEffect } from 'react'; +import ChatGPTAssistant from '../../components/ChatGPTAssistant'; +import { ImageGeneratorForm } from '../../components/ImageGeneratorForm'; + +interface GeneratedImage { + imageUrl: string; + prompt: string; +} export default function Home() { + const [imageUrl, setImageUrl] = useState(''); + const [generatedImages, setGeneratedImages] = useState([]); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + const savedImages = JSON.parse( + localStorage.getItem('generatedImages') || '[]' + ); + setGeneratedImages(savedImages); + }, []); + + const handleGenerate = (generatedImageUrl: string, promptText: string) => { + setImageUrl(generatedImageUrl); + const newGeneratedImages = [ + { imageUrl: generatedImageUrl, prompt: promptText }, + ...generatedImages.slice(-9), + ]; + setGeneratedImages(newGeneratedImages); + localStorage.setItem('generatedImages', JSON.stringify(newGeneratedImages)); + setIsLoading(false); + }; + return ( -
+
-

- Generate Image with Text +

+ Generate Images with Text

- +
+
+ +
+
+ {imageUrl && ( +
+

Generated Image:

+ Generated +
+ )} +
+
+
+

+ Last 10 Generated Images: +

+
+ {generatedImages.map((generatedImage, index) => ( +
+ Generated +

{generatedImage.prompt}

+
+ ))} +
+
+
); } diff --git a/components/ChatGPTAssistant.tsx b/components/ChatGPTAssistant.tsx new file mode 100644 index 0000000..a024c70 --- /dev/null +++ b/components/ChatGPTAssistant.tsx @@ -0,0 +1,210 @@ +import { useState } from 'react'; +import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css'; +// import { +// MainContainer, +// ChatContainer, +// MessageList, +// Message, +// MessageInput, +// TypingIndicator, +// } from '@chatscope/chat-ui-kit-react'; + +import { ExpandCollapseToggle } from './ExpandCollapseToggle'; + +type MessageObject = { + message: string; + direction?: string; + sender: string; + sentTime?: string; +}; + +const systemMessage = { + role: 'system', + content: + "You are an AI trained to assist with prompt engineering and generating images using generative AI. Provide only the prompt text for generating images. Explain concepts like you're addressing a junior/mid-level web developer.", +}; + +const API_KEY = process.env.NEXT_PUBLIC_OPENAI_API_KEY; + +function ChatGPTAssistant() { + const [typing, setTyping] = useState(false); + const [messages, setMessages] = useState([ + { + message: + "Hello, I'm ChatGPT! Let me know if you need help with your prompts!", + sentTime: 'just now', + sender: 'ChatGPT', + }, + ]); + + const [inputValue, setInputValue] = useState(''); + const [expanded, setExpanded] = useState(true); + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + if (inputValue.trim() === '') return; + const formattedInputValue = `Give me a prompt for ${inputValue}`; + await handleSend(formattedInputValue); + setInputValue(''); + }; + + // Add a function to toggle the expand/collapse state + const handleToggleExpand = () => { + setExpanded(!expanded); + }; + + const handleSend = async (message: string) => { + const newMessage = { + message, + direction: 'outgoing', + sender: 'user', + }; + + const newMessages = [...messages, newMessage]; + + setMessages(newMessages); + + setTyping(true); + + await processMessageToChatGPT(newMessages); + }; + + async function processMessageToChatGPT(chatMessages: MessageObject[]) { + // messages is an array of messages + // Format messages for chatGPT API + // API is expecting objects in format of { role: "user" or "assistant", "content": "message here"} + // So we need to reformat + + let apiMessages = chatMessages.map((messageObject: MessageObject) => { + let role = ''; + if (messageObject.sender === 'ChatGPT') { + role = 'assistant'; + } else { + role = 'user'; + } + return { role: role, content: messageObject.message }; + }); + + // Get the request body set up with the model we plan to use + // and the messages which we formatted above. We add a system message in the front to' + // determine how we want chatGPT to act. + const apiRequestBody = { + model: 'gpt-3.5-turbo', + messages: [ + systemMessage, // The system message DEFINES the logic of our chatGPT + ...apiMessages, // The messages from our chat with ChatGPT + ], + }; + + try { + const response = await fetch( + 'https://api.openai.com/v1/chat/completions', + { + method: 'POST', + headers: { + Authorization: 'Bearer ' + API_KEY, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(apiRequestBody), + } + ); + + if (!response.ok) { + throw new Error('Failed to fetch data from the ChatGPT API'); + } + + const data = await response.json(); + console.log(data); + setMessages([ + ...chatMessages, + { + message: data.choices[0].message.content, + sender: 'ChatGPT', + }, + ]); + setTyping(false); + } catch (error) { + console.error(error); + setTyping(false); + } + } + + function formatAssistantMessage(message: string) { + const parts = message.split(/("[^"]+")/); + return parts.map((part, index) => { + if (part.startsWith('"') && part.endsWith('"')) { + return ( + <> +
+

+ {part.slice(1, -1)} +

+
+ + ); + } else { + return {part}; + } + }); + } + + return ( +
+
+
+ ChatGPT Prompt Assistant +
+ +
+ + {expanded && ( + <> +
+ {messages.map((message, index) => ( +
+ + {message.sender === 'user' ? 'You: ' : 'Assistant: '} + + {message.sender === 'ChatGPT' + ? formatAssistantMessage(message.message) + : message.message} +
+ ))} +
+ +
+ setInputValue(e.target.value)} + /> + +
+ + )} +
+ ); +} + +export default ChatGPTAssistant; diff --git a/components/ExpandCollapseToggle.tsx b/components/ExpandCollapseToggle.tsx new file mode 100644 index 0000000..98e4a69 --- /dev/null +++ b/components/ExpandCollapseToggle.tsx @@ -0,0 +1,49 @@ +import React from 'react'; + +interface ExpandCollapseToggleProps { + expanded: boolean; + onToggleExpand: () => void; +} + +export const ExpandCollapseToggle: React.FC = ({ + expanded, + onToggleExpand, +}) => { + return ( + + ); +}; diff --git a/components/ImageGenerator.tsx b/components/ImageGeneratorForm.tsx similarity index 90% rename from components/ImageGenerator.tsx rename to components/ImageGeneratorForm.tsx index 85001c0..4f70c4e 100644 --- a/components/ImageGenerator.tsx +++ b/components/ImageGeneratorForm.tsx @@ -1,12 +1,16 @@ import { MutableRefObject, useRef, useState } from 'react'; import axios from 'axios'; -interface ImageGeneratorProps { - onGenerate: (imageUrl: string) => void; +interface ImageGeneratorFormProps { + onGenerate: (imageUrl: string, promptText: string) => void; + isLoading: boolean; + setIsLoading: (isLoading: boolean) => void; } -export const ImageGenerator: React.FC = ({ +export const ImageGeneratorForm: React.FC = ({ onGenerate, + isLoading, + setIsLoading, }) => { // State variables const [key, setKey] = useState(''); @@ -29,6 +33,7 @@ export const ImageGenerator: React.FC = ({ // Function to handle image generation const handleGenerateImage = async () => { try { + setIsLoading(true); // Prepare options for API call const options = { key, @@ -57,12 +62,21 @@ export const ImageGenerator: React.FC = ({ prompt: prompt, options: options, }); - setImageUrl(response.data.output[0]); + const generatedImageUrl = response.data.output[0]; + setImageUrl(generatedImageUrl); + onGenerate(generatedImageUrl, prompt); + resetForm(); } catch (error) { console.error('Error:', error); + setIsLoading(false); } }; + const resetForm = () => { + setPrompt(''); + setNegativePrompt(''); + }; + // Render the form return ( <> @@ -71,7 +85,7 @@ export const ImageGenerator: React.FC = ({ e.preventDefault(); handleGenerateImage(); }} - className="grid grid-cols-1 md:grid-cols-2 gap-4" + className="grid grid-cols-1 gap-4" >