统一思考与执行过程折叠样式及中文耗时
思考展开复用安全Markdown渲染,折叠显示浅色单行纯文本预览与右侧箭头。 共享过程摘要组件,执行过程按真实操作数显示统计并保留各组独立计时。 工具、整轮、生成任务和策划耗时统一为中文时分秒格式。 补齐格式与Markdown回归、更新已有界面断言并验证窄屏键盘操作。
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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 +1,2 @@
|
||||
export { formatElapsedDuration } from './formatElapsedDuration';
|
||||
export { cn } from './utils';
|
||||
|
||||
Reference in New Issue
Block a user