Files
pikasTech-HWLAB/web/hwlab-cloud-web/src/components/conversation/MessagePendingContext.tsx
T
Lyon 527c5b0dd5 fix(web): v0.2 round-7 workbench: message pending/session-continuity/runtime-path context panels (HWLAB #775 round 4) (#785)
Continues HWLAB #775 round 4 by restoring the three message-level
context panels that the React migration dropped:
- `messagePendingContextPanel` (`app-conv.ts:1228`)
- `messageSessionContinuityPanel` (`app-conv.ts:1183`)
- `messageRuntimePathPanel` (`app-conv.ts:1268`)

## 修复对照

| 类别 | 迁移前 (`893f46bc`) | 迁移后 (round 4) | 状态 |
|---|---|---|---|
| **Pending context 面板** | `app-conv.ts:1228` `messagePendingContextPanel`(traceId/conversation/session/thread/activityTimeout) | `components/conversation/MessagePendingContext.tsx`:grid 2 列 + activity timeout 用 `formatDuration` |  对齐 |
| **Session continuity 面板** | `app-conv.ts:1183` `messageSessionContinuityPanel`(conversation/session/thread/lifecycle/retryOf/missing/changed) | `components/conversation/MessageSessionContinuity.tsx`:grid 2 列 + lifecycleHint/lastEvent |  对齐 |
| **Runtime path 面板** | `app-conv.ts:1268` `messageRuntimePathPanel`(rows + missingFields + compact details) | `components/conversation/MessageRuntimePath.tsx`:复用 round-1 `codeAgentRuntimePathFromMessage`,加 missingFields 行 |  对齐 |

## 改动

- 新增 `web/hwlab-cloud-web/src/components/conversation/MessagePendingContext.tsx`(43 行)
- 新增 `web/hwlab-cloud-web/src/components/conversation/MessageRuntimePath.tsx`(37 行)
- 新增 `web/hwlab-cloud-web/src/components/conversation/MessageSessionContinuity.tsx`(58 行)
- `web/hwlab-cloud-web/src/components/conversation/ConversationPanel.tsx` — `MessageCard` 顺序:title → body → pending/continuity/runtime → footer → trace → actions
- `web/hwlab-cloud-web/src/App.tsx` — `<ConversationPanel>` 接 `codeAgentTimeoutMs`
- `web/hwlab-cloud-web/src/styles/workbench.css` — `.message-pending-context` / `.message-session-context` / `.message-runtime-path` 共享 grid 样式

## 验收

- `bun run check` 通过(Vite build + 2/2 dist freshness test pass)
- `bun run scripts/tsc-check.ts` 通过(strict React TSX, 0 explicit any)
- `bun test` 2 pass / 0 fail
- `bun run build` 9 dist files verified fresh;`app.js` 221256 → 226176 bytes;`index.css` 16041 → 17152 bytes
- dist `app.js` 含 `message-pending-context` / `message-session-context` / `message-runtime-path` / `message-pending-grid` / `message-session-grid` / `message-runtime-grid`
- 所有改动文件 < 400 行前端 guard

Refs: pikasTech/HWLAB#775 (round 4 of 7)

Co-authored-by: HWLAB <ci@hwlab.local>
2026-06-03 23:26:09 +08:00

44 lines
2.4 KiB
TypeScript

import type { ReactElement } from "react";
import type { ChatMessage } from "../../types/domain";
import { formatBeijingTime, formatDuration, shortToken, toneClass } from "../../utils";
interface MessagePendingContextProps {
message: ChatMessage;
codeAgentTimeoutMs: number;
}
export function MessagePendingContext({ message, codeAgentTimeoutMs }: MessagePendingContextProps): ReactElement | null {
if (message.status !== "running") return null;
const fields: Array<[string, string | null | undefined, boolean]> = [
["状态", "处理中", false],
["traceId", typeof message.traceId === "string" ? message.traceId : null, true],
["conversation", typeof message.conversationId === "string" ? message.conversationId : null, true],
["session", message.sessionId ?? message.runnerTrace?.sessionId ?? "等待后端分配", true],
["thread", message.threadId ?? message.runnerTrace?.threadId ?? "等待后端分配", false],
["sessionStatus", typeof message.runnerTrace?.sessionStatus === "string" ? message.runnerTrace.sessionStatus : null, false],
["lifecycle", typeof message.runnerTrace?.sessionLifecycleStatus === "string" ? message.runnerTrace.sessionLifecycleStatus : null, false],
["activityTimeout", `${formatDuration(codeAgentTimeoutMs)} 无新事件`, false]
];
return (
<section className="message-pending-context tone-border-pending" data-message-pending-context>
<div className="message-pending-head">
<span className={`state-tag tone-${toneClass("pending")}`} data-pending-tag>处理中</span>
<span className="message-pending-summary">{message.runnerTrace?.waitingFor ? `等待 ${message.runnerTrace.waitingFor}` : "等待后端事件"}</span>
</div>
<div className="message-pending-grid" role="list">
{fields.map(([label, value, copyable]) => {
if (value === undefined || value === null || value === "") return null;
return (
<div key={label} className="message-pending-row" role="listitem">
<span className="message-pending-key">{label}</span>
<span className="message-pending-value mono">{copyable ? shortToken(value, 32) : value}</span>
<span className="message-pending-time">{formatBeijingTime(message.updatedAt ?? message.createdAt, { withSeconds: true })}</span>
</div>
);
})}
</div>
</section>
);
}