style(canvas): Agent 面板 - 正文左边缘作为公共轴,过程标记统一挂轴居中
(cherry picked from commit ec4ea4a14c0c392ae1f765fa991cd87fed1910aa)
This commit is contained in:
@@ -1233,7 +1233,7 @@ export function AgentPanelTabs<T extends string>({
|
||||
function AgentTimelineMarker({ theme, tone, icon }: { theme: (typeof canvasThemes)[keyof typeof canvasThemes]; tone: "agent" | "muted" | "approval" | "error"; icon: ReactNode }) {
|
||||
const color = tone === "error" ? "#ef4444" : tone === "approval" ? "#f97316" : tone === "agent" ? theme.accent.primary : theme.node.muted;
|
||||
return (
|
||||
<span className="relative flex w-6 shrink-0 self-stretch justify-center" aria-hidden="true">
|
||||
<span className="agent-timeline-marker relative" aria-hidden="true">
|
||||
<span className="relative grid size-6 place-items-center rounded-full" style={{ background: tone === "agent" ? theme.accent.primarySoft : theme.node.fill, color }}>
|
||||
{icon}
|
||||
</span>
|
||||
|
||||
@@ -389,16 +389,18 @@
|
||||
}
|
||||
|
||||
/*
|
||||
* 过程内容的公共轴:模型思考的星标与工具调用的左轨压在同一条竖线上(默认 x = 22px)。
|
||||
* 轴由星标几何推导,不是拍出来的数:星标中心 = 思考缩进 + 摘要内边距 + 图标半宽;
|
||||
* 工具左轨用 `axis - 0.5px` 让 1px 的线居中在轴上。改任一项,两处会一起跟着走。
|
||||
* 过程内容的公共轴:正文的左边缘就是这条线,过程标记(星标、工具左轨、时间线标记)
|
||||
* 一律挂在轴上居中。轴由星标几何推导,不是拍出来的数:星标中心 = 思考缩进 + 摘要内边距
|
||||
* + 图标半宽;工具左轨用 -0.5px 让 1px 的线居中在轴上。改任一项,三处会一起跟着走。
|
||||
*/
|
||||
.agent-conversation-messages {
|
||||
--agent-axis: 22px;
|
||||
--agent-thought-icon: 16px;
|
||||
--agent-thought-gap: 9px;
|
||||
--agent-summary-inset: 4px;
|
||||
--agent-gutter-thought: calc(var(--agent-axis) - var(--agent-thought-icon) / 2 - var(--agent-summary-inset));
|
||||
--agent-timeline-marker: 24px;
|
||||
--agent-activity-inset: 8px;
|
||||
padding-left: var(--agent-axis);
|
||||
}
|
||||
|
||||
.agent-conversation-messages > * + * {
|
||||
@@ -935,9 +937,9 @@
|
||||
*/
|
||||
.agent-operation-feed {
|
||||
--agent-tool-accent: color-mix(in srgb, var(--foreground) 62%, transparent);
|
||||
/* 层级第二档:左轨与星标同轴(见 .agent-conversation-messages 的公共轴),
|
||||
/* 层级第二档:左轨与正文左边缘同轴(见 .agent-conversation-messages 的公共轴),
|
||||
轨道把"一连串动作"拢成一条活动流,内容落在轨的右侧。 */
|
||||
margin-left: calc(var(--agent-axis, 22px) - 0.5px);
|
||||
margin-left: -0.5px;
|
||||
border-left: 1px solid color-mix(in srgb, var(--foreground) 12%, transparent);
|
||||
padding-left: var(--agent-activity-inset, 8px);
|
||||
min-width: 0;
|
||||
@@ -1291,16 +1293,33 @@
|
||||
* 信息层级:正文 / 工具调用 / 模型思考 三段只靠「位置 + 字号 + 对比度」分层,
|
||||
* 不靠圆底图标或品牌 glyph。
|
||||
*
|
||||
* 正文(结论) 贴左边缘、14px、满对比 —— 主角
|
||||
* 工具调用 缩进一层并挂一条左轨(活动流),12px、次对比
|
||||
* 模型思考 与轨同一条缩进线上、但不带轨,12px/11px、最淡 —— 过程里的旁白
|
||||
* 正文(结论) 从公共轴开始、14px、满对比 —— 主角
|
||||
* 工具调用 左轨压在公共轴上(活动流),内容在轨右侧,12px、次对比
|
||||
* 模型思考 星标居中在公共轴上,12px/11px、最淡 —— 过程里的旁白
|
||||
*/
|
||||
.agent-reasoning {
|
||||
min-width: 0;
|
||||
margin-left: var(--agent-gutter-thought, 10px);
|
||||
/* 星标挂进旁注沟槽:中心落在公共轴上(正文左边缘)。 */
|
||||
margin-left: calc(-1 * (var(--agent-thought-icon, 16px) / 2 + var(--agent-summary-inset, 4px)));
|
||||
color: color-mix(in srgb, var(--foreground) 58%, transparent);
|
||||
}
|
||||
|
||||
/*
|
||||
* 时间线标记列(系统行 / 错误行 / 审批卡 / 上下文整理):宽度与挂点都走公共轴 token。
|
||||
*/
|
||||
.agent-timeline-marker {
|
||||
display: flex;
|
||||
width: var(--agent-timeline-marker, 24px);
|
||||
flex: 0 0 var(--agent-timeline-marker, 24px);
|
||||
align-self: stretch;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 只有对话流里的标记向左挂半格:中心与星标、工具左轨共用同一条公共轴。 */
|
||||
.agent-conversation-messages .agent-timeline-marker {
|
||||
margin-left: calc(-1 * (var(--agent-timeline-marker, 24px) / 2));
|
||||
}
|
||||
|
||||
.agent-reasoning-card {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
@@ -1319,7 +1338,7 @@
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
gap: var(--agent-thought-gap, 9px);
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
/* 内边距参与公共轴推导:改它,左轨会跟着一起挪(见 .agent-conversation-messages)。 */
|
||||
@@ -1432,8 +1451,8 @@
|
||||
gap: 0;
|
||||
overflow: auto;
|
||||
border-top: 0;
|
||||
/* 25px = 星标列(16px)+ 摘要的 gap(9px):正文与标题左对齐。 */
|
||||
padding: 4px 10px 10px 25px;
|
||||
/* 与摘要的标题左对齐:摘要内边距 + 星标列 + 摘要 gap。 */
|
||||
padding: 4px 10px 10px calc(var(--agent-summary-inset, 4px) + var(--agent-thought-icon, 16px) + var(--agent-thought-gap, 9px));
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ test("style contract: no per-step status ticks, vision tint and shimmer stay tok
|
||||
expect(css).toContain("--agent-tool-accent: var(--agent-accent, var(--foreground));");
|
||||
});
|
||||
|
||||
test("style contract: 正文 / 工具调用 / 模型思考 三档靠位置与明度分层", async () => {
|
||||
test("style contract: 正文 / 工具调用 / 模型思考 三档靠公共轴与明度分层", async () => {
|
||||
const css = await Bun.file(new URL("../src/components/canvas/canvas-cloud-agent.css", import.meta.url)).text();
|
||||
// 同一选择器可能在容器查询里被覆盖,这里把所有命中块拼起来看整体契约。
|
||||
const block = (selector: string) => [...css.matchAll(new RegExp(`${selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")} \\{([^}]*)\\}`, "g"))].map((match) => match[1]).join("\n");
|
||||
@@ -84,23 +84,27 @@ test("style contract: 正文 / 工具调用 / 模型思考 三档靠位置与明
|
||||
expect(icon).toContain("background: transparent");
|
||||
expect(icon).not.toContain("border-radius: 50%");
|
||||
|
||||
// ② 公共轴:星标与工具左轨压在同一条竖线上,且轴由星标几何推导
|
||||
// ② 公共轴:正文就从这条线开始(列表左内边距 = 轴)
|
||||
const axis = block(".agent-conversation-messages");
|
||||
expect(axis).toContain("--agent-axis: 22px");
|
||||
expect(axis).toContain("--agent-gutter-thought: calc(var(--agent-axis) - var(--agent-thought-icon) / 2 - var(--agent-summary-inset))");
|
||||
expect(axis).toContain("padding-left: var(--agent-axis)");
|
||||
|
||||
// ③ 过程标记挂在轴上:星标向左挂半格 + 摘要内边距,中心正好落在轴
|
||||
const reasoning = block(".agent-reasoning");
|
||||
expect(reasoning).toContain("margin-left: calc(-1 * (var(--agent-thought-icon, 16px) / 2 + var(--agent-summary-inset, 4px)))");
|
||||
expect(reasoning).not.toContain("border-left");
|
||||
expect(block(".agent-reasoning-summary")).toContain("padding: 6px var(--agent-summary-inset");
|
||||
|
||||
// ③ 工具调用缩进到轴上并挂左轨(1px 线居中在轴上)
|
||||
// ④ 工具左轨也压在轴上(1px 线居中),内容落在轨右侧
|
||||
const feed = block(".agent-operation-feed");
|
||||
expect(feed).toContain("margin-left: calc(var(--agent-axis, 22px) - 0.5px)");
|
||||
expect(feed).toContain("margin-left: -0.5px");
|
||||
expect(feed).toContain("border-left: 1px solid");
|
||||
expect(feed).toContain("padding-left: var(--agent-activity-inset");
|
||||
|
||||
// ④ 模型思考与轨同轴,但不带轨(更轻的那一档)
|
||||
const reasoning = block(".agent-reasoning");
|
||||
expect(reasoning).toContain("margin-left: var(--agent-gutter-thought");
|
||||
expect(reasoning).not.toContain("border-left");
|
||||
// ⑤ 时间线标记同样挂半格,中心与星标/左轨同轴
|
||||
expect(block(".agent-timeline-marker")).toContain("width: var(--agent-timeline-marker, 24px)");
|
||||
expect(block(".agent-conversation-messages .agent-timeline-marker")).toContain("margin-left: calc(-1 * (var(--agent-timeline-marker, 24px) / 2))");
|
||||
|
||||
// ⑤ 窄面板只把轴收细,不让两处错位
|
||||
// ⑥ 窄面板只把轴收细,不让三处错位
|
||||
expect(css).toContain("--agent-axis: 16px");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user