feat(trajectory): implement trajectory step cell and layout with bilingual support

- Added TrajectoryCell, TrajectoryGroupHeader, and TrajectoryTurn components for rendering trajectory steps and groups.
- Introduced bilingual support with English and Chinese translations for trajectory notes.
- Updated conversation session models to include timestamps for various message types.
- Enhanced layout logic to handle expanded assistant blocks and tool results with duration metrics.
- Added CSS styles for new components to ensure proper display and alignment.
This commit is contained in:
07akioni
2026-07-24 13:30:19 +08:00
parent 65d29da8a1
commit 3babb2cd42
29 changed files with 1190 additions and 70 deletions

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-07-23-trajectory-step-cell.md: edf31dcc72baa980caf0aa90fb8d5ec53d44346d
2026-07-23-trajectory-step-cell.zh.md: dbe813d48c3f3ac1c0926e45137624d758109c55

View File

@@ -0,0 +1,35 @@
# Agent Note: Trajectory step cell and turn list chrome
Status: implemented
English | [中文](2026-07-23-trajectory-step-cell.zh.md)
## Problem
The trajectory tab needs a reusable step row and turn-list chrome that can show expanded assistant blocks, own-duration times, Message token columns, and in-flight work. Without folding session event times into conversation nodes and expanding blocks into cells, the UI cannot match the product chrome.
## Decision
[`@deepseek-ai/dsh-client-ui-trajectory`](../../../../packages/client/ui-trajectory/README.md) owns the presentational trajectory list chrome:
- [`TrajectoryCell`](../../../../packages/client/ui-trajectory/src/client/TrajectoryCell.tsx) — 38px step row with kinds User / Message / Tool (no Think, Call, or Result rows). Reasoning blocks are skipped (no block-level clock). Each `tool-call` + paired `tool-result` folds into one Tool row (`name ·` truncated args) whose Time is `result.time callTime` when both are known. Message rows carry Input/Output/Think token columns from `assistant.usage`. Own-duration Time uses `+Ns` / `+N.1s`, or `—` when absent. Selected state draws a 2px inset `--dsw-alias-brand-primary-new-colorprimary-new-color` ring (`selected` prop) and is not wired to chat selection.
- [`TrajectoryTurn`](../../../../packages/client/ui-trajectory/src/client/TrajectoryTurn.tsx) / header / group header — sticky Turn bar paints full-bleed `ghost-active-fill`; title/columns and the Message/Step body sit in a centered `max-width: 880px` lane. Cell trailing columns share the Turn header geometry (`320 = 4×71 + 3×12`); cells use pad 20/8.
- [`deriveTrajectoryLayout`](../../../../packages/client/ui-trajectory/src/client/layout.ts) expands assistant `blocks[]` into cells, pairs tool-calls with `tool-result` by `callId` into Tool, folds `partial` and `runningCalls` (deduped), hangs usage on Message only, and builds group descriptions as wall-span + tool histogram (`1.5s bash×6`).
[`ConversationNode`](../../../../packages/client/runtime/src/client/sessions/conversation.ts) carries `time` from `SessionEvent.time`; `ToolResultNode.callTime` and `RunningToolCall.time` come from the paired `tool/call`. Duration rules: User `+0s`; Message = assistant.time previous surface time; Tool = result.time callTime when both known; in-flight Tool = `—`. Group header duration is earliest→latest absolute time in the group (wall span; Tool contributes start and start+duration).
## Alternatives considered
**Keep a Think cell for reasoning blocks.** Rejected: a single `assistant/message.time` cannot yield Think own-duration without chunk-level clocks; omit the row rather than show `—`.
**Keep separate Call and Result rows.** Rejected: Result had no own duration to show; one Tool row carries the call→result interval.
**Cumulative elapsed from session/turn start.** Rejected; the Time column is each row's own duration.
**Hang usage on the first expanded row.** Rejected; usage attaches to Message only.
**Show in-flight tool durations via Date.now().** Deferred; in-flight Time stays `—`.
## Consequences
The Trajectory tab can render expanded finalized and in-flight rows with own-duration times once fold emits `time`. Behavior-shaped coverage lives in `packages/client/ui-trajectory/tests/{cell,layout,views}.spec.tsx`. Chat selection deep-links and finer block-level clocks remain deferred.

View File

@@ -0,0 +1,35 @@
# Agent Note: Trajectory 步骤单元格与轮次列表 chrome
Status: implemented
[English](2026-07-23-trajectory-step-cell.md) | 中文
## Problem
trajectory 标签页需要可复用的步骤行与轮次列表 chrome以展示展开后的 assistant 块、自身耗时、Message token 列以及进行中的工作。若不将会话事件时间折叠进会话节点并将块展开为单元格UI 就无法对齐产品 chrome。
## Decision
[`@deepseek-ai/dsh-client-ui-trajectory`](../../../../packages/client/ui-trajectory/README.md) 拥有展示型 trajectory 列表 chrome
- [`TrajectoryCell`](../../../../packages/client/ui-trajectory/src/client/TrajectoryCell.tsx) — 高 38px 的步骤行,类型为 User / Message / Tool无 Think、Call、Result 行。reasoning 块跳过(无块级时钟)。每对 `tool-call` + `tool-result` 折成一行 Tool`name ·` 加截断参数Time 在两端皆知时为 `result.time callTime`。Message 行携带来自 `assistant.usage` 的 Input/Output/Think token 列。自身耗时 Time 使用 `+Ns` / `+N.1s`,缺失时为 `—`。选中态绘制 2px 内嵌的 `--dsw-alias-brand-primary-new-colorprimary-new-color` 环(`selected` prop且未接线到 chat 选中。
- [`TrajectoryTurn`](../../../../packages/client/ui-trajectory/src/client/TrajectoryTurn.tsx) / header / group header — 粘性 Turn 条背景通栏铺 `ghost-active-fill`;标题/列标与 Message/Step 主体落在居中的 `max-width: 880px` 内容道。单元格右侧列与 Turn 标头共用几何(`320 = 4×71 + 3×12`cell pad 20/8。
- [`deriveTrajectoryLayout`](../../../../packages/client/ui-trajectory/src/client/layout.ts) 将 assistant `blocks[]` 展开为单元格,按 `callId` 将 tool-call 与 tool-result 配对为 Tool折叠 `partial``runningCalls`(去重),仅将用量挂在 Message 上,并以墙钟跨度 + 工具直方图构建分组描述(`1.5s bash×6`)。
[`ConversationNode`](../../../../packages/client/runtime/src/client/sessions/conversation.ts) 携带来自 `SessionEvent.time``time``ToolResultNode.callTime``RunningToolCall.time` 来自配对的 `tool/call`。耗时规则User 为 `+0s`Message = assistant.time 上一表面时间Tool = 在两者皆知时 result.time callTime进行中 Tool = `—`。分组标头耗时为组内最早→最晚绝对时间墙钟跨度Tool 贡献起点与起点+自身耗时)。
## Alternatives considered
**为 reasoning 块保留 Think 单元格。** 否决:单条 `assistant/message.time` 无法给出 Think 自身耗时(除非上 chunk 级时钟);与其显示 `—`,不如省略该行。
**保留分开的 Call 与 Result 行。** 否决Result 没有可展示的自身耗时;一行 Tool 承载 call→result 区间。
**自会话/轮次起点累计耗时。** 否决Time 列是每行自身的耗时。
**将用量挂在展开后的第一行。** 否决;用量仅附着于 Message。
**用 Date.now() 显示进行中工具的耗时。** 延后;进行中的 Time 保持为 `—`
## Consequences
一旦 fold 发出 `time`Trajectory 标签页即可渲染带自身耗时的已定稿与进行中展开行。行为导向的覆盖位于 `packages/client/ui-trajectory/tests/{cell,layout,views}.spec.tsx`。chat 选中深链与更细的块级时钟仍延后。