fix(ui): 加回 agent 头像,范围带调深到看得见
产品真机走查: 1. agent 头像加回来(推翻 R3 的 D7)。R3 删它的理由是「一屏几十条时是重复度 最高的元素」,但没有它助手那一轮就没有视觉归属——生时校正面尤其明显, 旁白、题干、选项全是平铺文字。--assistant-content-inset 随之回到 「32px 头像 + 间隙」,追问 chip 与运行时间轴重新对齐正文。 2. 时间范围带用的是 --color-action-soft,那是给文字底色用的浅色调, 画在画布上几乎看不见——而它是整条轴上唯一必须一眼看到的标记。 改成 color-mix(--color-action-strong 32%, --color-canvas)。 测试 3369,fail 仍 31 且与基线逐条一致;四个路由标记不变。 未修:生时校正面顶部溢出。工作区在栅格里、没有脱离定位, chat-panel.is-rectification 仍是 46px + 1fr 两行,从代码上看不出 46px 顶栏为什么会消失,待产品确认截图是否裁切。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
This commit is contained in:
co-authored by
Claude Opus 5
parent
524015cfc1
commit
071317f7ee
@@ -357,7 +357,7 @@ button:disabled { cursor: default; opacity: .45; }
|
|||||||
.birth-time-assessment-progress > span { font-size: var(--type-body-sm); line-height: 1.55; text-wrap: pretty; word-break: auto-phrase; }
|
.birth-time-assessment-progress > span { font-size: var(--type-body-sm); line-height: 1.55; text-wrap: pretty; word-break: auto-phrase; }
|
||||||
.onboarding-inline-error { margin-left: 0; }
|
.onboarding-inline-error { margin-left: 0; }
|
||||||
|
|
||||||
.message-assistant { align-items: flex-start; justify-content: flex-start; }
|
.message-assistant { align-items: flex-start; justify-content: flex-start; gap: var(--space-3); }
|
||||||
.message-user { justify-content: flex-end; }
|
.message-user { justify-content: flex-end; }
|
||||||
.message-user .message-content { display: flex; flex-direction: column; align-items: flex-end; }
|
.message-user .message-content { display: flex; flex-direction: column; align-items: flex-end; }
|
||||||
.message-assistant .message-content { width: auto; max-width: none; flex: 1; }
|
.message-assistant .message-content { width: auto; max-width: none; flex: 1; }
|
||||||
@@ -1055,10 +1055,9 @@ button:disabled { cursor: default; opacity: .45; }
|
|||||||
|
|
||||||
.rectification-entry-error { grid-column: 1 / -1; margin: 0; }
|
.rectification-entry-error { grid-column: 1 / -1; margin: 0; }
|
||||||
.message-list {
|
.message-list {
|
||||||
/* The assistant reply starts at the column edge now; the 32px avatar and
|
/* Follow-up chips and the run timeline line up with the assistant's text,
|
||||||
its gap are gone, so anything that used to line up beside it (follow-up
|
which starts past the avatar and its gap. */
|
||||||
chips, the run timeline) lines up with the text itself. */
|
--assistant-content-inset: calc(32px + var(--space-3));
|
||||||
--assistant-content-inset: 0px;
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
width: min(900px, 100%);
|
width: min(900px, 100%);
|
||||||
padding: var(--space-8) var(--space-8) var(--space-16);
|
padding: var(--space-8) var(--space-8) var(--space-16);
|
||||||
@@ -1071,6 +1070,8 @@ button:disabled { cursor: default; opacity: .45; }
|
|||||||
color: var(--color-ink-secondary);
|
color: var(--color-ink-secondary);
|
||||||
}
|
}
|
||||||
.message { display: flex; padding: var(--space-2) 0; }
|
.message { display: flex; padding: var(--space-2) 0; }
|
||||||
|
/* Restored after real-device review; see chat-message-row.tsx for why. */
|
||||||
|
.agent-avatar { width: 32px; height: 32px; display: block; flex: 0 0 32px; margin-top: var(--space-2); border-radius: 50%; background: var(--color-canvas) url("/jyotish-logo.png") center / contain no-repeat; box-shadow: 0 0 0 1px var(--ring-hairline); }
|
||||||
.message-content { min-width: 0; max-width: min(80%, 680px); }
|
.message-content { min-width: 0; max-width: min(80%, 680px); }
|
||||||
.message-bubble { overflow: hidden; border: 0; padding: var(--space-3) var(--space-4); border-radius: var(--radius-lg); background: var(--color-canvas-muted); }
|
.message-bubble { overflow: hidden; border: 0; padding: var(--space-3) var(--space-4); border-radius: var(--radius-lg); background: var(--color-canvas-muted); }
|
||||||
.message-assistant .message-bubble { border-radius: 0; background: transparent; padding: var(--space-3) 0; }
|
.message-assistant .message-bubble { border-radius: 0; background: transparent; padding: var(--space-3) 0; }
|
||||||
@@ -2240,6 +2241,14 @@ button.nav-rail-identity:hover { background: var(--sidebar-accent); }
|
|||||||
gap: var(--space-4);
|
gap: var(--space-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.conversation:not(.is-empty):not(.is-rectification) .message-assistant .agent-avatar {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
flex-basis: 30px;
|
||||||
|
margin-top: 2px;
|
||||||
|
box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-border) 82%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.conversation:not(.is-empty):not(.is-rectification) .message-assistant .message-bubble {
|
.conversation:not(.is-empty):not(.is-rectification) .message-assistant .message-bubble {
|
||||||
padding: 0 0 var(--space-2);
|
padding: 0 0 var(--space-2);
|
||||||
@@ -2531,9 +2540,9 @@ button.nav-rail-identity:hover { background: var(--sidebar-accent); }
|
|||||||
The pending state keeps the same box for the same reason. */
|
The pending state keeps the same box for the same reason. */
|
||||||
.rectification-workspace__chat {
|
.rectification-workspace__chat {
|
||||||
--rectification-timeline-height: 64px;
|
--rectification-timeline-height: 64px;
|
||||||
/* Same token as `.message-list`. The bar sits outside the transcript, so it
|
/* Same token as `.message-list`: avatar 32px + gap. The bar sits outside the
|
||||||
cannot inherit that custom property from there. */
|
transcript, so it cannot inherit that custom property from there. */
|
||||||
--assistant-content-inset: 0px;
|
--assistant-content-inset: calc(32px + var(--space-3));
|
||||||
}
|
}
|
||||||
.rectification-timeline {
|
.rectification-timeline {
|
||||||
height: var(--rectification-timeline-height);
|
height: var(--rectification-timeline-height);
|
||||||
@@ -2614,7 +2623,10 @@ button.nav-rail-identity:hover { background: var(--sidebar-accent); }
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 11px;
|
height: 11px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background: var(--color-action-soft);
|
/* Was --color-action-soft, which is a tint for text backgrounds: on the
|
||||||
|
canvas it left the range band all but invisible. This is the one mark
|
||||||
|
that has to read at a glance. */
|
||||||
|
background: color-mix(in srgb, var(--color-action-strong) 32%, var(--color-canvas));
|
||||||
transform-origin: left center;
|
transform-origin: left center;
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
transition: transform 420ms cubic-bezier(.32, .72, .24, 1);
|
transition: transform 420ms cubic-bezier(.32, .72, .24, 1);
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ function motionPreferred() {
|
|||||||
|
|
||||||
if (motionPreferred()) prefetchOnIdle(loadGsap);
|
if (motionPreferred()) prefetchOnIdle(loadGsap);
|
||||||
|
|
||||||
|
/* Restored after real-device review: R3 removed it on the grounds that it was
|
||||||
|
the most repeated element in a long transcript, but without it the assistant
|
||||||
|
turn has no visual owner — in the rectification surface especially, where
|
||||||
|
narration, a question and its options all stack as plain text. */
|
||||||
|
export function AgentAvatar() {
|
||||||
|
return <span className="agent-avatar" aria-hidden="true" />;
|
||||||
|
}
|
||||||
|
|
||||||
export const ChatMessageRow = memo(function ChatMessageRow({
|
export const ChatMessageRow = memo(function ChatMessageRow({
|
||||||
message,
|
message,
|
||||||
showActivity = message.state !== "settled",
|
showActivity = message.state !== "settled",
|
||||||
@@ -123,6 +131,7 @@ export const ChatMessageRow = memo(function ChatMessageRow({
|
|||||||
className={`message message-${message.role}`}
|
className={`message message-${message.role}`}
|
||||||
aria-label={message.role === "assistant" ? assistantLabel : "你"}
|
aria-label={message.role === "assistant" ? assistantLabel : "你"}
|
||||||
>
|
>
|
||||||
|
{message.role === "assistant" && <AgentAvatar />}
|
||||||
<div className="message-content">
|
<div className="message-content">
|
||||||
<div className="message-bubble">
|
<div className="message-bubble">
|
||||||
{message.role === "assistant" ? (
|
{message.role === "assistant" ? (
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ChatMessageContent } from "@/components/chat-message-content";
|
import { ChatMessageContent } from "@/components/chat-message-content";
|
||||||
|
import { AgentAvatar } from "@/components/chat-message-row";
|
||||||
import { protectOnboardingPhrases } from "@/lib/onboarding-copy";
|
import { protectOnboardingPhrases } from "@/lib/onboarding-copy";
|
||||||
import type { Message } from "@/lib/home-types";
|
import type { Message } from "@/lib/home-types";
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ export function OnboardingChatMessage({ role, text, streaming = false, length =
|
|||||||
const protectedVisibleText = protectOnboardingPhrases(visibleText);
|
const protectedVisibleText = protectOnboardingPhrases(visibleText);
|
||||||
return (
|
return (
|
||||||
<article className={`message message-${role} onboarding-message${phraseSafe ? " is-phrase-safe" : ""}`} aria-label={role === "assistant" ? "Jyotisha" : "你"}>
|
<article className={`message message-${role} onboarding-message${phraseSafe ? " is-phrase-safe" : ""}`} aria-label={role === "assistant" ? "Jyotisha" : "你"}>
|
||||||
|
{role === "assistant" && <AgentAvatar />}
|
||||||
<div className="message-content">
|
<div className="message-content">
|
||||||
<div className="message-bubble">
|
<div className="message-bubble">
|
||||||
{role === "assistant" ? (
|
{role === "assistant" ? (
|
||||||
|
|||||||
@@ -487,7 +487,7 @@ test("candidate state renders from the snapshot API and never from sentinels", (
|
|||||||
assert.match(styles, /\.rectification-snapshot \{/);
|
assert.match(styles, /\.rectification-snapshot \{/);
|
||||||
assert.match(styles, /\.rectification-house-table \{[^}]*border:\s*1px solid var\(--color-border\)/);
|
assert.match(styles, /\.rectification-house-table \{[^}]*border:\s*1px solid var\(--color-border\)/);
|
||||||
assert.match(styles, /\.rectification-house-table \{[^}]*background:\s*var\(--color-canvas-soft\)/);
|
assert.match(styles, /\.rectification-house-table \{[^}]*background:\s*var\(--color-canvas-soft\)/);
|
||||||
assert.match(styles, /\.message-list \{[\s\S]*?--assistant-content-inset: 0px/);
|
assert.match(styles, /\.message-list \{[\s\S]*?--assistant-content-inset: calc\(32px \+ var\(--space-3\)\)/);
|
||||||
assert.match(styles, /\.rectification-message-wrap \.rectification-candidates \{[\s\S]*width: calc\(100% - var\(--assistant-content-inset\)\)/);
|
assert.match(styles, /\.rectification-message-wrap \.rectification-candidates \{[\s\S]*width: calc\(100% - var\(--assistant-content-inset\)\)/);
|
||||||
assert.match(styles, /\.rectification-message-wrap \.rectification-candidates \{[\s\S]*margin-inline-start: var\(--assistant-content-inset\)/);
|
assert.match(styles, /\.rectification-message-wrap \.rectification-candidates \{[\s\S]*margin-inline-start: var\(--assistant-content-inset\)/);
|
||||||
assert.match(styles, /\.rectification-workspace \{[\s\S]*grid-template-columns: minmax\(0, 1fr\) minmax\(18rem, 22\.5rem\)/);
|
assert.match(styles, /\.rectification-workspace \{[\s\S]*grid-template-columns: minmax\(0, 1fr\) minmax\(18rem, 22\.5rem\)/);
|
||||||
|
|||||||
@@ -408,7 +408,10 @@ test("the chat grid has three rows and the timeline row is a fixed height", () =
|
|||||||
// stick-to-bottom, since it sits outside the observed scroll container.
|
// stick-to-bottom, since it sits outside the observed scroll container.
|
||||||
assert.doesNotMatch(barRule, /min-height|height:\s*auto/);
|
assert.doesNotMatch(barRule, /min-height|height:\s*auto/);
|
||||||
// Inner padding matches the assistant column, not the chat-panel edge.
|
// Inner padding matches the assistant column, not the chat-panel edge.
|
||||||
assert.match(chatRule, /--assistant-content-inset: 0px;/);
|
// 原值 `0px` / 新值 `calc(32px + var(--space-3))`
|
||||||
|
// / 原因:产品在真机走查后要求把 agent 头像加回来(推翻 R3 的 D7);
|
||||||
|
// 这条缩进跟着头像走,时间轴仍与助手正文左边界对齐,规则本身没变。
|
||||||
|
assert.match(chatRule, /--assistant-content-inset: calc\(32px \+ var\(--space-3\)\);/);
|
||||||
assert.match(barRule, /padding: 0 calc\(var\(--space-8\) \+ var\(--assistant-content-inset\)\);/);
|
assert.match(barRule, /padding: 0 calc\(var\(--space-8\) \+ var\(--assistant-content-inset\)\);/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user