diff --git a/README.md b/README.md index b66f7d0..aa63024 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ flowchart TD │ └── ai/ # LLM / RAG / MCP related logic ├── web/ # unified Next.js frontend │ ├── app/dashboard/ # admin dashboard -│ ├── app/kefu/ # customer service entry and chat pages +│ ├── app/support/ # customer service entry and chat pages │ ├── components/ # React components │ ├── lib/ # API client, SDK source and utilities │ ├── public/sdk/ # built embeddable SDK assets @@ -255,8 +255,8 @@ make web-dev - 管理后台:`http://localhost:3000/dashboard` - 客服工作台:`http://localhost:3000/dashboard/conversations` -- 客户侧 Web 接入示例:`http://localhost:3000/kefu` -- 客户侧聊天页:`http://localhost:3000/kefu/chat` +- 客户侧 Web 接入示例:`http://localhost:3000/support/demo` +- 客户侧聊天页:`http://localhost:3000/support/chat` 生产构建时,前端统一由 `web` 工程构建,静态产物输出到 `web/out`,后端会从 `web/out` 提供静态资源。 @@ -308,7 +308,7 @@ docker run --rm -p 8083:8083 \ - 管理后台:负责 AI Agent、知识库、客服组、工单与运营配置 - 客服工作台:负责接管会话、处理消息与人工服务 -- 客户侧 Web 接入:通过 `/kefu`、`/kefu/chat` 和 `web/public/sdk` 中的嵌入式脚本承接用户咨询入口 +- 客户侧 Web 接入:通过 `/support/demo`、`/support/chat` 和 `web/public/sdk` 中的嵌入式脚本承接用户咨询入口 这使得`贝壳AI客服`可以同时覆盖: diff --git a/config/config.example.yaml b/config/config.example.yaml index 352d540..494c4e7 100644 --- a/config/config.example.yaml +++ b/config/config.example.yaml @@ -1,7 +1,7 @@ server: port: 8083 cors: - # 浏览器跨域白名单。生产环境必须改为实际前端/嵌入站点域名,例如 https://kefu.example.com。 + # 浏览器跨域白名单。生产环境必须改为实际前端/嵌入站点域名,例如 https://support.example.com。 # 留空表示不允许跨域请求,只支持同源或非浏览器调用。 allowedOrigins: - http://127.0.0.1:8083 diff --git a/docs b/docs index e0dc728..a86a0d2 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit e0dc72852cc50e5b885f2bf702c607b512bb91ab +Subproject commit a86a0d2c03bf5f53bba44b26f4dfbff61c6e694b diff --git a/web/app/dashboard/channels/_components/edit.tsx b/web/app/dashboard/channels/_components/edit.tsx index 40c9278..79005eb 100644 --- a/web/app/dashboard/channels/_components/edit.tsx +++ b/web/app/dashboard/channels/_components/edit.tsx @@ -7,7 +7,7 @@ import { z } from "zod/v4" import { CopyIcon, ExternalLinkIcon } from "lucide-react" import { toast } from "sonner" -import { getWidgetDemoPath } from "@/components/kefu/demo-navigation" +import { getWidgetDemoPath } from "@/components/support-chat/demo-navigation" import { OptionCombobox } from "@/components/option-combobox" import { ProjectDialog } from "@/components/project-dialog" import { Button } from "@/components/ui/button" diff --git a/web/app/support/chat/closed/page.tsx b/web/app/support/chat/closed/page.tsx index aa7d166..da1a0ab 100644 --- a/web/app/support/chat/closed/page.tsx +++ b/web/app/support/chat/closed/page.tsx @@ -14,9 +14,9 @@ export default function Page() {
-

{t("kefu.closedTitle")}

+

{t("supportChat.closedTitle")}

- {t("kefu.closedDescription")} + {t("supportChat.closedDescription")}

diff --git a/web/app/support/chat/page.tsx b/web/app/support/chat/page.tsx index f75b827..9037c2c 100644 --- a/web/app/support/chat/page.tsx +++ b/web/app/support/chat/page.tsx @@ -1,5 +1,5 @@ -import { KefuChatShell } from "@/components/kefu/chat-shell" +import { SupportChatShell } from "@/components/support-chat/chat-shell" export default function Page() { - return + return } diff --git a/web/app/support/demo/page.tsx b/web/app/support/demo/page.tsx index 0f99a02..5c4f42a 100644 --- a/web/app/support/demo/page.tsx +++ b/web/app/support/demo/page.tsx @@ -1,5 +1,5 @@ -import { KefuWidgetDemo } from "@/components/kefu/widget-demo" +import { SupportWidgetDemo } from "@/components/support-chat/widget-demo" export default function Page() { - return + return } diff --git a/web/components/kefu/close-navigation.ts b/web/components/kefu/close-navigation.ts deleted file mode 100644 index e9544fb..0000000 --- a/web/components/kefu/close-navigation.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const KEFU_CLOSED_PATH = "/support/chat/closed" - -export function getStandaloneClosedUrl() { - return KEFU_CLOSED_PATH -} diff --git a/web/components/kefu/demo-navigation.ts b/web/components/kefu/demo-navigation.ts deleted file mode 100644 index 54e6f9b..0000000 --- a/web/components/kefu/demo-navigation.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const KEFU_DEMO_PATH = "/support/demo" - -export function getWidgetDemoPath() { - return KEFU_DEMO_PATH -} diff --git a/web/components/kefu/chat-shell.tsx b/web/components/support-chat/chat-shell.tsx similarity index 84% rename from web/components/kefu/chat-shell.tsx rename to web/components/support-chat/chat-shell.tsx index 55bd9ea..5879aab 100644 --- a/web/components/kefu/chat-shell.tsx +++ b/web/components/support-chat/chat-shell.tsx @@ -20,20 +20,20 @@ import { } from "react" import { useShallow } from "zustand/react/shallow" -import { KefuConnectionStatus } from "@/components/kefu/connection-status" -import { getStandaloneClosedUrl } from "@/components/kefu/close-navigation" -import { CustomerMessageEditor } from "@/components/kefu/customer-message-editor" +import { SupportChatConnectionStatus } from "@/components/support-chat/connection-status" +import { getStandaloneClosedUrl } from "@/components/support-chat/close-navigation" +import { CustomerMessageEditor } from "@/components/support-chat/customer-message-editor" import { - KefuMessageList, - type KefuMessageListHandle, -} from "@/components/kefu/message-list" + SupportChatMessageList, + type SupportChatMessageListHandle, +} from "@/components/support-chat/message-list" import { - bindKefuHostBridge, - requestKefuHostClose, - requestKefuHostMinimize, - requestKefuHostToggleMaximize, -} from "@/lib/kefu-host-bridge" -import { useKefuChatStore } from "@/lib/stores/kefu-chat" + bindSupportHostBridge, + requestSupportHostClose, + requestSupportHostMinimize, + requestSupportHostToggleMaximize, +} from "@/lib/support-host-bridge" +import { useSupportChatStore } from "@/lib/stores/support-chat" import { Button } from "@/components/ui/button" import { Dialog, @@ -80,7 +80,7 @@ function getMobileStatusDotClass(status: string) { return "bg-muted-foreground shadow-[0_0_0_3px_rgba(148,163,184,0.14)]" } -function useKefuSystemTheme() { +function useSupportChatSystemTheme() { useLayoutEffect(() => { if (typeof window === "undefined") { return @@ -119,11 +119,11 @@ function isEmbeddedInHost() { } } -export function KefuChatShell() { +export function SupportChatShell() { const t = useI18n() - useKefuSystemTheme() + useSupportChatSystemTheme() - const messageListRef = useRef(null) + const messageListRef = useRef(null) const [isEmbedded, setIsEmbedded] = useState(false) const [isMaximized, setIsMaximized] = useState(false) const [isCloseDialogOpen, setIsCloseDialogOpen] = useState(false) @@ -152,7 +152,7 @@ export function KefuChatShell() { disconnectSocket, markConversationRead, closeConversation, - } = useKefuChatStore( + } = useSupportChatStore( useShallow((state) => ({ title: state.title, subtitle: state.subtitle, @@ -192,12 +192,12 @@ export function KefuChatShell() { return } void markConversationRead().catch((readError) => { - console.error("Failed to mark kefu conversation read", readError) + console.error("Failed to mark support chat conversation read", readError) }) }, [conversation?.id, isVisible, markConversationRead]) useEffect(() => { - return bindKefuHostBridge({ + return bindSupportHostBridge({ onOpen: () => { setIsOpen(true) setIsVisible(true) @@ -250,11 +250,11 @@ export function KefuChatShell() { function handleMinimize() { setIsVisible(false) - requestKefuHostMinimize() + requestSupportHostMinimize() } function handleToggleMaximize() { - requestKefuHostToggleMaximize() + requestSupportHostToggleMaximize() } async function confirmCloseConversation() { @@ -268,12 +268,12 @@ export function KefuChatShell() { } setIsCloseDialogOpen(false) if (isEmbedded) { - requestKefuHostClose() + requestSupportHostClose() } else { window.location.replace(getStandaloneClosedUrl()) } } catch (closeError) { - window.alert(closeError instanceof Error ? closeError.message : t("kefu.closeConversationFailed")) + window.alert(closeError instanceof Error ? closeError.message : t("supportChat.closeConversationFailed")) } finally { setIsClosingConversation(false) } @@ -326,8 +326,8 @@ export function KefuChatShell() { {!isEmbedded && status !== "connected" ? ( @@ -335,18 +335,18 @@ export function KefuChatShell() { {isEmbedded ? ( } + render={} > - {t("kefu.retry")} + {t("supportChat.retry")} - {t("kefu.minimize")} + {t("supportChat.minimize")} {isMaximized ? ( @@ -354,22 +354,22 @@ export function KefuChatShell() { ) : ( )} - {isMaximized ? t("kefu.restoreWindow") : t("kefu.maximize")} + {isMaximized ? t("supportChat.restoreWindow") : t("supportChat.maximize")} setIsCloseDialogOpen(true)} > - {t("kefu.closeWindow")} + {t("supportChat.closeWindow")} ) : ( setIsCloseDialogOpen(true)} - aria-label={t("kefu.closeChatWindow")} - title={t("kefu.closeChatWindow")} + aria-label={t("supportChat.closeChatWindow")} + title={t("supportChat.closeChatWindow")} className="hover:bg-rose-50 hover:text-rose-600 dark:hover:bg-rose-950/45 dark:hover:text-rose-300" > @@ -378,13 +378,13 @@ export function KefuChatShell() {
{status !== "connected" ? ( - + ) : null}
@@ -392,15 +392,15 @@ export function KefuChatShell() { <> {isMaximized ? ( @@ -412,8 +412,8 @@ export function KefuChatShell() { ) : null} setIsCloseDialogOpen(true)} - aria-label={t("kefu.closeChatWindow")} - title={t("kefu.closeChatWindow")} + aria-label={t("supportChat.closeChatWindow")} + title={t("supportChat.closeChatWindow")} className="hover:bg-rose-50 hover:text-rose-600 dark:hover:bg-rose-950/45 dark:hover:text-rose-300" > @@ -424,7 +424,7 @@ export function KefuChatShell() {
- - {t("kefu.closeDialogTitle")} + {t("supportChat.closeDialogTitle")} - {t("kefu.closeDialogDescription")} + {t("supportChat.closeDialogDescription")} @@ -471,7 +471,7 @@ export function KefuChatShell() { disabled={isClosingConversation} onClick={() => setIsCloseDialogOpen(false)} > - {t("kefu.continueConversation")} + {t("supportChat.continueConversation")} diff --git a/web/components/kefu/close-navigation.test.mjs b/web/components/support-chat/close-navigation.test.mjs similarity index 100% rename from web/components/kefu/close-navigation.test.mjs rename to web/components/support-chat/close-navigation.test.mjs diff --git a/web/components/support-chat/close-navigation.ts b/web/components/support-chat/close-navigation.ts new file mode 100644 index 0000000..60782e0 --- /dev/null +++ b/web/components/support-chat/close-navigation.ts @@ -0,0 +1,5 @@ +export const SUPPORT_CHAT_CLOSED_PATH = "/support/chat/closed" + +export function getStandaloneClosedUrl() { + return SUPPORT_CHAT_CLOSED_PATH +} diff --git a/web/components/kefu/connection-status.tsx b/web/components/support-chat/connection-status.tsx similarity index 86% rename from web/components/kefu/connection-status.tsx rename to web/components/support-chat/connection-status.tsx index 115ce2f..d8ceaef 100644 --- a/web/components/kefu/connection-status.tsx +++ b/web/components/support-chat/connection-status.tsx @@ -4,11 +4,11 @@ import { Badge } from "@/components/ui/badge" import { cn } from "@/lib/utils" import { useI18n } from "@/i18n/provider" -type KefuConnectionStatusProps = { +type SupportChatConnectionStatusProps = { status: "connecting" | "connected" | "disconnected" } -export function KefuConnectionStatus({ status }: KefuConnectionStatusProps) { +export function SupportChatConnectionStatus({ status }: SupportChatConnectionStatusProps) { const t = useI18n() const toneClass = status === "connected" @@ -32,7 +32,7 @@ export function KefuConnectionStatus({ status }: KefuConnectionStatusProps) { : "bg-muted-foreground shadow-[0_0_0_4px_rgba(148,163,184,0.14)]" )} /> - {t(`kefu.${status}`)} + {t(`supportChat.${status}`)} ) } diff --git a/web/components/kefu/customer-message-editor.tsx b/web/components/support-chat/customer-message-editor.tsx similarity index 100% rename from web/components/kefu/customer-message-editor.tsx rename to web/components/support-chat/customer-message-editor.tsx diff --git a/web/components/kefu/demo-navigation.test.mjs b/web/components/support-chat/demo-navigation.test.mjs similarity index 100% rename from web/components/kefu/demo-navigation.test.mjs rename to web/components/support-chat/demo-navigation.test.mjs diff --git a/web/components/support-chat/demo-navigation.ts b/web/components/support-chat/demo-navigation.ts new file mode 100644 index 0000000..410af74 --- /dev/null +++ b/web/components/support-chat/demo-navigation.ts @@ -0,0 +1,5 @@ +export const SUPPORT_DEMO_PATH = "/support/demo" + +export function getWidgetDemoPath() { + return SUPPORT_DEMO_PATH +} diff --git a/web/components/kefu/message-list.tsx b/web/components/support-chat/message-list.tsx similarity index 93% rename from web/components/kefu/message-list.tsx rename to web/components/support-chat/message-list.tsx index 1e1597b..9501617 100644 --- a/web/components/kefu/message-list.tsx +++ b/web/components/support-chat/message-list.tsx @@ -20,7 +20,7 @@ import { renderIMMessageHTML } from "@/lib/im-message" import { cn, formatDateTime } from "@/lib/utils" import { useI18n } from "@/i18n/provider" -type KefuMessageListProps = { +type SupportChatMessageListProps = { messages?: ImMessage[] | null onNearBottomVisible?: () => void hasMoreOlder?: boolean @@ -28,7 +28,7 @@ type KefuMessageListProps = { onLoadOlder?: () => Promise } -export type KefuMessageListHandle = { +export type SupportChatMessageListHandle = { scrollToBottom: () => void } @@ -50,7 +50,7 @@ function getTimelineLabel( t: (key: string, values?: Record) => string ) { if (!value) { - return t("kefu.justNow") + return t("supportChat.justNow") } const date = new Date(value) if (Number.isNaN(date.getTime())) { @@ -62,13 +62,13 @@ function getTimelineLabel( date.getMinutes() ).padStart(2, "0")}` if (currentDayKey === todayDayKey) { - return t("kefu.todayAt", { time: timeText }) + return t("supportChat.todayAt", { time: timeText }) } return `${currentDayKey} ${timeText}` } -export const KefuMessageList = forwardRef( - function KefuMessageList( +export const SupportChatMessageList = forwardRef( + function SupportChatMessageList( { messages, onNearBottomVisible, @@ -232,14 +232,14 @@ export const KefuMessageList = forwardRef void handleLoadOlder()} className="h-7 rounded-full bg-background/90 text-xs text-muted-foreground shadow-sm hover:bg-background hover:text-sky-700 dark:hover:text-sky-400" > - {loadingOlder ? t("kefu.loadingOlder") : t("kefu.loadOlder")} + {loadingOlder ? t("supportChat.loadingOlder") : t("supportChat.loadOlder")}
) : null} {safeMessages.length === 0 ? (
- {t("kefu.emptyPrompt")} + {t("supportChat.emptyPrompt")}
) : null} @@ -277,7 +277,7 @@ const MessageItem = memo( const t = useI18n() const { open } = useImageLightbox() const isCustomer = message.senderType === "customer" - const senderName = isCustomer ? t("kefu.customerSelf") : message.senderName?.trim() || t("kefu.agentLabel") + const senderName = isCustomer ? t("supportChat.customerSelf") : message.senderName?.trim() || t("supportChat.agentLabel") const avatarSrc = !isCustomer && message.senderAvatar?.trim() ? message.senderAvatar.trim() : undefined const htmlContent = renderIMMessageHTML(message) @@ -301,7 +301,7 @@ const MessageItem = memo( {avatarSrc ? : null} - {fallbackName || t("kefu.customerFallback")} + {fallbackName || t("supportChat.customerFallback")} ) : null} @@ -316,7 +316,7 @@ const MessageItem = memo( {senderName} {formatDateTime(message.sentAt)} {isCustomer ? ( - {message.agentRead ? t("kefu.agentRead") : t("kefu.agentUnread")} + {message.agentRead ? t("supportChat.agentRead") : t("supportChat.agentUnread")} ) : null}
strin .sign(new TextEncoder().encode(secret)) } -export function KefuWidgetDemo() { +export function SupportWidgetDemo() { const t = useI18n() const [config, setConfig] = useState({ ...INITIAL_CONFIG, @@ -193,7 +193,7 @@ export function KefuWidgetDemo() { const configLines = [` channelId: "${config.channelId || ""}"`] if (config.authMode === "jwt") { configLines.push(` async getUserToken() { - const res = await fetch("/api/kefu/user-token", { credentials: "include" }); + const res = await fetch("/api/support/user-token", { credentials: "include" }); const data = await res.json(); return data.userToken; }`) diff --git a/web/lib/api/im.ts b/web/lib/api/im.ts index a16eb97..47e114e 100644 --- a/web/lib/api/im.ts +++ b/web/lib/api/im.ts @@ -1,6 +1,6 @@ import { request } from "@/lib/api/client" import { translateCurrentMessage } from "@/i18n/messages" -import { readKefuChatRuntimeConfig } from "@/lib/sdk/runtime-config" +import { readSupportChatRuntimeConfig } from "@/lib/sdk/runtime-config" import { generateUUID } from "@/lib/utils" export type Paging = { @@ -159,7 +159,7 @@ export function getGuestId() { } function getRuntimeImConfig() { - const widgetConfig = readKefuChatRuntimeConfig() + const widgetConfig = readSupportChatRuntimeConfig() const baseUrl = (widgetConfig.apiBaseUrl || widgetConfig.baseUrl || API_BASE_URL) .trim() .replace(/\/$/, "") diff --git a/web/lib/im-message.ts b/web/lib/im-message.ts index 059d423..2f1b21f 100644 --- a/web/lib/im-message.ts +++ b/web/lib/im-message.ts @@ -51,12 +51,12 @@ export function renderIMMessageHTML(message: { asset.filename || "image" )}">

` } - return `

${escapeHTML(t("kefu.imageSummary"))}

` + return `

${escapeHTML(t("supportChat.imageSummary"))}

` } if (message.messageType === "attachment") { if (asset?.url) { - const title = escapeHTML(asset.filename || message.content || t("kefu.attachmentSummary")) + const title = escapeHTML(asset.filename || message.content || t("supportChat.attachmentSummary")) const meta = formatFileSize(asset.fileSize ?? 0) const metaHTML = meta ? `
${escapeHTML(meta)}
` : "" return `` } - return `

${escapeHTML(message.content || t("kefu.attachmentSummary"))}

` + return `

${escapeHTML(message.content || t("supportChat.attachmentSummary"))}

` } return renderTextMessageHTML(message.content || "") @@ -77,13 +77,13 @@ export function summarizeIMMessage(message: { payload?: string }) { if (message.messageType === "image") { - return t("kefu.imageSummary") + return t("supportChat.imageSummary") } if (message.messageType === "attachment") { const asset = parseMessageAssetPayload(message.payload) return asset?.filename?.trim() - ? `${t("kefu.attachmentSummary")} ${asset.filename.trim()}` - : t("kefu.attachmentSummary") + ? `${t("supportChat.attachmentSummary")} ${asset.filename.trim()}` + : t("supportChat.attachmentSummary") } if (message.messageType === "html") { const text = extractTextFromHTML(message.content) @@ -91,11 +91,11 @@ export function summarizeIMMessage(message: { return text.substring(0, 100) } if (message.content.includes(" & { +export type SupportChatRuntimeConfig = Omit & { /** Used only by /support/chat to exchange for a chat token; not part of CSAgentConfig. */ userToken?: string } @@ -33,7 +33,7 @@ declare global { interface Window { CSAgentConfig?: CSAgentConfig CSAgentWidget?: CSAgentWidget - __CS_AGENT_WIDGET_CONFIG__?: KefuChatRuntimeConfig + __CS_AGENT_WIDGET_CONFIG__?: SupportChatRuntimeConfig __CS_AGENT_WIDGET_STATE__?: unknown } } diff --git a/web/lib/sdk/cs-ai-agent-sdk.ts b/web/lib/sdk/cs-ai-agent-sdk.ts index 8ba96cb..6e557eb 100644 --- a/web/lib/sdk/cs-ai-agent-sdk.ts +++ b/web/lib/sdk/cs-ai-agent-sdk.ts @@ -1,7 +1,7 @@ import type { CSAgentConfig, CSAgentWidget, - KefuChatRuntimeConfig, + SupportChatRuntimeConfig, } from "./config-types" type NormalizedCSAgentConfig = CSAgentConfig & { @@ -24,7 +24,7 @@ type WidgetState = { frameHideTimer: number | null frameDestroyTimer: number | null config: NormalizedCSAgentConfig | null - frameConfig: KefuChatRuntimeConfig | null + frameConfig: SupportChatRuntimeConfig | null frameUrl: URL | null animationDuration: number listenerBound?: boolean @@ -57,7 +57,7 @@ function getLauncherText() { } type FrameMessage = - | { type: "cs-agent:init"; payload: KefuChatRuntimeConfig } + | { type: "cs-agent:init"; payload: SupportChatRuntimeConfig } | { type: "cs-agent:open" } | { type: "cs-agent:minimize" } | { type: "cs-agent:maximized"; payload: { isMaximized: boolean } } @@ -135,7 +135,7 @@ type FrameMessage = function createFrameConfig( config: NormalizedCSAgentConfig, userToken: string - ): KefuChatRuntimeConfig { + ): SupportChatRuntimeConfig { const { getUserToken: _getUserToken, ...payload } = config if (userToken) { return { ...payload, userToken } diff --git a/web/lib/sdk/runtime-config.ts b/web/lib/sdk/runtime-config.ts index 92689cf..e9ba067 100644 --- a/web/lib/sdk/runtime-config.ts +++ b/web/lib/sdk/runtime-config.ts @@ -1,6 +1,6 @@ -import type { KefuChatRuntimeConfig } from "@/lib/sdk/config-types" +import type { SupportChatRuntimeConfig } from "@/lib/sdk/config-types" -export function readKefuChatRuntimeConfig(): KefuChatRuntimeConfig { +export function readSupportChatRuntimeConfig(): SupportChatRuntimeConfig { if (typeof window === "undefined") { return { channelId: "", @@ -10,7 +10,7 @@ export function readKefuChatRuntimeConfig(): KefuChatRuntimeConfig { } const query = new URLSearchParams(window.location.search) - const fallback: KefuChatRuntimeConfig = { + const fallback: SupportChatRuntimeConfig = { channelId: query.get("channelId") ?? process.env.NEXT_PUBLIC_OPEN_IM_CHANNEL_ID?.trim() ?? @@ -46,7 +46,7 @@ export function readKefuChatRuntimeConfig(): KefuChatRuntimeConfig { return fallback } -export function setKefuChatRuntimeConfig(config: KefuChatRuntimeConfig) { +export function setSupportChatRuntimeConfig(config: SupportChatRuntimeConfig) { if (typeof window === "undefined") { return } diff --git a/web/lib/stores/kefu-chat.ts b/web/lib/stores/support-chat.ts similarity index 91% rename from web/lib/stores/kefu-chat.ts rename to web/lib/stores/support-chat.ts index cb51a2f..7a3dd18 100644 --- a/web/lib/stores/kefu-chat.ts +++ b/web/lib/stores/support-chat.ts @@ -38,8 +38,8 @@ import { summarizeIMMessage } from "@/lib/im-message" import { createRealtimeConnectionManager } from "@/lib/realtime-connection" import { generateUUID } from "@/lib/utils" import { - readKefuChatRuntimeConfig, - setKefuChatRuntimeConfig, + readSupportChatRuntimeConfig, + setSupportChatRuntimeConfig, } from "@/lib/sdk/runtime-config" import { translateCurrentMessage } from "@/i18n/messages" @@ -107,7 +107,7 @@ function markConversationReadMessages( return next } -export type KefuChatStore = { +export type SupportChatStore = { title: string subtitle: string themeColor: string @@ -149,7 +149,7 @@ function t(key: string) { return translateCurrentMessage(key) } -export const useKefuChatStore = create((set, get) => { +export const useSupportChatStore = create((set, get) => { const realtime = createRealtimeConnectionManager({ createSocket: createImRealtimeConnection, canReconnect: () => Boolean(get().isOpen && get().conversation?.id), @@ -204,7 +204,7 @@ export const useKefuChatStore = create((set, get) => { document.visibilityState !== "visible" ) { const state = get() - showNotification(t("kefu.newMessage"), getNotificationBody(message), () => { + showNotification(t("supportChat.newMessage"), getNotificationBody(message), () => { state.setIsOpen(true) state.setIsVisible(true) }) @@ -236,7 +236,7 @@ export const useKefuChatStore = create((set, get) => { } return { - title: t("kefu.title"), + title: t("supportChat.title"), subtitle: "", themeColor: "#2563eb", conversation: null, @@ -285,15 +285,15 @@ export const useKefuChatStore = create((set, get) => { } if (widgetConfig.channelId) { - setKefuChatRuntimeConfig({ - ...readKefuChatRuntimeConfig(), + setSupportChatRuntimeConfig({ + ...readSupportChatRuntimeConfig(), channelId: - widgetConfig.channelId || readKefuChatRuntimeConfig().channelId, + widgetConfig.channelId || readSupportChatRuntimeConfig().channelId, }) } set({ - title: widgetConfig.title || t("kefu.title"), + title: widgetConfig.title || t("supportChat.title"), subtitle: widgetConfig.subtitle || "", themeColor: widgetConfig.themeColor || "#2563eb", }) @@ -324,7 +324,7 @@ export const useKefuChatStore = create((set, get) => { } set({ status: "disconnected", - error: error instanceof Error ? error.message : t("kefu.initFailed"), + error: error instanceof Error ? error.message : t("supportChat.initFailed"), }) } } @@ -355,7 +355,7 @@ export const useKefuChatStore = create((set, get) => { }) } catch (error) { set({ - error: error instanceof Error ? error.message : t("kefu.loadMessagesFailed"), + error: error instanceof Error ? error.message : t("supportChat.loadMessagesFailed"), }) throw error } @@ -391,7 +391,7 @@ export const useKefuChatStore = create((set, get) => { }) } catch (error) { set({ - error: error instanceof Error ? error.message : t("kefu.syncMessagesFailed"), + error: error instanceof Error ? error.message : t("supportChat.syncMessagesFailed"), }) } }, @@ -434,7 +434,7 @@ export const useKefuChatStore = create((set, get) => { } catch (error) { set({ messagesLoadingMore: false, - error: error instanceof Error ? error.message : t("kefu.loadHistoryFailed"), + error: error instanceof Error ? error.message : t("supportChat.loadHistoryFailed"), }) throw error } @@ -496,7 +496,7 @@ export const useKefuChatStore = create((set, get) => { conversationId, messageType: "html", content, - clientMsgId: `kefu_html_${generateUUID()}`, + clientMsgId: `support_chat_html_${generateUUID()}`, }) set((state) => ({ sending: false, @@ -519,7 +519,7 @@ export const useKefuChatStore = create((set, get) => { } catch (error) { set({ sending: false, - error: error instanceof Error ? error.message : t("kefu.sendMessageFailed"), + error: error instanceof Error ? error.message : t("supportChat.sendMessageFailed"), }) throw error } @@ -540,7 +540,7 @@ export const useKefuChatStore = create((set, get) => { return await uploadImImage(conversationId, file) } catch (error) { set({ - error: error instanceof Error ? error.message : t("kefu.uploadImageFailed"), + error: error instanceof Error ? error.message : t("supportChat.uploadImageFailed"), }) return null } finally { @@ -562,7 +562,7 @@ export const useKefuChatStore = create((set, get) => { messageType: "attachment", content: asset.filename, payload: JSON.stringify({ assetId: asset.assetId }), - clientMsgId: `kefu_attachment_${generateUUID()}`, + clientMsgId: `support_chat_attachment_${generateUUID()}`, }) set((state) => ({ uploadingAsset: false, @@ -585,7 +585,7 @@ export const useKefuChatStore = create((set, get) => { } catch (error) { set({ uploadingAsset: false, - error: error instanceof Error ? error.message : t("kefu.sendAttachmentFailed"), + error: error instanceof Error ? error.message : t("supportChat.sendAttachmentFailed"), }) throw error } @@ -614,7 +614,7 @@ export const useKefuChatStore = create((set, get) => { } catch (error) { set({ closingConversation: false, - error: error instanceof Error ? error.message : t("kefu.closeConversationFailed"), + error: error instanceof Error ? error.message : t("supportChat.closeConversationFailed"), }) throw error } @@ -634,7 +634,7 @@ export const useKefuChatStore = create((set, get) => { } catch (error) { set({ status: "disconnected", - error: error instanceof Error ? error.message : t("kefu.refreshFailed"), + error: error instanceof Error ? error.message : t("supportChat.refreshFailed"), }) } }, diff --git a/web/lib/kefu-host-bridge.ts b/web/lib/support-host-bridge.ts similarity index 79% rename from web/lib/kefu-host-bridge.ts rename to web/lib/support-host-bridge.ts index e50c6fe..aa7f570 100644 --- a/web/lib/kefu-host-bridge.ts +++ b/web/lib/support-host-bridge.ts @@ -1,5 +1,5 @@ -import { setKefuChatRuntimeConfig } from "@/lib/sdk/runtime-config" -import type { KefuChatRuntimeConfig } from "@/lib/sdk/config-types" +import { setSupportChatRuntimeConfig } from "@/lib/sdk/runtime-config" +import type { SupportChatRuntimeConfig } from "@/lib/sdk/config-types" type HostBridgeOptions = { onInit?: () => void @@ -17,7 +17,7 @@ const REQUEST_MINIMIZE_MESSAGE_TYPE = "cs-agent:request-minimize" const REQUEST_CLOSE_MESSAGE_TYPE = "cs-agent:request-close" const REQUEST_TOGGLE_MAXIMIZE_MESSAGE_TYPE = "cs-agent:request-toggle-maximize" -export function bindKefuHostBridge(options: HostBridgeOptions = {}) { +export function bindSupportHostBridge(options: HostBridgeOptions = {}) { if (typeof window === "undefined") { return () => undefined } @@ -30,7 +30,7 @@ export function bindKefuHostBridge(options: HostBridgeOptions = {}) { const data = event.data as | { type?: string - payload?: KefuChatRuntimeConfig | { isMaximized?: boolean } + payload?: SupportChatRuntimeConfig | { isMaximized?: boolean } } | undefined if (!data?.type) { @@ -38,7 +38,7 @@ export function bindKefuHostBridge(options: HostBridgeOptions = {}) { } if (data.type === INIT_MESSAGE_TYPE && data.payload) { - setKefuChatRuntimeConfig(data.payload as KefuChatRuntimeConfig) + setSupportChatRuntimeConfig(data.payload as SupportChatRuntimeConfig) options.onInit?.() return } @@ -72,14 +72,14 @@ function postToParent(type: string) { } } -export function requestKefuHostMinimize() { +export function requestSupportHostMinimize() { postToParent(REQUEST_MINIMIZE_MESSAGE_TYPE) } -export function requestKefuHostClose() { +export function requestSupportHostClose() { postToParent(REQUEST_CLOSE_MESSAGE_TYPE) } -export function requestKefuHostToggleMaximize() { +export function requestSupportHostToggleMaximize() { postToParent(REQUEST_TOGGLE_MAXIMIZE_MESSAGE_TYPE) } diff --git a/web/messages/en-US.json b/web/messages/en-US.json index 5f26aca..50eff03 100644 --- a/web/messages/en-US.json +++ b/web/messages/en-US.json @@ -270,7 +270,7 @@ "claiming": "Claiming...", "confirmClaim": "Claim" }, - "kefu": { + "supportChat": { "title": "Support", "agentLabel": "Support", "customerSelf": "You", diff --git a/web/messages/zh-CN.json b/web/messages/zh-CN.json index c3b969d..96b2849 100644 --- a/web/messages/zh-CN.json +++ b/web/messages/zh-CN.json @@ -270,7 +270,7 @@ "claiming": "认领中...", "confirmClaim": "确认认领" }, - "kefu": { + "supportChat": { "title": "在线客服", "agentLabel": "客服", "customerSelf": "我",