优化: ai-chat 对话流布局与轨迹信息密度改造

- 分组行去统计化,调用统计归并轨迹卡头部
- 相对时间改时间分隔线,跨日显绝对时间
- 思考过程单行 pill 化,答案卡加 accent 边
- 工具卡短路径展示与耗时提示统一
- 高风险工具名单启动时同步后端注册表
- 自动重试改琥珀细条,红卡与手动重试仅终态
- 轨迹卡头并时间线行,显轮次号与绝对时间
- 运行中摘要补当前工具名与失败计数
- 执行中工具卡加实时计时,耗时统一人话口径
- 重试残留清理改标记匹配,替代中文子串硬编码
This commit is contained in:
lxy
2026-10-01 19:35:20 +08:00
parent 0560137840
commit 66c6553cdf
14 changed files with 562 additions and 209 deletions
+35
View File
@@ -76,6 +76,41 @@ export function formatRelative(v: string | number | null | undefined, opts?: For
return t('common.dayAgo', { n: diffD })
}
/** 消息间隔分隔线用绝对时间:当天只 HH:mm,跨天 MM-DD HH:mm;无效返回 '—'(格式定宽不随 locale) */
export function formatAbsolute(v: string | number | null | undefined): string {
const ms = parseTs(v)
if (ms == null) return '—'
const d = new Date(ms)
const now = new Date()
const sameDay = d.getFullYear() === now.getFullYear()
&& d.getMonth() === now.getMonth()
&& d.getDate() === now.getDate()
const hm = `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`
if (sameDay) return hm
return `${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} ${hm}`
}
/**
* 时间跨度格式化。precise=true(单卡耗时)亚秒显毫秒;false(轨迹总耗时)亚秒归 <1s。
* 超时跨度进位到分/时。负值/非有限值返回空串(调用方隐藏该段)。
*/
export function formatDuration(ms: number | undefined | null, precise = false): string {
if (ms == null || !Number.isFinite(ms) || ms < 0) return ''
if (ms < 1000) return precise ? `${Math.round(ms)}ms` : '<1s'
const sec = ms / 1000
if (sec < 60) return `${sec.toFixed(sec < 10 ? 1 : 0)}s`
let min = Math.floor(sec / 60)
let remainSec = Math.round(sec % 60)
// 秒四舍五入进位(sec∈[119.5,120) 得 60s):并入分,避免显「1m 60s」
if (remainSec === 60) {
min++
remainSec = 0
}
if (min < 60) return `${min}m ${remainSec}s`
const hour = Math.floor(min / 60)
return `${hour}h ${min % 60}m`
}
// ── 审批等待时长(ToolCard / ApprovalPopup 双写收敛) ──
export type WaitLevel = 'normal' | 'long' | 'urgent'