From 9d9c83a72ca6b6a8e77200737a11df05794cd5f0 Mon Sep 17 00:00:00 2001 From: mlogclub Date: Sat, 25 Apr 2026 11:31:16 +0800 Subject: [PATCH] refactor: update styling for chat components to enhance visual consistency and accessibility --- web/components/kefu/chat-shell.tsx | 62 ++++++++++++++++++----- web/components/kefu/connection-status.tsx | 8 +-- web/components/kefu/message-editor.tsx | 12 ++--- web/components/kefu/message-list.tsx | 16 +++--- 4 files changed, 67 insertions(+), 31 deletions(-) diff --git a/web/components/kefu/chat-shell.tsx b/web/components/kefu/chat-shell.tsx index b16f975..8b723be 100644 --- a/web/components/kefu/chat-shell.tsx +++ b/web/components/kefu/chat-shell.tsx @@ -7,7 +7,14 @@ import { RotateCwIcon, XIcon, } from "lucide-react" -import { useCallback, useEffect, useRef, useState, type CSSProperties } from "react" +import { + useCallback, + useEffect, + useLayoutEffect, + useRef, + useState, + type CSSProperties, +} from "react" import { useShallow } from "zustand/react/shallow" import { KefuConnectionStatus } from "@/components/kefu/connection-status" @@ -33,7 +40,36 @@ import { DialogTitle, } from "@/components/ui/dialog" +function useKefuSystemTheme() { + useLayoutEffect(() => { + if (typeof window === "undefined") { + return + } + const root = document.documentElement + const query = window.matchMedia("(prefers-color-scheme: dark)") + const previousDarkClass = root.classList.contains("dark") + const previousColorScheme = root.style.colorScheme + + const syncTheme = () => { + const isDark = query.matches + root.classList.toggle("dark", isDark) + root.style.colorScheme = isDark ? "dark" : "light" + } + + syncTheme() + query.addEventListener("change", syncTheme) + + return () => { + query.removeEventListener("change", syncTheme) + root.classList.toggle("dark", previousDarkClass) + root.style.colorScheme = previousColorScheme + } + }, []) +} + export function KefuChatShell() { + useKefuSystemTheme() + const messageListRef = useRef(null) const [isMaximized, setIsMaximized] = useState(false) const [isCloseDialogOpen, setIsCloseDialogOpen] = useState(false) @@ -200,23 +236,23 @@ export function KefuChatShell() { return (
-
-
+
+
-
+
{title}
-
{subtitle}
+
{subtitle}
{status !== "connected" ? ( ) : null} -
+
@@ -236,7 +272,7 @@ export function KefuChatShell() { onClick={handleMinimize} aria-label="收起聊天窗口" title="收起聊天窗口" - className="text-slate-500 hover:bg-white hover:text-slate-800" + className="text-muted-foreground hover:bg-background hover:text-foreground" > @@ -249,7 +285,7 @@ export function KefuChatShell() { onClick={handleToggleMaximize} aria-label={isMaximized ? "取消最大化" : "最大化聊天窗口"} title={isMaximized ? "取消最大化" : "最大化聊天窗口"} - className="text-slate-500 hover:bg-white hover:text-emerald-700" + className="text-muted-foreground hover:bg-background hover:text-emerald-700 dark:hover:text-emerald-400" > {isMaximized ? ( @@ -265,7 +301,7 @@ export function KefuChatShell() { onClick={() => setIsCloseDialogOpen(true)} aria-label="关闭聊天窗口" title="关闭聊天窗口" - className="text-rose-500 hover:bg-rose-50 hover:text-rose-600" + className="text-rose-500 hover:bg-rose-50 hover:text-rose-600 dark:hover:bg-rose-950/40 dark:hover:text-rose-300" > @@ -274,7 +310,7 @@ export function KefuChatShell() {
-
+
{error ? ( -
+
{error}
) : null} diff --git a/web/components/kefu/connection-status.tsx b/web/components/kefu/connection-status.tsx index a27811f..c2c338f 100644 --- a/web/components/kefu/connection-status.tsx +++ b/web/components/kefu/connection-status.tsx @@ -16,10 +16,10 @@ const statusText: Record = { export function KefuConnectionStatus({ status }: KefuConnectionStatusProps) { const toneClass = status === "connected" - ? "border-emerald-200 bg-emerald-50 text-emerald-700" + ? "border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900/70 dark:bg-emerald-950/50 dark:text-emerald-300" : status === "connecting" - ? "border-amber-200 bg-amber-50 text-amber-700" - : "border-slate-200 bg-slate-100 text-slate-600" + ? "border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900/70 dark:bg-amber-950/50 dark:text-amber-300" + : "border-border bg-muted text-muted-foreground" return ( {statusText[status]} diff --git a/web/components/kefu/message-editor.tsx b/web/components/kefu/message-editor.tsx index d40b077..3a6e5be 100644 --- a/web/components/kefu/message-editor.tsx +++ b/web/components/kefu/message-editor.tsx @@ -100,7 +100,7 @@ export function KefuMessageEditor({ editorProps: { attributes: { class: - "min-h-12 max-h-40 overflow-y-auto px-1.5 py-1 text-sm leading-6 text-slate-900 outline-none [&_p]:m-0 [&_p+*]:mt-2 [&_img]:my-2 [&_img]:max-h-64 [&_img]:rounded-lg [&_img]:object-contain", + "min-h-12 max-h-40 overflow-y-auto px-1.5 py-1 text-sm leading-6 text-foreground outline-none [&_p]:m-0 [&_p+*]:mt-2 [&_img]:my-2 [&_img]:max-h-64 [&_img]:rounded-lg [&_img]:object-contain", }, handleKeyDown: (_view, event) => { if (event.key === "Enter" && !event.shiftKey) { @@ -233,8 +233,8 @@ export function KefuMessageEditor({ } return ( -
-
+
+
@@ -281,13 +281,13 @@ export function KefuMessageEditor({ disabled={disabled || isUploading} aria-label={isUploading ? "附件上传中" : "发送附件"} title={isUploading ? "附件上传中" : "发送附件"} - className="text-slate-500 hover:bg-slate-100 hover:text-slate-800" + className="text-muted-foreground hover:bg-muted hover:text-foreground" >
-

Enter 发送

+

Enter 发送

@@ -269,7 +269,7 @@ const MessageItem = memo(
{getTimelineLabel(message.sentAt)} @@ -280,7 +280,7 @@ const MessageItem = memo( {!isCustomer ? ( {avatarSrc ? : null} - + {fallbackName || "客"} @@ -292,7 +292,7 @@ const MessageItem = memo( isCustomer ? "items-end" : "items-start" )} > -
+
{senderName} {formatDateTime(message.sentAt)} {isCustomer ? ( @@ -303,16 +303,16 @@ const MessageItem = memo( className={cn( "rounded-lg px-3 py-2 text-sm leading-normal shadow-[0_10px_22px_rgba(15,23,42,0.06)]", isCustomer - ? "bg-[#a9ea7a] text-[#161616]" - : "border border-slate-200/80 bg-white text-slate-900" + ? "bg-[#a9ea7a] text-[#161616] dark:bg-emerald-500 dark:text-emerald-950" + : "border border-border bg-card text-card-foreground dark:bg-background" )} >