diff --git a/src/app/chat/components/__tests__/tailwind-components.test.tsx b/src/app/chat/components/__tests__/tailwind-components.test.tsx
index 39eb48ad..75b73de0 100644
--- a/src/app/chat/components/__tests__/tailwind-components.test.tsx
+++ b/src/app/chat/components/__tests__/tailwind-components.test.tsx
@@ -1,6 +1,6 @@
import type { ReactNode } from "react";
import { renderToStaticMarkup } from "react-dom/server";
-import { describe, expect, it, vi } from "vitest";
+import { beforeEach, describe, expect, it, vi } from "vitest";
import { getCharacterBySlug } from "@/data/constants/character";
import { CharacterProvider } from "@/providers/character-provider";
@@ -18,13 +18,27 @@ import { PrivateMessageCard } from "../private-message-card";
import { PwaInstallDialog } from "../pwa-install-dialog";
import { TextBubble } from "../text-bubble";
+const userMocks = vi.hoisted(() => ({
+ avatarUrl: null as string | null,
+}));
+
vi.mock("@/router/use-app-navigator", () => ({
useAppNavigator: () => ({
openSubscription: vi.fn(),
}),
}));
+vi.mock("@/stores/user/user-context", () => ({
+ useUserSelector: (
+ selector: (state: { context: { avatarUrl: string | null } }) => unknown,
+ ) => selector({ context: { avatarUrl: userMocks.avatarUrl } }),
+}));
+
describe("chat Tailwind components", () => {
+ beforeEach(() => {
+ userMocks.avatarUrl = null;
+ });
+
it("renders MessageAvatar AI and user branches with image utilities", () => {
const aiHtml = renderWithCharacter();
const userHtml = renderWithCharacter(
@@ -228,6 +242,7 @@ describe("chat Tailwind components", () => {
expect(memberHtml).not.toContain("px-(--spacing-md,12px)");
expect(memberHtml).toContain("bg-[rgba(13,11,20,0.7)]");
expect(memberHtml).toContain("size-(--responsive-icon-button-size,42px)");
+ expect(memberHtml).toContain("lucide-user-round");
expect(memberWithHintHtml).toContain(
'data-analytics-key="chat.external_browser_hint"',
);
@@ -249,6 +264,20 @@ describe("chat Tailwind components", () => {
);
});
+ it("renders the user avatar in the ChatHeader Profile action", () => {
+ userMocks.avatarUrl = "/images/avatar/profile-user.png";
+
+ const html = renderWithCharacter();
+
+ expect(html).toContain("%2Fimages%2Favatar%2Fprofile-user.png");
+ expect(html).toContain('aria-label="Profile"');
+ expect(html).toContain('data-analytics-key="chat.open_profile"');
+ expect(html).toContain(
+ "var(--responsive-icon-button-size, 42px)",
+ );
+ expect(html).not.toContain("lucide-user-round");
+ });
+
it("links guest chat back to the active character splash", () => {
const maya = getCharacterBySlug("maya");
if (!maya) throw new Error("Missing Maya character fixture");
diff --git a/src/app/chat/components/chat-header.tsx b/src/app/chat/components/chat-header.tsx
index 32e2e771..97566b34 100644
--- a/src/app/chat/components/chat-header.tsx
+++ b/src/app/chat/components/chat-header.tsx
@@ -2,17 +2,17 @@
/**
* ChatHeader 顶部栏
*
- * 图标:lucide-react (游客 banner)+ (Profile)
- * tree-shakable,currentColor 继承父 color
+ * Profile 优先展示用户头像;头像缺失时回退到 lucide-react 。
*/
import type { ReactNode } from "react";
import { Lock, UserRound } from "lucide-react";
-import { BackButton } from "@/app/_components";
+import { BackButton, UserMessageAvatar } from "@/app/_components";
import { useActiveCharacterRoutes } from "@/providers/character-provider";
import { buildGlobalPageUrl } from "@/router/global-route-context";
import { ROUTES } from "@/router/routes";
import { useAppNavigator } from "@/router/use-app-navigator";
+import { useUserSelector } from "@/stores/user/user-context";
import { BrowserHintOverlay } from "./browser-hint-overlay";
@@ -29,6 +29,10 @@ export function ChatHeader({
}: ChatHeaderProps) {
const navigator = useAppNavigator();
const characterRoutes = useActiveCharacterRoutes();
+ const avatarUrl = useUserSelector((state) => state.context.avatarUrl);
+ const handleOpenProfile = () => {
+ navigator.push(buildGlobalPageUrl(ROUTES.profile, characterRoutes.chat));
+ };
return (
@@ -72,20 +76,26 @@ export function ChatHeader({
{showBrowserHint ? : null}
-
+ {avatarUrl ? (
+
+ ) : (
+
+ )}
>
) : null}