diff --git a/messages/en.json b/messages/en.json index 4f35f10..cbd0f4d 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1044,17 +1044,18 @@ }, "AIImagePage": { "title": "AI Image", - "description": "MkSaaS lets you make AI SaaS in days, simply and effortlessly", - "content": "Working in progress" + "description": "MkSaaS lets you make AI SaaS in days, simply and effortlessly" + }, + "AIChatPage": { + "title": "AI Chat", + "description": "MkSaaS lets you make AI SaaS in days, simply and effortlessly" }, "AIVideoPage": { "title": "AI Video", - "description": "MkSaaS lets you make AI SaaS in days, simply and effortlessly", - "content": "Working in progress" + "description": "MkSaaS lets you make AI SaaS in days, simply and effortlessly" }, "AIAudioPage": { "title": "AI Audio", - "description": "MkSaaS lets you make AI SaaS in days, simply and effortlessly", - "content": "Working in progress" + "description": "MkSaaS lets you make AI SaaS in days, simply and effortlessly" } } diff --git a/messages/zh.json b/messages/zh.json index 9356d11..ffb9f34 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -1044,17 +1044,18 @@ }, "AIImagePage": { "title": "AI 图片", - "description": "MkSaaS 让您在几天内轻松构建您的 AI SaaS,简单且毫不费力", - "content": "正在开发中" + "description": "MkSaaS 让您在几天内轻松构建您的 AI SaaS,简单且毫不费力" + }, + "AIChatPage": { + "title": "AI 聊天", + "description": "MkSaaS 让您在几天内轻松构建您的 AI SaaS,简单且毫不费力" }, "AIVideoPage": { "title": "AI 视频", - "description": "MkSaaS 让您在几天内轻松构建您的 AI SaaS,简单且毫不费力", - "content": "正在开发中" + "description": "MkSaaS 让您在几天内轻松构建您的 AI SaaS,简单且毫不费力" }, "AIAudioPage": { "title": "AI 音频", - "description": "MkSaaS 让您在几天内轻松构建您的 AI SaaS,简单且毫不费力", - "content": "正在开发中" + "description": "MkSaaS 让您在几天内轻松构建您的 AI SaaS,简单且毫不费力" } } diff --git a/src/ai/chat/components/ChatBot.tsx b/src/ai/chat/components/ChatBot.tsx new file mode 100644 index 0000000..10408ee --- /dev/null +++ b/src/ai/chat/components/ChatBot.tsx @@ -0,0 +1,181 @@ +'use client'; + +import { + Conversation, + ConversationContent, + ConversationScrollButton, +} from '@/components/ai-elements/conversation'; +import { Loader } from '@/components/ai-elements/loader'; +import { Message, MessageContent } from '@/components/ai-elements/message'; +import { + PromptInput, + PromptInputButton, + PromptInputModelSelect, + PromptInputModelSelectContent, + PromptInputModelSelectItem, + PromptInputModelSelectTrigger, + PromptInputModelSelectValue, + PromptInputSubmit, + PromptInputTextarea, + PromptInputToolbar, + PromptInputTools, +} from '@/components/ai-elements/prompt-input'; +import { + Reasoning, + ReasoningContent, + ReasoningTrigger, +} from '@/components/ai-elements/reasoning'; +import { Response } from '@/components/ai-elements/response'; +import { + Source, + Sources, + SourcesContent, + SourcesTrigger, +} from '@/components/ai-elements/source'; +import { useChat } from '@ai-sdk/react'; +import { GlobeIcon } from 'lucide-react'; +import { useState } from 'react'; + +const models = [ + { + name: 'GPT 4o', + value: 'openai/gpt-4o', + }, + { + name: 'Deepseek R1', + value: 'deepseek/deepseek-r1', + }, +]; + +export default function ChatBot() { + const [input, setInput] = useState(''); + const [model, setModel] = useState(models[0].value); + const [webSearch, setWebSearch] = useState(false); + const { messages, sendMessage, status } = useChat(); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (input.trim()) { + sendMessage( + { text: input }, + { + body: { + model: model, + webSearch: webSearch, + }, + } + ); + setInput(''); + } + }; + + return ( +
+
+ + + {messages.map((message) => ( +
+ {message.role === 'assistant' && ( + + {message.parts.map((part, i) => { + switch (part.type) { + case 'source-url': + return ( + <> + part.type === 'source-url' + ).length + } + /> + + + + + ); + } + })} + + )} + + + {message.parts.map((part, i) => { + switch (part.type) { + case 'text': + return ( + + {part.text} + + ); + case 'reasoning': + return ( + + + {part.text} + + ); + default: + return null; + } + })} + + +
+ ))} + {status === 'submitted' && } +
+ +
+ + + setInput(e.target.value)} + value={input} + /> + + + setWebSearch(!webSearch)} + > + + Search + + { + setModel(value); + }} + value={model} + > + + + + + {models.map((model) => ( + + {model.name} + + ))} + + + + + + +
+
+ ); +} diff --git a/src/app/[locale]/(marketing)/ai/chat/page.tsx b/src/app/[locale]/(marketing)/ai/chat/page.tsx index eb95e05..ba4041d 100644 --- a/src/app/[locale]/(marketing)/ai/chat/page.tsx +++ b/src/app/[locale]/(marketing)/ai/chat/page.tsx @@ -1,183 +1,44 @@ -'use client'; +import ChatBot from '@/ai/chat/components/ChatBot'; +import { constructMetadata } from '@/lib/metadata'; +import { getUrlWithLocale } from '@/lib/urls/urls'; +import { ZapIcon } from 'lucide-react'; +import type { Metadata } from 'next'; +import type { Locale } from 'next-intl'; +import { getTranslations } from 'next-intl/server'; -import { - Conversation, - ConversationContent, - ConversationScrollButton, -} from '@/components/ai-elements/conversation'; -import { Loader } from '@/components/ai-elements/loader'; -import { Message, MessageContent } from '@/components/ai-elements/message'; -import { - PromptInput, - PromptInputButton, - PromptInputModelSelect, - PromptInputModelSelectContent, - PromptInputModelSelectItem, - PromptInputModelSelectTrigger, - PromptInputModelSelectValue, - PromptInputSubmit, - PromptInputTextarea, - PromptInputToolbar, - PromptInputTools, -} from '@/components/ai-elements/prompt-input'; -import { - Reasoning, - ReasoningContent, - ReasoningTrigger, -} from '@/components/ai-elements/reasoning'; -import { Response } from '@/components/ai-elements/response'; -import { - Source, - Sources, - SourcesContent, - SourcesTrigger, -} from '@/components/ai-elements/source'; -import { useChat } from '@ai-sdk/react'; -import { GlobeIcon } from 'lucide-react'; -import { useState } from 'react'; +export async function generateMetadata({ + params, +}: { + params: Promise<{ locale: Locale }>; +}): Promise { + const { locale } = await params; + const t = await getTranslations({ locale, namespace: 'Metadata' }); + const pt = await getTranslations({ locale, namespace: 'AIChatPage' }); -const models = [ - { - name: 'GPT 4o', - value: 'openai/gpt-4o', - }, - { - name: 'Deepseek R1', - value: 'deepseek/deepseek-r1', - }, -]; + return constructMetadata({ + title: pt('title') + ' | ' + t('title'), + description: pt('description'), + canonicalUrl: getUrlWithLocale('/ai/chat', locale), + }); +} -const ChatBotDemo = () => { - const [input, setInput] = useState(''); - const [model, setModel] = useState(models[0].value); - const [webSearch, setWebSearch] = useState(false); - const { messages, sendMessage, status } = useChat(); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - if (input.trim()) { - sendMessage( - { text: input }, - { - body: { - model: model, - webSearch: webSearch, - }, - } - ); - setInput(''); - } - }; +export default async function AIChatPage() { + const t = await getTranslations('AIChatPage'); return ( -
-
- - - {messages.map((message) => ( -
- {message.role === 'assistant' && ( - - {message.parts.map((part, i) => { - switch (part.type) { - case 'source-url': - return ( - <> - part.type === 'source-url' - ).length - } - /> - - - - - ); - } - })} - - )} - - - {message.parts.map((part, i) => { - switch (part.type) { - case 'text': - return ( - - {part.text} - - ); - case 'reasoning': - return ( - - - {part.text} - - ); - default: - return null; - } - })} - - -
- ))} - {status === 'submitted' && } -
- -
+
+
+ {/* Header Section */} +
+
+ + {t('title')} +
+
- - setInput(e.target.value)} - value={input} - /> - - - setWebSearch(!webSearch)} - > - - Search - - { - setModel(value); - }} - value={model} - > - - - - - {models.map((model) => ( - - {model.name} - - ))} - - - - - - + {/* Chat Bot */} +
); -}; - -export default ChatBotDemo; +} diff --git a/src/app/[locale]/(marketing)/ai/text/page.tsx b/src/app/[locale]/(marketing)/ai/text/page.tsx index 248e850..8a1232b 100644 --- a/src/app/[locale]/(marketing)/ai/text/page.tsx +++ b/src/app/[locale]/(marketing)/ai/text/page.tsx @@ -26,7 +26,7 @@ export default async function AITextPage() { const t = await getTranslations('AITextPage'); return ( -
+
{/* Header Section */}