feat: add markdown consultation report download
This commit is contained in:
@@ -158,6 +158,7 @@ button:disabled { cursor: default; opacity: .45; }
|
||||
.raw-evidence-receipt { margin: 0 var(--space-3) var(--space-3); border-top: 1px solid color-mix(in srgb, var(--color-border) 64%, transparent); padding-top: var(--space-2); }
|
||||
.raw-evidence-receipt summary { padding: var(--space-1) 0; color: var(--color-ink-tertiary); font-size: 12px; }
|
||||
.raw-evidence-receipt pre { max-height: 180px; overflow: auto; margin: var(--space-2) 0 0; padding: var(--space-3); border-radius: var(--radius-md); background: var(--color-canvas); color: var(--color-ink-secondary); font-size: 11px; line-height: 1.5; }
|
||||
.report-download-button { margin-top: var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-canvas-soft); color: var(--color-ink-secondary); cursor: pointer; padding: 7px 10px; font-size: 12px; }
|
||||
.message-markdown p { margin: 0 0 14px; }
|
||||
.message-markdown ul, .message-markdown ol { margin: 10px 0 16px; padding-left: 24px; }
|
||||
.message-markdown li { margin: 6px 0; }
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ChatMessageContent } from "@/components/chat-message-content";
|
||||
import { ClaimBoundaryBadge } from "@/components/claim-boundary-badge";
|
||||
import { EvidenceAuditPanel } from "@/components/evidence-audit-panel";
|
||||
import type { ChatMessageView } from "@/lib/chat-message-view";
|
||||
import { consultationReportMarkdown, downloadMarkdownReport } from "@/lib/consultation-report-export";
|
||||
|
||||
export function AgentAvatar() {
|
||||
return <span className="agent-avatar" aria-hidden="true" />;
|
||||
@@ -25,7 +26,7 @@ export function ChatMessageRow({ message }: { readonly message: ChatMessageView
|
||||
{message.role === "assistant" ? (
|
||||
message.state === "thinking"
|
||||
? <div className="thinking"><i /><i /><i /></div>
|
||||
: <><ClaimBoundaryBadge status={message.techniqueTruth} /><EvidenceAuditPanel claimStatus={message.techniqueTruth} workflowReceipt={message.workflowReceipt} /><ChatMessageContent text={message.text} /></>
|
||||
: <><ClaimBoundaryBadge status={message.techniqueTruth} /><EvidenceAuditPanel claimStatus={message.techniqueTruth} workflowReceipt={message.workflowReceipt} /><ChatMessageContent text={message.text} /><button className="report-download-button" type="button" onClick={() => downloadMarkdownReport("Jyotisha 咨询报告", consultationReportMarkdown({ title: "Jyotisha 咨询报告", messages: [message] }))}>下载本次报告</button></>
|
||||
) : <p>{message.text}</p>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,3 +23,15 @@ export function consultationReportMarkdown(input: {
|
||||
"本报告保留证据边界;未闭环内容不得包装成确定预测。",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
export function downloadMarkdownReport(title: string, markdown: string) {
|
||||
const blob = new Blob([markdown], { type: "text/markdown;charset=utf-8" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const anchor = document.createElement("a");
|
||||
anchor.href = url;
|
||||
anchor.download = `${title.replace(/[\\/:*?"<>|]+/g, "-")}.md`;
|
||||
document.body.append(anchor);
|
||||
anchor.click();
|
||||
anchor.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import test from "node:test";
|
||||
import { consultationReportMarkdown } from "../src/lib/consultation-report-export.ts";
|
||||
import { consultationReportMarkdown, downloadMarkdownReport } from "../src/lib/consultation-report-export.ts";
|
||||
const rowSource = readFileSync(new URL("../src/components/chat-message-row.tsx", import.meta.url), "utf8");
|
||||
const globalStyles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8");
|
||||
|
||||
test("exports latest consultation answer with workflow receipt and claim boundary", () => {
|
||||
const report = consultationReportMarkdown({
|
||||
@@ -28,3 +31,10 @@ test("exports latest consultation answer with workflow receipt and claim boundar
|
||||
assert.match(report, /missing_layers: MEVG/);
|
||||
assert.match(report, /未闭环内容不得包装成确定预测/);
|
||||
});
|
||||
|
||||
test("assistant answer exposes a markdown report download button", () => {
|
||||
assert.equal(typeof downloadMarkdownReport, "function");
|
||||
assert.match(rowSource, /下载本次报告/);
|
||||
assert.match(rowSource, /downloadMarkdownReport/);
|
||||
assert.match(globalStyles, /\.report-download-button/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user