diff --git a/plugins/docuservix/pages/chat/ChatPage.tsx b/plugins/docuservix/pages/chat/ChatPage.tsx index 3bd719b..b9d84cc 100644 --- a/plugins/docuservix/pages/chat/ChatPage.tsx +++ b/plugins/docuservix/pages/chat/ChatPage.tsx @@ -1,10 +1,28 @@ import Layout from '@theme/Layout'; import { ReactNode } from 'react'; +import { IChat } from '@docuservix/models/chat'; +import { Chat } from '@docuservix/widgets/chat'; + +const dialog: IChat = { + messages: [ + { + role: 'user', + content: 'Can you show me some CSS animations? It can be simple tools like chatbots...', + }, + { + role: 'assistant', + content: "Hello! I'm your AI assistant. How can I help you today?", + }, + ], +}; + export function ChatPage(): ReactNode { return ( -
Hello world!
+
+ +
); } diff --git a/plugins/docuservix/widgets/chat/Chat.module.css b/plugins/docuservix/widgets/chat/Chat.module.css new file mode 100644 index 0000000..2870044 --- /dev/null +++ b/plugins/docuservix/widgets/chat/Chat.module.css @@ -0,0 +1,9 @@ +.Chat { + display: flex; + flex-direction: column; + width: 100%; + background: var(--ifm-background-color); + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: var(--ifm-global-radius); + overflow: hidden; +} diff --git a/plugins/docuservix/widgets/chat/Chat.tsx b/plugins/docuservix/widgets/chat/Chat.tsx new file mode 100644 index 0000000..79a3d6f --- /dev/null +++ b/plugins/docuservix/widgets/chat/Chat.tsx @@ -0,0 +1,35 @@ +import block from 'bem-css-modules'; +import React, { ReactNode } from 'react'; + +import { IChat, IChatMessage } from '@docuservix/models/chat'; + +import styles from './Chat.module.css'; +import { Header } from './Header'; +import { Input } from './Input'; +import { Messages } from './Messages'; + +const b = block(styles, 'Chat'); + +interface ChatProps { + dialog: IChat; + loading?: boolean; + onSend?: (text: string) => void; +} + +export function Chat({ dialog, loading, onSend }: ChatProps): ReactNode { + const { messages } = dialog; + + return ( +
+
+ + +
+ ); +} diff --git a/plugins/docuservix/widgets/chat/Header.module.css b/plugins/docuservix/widgets/chat/Header.module.css new file mode 100644 index 0000000..e48bbde --- /dev/null +++ b/plugins/docuservix/widgets/chat/Header.module.css @@ -0,0 +1,32 @@ +.Header { + display: flex; + align-items: center; + gap: 1rem; + padding: 1rem 1.25rem; + border-bottom: 1px solid var(--ifm-color-emphasis-200); +} + +.Header__avatar { + width: 50px; + height: 50px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 1.5rem; + flex-shrink: 0; +} + +.Header__info h3 { + margin: 0 0 0.25rem; + color: var(--ifm-font-color-base); + font-size: 1.125rem; +} + +.Header__info p { + margin: 0; + color: var(--ifm-color-emphasis-600); + font-size: 0.85rem; +} diff --git a/plugins/docuservix/widgets/chat/Header.tsx b/plugins/docuservix/widgets/chat/Header.tsx new file mode 100644 index 0000000..b438758 --- /dev/null +++ b/plugins/docuservix/widgets/chat/Header.tsx @@ -0,0 +1,21 @@ +import block from 'bem-css-modules'; +import React, { ReactNode } from 'react'; + +import styles from './Header.module.css'; +import { RobotIcon } from './icons'; + +const b = block(styles, 'Header'); + +export function Header(): ReactNode { + return ( +
+
+ +
+
+

AI Assistant

+

Ready to help

+
+
+ ); +} diff --git a/plugins/docuservix/widgets/chat/Input.module.css b/plugins/docuservix/widgets/chat/Input.module.css new file mode 100644 index 0000000..c38e4e2 --- /dev/null +++ b/plugins/docuservix/widgets/chat/Input.module.css @@ -0,0 +1,55 @@ +.Input { + display: flex; + align-items: flex-end; + gap: 0.75rem; + padding: 1rem 1.25rem; + border-top: 1px solid var(--ifm-color-emphasis-200); +} + +.Input__field { + flex: 1; + padding: 0.75rem 1rem; + border: 2px solid var(--ifm-color-emphasis-200); + border-radius: 1.5rem; + font-size: 1rem; + font-family: inherit; + color: var(--ifm-font-color-base); + background: var(--ifm-background-surface-color); + outline: none; + transition: border-color 0.3s; + resize: none; + min-height: 3.25rem; + max-height: 8rem; + field-sizing: content; +} + +.Input__field:focus { + border-color: #667eea; +} + +.Input__field:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.Input__send { + width: 3.25rem; + height: 3.25rem; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border: none; + border-radius: 50%; + color: white; + font-size: 1.125rem; + cursor: pointer; + flex-shrink: 0; + transition: transform 0.3s; +} + +.Input__send:hover:not(:disabled) { + transform: scale(1.1); +} + +.Input__send:disabled { + opacity: 0.5; + cursor: not-allowed; +} diff --git a/plugins/docuservix/widgets/chat/Input.tsx b/plugins/docuservix/widgets/chat/Input.tsx new file mode 100644 index 0000000..7e21347 --- /dev/null +++ b/plugins/docuservix/widgets/chat/Input.tsx @@ -0,0 +1,55 @@ +import block from 'bem-css-modules'; +import React, { ReactNode, useState } from 'react'; + +import { PaperPlaneIcon } from './icons'; +import styles from './Input.module.css'; + +const b = block(styles, 'Input'); + +interface InputProps { + loading?: boolean; + onSend?: (text: string) => void; +} + +export function Input({ loading, onSend }: InputProps): ReactNode { + const [input, setInput] = useState(''); + + const handleSend = () => { + const text = input.trim(); + + if (!text || loading) { + return; + } + + setInput(''); + onSend?.(text); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSend(); + } + }; + + return ( +
+