fix(consult): fold the technique audit out of the spoken answer
Independent Staging Quality Gate / validate (push) Has been cancelled
Independent Staging Quality Gate / publish (push) Has been cancelled

Keep comparative tables in chat, but hide the long audit behind a collapsed control so the reply stays readable.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-08-19 15:29:04 +08:00
co-authored by Cursor
parent 7c7a25773c
commit e635c40224
18 changed files with 590 additions and 28 deletions
@@ -14,6 +14,8 @@ const markdownComponents: Components = {
<table>{children}</table>
</div>
),
ul: ({ children }) => <ul className="markdown-list">{children}</ul>,
ol: ({ children }) => <ol className="markdown-list">{children}</ol>,
};
export function renderChatMarkdown(text: string) {
@@ -4,6 +4,12 @@ import { useEffect, useState, type ReactNode } from "react";
import { prefetchOnIdle } from "@/components/chat-chunk-prefetch";
import { plainParagraphs } from "@/components/chat-message-paragraphs";
import { TechniqueAuditDisclosure } from "@/components/technique-audit-disclosure";
import type { TechniqueAuditRow } from "@/lib/consultation-agent-events";
import {
resolveTechniqueAuditRows,
splitSpokenAnswerAndTechniqueAudit,
} from "@/lib/consultation-technique-audit";
type MarkdownRenderer = (text: string) => ReactNode;
@@ -35,16 +41,30 @@ function useMarkdownRenderer() {
return renderer;
}
export function ChatMessageContent({ text }: { text: string }) {
export function ChatMessageContent({
text,
auditRows,
}: {
text: string;
auditRows?: readonly TechniqueAuditRow[];
}) {
const renderMarkdown = useMarkdownRenderer();
const split = splitSpokenAnswerAndTechniqueAudit(text);
const rows = resolveTechniqueAuditRows(split, auditRows);
const spoken = split.spoken;
return (
<div className="message-markdown">
{renderMarkdown
? renderMarkdown(text)
: (plainParagraphs(text) ?? []).map((paragraph, index) => (
<p key={index}>{paragraph}</p>
))}
<div className="message-answer">
{spoken ? (
<div className="message-markdown">
{renderMarkdown
? renderMarkdown(spoken)
: (plainParagraphs(spoken) ?? []).map((paragraph, index) => (
<p key={index}>{paragraph}</p>
))}
</div>
) : null}
{rows.length > 0 ? <TechniqueAuditDisclosure rows={rows} /> : null}
</div>
);
}
+6 -1
View File
@@ -97,7 +97,12 @@ export function ChatMessageRow({
{showActivity && (
<AgentActivityStatus state={activityState} label={activityLabel} />
)}
{message.text && <ChatMessageContent text={message.text} />}
{message.text && (
<ChatMessageContent
text={message.text}
auditRows={message.agentExecutionReceipt?.techniqueAuditTable}
/>
)}
</>
) : <p>{message.text}</p>}
</div>
@@ -0,0 +1,45 @@
"use client";
import {
displayTechniqueAuditLabel,
displayTechniqueAuditNote,
techniqueAuditStatusLabel,
} from "@/lib/consultation-technique-audit";
import type { TechniqueAuditRow } from "@/lib/consultation-agent-events";
export function TechniqueAuditDisclosure({
rows,
}: {
readonly rows: readonly TechniqueAuditRow[];
}) {
if (rows.length === 0) return null;
return (
<details className="technique-audit">
<summary>
本轮技法
<small>{rows.length} 项</small>
</summary>
<div className="technique-audit-panel">
<table>
<caption className="sr-only">本轮技法审计</caption>
<thead>
<tr>
<th scope="col">技法</th>
<th scope="col">状态</th>
<th scope="col">说明</th>
</tr>
</thead>
<tbody>
{rows.map((row, index) => (
<tr key={`${row.technique}-${index}`}>
<th scope="row">{displayTechniqueAuditLabel(row.technique)}</th>
<td data-status={row.status}>{techniqueAuditStatusLabel(row.status)}</td>
<td>{displayTechniqueAuditNote(row.note)}</td>
</tr>
))}
</tbody>
</table>
</div>
</details>
);
}