feat: add evidence audit panel shell
This commit is contained in:
@@ -149,6 +149,12 @@ button:disabled { cursor: default; opacity: .45; }
|
||||
.message-markdown > *:first-child { margin-top: 0; }
|
||||
.message-markdown > *:last-child { margin-bottom: 0; }
|
||||
.claim-boundary-badge { width: fit-content; margin: 0 0 var(--space-2); padding: 4px 9px; border: 1px solid color-mix(in srgb, var(--color-action) 24%, transparent); border-radius: 999px; background: var(--color-action-soft); color: var(--color-action-hover); font-size: 12px; line-height: 1.45; }
|
||||
.evidence-audit-panel { margin: 0 0 var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: color-mix(in srgb, var(--color-canvas-soft) 68%, transparent); }
|
||||
.evidence-audit-panel summary { cursor: pointer; padding: var(--space-2) var(--space-3); color: var(--color-ink-secondary); font-size: 13px; }
|
||||
.evidence-audit-table { display: grid; gap: 1px; padding: 0 var(--space-3) var(--space-3); }
|
||||
.evidence-audit-row { display: grid; grid-template-columns: minmax(120px, 1fr) auto minmax(160px, 1.2fr); gap: var(--space-3); align-items: center; padding: var(--space-2) 0; border-top: 1px solid color-mix(in srgb, var(--color-border) 64%, transparent); font-size: 12px; }
|
||||
.evidence-audit-row b { color: var(--color-action-hover); font-weight: 600; }
|
||||
.evidence-audit-row small { color: var(--color-ink-tertiary); line-height: 1.45; }
|
||||
.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; }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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";
|
||||
|
||||
export function AgentAvatar() {
|
||||
@@ -24,7 +25,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} /><ChatMessageContent text={message.text} /></>
|
||||
: <><ClaimBoundaryBadge status={message.techniqueTruth} /><EvidenceAuditPanel claimStatus={message.techniqueTruth} /><ChatMessageContent text={message.text} /></>
|
||||
) : <p>{message.text}</p>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
type AuditRow = {
|
||||
label: string;
|
||||
status: "required" | "partial" | "blocked";
|
||||
boundary: string;
|
||||
};
|
||||
|
||||
const defaultAuditRows: AuditRow[] = [
|
||||
{ label: "D1 / Natal", status: "required", boundary: "基础命盘层" },
|
||||
{ label: "Varga: D9 / D10 / D2 / D11", status: "required", boundary: "按问题域调用,不混用" },
|
||||
{ label: "Vimshottari Dasha", status: "required", boundary: "阶段证据之一" },
|
||||
{ label: "Narayana Dasha", status: "required", boundary: "应期/校时必须交叉" },
|
||||
{ label: "Transit / Gochara", status: "partial", boundary: "精确日/月仍需 holdout" },
|
||||
{ label: "Shadbala / Ashtakavarga", status: "partial", boundary: "组件 parity 未全闭环" },
|
||||
{ label: "Functional Benefic/Malefic", status: "required", boundary: "不能只看自然吉凶" },
|
||||
{ label: "MEVG / Real Case Calibration", status: "blocked", boundary: "外部校准不足时降级" },
|
||||
];
|
||||
|
||||
export function EvidenceAuditPanel({ claimStatus }: { readonly claimStatus?: string }) {
|
||||
return (
|
||||
<details className="evidence-audit-panel">
|
||||
<summary>证据链摘要 · {claimStatus || "unknown"}</summary>
|
||||
<div className="evidence-audit-table" role="table" aria-label="Technique Audit Table">
|
||||
{defaultAuditRows.map((row) => (
|
||||
<div className="evidence-audit-row" role="row" key={row.label}>
|
||||
<span role="cell">{row.label}</span>
|
||||
<b role="cell" data-status={row.status}>{row.status}</b>
|
||||
<small role="cell">{row.boundary}</small>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</details>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import test from "node:test";
|
||||
|
||||
const rowSource = readFileSync(new URL("../src/components/chat-message-row.tsx", import.meta.url), "utf8");
|
||||
const panelSource = readFileSync(new URL("../src/components/evidence-audit-panel.tsx", import.meta.url), "utf8");
|
||||
const globalStyles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8");
|
||||
|
||||
test("assistant messages include a collapsible Technique Audit Table shell", () => {
|
||||
assert.match(rowSource, /EvidenceAuditPanel/);
|
||||
assert.match(rowSource, /claimStatus=\{message\.techniqueTruth\}/);
|
||||
assert.match(panelSource, /Technique Audit Table/);
|
||||
assert.match(panelSource, /D1 \/ Natal/);
|
||||
assert.match(panelSource, /Narayana Dasha/);
|
||||
assert.match(panelSource, /Functional Benefic\/Malefic/);
|
||||
assert.match(panelSource, /MEVG \/ Real Case Calibration/);
|
||||
assert.match(panelSource, /组件 parity 未全闭环/);
|
||||
assert.match(globalStyles, /\.evidence-audit-panel/);
|
||||
});
|
||||
Reference in New Issue
Block a user