import React, { createContext, useContext, useState, ReactNode } from "react"; import { Button, Tooltip, theme } from "antd"; import { MessageOutlined, CloseOutlined } from "@ant-design/icons"; import { ChatWidget } from "@cundi/react-chat-widget"; import "@cundi/react-chat-widget/styles.css"; import { useTranslation } from "react-i18next"; const { useToken } = theme; // ============ Chat Context ============ interface ChatContextType { isOpen: boolean; open: () => void; close: () => void; toggle: () => void; } const ChatContext = createContext(null); export const useChatPanel = () => { const context = useContext(ChatContext); if (!context) { throw new Error("useChatPanel must be used within ChatProvider"); } return context; }; // ============ Mock Data for Demo ============ const MOCK_CHAT_RESPONSES: Record any> = { help: (_, t) => ({ output: `## 測試指令說明 您可以輸入以下關鍵字來測試不同功能: | 指令 | 說明 | |------|------| | \`markdown\` | 顯示 Markdown 格式範例 | | \`table\` | 顯示表格範例 | | \`image\` | 顯示圖片範例 | | \`file\` | 顯示檔案下載連結 | | \`code\` | 顯示程式碼區塊範例 | | \`list\` | 顯示清單範例 | | \`help\` | 顯示此說明 |` }), markdown: () => ({ output: `### Markdown 測試 這是一個 **Markdown** 格式的回應! - 支援 **粗體** 和 *斜體* - 支援 \`程式碼\` 區塊 - 支援 [連結](https://google.com) > 這是一段引用文字` }), table: () => ({ output: `### 產品清單表格 | 產品名稱 | 價格 | 庫存 | |---------|------|------| | 筆記型電腦 | $999 | 15 | | 無線滑鼠 | $29 | 150 | | 機械鍵盤 | $149 | 0 |` }), image: () => ({ output: `### 圖片展示\n這是一張隨機範例圖片:`, image: 'https://picsum.photos/400/300' }), file: () => ({ output: `### 檔案下載\n以下是可供下載的檔案:`, files: [ { name: '範例文件.pdf', url: 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf', type: 'application/pdf', size: 13264, } ] }), code: () => ({ output: `### 程式碼範例\n\`\`\`tsx const HelloWorld = () => { return
Hello Cundi!
; }; \`\`\`` }), list: () => ({ output: `### 開發進度\n- [x] 建立專案 - [x] 整合聊天面板 - [ ] 介面優化 - [ ] 發布正式版` }) }; // ============ Chat Provider ============ interface ChatProviderProps { children: ReactNode; /** Width of chat panel */ panelWidth?: number; /** Custom message handler for chat */ onSendMessage?: (message: string, files?: File[]) => Promise<{ output: string } | string>; /** Webhook URL for webhook mode */ webhookUrl?: string; /** Session ID for webhook mode */ sessionId?: string; } export const ChatProvider: React.FC = ({ children, panelWidth = 500, onSendMessage, webhookUrl, sessionId, }) => { const [isOpen, setIsOpen] = useState(false); const { token } = useToken(); const { t } = useTranslation(); // Default message handler (mock response generator) const defaultMessageHandler = async (message: string) => { await new Promise((resolve) => setTimeout(resolve, 800)); const input = message.toLowerCase().trim(); for (const [keyword, generator] of Object.entries(MOCK_CHAT_RESPONSES)) { if (input.includes(keyword)) { return generator(message, t); } } return { output: t("chat.defaultResponse", `收到您的訊息:「${message}」\n\n這是一個示範回應。您可以輸入 \`help\` 來查看更多指令測試。`), }; }; const contextValue: ChatContextType = { isOpen, open: () => setIsOpen(true), close: () => setIsOpen(false), toggle: () => setIsOpen((prev) => !prev), }; return ( {/* Main content with margin adjustment */}
{children}
{/* Fixed Chat Panel */}
{isOpen && ( <> {/* Panel Header */}
{t("chat.panelTitle", "AI Assistant")}
{/* Chat Widget */}
{webhookUrl ? ( ) : ( )}
)}
); }; // ============ Chat Toggle Button ============ /** * Button component to toggle the chat panel. * Place this anywhere in your app (e.g., in the Header). */ export const ChatToggleButton: React.FC = () => { const { isOpen, toggle } = useChatPanel(); const { t } = useTranslation(); return (