统一思考与执行过程折叠样式及中文耗时

思考展开复用安全Markdown渲染,折叠显示浅色单行纯文本预览与右侧箭头。
共享过程摘要组件,执行过程按真实操作数显示统计并保留各组独立计时。
工具、整轮、生成任务和策划耗时统一为中文时分秒格式。
补齐格式与Markdown回归、更新已有界面断言并验证窄屏键盘操作。
This commit is contained in:
2026-09-18 14:34:10 +08:00
parent 21b059bd41
commit a0f6b7c67d
21 changed files with 604 additions and 177 deletions
@@ -0,0 +1,65 @@
/**
* 过程区单行摘要(思考过程 / 外层执行过程 / 工具组共用)。
*
* 只有一行、没有大底色:预览过长省略,chevron 永远贴右。展开态由宿主的
* `[aria-expanded='true']` 或 `details[open]` 决定,这里只负责视觉。
*/
.agent-process-summary {
display: flex;
align-items: center;
gap: 6px;
width: 100%;
min-width: 0;
min-height: 20px;
line-height: 1.4;
background: none;
}
.agent-process-summary-icon {
display: inline-flex;
flex: none;
align-items: center;
color: var(--platform-text-soft, #988476);
}
.agent-process-summary-preview {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.agent-process-summary-meta {
flex: none;
color: var(--platform-text-soft, #988476);
white-space: nowrap;
}
.agent-process-summary-chevron {
display: inline-flex;
flex: none;
align-items: center;
margin-left: auto;
color: var(--platform-text-soft, #988476);
transition: transform 120ms ease;
}
[aria-expanded='true'] > .agent-process-summary .agent-process-summary-chevron,
details[open] > summary .agent-process-summary .agent-process-summary-chevron {
transform: rotate(180deg);
}
/* 焦点画在共享摘要内,避免宿主按钮的 outline:none 抹掉键盘定位。 */
button:focus-visible > .agent-process-summary,
summary:focus-visible > .agent-process-summary {
outline: 2px solid var(--platform-accent, #c26a3e);
outline-offset: 2px;
border-radius: 3px;
}
@media (prefers-reduced-motion: reduce) {
.agent-process-summary-chevron {
transition: none;
}
}
@@ -0,0 +1,48 @@
import './AgentProcessSummary.css';
import type { ReactNode } from 'react';
/**
* 过程区单行摘要的**纯展示**骨架:`icon + 预览文字 + 右对齐 meta + chevron`。
*
* 它只负责视觉,不含状态、不含业务规则,也不渲染任何交互元素:宿主决定外层是可点的
* `button`(工具组)还是 `<details><summary>`(思考过程 / 外层执行过程),并把展开态交给宿主。
* 视觉上固定一行:预览过长省略,chevron 永远贴右,展开时由 `.agent-process-summary` 的
* `[aria-expanded='true']` / `details[open]` 规则旋转。
*/
export function AgentProcessSummary({
icon,
preview,
meta,
chevron,
}: {
/** 行首图标(通常由宿主传入 lucide 图标)。 */
icon?: ReactNode;
/** 单行预览文字;调用方负责先把它压成一行(Markdown 预览模型)。 */
preview: string;
/** 右侧补充信息(耗时 / 状态),可为空。 */
meta?: ReactNode;
/** 右端 chevron。 */
chevron?: ReactNode;
}) {
return (
<span className="agent-process-summary" data-agent-process-summary="">
{icon ? (
<span className="agent-process-summary-icon" aria-hidden="true">
{icon}
</span>
) : null}
<span className="agent-process-summary-preview">
{preview}
{meta ? (
<span className="agent-process-summary-meta">{meta}</span>
) : null}
</span>
{chevron ? (
<span className="agent-process-summary-chevron" aria-hidden="true">
{chevron}
</span>
) : null}
</span>
);
}
+1
View File
@@ -220,6 +220,7 @@ export { Textarea } from './ui/textarea';
// existing application adapters while keeping the public API product-neutral.
export type { AgentMessageTone } from './AgentMessageContent';
export { AgentMessageContent } from './AgentMessageContent';
export { AgentProcessSummary } from './AgentProcessSummary';
export { CanvasCardCornerActions } from './CanvasCardCornerActions';
export { OverflowActions } from './OverflowActions';
export type {
@@ -0,0 +1,26 @@
/**
* 毫秒耗时的统一显示:5.2秒 / 2分05.2秒 / 1时02分05.2秒。
* 保留十分之一秒,先整体舍入再拆单位,避免 60.0 秒或 60 分溢出。
* 非法或未知耗时返回 null,不能以 0 冒充已测得结果。
*/
export function formatElapsedDuration(
milliseconds: number | null | undefined,
): string | null {
if (
milliseconds == null ||
!Number.isFinite(milliseconds) ||
milliseconds < 0
) {
return null;
}
const tenths = Math.round(milliseconds / 100);
const hours = Math.floor(tenths / 36_000);
const minutes = Math.floor((tenths % 36_000) / 600);
const seconds = ((tenths % 600) / 10).toFixed(1);
if (hours > 0) {
return `${hours}${String(minutes).padStart(2, '0')}${seconds.padStart(4, '0')}`;
}
return minutes > 0
? `${minutes}${seconds.padStart(4, '0')}`
: `${seconds}`;
}
+1
View File
@@ -1 +1,2 @@
export { formatElapsedDuration } from './formatElapsedDuration';
export { cn } from './utils';